Documentation Index
Fetch the complete documentation index at: https://anypay-trails-api-docs.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. If you want to deep-link users directly into withdrawals, use Withdraw mode.
Configuration
Required Props
| Prop | Type | Description |
|---|
mode | "earn" | Sets the widget to earn mode |
Required Props (Custom Protocol Integration)
| Prop | Type | Description |
|---|
toAddress | string | Protocol contract address |
toChainId | number | Protocol’s chain ID |
toToken | string | Token to deposit |
toCalldata | string | Encoded function call |
Optional Props (Custom Integration)
| Prop | Type | Description |
|---|
toAmount | string | Fixed deposit amount |
slippageTolerance | string | number | Slippage tolerance |
Implementation
Use pre-integrated protocols (Aave, Morpho, etc.):
import { TrailsWidget } from '0xtrails/widget'
<TrailsWidget
apiKey="YOUR_API_KEY"
mode="earn"
onCheckoutComplete={({ sessionId }) => {
console.log('Deposit completed:', sessionId)
}}
/>
Fixed Amount Protocol Deposit
Deposit a specific amount into a protocol:
import { TrailsWidget } from '0xtrails/widget'
import { encodeFunctionData } from 'viem'
const AAVE_POOL = '0x794a61358D6845594F94dc1DB02A252b5b4814aD' // Arbitrum
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, // 1 USDC (6 decimals)
'0x97c4A952b46bEcaD0663f76357d3776ba11566E1', // User address
0, // No referral code
],
})
<TrailsWidget
apiKey="YOUR_API_KEY"
mode="earn"
toAddress={AAVE_POOL}
toAmount="1"
toChainId={42161} // Arbitrum
toToken="USDC"
toCalldata={supplyCalldata}
>
<button>Deposit 1 USDC to Aave</button>
</TrailsWidget>
Dynamic Amount Deposits
Use placeholder amount for user-selected deposit amounts:
import { TrailsWidget, TRAILS_ROUTER_PLACEHOLDER_AMOUNT } from '0xtrails'
import { encodeFunctionData } from 'viem'
const STAKING_CONTRACT = '0x...'
// Encode stake function with placeholder amount
const stakeCalldata = encodeFunctionData({
abi: [{
name: 'stake',
type: 'function',
stateMutability: 'nonpayable',
inputs: [{ name: 'amount', type: 'uint256' }],
outputs: [],
}],
functionName: 'stake',
args: [TRAILS_ROUTER_PLACEHOLDER_AMOUNT], // Replaced at execution
})
<TrailsWidget
apiKey="YOUR_API_KEY"
mode="earn"
toAddress={STAKING_CONTRACT}
toChainId={1}
toToken="ETH"
toCalldata={stakeCalldata}
>
<button>Stake ETH (Choose Amount)</button>
</TrailsWidget>
ERC-4626 Vault Deposit
Standard vault deposit pattern:
import { TrailsWidget, TRAILS_ROUTER_PLACEHOLDER_AMOUNT } from '0xtrails'
import { encodeFunctionData } from 'viem'
const VAULT_ADDRESS = '0x...'
const USER_ADDRESS = '0x...'
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,
USER_ADDRESS,
],
})
<TrailsWidget
apiKey="YOUR_API_KEY"
mode="earn"
toAddress={VAULT_ADDRESS}
toChainId={1}
toToken="USDC"
toCalldata={depositCalldata}
>
<button>Deposit to Vault</button>
</TrailsWidget>
Calldata Patterns
When to Use TRAILS_ROUTER_PLACEHOLDER_AMOUNT
Use the placeholder when:
- Deposit amount is a function parameter
- User selects the deposit amount
- Amount needs to reflect post-swap/bridge value
// ✅ Correct - dynamic amount in calldata
encodeFunctionData({
functionName: 'deposit',
args: [TRAILS_ROUTER_PLACEHOLDER_AMOUNT, userAddress],
})
When Static Values Work
Use static values when:
- Function doesn’t take amount as parameter
- Amount is fixed and known in advance
- Contract reads balance internally
// ✅ Correct - depositAll() reads balance
encodeFunctionData({
functionName: 'depositAll',
args: [userAddress],
})
Protocol-Specific Examples
Aave V3 Deposit
const aaveSupply = encodeFunctionData({
abi: aavePoolABI,
functionName: 'supply',
args: [
tokenAddress, // Asset to supply
amount, // Amount to supply
onBehalfOf, // Who receives aTokens
referralCode, // Referral code (usually 0)
],
})
Morpho Vault Deposit
const morphoDeposit = encodeFunctionData({
abi: morphoVaultABI,
functionName: 'deposit',
args: [
assets, // Amount of assets
receiver, // Who receives shares
],
})
Yearn Vault Deposit
const yearnDeposit = encodeFunctionData({
abi: yearnVaultABI,
functionName: 'deposit',
args: [
amount, // Amount to deposit (use placeholder)
receiver, // Share recipient
],
})
Liquid Staking (Lido)
const lidoSubmit = encodeFunctionData({
abi: lidoABI,
functionName: 'submit',
args: [
referralAddress, // Referral (use zero address if none)
],
})
// Note: Amount sent via msg.value, not parameter
Event Handling
<TrailsWidget
apiKey="YOUR_API_KEY"
mode="earn"
toAddress="0x..."
toChainId={1}
toToken="USDC"
toCalldata={calldata}
onCheckoutStart={({ sessionId }) => {
console.log('Deposit started:', sessionId)
}}
onCheckoutComplete={({ sessionId }) => {
console.log('Deposit completed:', sessionId)
// Refresh user's vault balance
// Update TVL display
// Show success notification
}}
onCheckoutError={({ sessionId, error }) => {
console.error('Deposit failed:', error)
// Handle error state
}}
/>
Supported Protocols
Pre-integrated
Trails has built-in support for:
- Aave V3: Lending and borrowing across multiple chains
- Morpho: Optimized lending pools
Custom Integration
Any protocol with a deposit/stake function can be integrated:
- Yearn Finance vaults
- Compound V3
- Convex Finance
- Curve pools
- Custom staking contracts
- Any ERC-4626 vault
Use Cases
- Lending Protocol Deposits: Supply assets to earn interest (Aave, Compound, Morpho)
- Yield Vault Deposits: Deposit into automated yield strategies (Yearn)
- Liquid Staking: Stake ETH while maintaining liquidity (Lido, Rocket Pool)
- LP Token Staking: Stake LP tokens in farming contracts
- Governance Staking: Lock tokens for governance rights
- Auto-compounding Vaults: Deposit into vaults that auto-compound rewards
Technical Notes
- Earn mode handles token approvals automatically
- Supports both ERC-20 tokens and native tokens (ETH, MATIC, etc.)
- All cross-chain swapping and bridging is handled
- Calldata execution is atomic with the deposit
TRAILS_ROUTER_PLACEHOLDER_AMOUNT = uint256.max internally
- Works with any contract that has
onBehalfOf or receiver parameter
Error Handling
import { getIsUserRejectionError, InsufficientBalanceError } from '0xtrails'
<TrailsWidget
apiKey="YOUR_API_KEY"
mode="earn"
toAddress="0x..."
toCalldata={calldata}
onCheckoutError={({ sessionId, error }) => {
if (getIsUserRejectionError(error)) {
// User cancelled the transaction
showNotification('Transaction cancelled')
} else if (error.includes('insufficient balance')) {
showNotification('Insufficient balance for deposit')
} else if (error.includes('slippage')) {
showNotification('Price impact too high, try smaller amount')
} else {
showNotification('Deposit failed, please try again')
}
}}
/>
See Also