Skip to main content

Overview

Fund mode implements input-driven deposit flows where users select the amount they want to send. The widget automatically routes funds from any supported chain/token to the destination. Trade Type: EXACT_INPUT - User specifies input amount, variable output amount after fees.

Configuration

Required Props

PropTypeDescription
apiKeystringYour project access key provided by Trails
mode"fund"Sets the widget to fund mode

Optional Props

PropTypeDescription
toAmountstringPre-set deposit amount (user can still modify)
toAddressstringDestination address or contract
toChainIdnumberDestination chain ID
toTokenstringDestination token symbol or address
toCalldatastringCalldata to execute on destination
slippageTolerancestring | numberSlippage tolerance (default: 0.5%)
swapProviderRouteProviderSwap provider: "AUTO", "LIFI", "RELAY", "SUSHI", "ZEROX", "CCTP"
bridgeProviderRouteProviderBridge provider: "AUTO", "LIFI", "RELAY", "CCTP"

Implementation

Basic Chain Funding

Simple deposit to a chain:
import { TrailsWidget } from '0xtrails/widget'

<TrailsWidget
  apiKey="YOUR_API_KEY"
  mode="fund"
  toAddress="0x97c4A952b46bEcaD0663f76357d3776ba11566E1"
  toChainId={8453} // Base
  toToken="USDC"
  onCheckoutComplete={({ sessionId }) => {
    console.log('Deposit completed:', sessionId)
  }}
>
  <button>Deposit to Base</button>
</TrailsWidget>

Deposit with Fiat Onramp

Fiat onramp functionality is enabled by default in fund mode, allowing users to purchase crypto with credit/debit cards, bank transfers, and other payment methods.
fundOnramp and buyCryptoOnramp props have been removed. Onramp funding is now enabled by default, so you can drop those props from your widget configuration.
<TrailsWidget
  apiKey="YOUR_API_KEY"
  mode="fund"
  toAddress="0x97c4A952b46bEcaD0663f76357d3776ba11566E1"
  toChainId={8453} // Base
  toToken="USDC"
  onCheckoutComplete={({ sessionId }) => {
    console.log('Deposit completed:', sessionId)
  }}
>
  <button>Deposit to Base</button>
</TrailsWidget>

Fixed Amount Deposit

Pre-populate a specific amount:
<TrailsWidget
  apiKey="YOUR_API_KEY"
  mode="fund"
  toAddress="0x..."
  toAmount="100" // Pre-filled, user can change
  toChainId={8453}
  toToken="USDC"
>
  <button>Deposit 100 USDC</button>
</TrailsWidget>

Protocol Deposit with Static Calldata

Deposit into a DeFi protocol with predefined parameters:
import { TrailsWidget } from '0xtrails/widget'
import { encodeFunctionData } from 'viem'

const aaveDepositCalldata = encodeFunctionData({
  abi: [{
    name: 'depositETH',
    type: 'function',
    stateMutability: 'payable',
    inputs: [
      { name: 'pool', type: 'address' },
      { name: 'onBehalfOf', type: 'address' },
      { name: 'referralCode', type: 'uint16' },
    ],
    outputs: [],
  }],
  functionName: 'depositETH',
  args: [
    '0xA238Dd80C259a72e81d7e4664a9801593F98d1c5', // Aave pool
    '0x97c4A952b46bEcaD0663f76357d3776ba11566E1', // User address
    0, // No referral code
  ],
})

<TrailsWidget
  apiKey="YOUR_API_KEY"
  mode="fund"
  toAddress="0xa0d9C1E9E48Ca30c8d8C3B5D69FF5dc1f6DFfC24" // Aave Pool on Base
  toAmount="100"
  toChainId={8453}
  toToken="USDC"
  toCalldata={aaveDepositCalldata}
/>

Dynamic Amount Protocol Deposit

Use placeholder amount for user-selected deposit amounts in calldata:
import { TrailsWidget, TRAILS_ROUTER_PLACEHOLDER_AMOUNT } from '0xtrails'
import { encodeFunctionData } from 'viem'

// Encode vault deposit with placeholder that gets replaced at execution
const vaultDepositCalldata = encodeFunctionData({
  abi: [{
    name: 'deposit',
    type: 'function',
    stateMutability: 'nonpayable',
    inputs: [
      { name: 'amount', type: 'uint256' },
      { name: 'receiver', type: 'address' },
    ],
    outputs: [{ name: 'shares', type: 'uint256' }],
  }],
  functionName: 'deposit',
  args: [
    TRAILS_ROUTER_PLACEHOLDER_AMOUNT, // Replaced with actual bridged amount
    '0x97c4A952b46bEcaD0663f76357d3776ba11566E1', // Receiver
  ],
})

<TrailsWidget
  apiKey="YOUR_API_KEY"
  mode="fund"
  toAddress="0x80c34BD3A3569E126e7055831036aa7b212cB159" // Yearn Vault
  toChainId={747474} // Katana
  toToken="USDC"
  toCalldata={vaultDepositCalldata}
>
  <button>Deposit to Yearn Vault</button>
</TrailsWidget>

Dynamic Calldata Pattern

When the deposit amount is part of the calldata and user-selected, use TRAILS_ROUTER_PLACEHOLDER_AMOUNT: Why it’s needed: In fund mode, the final output amount after swaps/bridging isn’t known until execution. The placeholder gets replaced with the actual output amount. When to use:
  • ERC-4626 vault deposits with dynamic amounts
  • Staking contracts that require amount parameter
  • Any function where amount is a parameter and user-selectable
When not needed:
  • Simple transfers to addresses
  • Static calldata where amount isn’t a parameter
  • Functions that read balance directly (e.g., depositAll())

Event Handlers

<TrailsWidget
  apiKey="YOUR_API_KEY"
  mode="fund"
  toAddress="0x..."
  toChainId={8453}
  toToken="USDC"
  onCheckoutStart={({ sessionId }) => {
    console.log('Deposit started:', sessionId)
  }}
  onCheckoutComplete={({ sessionId }) => {
    console.log('Deposit completed:', sessionId)
    // Update TVL, refresh balances, etc.
  }}
  onCheckoutError={({ sessionId, error }) => {
    console.error('Deposit failed:', error)
    // Handle error appropriately
  }}
/>

Use Cases

  • Chain Onboarding: Help users get started on new chains
  • Protocol Deposits: Deposit into lending protocols (Aave, Compound, Morpho)
  • Vault Deposits: Fund yield aggregators and vaults
  • Perp Exchange Funding: Deposit into perpetual exchanges
  • Liquidity Provision: Add liquidity to AMMs and pools
  • Staking: Fund staking contracts

See Also