Skip to main content

Documentation Index

Fetch the complete documentation index at: https://anypay-docs-sdk-0-15-0-updates.mintlify.app/llms.txt

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

Overview

Earn mode enables users to deposit into DeFi protocols for yield generation and staking. It supports both pre-integrated protocols (Aave, Morpho) and custom contract integrations with arbitrary calldata.
Earn mode includes both deposit and withdrawal tabs. To deep-link users directly into withdrawals, use Withdraw mode.

Quick start

import { Earn } from '0xtrails/widget'

// Open protocol selection — user picks everything
<Earn apiKey="YOUR_API_KEY" />
Pre-configure the destination protocol:
<Earn
  apiKey="YOUR_API_KEY"
  to={{
    recipient: "0xYearnVault",
    currency: "USDC",
    chain: "katana",
    calldata: encodedDepositCalldata,
  }}
  onEarnSuccess={({ sessionId }) => console.log("deposited", sessionId)}
/>

Props

Required

PropTypeDescription
apiKeystringTrails API key

Destination (optional)

PropTypeDescription
to.recipientstringProtocol contract address
to.currencystringToken to deposit (symbol or address)
to.chainChainIdentifierProtocol chain — name, ID, or viem Chain
to.defaultCurrencystringDefault token — user can change
to.defaultChainChainIdentifierDefault chain — user can change
to.amountstring | numberFixed deposit amount
to.calldatastringABI-encoded function call to execute after deposit

Source (optional)

PropTypeDescription
from.currencystringSource token symbol or address
from.chainChainIdentifierSource chain
from.amountstring | numberSource amount
from.walletAddressstringSource wallet (defaults to connected wallet)

Payment method (optional)

paymentMethod controls how the user sources funds:
ValueMethod
"CONNECTED_WALLET"Connected wallet (default)
"CRYPTO_TRANSFER"QR code / address deposit
"CREDIT_DEBIT_CARD"Fiat on-ramp
"EXCHANGE"CEX transfer

Lifecycle callbacks

CallbackSignatureWhen it fires
onEarnStart({ sessionId }) => voidUser begins the deposit flow
onEarnSuccess({ sessionId }) => voidDeposit completes successfully
onEarnError({ sessionId, error }) => voidDeposit encounters an error

Examples

Protocol selection — user picks everything

import { Earn } from '0xtrails/widget'

<Earn
  apiKey="YOUR_API_KEY"
  onEarnSuccess={({ sessionId }) => console.log("deposited", sessionId)}
/>

Fixed amount protocol deposit

Deposit 1 USDC into Aave V3 on Arbitrum:
import { Earn } from '0xtrails/widget'
import { encodeFunctionData } from 'viem'

const AAVE_POOL = '0x794a61358D6845594F94dc1DB02A252b5b4814aD'
const USDC_ADDRESS = '0xaf88d065e77c8cC2239327C5EDb3A432268e5831'

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: [USDC_ADDRESS, 1000000n, '0xUserAddress', 0],
})

<Earn
  apiKey="YOUR_API_KEY"
  to={{
    recipient: AAVE_POOL,
    currency: "USDC",
    chain: "arbitrum",
    amount: "1",
    calldata: supplyCalldata,
  }}
>
  <button>Deposit 1 USDC to Aave</button>
</Earn>

ERC-4626 vault with dynamic amount

Use TRAILS_ROUTER_PLACEHOLDER_AMOUNT when the deposit amount is a parameter and the user selects it:
import { Earn } from '0xtrails/widget'
import { TRAILS_ROUTER_PLACEHOLDER_AMOUNT } from '0xtrails'
import { encodeFunctionData } from 'viem'

const depositCalldata = encodeFunctionData({
  abi: [{
    name: 'deposit',
    type: 'function',
    stateMutability: 'nonpayable',
    inputs: [
      { name: 'assets', type: 'uint256' },
      { name: 'receiver', type: 'address' },
    ],
    outputs: [{ name: 'shares', type: 'uint256' }],
  }],
  functionName: 'deposit',
  args: [TRAILS_ROUTER_PLACEHOLDER_AMOUNT, '0xUserAddress'],
})

<Earn
  apiKey="YOUR_API_KEY"
  to={{
    recipient: "0xVaultContract",
    currency: "USDC",
    chain: "base",
    calldata: depositCalldata,
  }}
>
  <button>Deposit to Vault</button>
</Earn>
Use TRAILS_ROUTER_PLACEHOLDER_AMOUNT when:
  • Deposit amount is a function parameter
  • User selects the deposit amount
  • Amount needs to reflect post-swap/bridge output
Do not use it for functions that read balance internally (e.g. depositAll()).

Composable actions (pre-integrated protocols)

For Aave, Compound, Morpho, Yearn, and other natively supported protocols, use composable actions instead of manual calldata encoding:
import { useTrailsSendTransaction, lend, erc20Utils } from '0xtrails'

export function AaveLendButton({ recipient }: { recipient: `0x${string}` }) {
  const { sendTransaction, isPending } = useTrailsSendTransaction({
    actions: [
      lend({
        marketId: 'base-usdc-aave-v3-lending',
        amount: '100',
      }),
    ],
  })

  return (
    <button
      disabled={isPending}
      onClick={() =>
        sendTransaction({
          to: recipient,
          tokenAddress: erc20Utils.USDC.addressOn('base'),
          tokenAmount: '100000000',
        })
      }
    >
      {isPending ? 'Sending...' : 'Supply 100 USDC to Aave'}
    </button>
  )
}
Use useEarnMarkets to discover available market IDs. See Markets and Providers.

Calldata reference

When to use TRAILS_ROUTER_PLACEHOLDER_AMOUNT

// Correct — dynamic amount in calldata
encodeFunctionData({
  functionName: 'deposit',
  args: [TRAILS_ROUTER_PLACEHOLDER_AMOUNT, userAddress],
})

// Correct — function reads balance directly, no placeholder needed
encodeFunctionData({
  functionName: 'depositAll',
  args: [userAddress],
})
TRAILS_ROUTER_PLACEHOLDER_AMOUNT is uint256.max internally. Trails replaces it with the actual output amount at execution time.

See also