Posted By : Mudit
Ethereum and other blockchain app development provide decentralization and security but operate in isolation, making interoperability a challenge. Axelar solves this problem by enabling seamless cross-chain asset transfers. It acts as a decentralized transport layer, allowing users to send tokens and data across different blockchain networks efficiently.
Building a cross-chain asset transfer system using Axelar requires these elements:
Tools and Dependencies:
AxelarJS SDK: A JavaScript SDK for interacting with Axelar's cross-chain infrastructure.
Node.js and npm: Required for managing dependencies and running scripts.
Hardhat: A development tool used for compiling, deploying, and testing Ethereum smart contracts.
dotenv: Used to store private environment variables.
To set up your project, start by creating a new directory called 'axelar-cross-chain' and navigate to it. Then, initialize a new Node.js project with the npm init -y command. After that, install the necessary dependencies: for development tools like Hardhat and dotenv, use npm install --save-dev hardhat dotenv. For Axelar's SDK and other utilities, run npm install @axelar-network/[email protected] crypto @nomicfoundation/hardhat-toolbox. Finally, create a .env file to store your private environment variables. This setup will prepare your environment for building with Axelar's cross-chain infrastructure.
Also, Explore | Building a Cross-Chain NFT Bridge using Solana Wormhole
The provided Solidity contract defines an ERC-20 token called 'Cross' with minting and burning functionalities. It includes features for transferring tokens between different blockchain networks using Axelar's cross-chain capabilities. The contract allows users to mint additional tokens and send tokens to remote addresses on other chains while paying for the gas fees associated with these transactions.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IAxelarGateway} from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol';
import {IAxelarGasService} from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol';
import {ERC20} from '@axelar-network/axelar-gmp-sdk-solidity/contracts/test/token/ERC20.sol';
import {AxelarExecutable} from '@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol';
import {StringToAddress, AddressToString} from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/AddressString.sol';
import './Interfaces/ICROSS.sol';
contract Cross is AxelarExecutable, ERC20, ICROSS {
using StringToAddress for string;
using AddressToString for address;
error FalseSender(string sourceChain, string sourceAddress);
event FalseSenderEvent(string sourceChain, string sourceAddress);
IAxelarGasService public immutable gasService;
constructor(
address gateway_,
address gasReceiver_,
string memory name_,
string memory symbol_,
uint8 decimals_
) AxelarExecutable(gateway_) ERC20(name_, symbol_, decimals_) {
gasService = IAxelarGasService(gasReceiver_);
_mint(msg.sender, 1000 * 10 ** decimals_);
}
function giveMe(uint256 amount) external {
_mint(msg.sender, amount);
}
function transferRemote(
string calldata destinationChain,
address destinationAddress,
uint256 amount
) public payable override {
require(msg.value > 0, 'Gas payment is required');
_burn(msg.sender, amount);
bytes memory payload = abi.encode(destinationAddress, amount);
string memory stringAddress = address(destinationAddress).toString();
gasService.payNativeGasForContractCall{value: msg.value}(
address(this),
destinationChain,
stringAddress,
payload,
msg.sender
);
gateway().callContract(destinationChain, stringAddress, payload);
}
function _execute(
bytes32,
string calldata,
string calldata sourceAddress,
bytes calldata payload
) internal override {
if (sourceAddress.toAddress() != address(this)) {
emit FalseSenderEvent(sourceAddress, sourceAddress);
return;
}
(address to, uint256 amount) = abi.decode(payload, (address, uint256));
_mint(to, amount);
}
}
Step 1: Create a utils.js File
Add the following chain configuration values to utils.js:
const chainConfigs = {
Moonbeam: {
name: 'Moonbeam',
id: 'Moonbeam',
axelarId: 'Moonbeam',
chainId: 1287,
rpc: 'https://moonbase-alpha.drpc.org',
tokenSymbol: 'DEV',
constAddressDeployer: '0x98b2920d53612483f91f12ed7754e51b4a77919e',
gateway: '0x5769D84DD62a6fD969856c75c7D321b84d455929',
gasService: '0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6',
contract: '',
},
Avalanche: {
name: 'Avalanche',
id: 'Avalanche',
axelarId: 'Avalanche',
chainId: 43113,
rpc: 'https://api.avax-test.network/ext/bc/C/rpc',
tokenSymbol: 'AVAX',
constAddressDeployer: '0x98b2920d53612483f91f12ed7754e51b4a77919e',
gateway: '0xC249632c2D40b9001FE907806902f63038B737Ab',
gasService: '0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6',
contract: '',
},
};
Gateway and Gas Service contracts are deployed by Axelar to enable cross-chain transfer . For different chains, there are different gateways and gas services, which can be found in Axelar documentation.
Also, Read | Creating Cross-Chain Smart Contracts with Polkadot and Substrate
Step 2: Create the deploy.js File
Create a folder named scripts and add a file called deploy.js with the following code:
require('dotenv').config();
const { ethers, ContractFactory } = require('ethers');
const ERC20CrossChain = require('../artifacts/contracts/Cross.sol/Cross.json');
const chainConfigs = require('./utils');
const name = 'Cross Chain Token';
const symbol = 'CCT';
const decimals = 18;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
async function deploy(chainName) {
const chain = chainConfigs[chainName];
if (!chain) {
throw new Error(`⌠Invalid chain name: ${chainName}`);
}
try {
const provider = new ethers.providers.JsonRpcProvider(chain.rpc);
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
console.log(`🚀 Deploying ERC20CrossChain on ${chain.name}...`);
const implementationFactory = new ContractFactory(
ERC20CrossChain.abi,
ERC20CrossChain.bytecode,
wallet
);
const implementationConstructorArgs = [
chain.gateway,
chain.gasService,
name,
symbol,
decimals,
];
const deploymentOptions = {
maxPriorityFeePerGas: ethers.utils.parseUnits('30', 'gwei'),
maxFeePerGas: ethers.utils.parseUnits('40', 'gwei'),
};
const implementation = await implementationFactory.deploy(
...implementationConstructorArgs,
deploymentOptions
);
await implementation.deployed();
console.log(
`✅ ERC20CrossChain deployed on ${chain.name} at address: ${implementation.address}`
);
} catch (error) {
console.error(`⌠Deployment failed on ${chainName}:`, error.message);
}
}
async function main() {
try {
await deploy('Moonbeam');
await deploy('Avalanche');
console.log('✅ Deployment completed on both Moonbeam and Avalanche.');
} catch (error) {
console.error('⌠Deployment failed:', error);
}
}
main().catch((error) => {
console.error('Error in the main function:', error);
});
Step 3: Deploy the Contracts
Navigate to the scripts folder and run the deployment script: node deploy.js
Deploying ERC20CrossChain on Moonbeam...
ERC20CrossChain deployed on Moonbeam at address: 0x116e1b3281AB181cBCE1a76a0cB98e8d178325Bb
Deploying ERC20CrossChain on Avalanche...
ERC20CrossChain deployed on Avalanche at address: 0x116e1b3281AB181cBCE1a76a0cB98e8d178325Bb
Deployment completed on both Moonbeam and Avalanche.
We need to add the contract address in the utils folder for both the chains.
You may also like to explore | Create a Cross-Chain Interoperability Protocol Using Cosmos SDK
To enable cross-chain transfers, add the following function to the deploy.js file:
In this function, we initiate a cross-chain transfer of ERC-20 tokens from the Avalanche network to the Moonbeam network. By defining the source and destination chains, we set up two separate providers and wallets for each chain. The transferRemote function is called to transfer a specified token amount, with proper gas fees, while the transaction hash is logged once the transfer is complete, ensuring a seamless cross-chain interaction.
async function execute() {
const deploymentOptions = {
maxPriorityFeePerGas: ethers.utils.parseUnits('30', 'gwei'),
maxFeePerGas: ethers.utils.parseUnits('40', 'gwei'),
};
const tokenAmount = ethers.utils.parseUnits('20', 18);
const source = chainConfigs.Avalanche;
const destination = chainConfigs.Moonbeam;
const sourceProvider = new ethers.providers.JsonRpcProvider(source.rpc);
const destinationProvider = new ethers.providers.JsonRpcProvider(
destination.rpc
);
const sourceWallet = new ethers.Wallet(PRIVATE_KEY, sourceProvider);
const destinationWallet = new ethers.Wallet(PRIVATE_KEY, destinationProvider);
const sourceContract = new ethers.Contract(
source.contract,
ERC20CrossChain.abi,
sourceWallet
);
const destinationContract = new ethers.Contract(
destination.contract,
ERC20CrossChain.abi,
destinationWallet
);
console.log('1: Source Contract Address:', source.name);
console.log('2: Destination Contract Address:', destination.name);
const tx2 = await sourceContract.transferRemote(
destination.name,
destination.contract,
tokenAmount,
{
value: ethers.utils.parseEther('0.01'),
maxPriorityFeePerGas: deploymentOptions.maxPriorityFeePerGas,
maxFeePerGas: deploymentOptions.maxFeePerGas,
}
);
console.log('Transaction Hash for transferRemote:', tx2.hash);
await tx2.wait();
}
Add the following function to the main function and run the script again : node deploy.js , which will console.log the following things.
1: Source : Avalanche
2: Destination : Moonbeam
Transaction Hash for transferRemote: 0x8fd7401cbd54f34391307705c70b84ebf5c699538c37e7c19da15e2c980ce9ec
We can also check the status of the transfer on the Axelar testnet explorer at https://testnet.axelarscan.io/gmp/0x8fd7401cbd54f34391307705c70b84ebf5c699538c37e7c19da15e2c980ce9ec.
Also, Discover | How to Build a Cross-Chain Bridge Using Solidity and Rust
In conclusion, Axelar provides a robust and efficient framework for seamless cross-chain asset transfers, addressing interoperability challenges in the blockchain ecosystem. By leveraging Axelar's decentralized network, developers can enable secure and trustless communication between multiple blockchains, enhancing liquidity and expanding DeFi opportunities. As the demand for cross-chain solutions grows, Axelar's infrastructure plays a crucial role in fostering a more interconnected and scalable Web3 ecosystem, unlocking new possibilities for decentralized applications and asset mobility. For more related to blockchain development, connect with our blockchain developers to get started.
April 19, 2025 at 10:30 am
Your comment is awaiting moderation.