These instructions describe how to perform token transfers between Ethereum Sepolia and the XRPL Testnet as well as GMP calls from XRPL Testnet to Ethereum Sepolia, via the Axelar Amplifier.
- The following tokens are currently supported:
AxelarGateway
smart contract on Ethereum Sepolia:0xAABdd46ba1B3147d0Cf6aCc9605a74fE8668fC74
- XRPL multisig account on XRPL Testnet:
rfEf91bLxrTVC76vw1W3Ur8Jk4Lwujskmb
- This is a testnet deployment. Any mainnet funds that are transferred to the addresses mentioned in this doc may be irrecoverable.
- Please do not use any mainnet wallet to perform any of the actions outlined in this doc.
- Gas payments are not currently supported. You do not need to call the
AxelarGasService
on Ethereum Sepolia to refund the relayer. Our relayer is running 'pro bono.' - The bridge does not charge any fees/tolls at the moment.
- The
IAxelarGateway
interface andAxelarExecutable
smart contract in this repo are different to those currently deployed by Axelar. Any instructions you find elsewhere will likely not be fully compatible with this testnet deployment. - The deployment addresses are subject to change.
- Only one validator is currently used to secure this testnet bridge.
- Only one relayer is currently active.
- Since this is a testnet deployment, there is no guaranteed SLA. Please let us know if your transactions are taking too long to appear on the destination chain.
- These instructions mainly rely on Foundry's
cast
command to perform Ethereum Sepolia transactions (although you can use any library of your choice). Foundry can be installed using the following instructions: book.getfoundry.sh/getting-started/installation. - You can use an RPC provider like Alchemy or Infura to broadcast transactions to Ethereum Sepolia. These instructions assume that you have set the
SEPOLIA_RPC_URL
environment variable to a working Sepolia RPC URL. - Generate a new wallet for each side. For Ethereum Sepolia, you can run
cast wallet new
. These instructions assume that you have set thePRIVATE_KEY
environment variable to a funded Sepolia wallet private key. - Fund your accounts using a faucet, such as these:
- Ethereum Sepolia
ETH
faucet: alchemy.com/faucets/ethereum-sepolia - XRPL Testnet
XRP
faucet: faucet.tequ.dev
- Ethereum Sepolia
- To bridge to XRPL Testnet, you can use an XRPL library of your choice, such as
xrpl.js
orxrpl-py
. - To perform general message passing (GMP) from XRPL to Ethereum Sepolia, the Ethereum smart contract (that you wish to call) needs to implement the
AxelarExecutable
contract. Again, please keep in mind that this contract is not the standardAxelarExecutable
contract that you will find on other Axelar resources.
This section outlines how to transfer any of the supported tokens between Ethereum Sepolia and XRPL Testnet.
- Create a Trust Line on XRPL Testnet between the recipient user and the XRPL multisig, using a
TrustSet
transaction, such as the following:
{
"TransactionType": "TrustSet",
"Account": user.address, // the XRPL address of the ETH transfer recipient
"LimitAmount": {
"currency": "ETH",
"issuer": "rfEf91bLxrTVC76vw1W3Ur8Jk4Lwujskmb",
"value": "10000000000",
},
...
},
- Wrap
ETH
intoWETH
on Ethereum Sepolia:- Using
cast
:
WETH=0x7b79995e5f793a07bc00c21412e50ecae098e7f9 cast send $WETH "deposit()" --value 0.1ether --private-key $PRIVATE_KEY --rpc-url $SEPOLIA_RPC_URL
- Alternatively, via Etherescan, select "Connect to Web3" to connect your wallet, expand the
deposit
dropdown, type the amount ofETH
that you would like to bridge (e.g.,0.1
) in the input, and click "Write" to perform the transcation.
- Using
- Approve the
AxelarGateway
contract to spend yourWETH
on Ethereum Sepolia:- Using
cast
:
WETH=0x7b79995e5f793a07bc00c21412e50ecae098e7f9 AXELAR_GATEWAY=0xAABdd46ba1B3147d0Cf6aCc9605a74fE8668fC74 cast send $WETH "approve(address guy, uint256 amount)" $AXELAR_GATEWAY $(cast to-wei 0.1) --private-key $PRIVATE_KEY --rpc-url $SEPOLIA_RPC_URL
- Alternatively, via Etherescan, select "Connect to Web3" to connect your wallet, expand the
approve
dropdown, type theAxelarGateway
's address (0x832B2dfe8a225550C144626111660f61B8690efD
) in the address input, and the amount ofETH
that you would like to bridge (e.g.,0.1
) in the input underdeposit
, and click "Write" to perform the transcation.
- Using
- On Ethereum Sepolia, call
AxelarGateway.sendToken()
to bridge yourWETH
:- Using
cast
:
AXELAR_GATEWAY=0xAABdd46ba1B3147d0Cf6aCc9605a74fE8668fC74 XRPL_DESTINATION= # your XRPL recipient address cast send $AXELAR_GATEWAY "sendToken(string destinationChain, string destinationAddress, string symbol, uint256 amount)" "xrpl" $XRPL_DESTINATION "WETH" $(cast to-wei 0.1) --private-key $PRIVATE_KEY --rpc-url $SEPOLIA_RPC_URL
- Using
- Within a few minutes, your designated recipient address should receive the bridged
ETH
on XRPL.- Check that your recipient account received the
ETH
tokens via an XRPL Testnet explorer
- Check that your recipient account received the
- Deposit
ETH
into the XRPL multisig account (rfEf91bLxrTVC76vw1W3Ur8Jk4Lwujskmb
) using aPayment
transaction of the following format:
{
TransactionType: "Payment",
Account: user.address,
Amount: {
currency: "ETH",
value: "0.001", // = 10^15 wei - the amount of ETH you want to bridge, in ETH
issuer: "rfEf91bLxrTVC76vw1W3Ur8Jk4Lwujskmb",
},
Destination: "rfEf91bLxrTVC76vw1W3Ur8Jk4Lwujskmb",
Memos: [
{
Memo: {
MemoData: "605459C28E6bE7B31B8b622FD29C82B3059dB1C6", // your ETH recipient address, without the 0x prefix
MemoType: "64657374696E6174696F6E5F61646472657373", // hex("destination_address")
},
},
{
Memo: {
MemoData: "657468657265756D", // hex("ethereum")
MemoType: "64657374696E6174696F6E5F636861696E", // hex("destination_chain")
},
},
{
Memo: {
MemoData: "0000000000000000000000000000000000000000000000000000000000000000", // bytes32(0) indicates pure token transfer, without GMP
MemoType: "7061796C6F61645F68617368", // hex("payload_hash")
},
},
],
...
}
- Within a few minutes, your designated recipient address should receive the bridged
ETH
on Ethereum Sepolia, asWETH
:- Check that your recipient account received the
WETH
tokens via an Ethereum Sepolia explorer. - Please keep in mind that the explorer might take a few minutes to index the transaction.
- Check that your recipient account received the
- Deposit
XRP
into the XRPL multisig account (rfEf91bLxrTVC76vw1W3Ur8Jk4Lwujskmb
) using aPayment
transaction of the following format:
{
TransactionType: "Payment",
Account: user.address,
Amount: "1000000", // = 1 XRP - the amount of XRP you want to bridge, in drops
Destination: "rfEf91bLxrTVC76vw1W3Ur8Jk4Lwujskmb",
Memos: [
{
Memo: {
MemoData: "605459C28E6bE7B31B8b622FD29C82B3059dB1C6", // your ETH recipient address, without the 0x prefix
MemoType: "64657374696E6174696F6E5F61646472657373", // hex("destination_address")
},
},
{
Memo: {
MemoData: "657468657265756D", // hex("ethereum")
MemoType: "64657374696E6174696F6E5F636861696E", // hex("destination_chain")
},
},
{
Memo: {
MemoData: "0000000000000000000000000000000000000000000000000000000000000000", // bytes32(0) indicates pure token transfer, without GMP
MemoType: "7061796C6F61645F68617368", // hex("payload_hash")
},
},
],
...
}
- Within a few minutes, your designated recipient address should receive the bridged
XRP
on Ethereum Sepolia, asaxlXRP
:- Check that your recipient account received the
XRP
tokens via an explorer. - Please keep in mind that the explorer might take a few minutes to index the transaction.
- Check that your recipient account received the
- Approve the
AxelarGateway
contract to spend youraxlXRP
on Ethereum Sepolia:- Using Foundry's
cast
command:
AXL_XRP=0x40d5ed73982468499ecfa9c8cc0abb63ff13a409 AXELAR_GATEWAY=0xAABdd46ba1B3147d0Cf6aCc9605a74fE8668fC74 cast send $AXL_XRP "approve(address guy, uint256 amount)" $AXELAR_GATEWAY 1000000 --private-key $PRIVATE_KEY --rpc-url $SEPOLIA_RPC_URL
- Alternatively, via Etherescan, select "Connect to Web3" to connect your wallet, expand the
approve
dropdown, type theAxelarGateway
's address (0x832B2dfe8a225550C144626111660f61B8690efD
) in theguy
input and the amount ofETH
that you would like to bridge (e.g.,0.1
) in the input underdeposit
, and click "Write" to perform the transcation.
- Using Foundry's
- On Ethereum Sepolia, call
AxelarGateway.sendToken()
to bridge youraxlXRP
:- Using Foundry's
cast
command:
AXELAR_GATEWAY=0xAABdd46ba1B3147d0Cf6aCc9605a74fE8668fC74 XRPL_DESTINATION= # your XRPL recipient address cast send $AXELAR_GATEWAY "sendToken(string destinationChain, string destinationAddress, string symbol, uint256 amount)" "xrpl" $XRPL_DESTINATION "axlXRP" 1000000 --private-key $PRIVATE_KEY --rpc-url $SEPOLIA_RPC_URL
- Using Foundry's
- Within a few minutes, your designated recipient address should receive the bridged
XRP
on XRPL Testnet:- Check that your recipient account received the ETH tokens via an explorer.
This section outlines how to call a function of a smart contract on Ethereum Sepolia from XRPL Testnet.
- Compute the payload (let's call it
gmpPayload
) that you would like to call your Ethereum SepoliaAxelarExecutable
smart contract's_execute
function with. - Create a
Payment
transaction in the same format as the ones given in the previous section, with the following changes:- Set the transaction
Amount
as in the previous section, depending on what token you would like to transfer to the destination smart contract during GMP (either{ currency: "ETH", ... }
for ETH or just the number of drops for XRP). If you just want to GMP, without any token transfer, set theAmount
to1
drop. - Set the destination address's
MemoData
field to the address of your Ethereum SepoliaAxelarExecutable
smart contract. - Set the payload hash's
MemoData
field tokeccak256(abi.encode(gmpPayload))
. You can use theeth-abi
andeth-utils
python libraries to compute this hash, as such:
from eth_abi import encode from eth_utils import keccak keccak(encode(['string'], ['hello, world!'])).hex()
- Set the transaction
- Within a few minutes, the relayer should submit validator signatures of the XRPL Testnet deposit transaction to the Ethereum Sepolia
AxelarGateway
contract, which records the approval of the payload hash and emits aContractCallApproved
event. You can verify that this event was called using an explorer. - Call the
execute
function on yourAxelarExecutable
Ethereum Sepolia smart contract:
AXELAR_EXECUTABLE= # your `AxelarExecutable` contract
COMMAND_ID= # the `commandId` that was emitted in the `ContractCallApproved` event
SOURCE_ADDRESS= # the XRPL address that performed the `Payment` deposit transaction
PAYLOAD= # abi.encode(['string', 'uint256', 'bytes'], [symbol, amount, gmpPayload])
cast send $AXELAR_EXECUTABLE 'function execute(bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes calldata payload)' $COMMAND_ID xrpl $SOURCE_ADDRESS $PAYLOAD --private-key $PRIVATE_KEY --rpc-url $SEPOLIA_RPC_URL
We have created and deployed to Ethereum Sepolia an example AxelarExecutable
contract called ExecutableSample
.
Let's call the ExecutableSample
contract from XRPL, to update its message
state variable:
- We'll aim to update the
ExecutableSample.message
state variable to'Just transferred XRP to Ethereum!'
from XRPL Testnet. - Initiate the GMP call by making a
Payment
to the XRPL multisig:
import * as xrpl from "xrpl";
const XRPL_RPC_URL = "wss://s.altnet.rippletest.net:51233";
async function gmp() {
const client = new xrpl.Client(XRPL_RPC_URL);
await client.connect();
// const user = xrpl.Wallet.fromSeed(SEED); // Read XRPL wallet seed from environment or generate and fund new wallet:
const user = xrpl.Wallet.generate();
await client.fundWallet(user);
const paymentTx: xrpl.Transaction = {
TransactionType: "Payment",
Account: user.address,
Amount: xrpl.xrpToDrops(1),
Destination: "rfEf91bLxrTVC76vw1W3Ur8Jk4Lwujskmb",
SigningPubKey: "",
Flags: 0,
Fee: "30",
Memos: [
{
Memo: {
MemoData: "189C2572063f25FEf5Cdd3516DDDd9fA6e9CB187", // the `ExecutableSample` contract
MemoType: Buffer.from("destination_address").toString('hex').toUpperCase(),
},
},
{
Memo: {
MemoData: Buffer.from("ethereum").toString('hex').toUpperCase(),
MemoType: Buffer.from("destination_chain").toString('hex').toUpperCase(),
},
},
{
Memo: {
MemoData: "df031b281246235d0e8c8254cd731ed95d2caf4db4da67f41a71567664a1fae8", // keccak256(abi.encode(gmpPayload))
MemoType: Buffer.from("payload_hash").toString('hex').toUpperCase(),
},
},
],
};
const signed = user.sign(await client.autofill(paymentTx));
console.log(signed);
await client.submitAndWait(signed.tx_blob);
await client.disconnect();
}
gmp();
- Wait for the relayer to call
AxelarGateway.execute()
. Verify that theContractCallApproved
event was called using an explorer. - Call the
ExecutableSample.execute()
:
AXELAR_EXECUTABLE=0x189C2572063f25FEf5Cdd3516DDDd9fA6e9CB187
COMMAND_ID= # the `commandId` that was emitted in the `ContractCallApproved` event
SOURCE_ADDRESS= # the XRPL address of the `user` who performed the `Payment` deposit transaction
PAYLOAD=000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000661786c58525000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000214a757374207472616e736665727265642058525020746f20457468657265756d2100000000000000000000000000000000000000000000000000000000000000 # encode(['string', 'uint256', 'bytes'], [symbol, amount, encode(['string'], ['Just transferred XRP to Ethereum!'])])
cast send $AXELAR_EXECUTABLE 'function execute(bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes calldata payload)' $COMMAND_ID xrpl $SOURCE_ADDRESS $PAYLOAD --private-key $PRIVATE_KEY --rpc-url $SEPOLIA_RPC_URL
AxelarExecutable.message
should now be set to'Just transferred XRP to Ethereum!'
:
AXELAR_EXECUTABLE=0x189C2572063f25FEf5Cdd3516DDDd9fA6e9CB187
cast call $AXELAR_EXECUTABLE 'message()(string)' --rpc-url $SEPOLIA_RPC_URL
# Just transferred XRP to Ethereum!