Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.trails.build/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Trails doesn’t just move tokens cross-chain—it can execute arbitrary smart contract functions on the destination chain as part of the same transaction flow. This enables powerful use cases like cross-chain NFT mints, DeFi deposits, and protocol interactions.

How It Works

When you include to.calldata in your Trails transaction, the protocol:
  1. Routes funds from the user’s selected source chain/token
  2. Bridges and swaps as needed to the destination chain/token
  3. Executes your calldata on the destination contract with the funds attached
All of this happens atomically from the user’s perspective—one confirmation, one transaction.

Example: Cross-Chain NFT Mint

A user on Arbitrum wants to mint an NFT on Base that costs 0.01 ETH. They only have USDC:
import { Pay } from '0xtrails/widget'
import { encodeFunctionData } from 'viem'

const mintCalldata = encodeFunctionData({
  abi: [{
    name: 'mint',
    type: 'function',
    stateMutability: 'payable',
    inputs: [{ name: 'to', type: 'address' }],
    outputs: [],
  }],
  functionName: 'mint',
  args: ['0xUserAddress'],
})

<Pay
  apiKey="YOUR_API_KEY"
  to={{
    recipient: "0xNFTContract",
    currency: "ETH",
    chain: "base",
    amount: "0.01",
    calldata: mintCalldata,
  }}
>
  <button>Mint NFT</button>
</Pay>
What happens:
  1. User confirms a single transaction on Arbitrum
  2. Trails swaps USDC → ETH and bridges to Base
  3. On Base, Trails calls mint() on the NFT contract with 0.01 ETH attached
  4. User receives their NFT

Example: Cross-Chain DeFi Deposit

Deposit into an Aave lending pool on Arbitrum using tokens from any chain:
import { Fund } from '0xtrails/widget'
import { TRAILS_ROUTER_PLACEHOLDER_AMOUNT } from '0xtrails'
import { encodeFunctionData } from 'viem'

const supplyCalldata = encodeFunctionData({
  abi: [{
    name: 'supply',
    type: 'function',
    stateMutability: 'nonpayable',
    inputs: [
      { name: 'asset', type: 'address' },
      { name: 'amount', type: 'uint256' },
      { name: 'onBehalfOf', type: 'address' },
      { name: 'referralCode', type: 'uint16' },
    ],
    outputs: [],
  }],
  functionName: 'supply',
  args: [
    '0xUSDCAddress',
    TRAILS_ROUTER_PLACEHOLDER_AMOUNT, // Replaced with actual amount at execution
    '0xUserAddress',
    0,
  ],
})

<Fund
  apiKey="YOUR_API_KEY"
  to={{
    recipient: "0xAavePool",
    currency: "USDC",
    chain: "arbitrum",
    calldata: supplyCalldata,
  }}
>
  <button>Deposit to Aave</button>
</Fund>

Supported Calldata Patterns

Static Calldata

When the function parameters are known in advance:
  • NFT mints with fixed recipient
  • Staking with fixed parameters
  • Any function where amount isn’t a parameter

Dynamic Calldata

When the amount needs to be determined at execution time, use TRAILS_ROUTER_PLACEHOLDER_AMOUNT:
  • ERC-4626 vault deposits
  • Lending protocol supplies
  • Any function with an amount parameter that should match bridged funds

Key Benefits

  • Single Transaction: Users confirm once, regardless of complexity
  • Any Token → Any Function: Pay with USDC, execute with ETH
  • Atomic Execution: Calldata executes only if funds arrive successfully
  • No Contract Changes: Works with existing smart contracts

Use Cases

Use CaseExample
NFT MintingMint on any chain with any token
DeFi DepositsSupply to lending protocols cross-chain
StakingStake tokens on any chain
GamingPurchase in-game items across chains
DAO InteractionsVote or participate with any token

See Also

  • Pay Mode - Configuration for payment with calldata
  • Fund Mode - Configuration for deposits with calldata
  • How It Works - Detailed protocol architecture