XRPL Interchain Token Service

The XRPL Blockchain is unique in comparison to other blockchains integrated with Axelar. Unlike other blockchains, XRPL does not support smart contracts. As such there is no Interchain Token Service contract deployed on the chain the way there is with other EVM and non-EVM blockchain integrations.

The integration leverages the XRPL Multisig Signing account type to facilitate token transfers. The gateway is controlled by the Axelar Verifier set.

The testnet address for the XRPL Gateway is: rNrjh1KGZk2jBR3wPfAQnoidtFFYQKbQn2.

The mainnet address for the XRPL Gateway is: rfmS3zqrQrka8wVyhXifEeyTwe8AMz2Yhw.

Axelar’s integration to XRPL allows for the cross-chain transfer of both the XRPL native token (XRP itself) as well as custom tokens (IOU).

The interchain transfer flow is similar to that of the regular GMP flow. As with the regular GMP flow, users will be sending a payment transaction to the Gateway account. The main difference is that the MessageType being sent to the Gateway is an interchain_transfer message type instead of a call_contract message.

When transferring a token out of XRPL you will be sending the token from your XRPL account to the Gateway contract. The Gateway will be the custodian of your token on XRPL while it is sent via ITS hub to the destination chain until is bridged back to XRPL. Sending a token will be similar to sending a regular GMP message in that the information that is sent to the Gateway (and eventually to ITS hub) will need to be specified in the memo field of the transaction.

What needs to be specified in the memo is the following:

  1. type: The type of the transaction (which will be interchain_transfer)
  2. destination_address: The address of the destination contract on the destination chain.
  3. destination_chain: The name of the destination chain.
  4. gas_fee_amount: The amount to be used to cover the the cross-chain transaction fees.
  5. payload: A message to be sent along with the cross-chain asset transfer. This parameter is only needed if you are sending a gmp message, can be ignored if you are simply sending a token without an executable message.

Each of the previously mentioned fields will be passed into the memo as a MemoType as an encoded hex. You can then specify the value for each of these fields in the MemoData field, also encoded in hex.

A full JSON example of the entire required XRPL Interchain Transfer fields can be as follows:

{
TransactionType: "Payment",
Account: user.address,
Amount: "1000000", // (1 xrp)
// Amount: { // alternatively, an IOU token amount can be used to cover gas fees
// currency: "ABC", // IOU's currency code
// issuer: "r4DVHyEisbgQRAXCiMtP2xuz5h3dDkwqf1", // IOU issuer's account address
// value: "1" // IOU amount to bridge (in this case, 1 ABC.r4DVH), *including* gas fee amount
// },
Destination: gateway.address,
Memos: [
{
Memo: {
MemoType: "74797065", // hex("msg type")
MemoData: "696e746572636861696e5f7472616e73666572" // hex("interchain_transfer")
},
},
{
Memo: {
MemoType: "64657374696e6174696f6e5f61646472657373", // hex("destination_address")
MemoData: "30413930633041663142303766364143333466333532303334384462666165373342446133353845" // hex("0A90c0Af1B07f6AC34f3520348Dbfae73BDa358E"), in this case: (0x is omitted)
},
},
{
Memo: {
MemoType: "64657374696E6174696F6E5F636861696E", // hex("destination_chain")
MemoData: "7872706c2d65766d2d6465766e6574", // destination chain, hex encoded - hex("xrpl-evm-devnet"), in this case
},
},
{
Memo: {
MemoType: "6761735f6665655f616d6f756e74", // hex("gas_fee_amount")
MemoData: "30", // amount of tokens to allocate to gas fees
},
},
{ // Only include this Memo object when performing a GMP call:
Memo: {
MemoType: "7061796c6f6164", // hex("payload")
// abi-encoded payload/data with which to call the Executable destination contract address:
MemoData: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000e474d5020776f726b7320746f6f3f000000000000000000000000000000000000",
},
},
],
}

A full example can be found here.

The example makes use of the XRPL Client library

The XRPL client simplifies the process of connecting to the XRPL network, building transactions, signing them, and submitting them for processing. Key components of the XRPL client include:

The key function in this example is the interchainTransfer() function written out as follows.

async function interchainTransfer(_config, wallet, client, chain, options, args) {
await client.sendPayment(
wallet,
{
destination: chain.contracts.InterchainTokenService.address,
amount: parseTokenAmount(args.token, args.amount), // token is either "XRP" or "<currency>.<issuer-address>"
memos: [
{ memoType: hex('type'), memoData: hex('interchain_transfer') },
{ memoType: hex('destination_address'), memoData: hex(args.destinationAddress.replace('0x', '')) },
{ memoType: hex('destination_chain'), memoData: hex(args.destinationChain) },
{ memoType: hex('gas_fee_amount'), memoData: hex(options.gasFeeAmount) },
...(options.payload ? [{ memoType: hex('payload'), memoData: options.payload }] : []),
],
},
options,
);
}

This function constructs the memo that will be used for the transaction, passing in the type, destination_address, destination_chain, and gas_fee_amount. The request functionality of the XRPL client is then used to submit a signed transaction.

To interact with the example run the following command

Terminal window
node xrpl/interchain-transfer.js -e testnet -n xrpl XRP 1 xrpl-evm 0x312dba807EAE77f01EF3dd21E885052f8F617c5B --gasFeeAmount 100000

This will send an ITS transfer to the 0x312dba807EAE77f01EF3dd21E885052f8F617c5B address on the XRPL-EVM chain.

Once the command is run you should as a transaction on the Axelarscan explorer where you can track the entire cross-chain transaction.

In the event of an insufficient gas error, you can unblock the underfunded transaction by running the Add Gas top up the missing funds.

Edit on GitHub