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
| Prop | Type | Description |
|---|
apiKey | string | Trails API key |
Destination (optional)
| Prop | Type | Description |
|---|
to.recipient | string | Protocol contract address |
to.currency | string | Token to deposit (symbol or address) |
to.chain | ChainIdentifier | Protocol chain — name, ID, or viem Chain |
to.defaultCurrency | string | Default token — user can change |
to.defaultChain | ChainIdentifier | Default chain — user can change |
to.amount | string | number | Fixed deposit amount |
to.calldata | string | ABI-encoded function call to execute after deposit |
Source (optional)
| Prop | Type | Description |
|---|
from.currency | string | Source token symbol or address |
from.chain | ChainIdentifier | Source chain |
from.amount | string | number | Source amount |
from.walletAddress | string | Source wallet (defaults to connected wallet) |
Payment method (optional)
paymentMethod controls how the user sources funds:
| Value | Method |
|---|
"CONNECTED_WALLET" | Connected wallet (default) |
"CRYPTO_TRANSFER" | QR code / address deposit |
"CREDIT_DEBIT_CARD" | Fiat on-ramp |
"EXCHANGE" | CEX transfer |
Lifecycle callbacks
| Callback | Signature | When it fires |
|---|
onEarnStart | ({ sessionId }) => void | User begins the deposit flow |
onEarnSuccess | ({ sessionId }) => void | Deposit completes successfully |
onEarnError | ({ sessionId, error }) => void | Deposit 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