Posted By : Ashish
The capacity to transfer assets across networks effortlessly is more important than ever in the ever-changing world of blockchain development. Envision a bridge that unites the Ethereum and Solana worlds, letting tokens move freely while upholding security and openness. In this project, we use the robust programming languages Solidity and Rust to set out on the task of creating a cross-chain bridge. Through utilizing Rust's efficiency for Solana's on-chain logic and Solidity's capabilities for Ethereum smart contracts, our goal is to provide a solid framework that makes token exchanges simple. Whether you're a crypto enthusiast excited to explore new possibilities or a developer trying to improve interoperability, this guide will take you through all the necessary steps to realize this ambitious aim. Now let's dive in.
Programming Languages:
Solidity, Javascript/Typescript, and Rust
IDE:
VS-Code and any preferred IDE
Libraries:
ETH: Hardhat, Ethers, Axios, Openzepplin
SOL: Solana Web3.js, Solana Spl-token
Also, Explore | How To Build "Buy Me a Coffee" DeFi dApp Using Solidity
It's preferred that you setup 3 projects, separately for:
You'll need to setup node.js, solidity and hardhat in your IDE/ system. So, we'll begin with setting up hardhat code, for example "click-here". Here's the code for the ERC-20 Token using Openzepplin's library.
code..................................................................................................
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract Testtoken is ERC20, Ownable {
event BurnAndMoveToSolana(address indexed user, string solanaAddress, uint256 amount);
event MintFromSolana(address indexed user, uint256 amount);
address public relayer;
constructor() ERC20("EthereumToken", "ETHR") Ownable(msg.sender){
_mint(msg.sender, 1000000 * (10 ** decimals())); // change amount as per your understanding
}
modifier onlyRelayer() {
require(msg.sender == relayer, "Not authorized");
_;
}
function setRelayer(address _relayer) external onlyOwner {
relayer = _relayer;
}
function burnAndMoveToSolana(uint256 amount, string memory solanaAddress) external { // main transfering function
_burn(msg.sender, amount);
emit BurnAndMoveToSolana(msg.sender, solanaAddress, amount);
}
function mintFromSolana(address to, uint256 amount) external onlyRelayer {
_mint(to, amount);
emit MintFromSolana(to, amount);
}
event TokensBurned(address indexed from, address indexed solanaAddress, uint256 amount);
}
You may also like | Building a Decentralized Voting System with Solidity and Hardhat
You'll need to setup node.js, Solana, and Rust in your IDE/ system. To begin with, we'll set-up a empty solana-sdk code. Here's the full code/implementation for the SPL Token using Solana-web3.js & Solana spl-token.
code.................................................................................................
const {
Connection,
Keypair,
PublicKey,
clusterApiUrl,
LAMPORTS_PER_SOL
} = require('@solana/web3.js');
const {
createMint,
getOrCreateAssociatedTokenAccount,
mintTo,
getAccount,
burn
} = require('@solana/spl-token');
async function mintAndBurnTokens(connection, fromWallet, tokenAccount, mint, amountToMint, amountToBurn, ethAddress) {
await mintTo(
connection,
fromWallet,
mint,
tokenAccount.address,
fromWallet.publicKey,
amountToMint
);
console.log(`Minted ${amountToMint / (10 ** 9)} tokens to your associated token account.`);
const tokenAccountBalance = await getAccount(connection, tokenAccount.address);
console.log(`Token account balance after minting: ${Number(tokenAccountBalance.amount) / (10 ** 9)} tokens`);
if (Number(tokenAccountBalance.amount) < amountToBurn) {
console.log(`Insufficient funds. Current balance: ${Number(tokenAccountBalance.amount) / (10 ** 9)} tokens.`);
return;
}
await burn(
connection,
fromWallet,
tokenAccount.address,
mint,
fromWallet.publicKey,
amountToBurn
);
console.log(`Burned ${amountToBurn / (10 ** 9)} tokens from ${fromWallet.publicKey} and moving to Ethereum wallet ${ethAddress}.`);
console.log(`Relaying burn event to Ethereum relayer for Ethereum wallet: ${ethAddress}, amount: ${amountToBurn / (10 ** 9)}.`);
}
(async () => {
const fromWallet = Keypair.fromSecretKey(new Uint8Array([your,secret,keypair]));
const ethAddress = "0xyourAddress"; //add your eth wallet address
const mintAmount = 100000 * 10 ** 9; // amount of SPL tokens to mint
const burnAmount = 1000 * 10 ** 9; // amount of SPL tokens to burn/transfer
const connection = new Connection(clusterApiUrl('devnet'), 'confirmed'); // put your preferred cluster
console.log('Creating SPL token...');
const mint = await createMint(
connection,
fromWallet,
fromWallet.publicKey,
null,
9
);
const fromTokenAccount = await getOrCreateAssociatedTokenAccount(
connection,
fromWallet,
mint,
fromWallet.publicKey
);
console.log('Minting tokens...');
await mintAndBurnTokens(connection, fromWallet, fromTokenAccount, mint, mintAmount, burnAmount, ethAddress);
console.log(`View token account on Solana Explorer: https://explorer.solana.com/address/${fromTokenAccount.address}?cluster=devnet`);
})();
////////////////////////////////////////////////////////////////////////
Also, Read | How To Create a Daily Game Reward System in Solidity
In order to facilitate safe and transparent token transfers between two blockchains, a relayer-bridge project serves as an essential bridge. Using smart contracts and event listeners, the relayer in the Ethereum and Solana context watches on particular occurrences on one blockchain, like an Ethereum token burn. When the relayer notices one of these events, it sends the required information"”such as the recipient's address and the quantity of tokens"”to the other chain so that the corresponding action"”like minting the tokens on Solana"”can take place there. In order to preserve network balance, this bi-directional communication makes sure that tokens burned on one chain are minted on the other. In order to smoothly connect the two ecosystems, the relayer's job is to validate and relay these transactions without sacrificing security or speed.
Here's the Code for the Relayer-Bridge :
code..................................................................................................
const WebSocket = require("ws");
const { ethers } = require("ethers");
const fs = require("fs");
require('dotenv').config();
const wsUrl = "wss://api.devnet.solana.com"; //your desired network
const connection = new WebSocket(wsUrl);
const provider = new ethers.WebSocketProvider(process.env.ETH_WSS_URL);
const wallet = new ethers.Wallet(process.env.ETH_PRIVATE_KEY, provider);
const contractAddress = process.env.ETH_CONTRACT_ADDRESS;
const abi = JSON.parse(fs.readFileSync("./path_to_your/eth_contract_abi.json"));
const contract = new ethers.Contract(contractAddress, abi, wallet);
connection.on("open", () => {
console.log("Connected to Solana WebSocket");
const subscriptionMessage = JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "logsSubscribe",
params: [
{
mentions: [""], // Your SPL token address
},
{
commitment: "finalized",
},
],
});
connection.send(subscriptionMessage);
});
connection.on("message", async (data) => {
const response = JSON.parse(data);
if (response.method === "logsNotification") {
const logData = response.params.result;
// Check if the log indicates a burn
if (isBurnLog(logData)) {
const amountBurned = extractBurnAmount(logData);
console.log(`Burn detected: ${amountBurned} tokens`);
await mintTokens(amountBurned);
}
} else {
console.log("Received message:", response);
}
});
connection.on("close", () => {
console.log("Connection closed");
});
connection.on("error", (error) => {
console.error("WebSocket error:", error);
});
// Function to Check the log data structure to confirm it's a burn event
function isBurnLog(logData) {
return logData && logData.err === null && logData.logs && logData.logs.some(log => log.includes("burn"));
}
// Function to extract the amount burned from the log data
function extractBurnAmount(logData) {
const amountLog = logData.logs.find(log => log.includes("burn"));
if (amountLog) {
const amount = /* logic to parse your burn amount format */;
return parseFloat(amount); // Return the amount as a number
}
return 0;
}
// Function to mint tokens on Ethereum
async function mintTokens(amount) {
try {
const tx = await contract.mint(wallet.address, ethers.utils.parseUnits(amount.toString(), 18));
console.log(`Mint transaction sent: ${tx.hash}`);
await tx.wait();
console.log("Minting successful");
} catch (error) {
console.error("Minting failed:", error);
}
}
/////////////////////////////////////////////////////////////////////////
This part of the relayer works for the transfer of SPL tokens to the ERC-20 tokens on Ethereum. Similarly, we can perform the transfer of ERC-20 tokens to SPL Tokens on the Solana blockchain, burn them, and its functionality will trigger the SPL Token's mint function to complete the cross-chain transaction.
Also, Discover | How to Create a MultiSig Wallet in Solidity
In conclusion, creating a relayer-equipped cross-chain bridge enables users to transfer assets between Ethereum and Solana with ease, opening up a world of decentralised opportunities. Utilising Solidity and Rust's respective advantages, you can build a scalable, secure solution that connects two robust blockchain ecosystems. This project shapes the future of decentralised finance by paving the ground for true blockchain interoperability with the correct tools and knowledge. Connect with our skilled Solidity developers to bring your blockchain-related vision into reality.
December 17, 2024 at 08:09 pm
Your comment is awaiting moderation.