Axelar is a permissionless network and gateway that enables you to build secure cross-chain applications.
Build a 1-click experience: enable users to interact with any asset, any application, from any chain.
Send your tokens across-chain via any wallet, CEX.
GetDepositAddress(src chain, dst chain, asset, dst address)
Send any payload with a single smart contract call.
function callContract(
string memory destinationChain,
string memory destinationContractAddress,
bytes memory payload
)
A wallet with universal borrow-lend; NFTs on Ethereum can be used as collateral in DeFi apps on any IBC or EVM-compatible chain.
Pool liquidity from any user, in any asset, on any chain.
Call data from dApps on other chains, to index derivatives on the chain that fits your use-case best.
Any asset, anywhere, becomes currency or credential.
This example showcases how ERC20 tokens native to a chain can be linked to other chains connected through Axelar.
pragma solidity 0.8.9;
import { IAxelarExecutable } from '@axelar-network/axelar-cgp-solidity/src/interfaces/IAxelarExecutable.sol';
import { AxelarGasReceiver } from '@axelar-network/axelar-cgp-solidity/src/util/AxelarGasReceiver.sol';
import { IERC20 } from '@axelar-network/axelar-cgp-solidity/src/interfaces/IERC20.sol';
/// @dev An abstract contract responsible for sending token to and receiving token from another TokenLinker.
abstract contract TokenLinker is IAxelarExecutable {
event SendInitiated(string destinationChain, address indexed recipient, uint256 amount);
event ReceiveCompleted(string sourceChain, address indexed recipient, uint256 amount);
address public immutable token;
mapping(string => string) public links;
AxelarGasReceiver gasReceiver;
constructor(address gateway_, address gasReceiver_, address token_) IAxelarExecutable(gateway_) {
token = token_;
gasReceiver = AxelarGasReceiver(gasReceiver_);
}
//Call this function on setup to tell this contract who it's sibling contracts are.
function addLinker(string calldata chain_, string calldata address_) external {
links[chain_] = address_;
}
function sendTo(
string memory chain_,
address recipient_,
uint256 amount_,
address gasToken,
uint256 gasAmount
) external {
require(bytes(links[chain_]).length != 0, 'IVALID_DESTINATION_CHAIN');
_collectToken(msg.sender, amount_);
IERC20(gasToken).transferFrom(msg.sender, address(this), gasAmount);
IERC20(gasToken).approve(address(gasReceiver), gasAmount);
bytes memory payload = abi.encode(recipient_, amount_);
gasReceiver.receiveGas(
chain_,
links[chain_],
payload,
gasToken,
gasAmount
);
gateway.callContract(
chain_,
links[chain_],
payload
);
emit SendInitiated(chain_, recipient_, amount_);
}
function _execute(
string memory sourceChain_,
string memory sourceAddress_,
bytes calldata payload_
) internal override {
require(keccak256(bytes(links[sourceChain_])) == keccak256(bytes(sourceAddress_)), 'IVALID_SOURCE');
address recipient;
uint256 amount;
(recipient, amount) = abi.decode(payload_, (address, uint256));
_giveToken(recipient, amount);
emit ReceiveCompleted(sourceChain_, recipient, amount);
}
function _collectToken(address from, uint256 amount) internal virtual;
function _giveToken(address to, uint256 amount) internal virtual;
}
This example showcases a contract that can be used to send NFTs to different blockchains.
pragma solidity 0.8.9;
import '@openzeppelin/contracts/token/ERC721/ERC721.sol';
import { AxelarGasReceiver } from '@axelar-network/axelar-cgp-solidity/src/util/AxelarGasReceiver.sol';
import { IAxelarExecutable } from '@axelar-network/axelar-cgp-solidity/src/interfaces/IAxelarExecutable.sol';
import { IERC20 } from '@axelar-network/axelar-cgp-solidity/src/interfaces/IERC20.sol';
contract NftLinker is ERC721, IAxelarExecutable {
mapping(uint256 => bytes) public original; //abi.encode(originaChain, operator, tokenId);
mapping(string => string) public linkers; //Who we trust.
string chainName; //To check if we are the source chain.
AxelarGasReceiver gasReceiver;
//Contructor that initializes the ERC721 portion of our linker as well as knows where the gateway and gasReceiver are.
constructor(string memory chainName_, address gateway_, address gasReceiver_)
ERC721('Axelar NFT Linker', 'ANL')
IAxelarExecutable(gateway_) {
chainName = chainName_;
gasReceiver = AxelarGasReceiver(gasReceiver_);
}
//Used to add linkers. This should be only callably by trusted sources normally.
function addLinker(string memory chain, string memory linker) external {
linkers[chain] = linker;
}
//The main function users will interract with.
function sendNFT(
address operator,
uint256 tokenId,
string memory destinationChain,
address destinationAddress
) external payable {
//If we are the operator then this is a minted token that lives remotely.
if(operator == address(this)) {
require(ownerOf(tokenId) == _msgSender(), 'NOT_YOUR_TOKEN');
_sendMintedToken(tokenId, destinationChain, destinationAddress);
} else {
IERC721(operator).transferFrom(_msgSender(), address(this), tokenId);
_sendNativeToken(operator, tokenId, destinationChain, destinationAddress);
}
}
//Burns and sends a token.
function _sendMintedToken(
uint256 tokenId,
string memory destinationChain,
address destinationAddress
) internal {
_burn(tokenId);
//Get the original information.
(
string memory originalChain,
address operator,
uint256 originalTokenId
) = abi.decode(original[tokenId], (string, address, uint256));
//Create the payload.
bytes memory payload = abi.encode(originalChain, operator, originalTokenId, destinationAddress);
//Pay for gas. We could also send the contract call here but then the sourceAddress will be that of the gas receiver which is a problem later.
gasReceiver.receiveGasNative{value:msg.value}(
destinationChain,
linkers[destinationChain],
payload
);
//Call the remote contract.
gateway.callContract(
destinationChain,
linkers[destinationChain],
payload
);
}
//Locks and sends a token.
function _sendNativeToken(
address operator,
uint256 tokenId,
string memory destinationChain,
address destinationAddress
) internal {
//Create the payload.
bytes memory payload = abi.encode(chainName, operator, tokenId, destinationAddress);
//Pay for gas. We could also send the contract call here but then the sourceAddress will be that of the gas receiver which is a problem later.
gasReceiver.receiveGasNative{value: msg.value}(
destinationChain,
linkers[destinationChain],
payload
);
//Call remote contract.
gateway.callContract(
destinationChain,
linkers[destinationChain],
payload
);
}
//This is automatically executed by Axelar Microservices since gas was payed for.
function _execute(string memory sourceChain, string memory sourceAddress, bytes calldata payload) internal override {
//Check that the sender is another token linker.
require(keccak256(bytes(sourceAddress)) == keccak256(bytes(linkers[sourceChain])), 'NOT_A_LINKER');
//Decode the payload.
(
string memory originalChain,
address operator,
uint256 tokenId,
address destinationAddress
) = abi.decode(payload, (string, address ,uint256, address));
//If this is the original chain then we give the NFT locally.
if(keccak256(bytes(originalChain)) == keccak256(bytes(chainName))) {
IERC721(operator).transferFrom(address(this), destinationAddress, tokenId);
//Otherwise we need to mint a new one.
} else {
//We need to save all the relevant information.
bytes memory originalData = abi.encode(originalChain, operator, tokenId);
//Avoids tokenId collisions.
uint256 newTokenId = uint256(keccak256(originalData));
original[newTokenId] = originalData;
_safeMint(destinationAddress, newTokenId);
}
}
}
Just keep me updated
© 2022 Axelar Network. All Rights Reserved.