Skip to main content
New to Relay? Start with the Quickstart Guide and work your way through your first cross-chain transaction.
You can use Relay cross-chain execution to perform any action (tx) on any chain. This works by specifying the transaction data you wish to execute on the destination chain as part of the initial quoting process.

Before you start

While not mandatory for integration, doing the following will improve the UX for your users considerably:
  • Verify user balance - Confirm user has sufficient funds for amount + fees
  • Check chain support - Confirm both origin and destination chains are supported
  • Validate quote - Quotes are revalidated when being filled, keep your quotes as fresh as possible.
  • Handle errors - Implement proper error handling for API requests and transaction failures
1

Get a Quote

Get a Quote

To execute a cross-chain transaction, you need to specify the origin chain for payment, the destination chain where the contract is deployed, and the transaction data to execute. Use the quote endpoint with specific parameters for cross-chain calling.
curl -X POST "https://api.relay.link/quote/v2" \
  -H "Content-Type: application/json" \
  -d '{
    "user": "0x03508bb71268bba25ecacc8f620e01866650532c",
    "originChainId": 1,
    "destinationChainId": 8453,
    "originCurrency": "0x0000000000000000000000000000000000000000",
    "destinationCurrency": "0x0000000000000000000000000000000000000000",
    "amount": "100000000000000000",
    "tradeType": "EXACT_OUTPUT",
    "txs": [
      {
        "to": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
        "value": "100000000000000000",
        "data": "0xd0e30db0"
      }
    ]
  }'
To get a higher rate limit, you can request an API key.

Contract Compatibility

Before integrating cross-chain calls, ensure your contract is compatible with Relay. Review our Contract Compatibility overview to make any necessary changes to your smart contracts. ​

Contract Interaction with Web3

When calling smart contracts, you’ll need to encode the function call data. Here’s how to do it with popular libraries:
Important for ERC20 transactions: If your contract call involves spending ERC20 tokens, you must include an approval transaction in your txs array before the actual contract call. See the ERC20 examples below.
import { ethers } from "ethers";

// Contract ABI for the function you want to call
const contractABI = [
  "function mint(address to, uint256 amount) external payable",
];

// Create interface to encode function data
const iface = new ethers.Interface(contractABI);
const callData = iface.encodeFunctionData("mint", [
  "0x03508bb71268bba25ecacc8f620e01866650532c", // recipient
  1, // amount to mint
]);

// Use this callData in your quote request
const quoteRequest = {
  user: "0x03508bb71268bba25ecacc8f620e01866650532c",
  originChainId: 1,
  destinationChainId: 8453,
  originCurrency: "eth",
  destinationCurrency: "eth",
  amount: "100000000000000000",
  tradeType: "EXACT_OUTPUT",
  txs: [
    {
      to: "0xContractAddress",
      value: "100000000000000000",
      data: callData,
    },
  ],
};

ERC20 Contract Calls

Critical: When your contract call involves spending ERC20 tokens, you must include an approval transaction in your txs array. The approval must come before the actual contract call.

ERC20 Approval + Contract Call Pattern

import { ethers } from "ethers";

// ERC20 ABI for approval
const erc20ABI = [
  "function approve(address spender, uint256 amount) external returns (bool)",
];

// Contract ABI for the function you want to call
const contractABI = [
  "function purchaseWithUSDC(address to, uint256 usdcAmount) external",
];

// Encode approval transaction
const erc20Interface = new ethers.Interface(erc20ABI);
const approvalData = erc20Interface.encodeFunctionData("approve", [
  "0xContractAddress", // Contract that will spend tokens
  "1000000000", // Amount to approve (1000 USDC with 6 decimals)
]);

// Encode contract call transaction
const contractInterface = new ethers.Interface(contractABI);
const contractCallData = contractInterface.encodeFunctionData(
  "purchaseWithUSDC",
  [
    "0x742d35Cc6634C0532925a3b8D9d4DB0a2D7DD5B3", // recipient
    "1000000000", // 1000 USDC
  ]
);

const quoteRequest = {
  user: "0x742d35Cc6634C0532925a3b8D9d4DB0a2D7DD5B3",
  originChainId: 1,
  destinationChainId: 8453,
  originCurrency: "usdc",
  destinationCurrency: "usdc",
  amount: "1000000000", // Amount required for call (1000 USDC with 6 decimals)
  tradeType: "EXACT_OUTPUT",
  txs: [
    {
      to: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", // USDC contract address
      value: "0",
      data: approvalData,
    },
    {
      to: "0xContractAddress",
      value: "0",
      data: contractCallData,
    },
  ],
};

Sweeping ERC20 Balance

Relay’s router contract has a useful function that you can call to transfer out full balance of an ERC20 token, even if you don’t know the full balance. There are currently two methods for doing this:You can use these by passing in the txs field as follows:
{
  "to": "0xRouterContractAddress",
  "data": "0xEncodedCalldata", // encoded calldata for cleanupErc20s or cleanupNative
  "value": 0
}

ERC20 Troubleshooting Guide

Problem: “ERC20: transfer amount exceeds allowance” error Solution: Ensure you include the approval transaction before your contract callProblem: Transaction reverts with “ERC20: insufficient allowance” Solution: Check that the approval amount is sufficient for your contract callProblem: Approval transaction succeeds but contract call fails Solution: Verify the contract address in the approval matches the contract you’re calling

ERC20 Best Practices

  1. Always approve before spending: Include approval as the first transaction
  2. Use exact amounts: Approve the exact amount your contract will spend
  3. Check token decimals: USDC uses 6 decimals, most others use 18
  4. Verify contract addresses: Use the correct token contract for each chain
  5. Handle allowances: Some tokens require setting allowance to 0 before setting a new amount

Quote Parameters for Cross-Chain Calls

ParameterTypeDescription
amountstringTotal value of all txs combined
tradeTypestringMust be “EXACT_OUTPUT”
txsarrayArray of transaction objects
txs[].tostringContract address to call
txs[].valuestringETH value to send with call
txs[].datastringEncoded function call data
You can learn more about quote request parameters and response data here.
2

Execute the Call

Execute the Call

After receiving a call quote, execute it by processing each step in the response. The execution handles both the origin chain transaction and destination chain fulfillment.

Learn more about step execution using the API here.
3

Monitor Cross-Chain Call Status

Monitor Cross-Chain Call Status

Track the progress of your cross-chain call using the status endpoint:
curl "https://api.relay.link/intents/status/v3?requestId=0xed42e2e48c56b06f8f384d66d5f3e6c450fc3a2c7cba19d92a01a649a31a0e94"
Learn more about how to check the status of the fill here.Learn more about the status lifecycle, see the status lifecycle diagram.

Preflight Checklist

Contract compatibility - Before integrating cross-chain calls, ensure your contract is compatible with Relay. Review our Contract Compatibility overview to make any necessary changes to your smart contracts. ERC20 approvals - Include approval transactions before any ERC20 spending calls Verify transaction data - Confirm amount equals the sum of all txs[].value fields Check tradeType - Must be set to "EXACT_OUTPUT" for cross-chain calls Validate call data - Ensure contract function calls are properly encoded Test contract calls - Verify contract functions work as expected on destination chain Gas estimation - Account for potential gas usage variations in contract calls

Common Use Cases

Below are some common use cases for cross-chain calls.
  • NFT Minting with ETH: Mint NFTs on L2s while paying from L1
  • NFT Minting with ERC20: Mint NFTs using USDC
  • DeFi Operations: Execute swaps, provide liquidity, or claim rewards on other chains
  • Gaming: Execute game actions, purchase items, or claim rewards across chains
// Mint NFT cross-chain with ETH
const mintTx = {
  to: "0xNFTContract",
  value: "50000000000000000", // 0.05 ETH mint price
  data: encodeFunctionData({
    abi: nftABI,
    functionName: "mint",
    args: [userAddress, tokenId],
  }),
};

See Also

  • App Fees: Monetize your integration by adding a fee (in bps) to every quote.
  • Fee Sponsorship: Sponsor fees for your users to reduce friction and improve the user experience.
  • Handling Errors: Handle quote errors when using the Relay API.
  • Relay Fees: Understand the fees associated with using the Relay API.