Skip to main content

Fund with Trails

Trails seamlessly enables users to deposit funds into your chain, app, or protocol with optimized UX flows and native integrations with multiple liquidity sources to optimize routes for low-slippage. Users can fund from any token they hold across any supported chain, eliminating the need for manual bridging and swapping - embedded seamlessly in the experience. Traditional funding flows require users to:
  • Find an external bridge supported by the chain / app with unknown trust assumptions
  • Swap to the required deposit token
  • Pay multiple gas fees across different networks
  • Wait for bridge confirmations
  • Navigate complex interfaces
Trails simplifies this by allowing users to deposit funds using any token from any supported chain in a single transaction, automatically handling all bridging, swapping, routing, and execution logic where the user deposit with a variable or fixed amount.

Use Cases

Funding flows in Trails are modeled as exact input by default. For example, “I have exactly 10 USDC tokens I want to send out from the origin chain, and I’ll receive 9.99 USDC on the destination.” Funding can done through a variety of methods such as the user’s connected wallet, QR code deposit, or onramping from various fiat options. This is ideal for a variety of use cases and apps such as the following:
  • Transfer funds into a perpetual exchange deposit address.
  • Bootstrap liquidity for lending protocols by transferring funds and executing a vault deposit.
  • Add funds to your balance on an application with crypto support.
  • Swap and deposit funds into staking contracts.
  • Onboard users to new chains seamlessly from any origin chain.
  • Top up a user’s account on a prediction market.

Examples

Depositing USDC into Base

This example shows how to use the Trails widget to enable a user to deposit into a chain with any token, in this case USDC on Base with a fixed amount:
import { TrailsWidget } from '0xtrails/widget'

export const BaseDeposit = () => {
  return (
    <TrailsWidget
      apiKey="YOUR_API_KEY"
      mode="fund"
      toAddress="0x..." // Recipient Address
      toChainId={8453} // Base
      toToken="USDC"
      toAmount="100" // 100 USDC
    />
  )
}

Depositing Fixed Amount into a DeFi Yield Vault

Trails is able to call any smart contract function on the destination chain. A common pattern is to fund a DeFi vault, for example depositing into an Aave pool on Base:
import { TrailsWidget } from '0xtrails/widget'
import { encodeFunctionData } from 'viem'
import { aaveABI } from './abi.ts'

export const AaveDepositExample = () => {
  // Aave V3 Pool contract on Base
  const AAVE_POOL_CONTRACT = "0xa0d9C1E9E48Ca30c8d8C3B5D69FF5dc1f6DFfC24"
  
  // Encode the deposit function call on Aave
  const depositCalldata = encodeFunctionData({
    abi: aaveABI,
    functionName: 'depositETH',
    args: [
      "0xA238Dd80C259a72e81d7e4664a9801593F98d1c5", // pool address
      "0x97...", // receiver's address
      0, // referralCode
    ],
  })

  return (
    <TrailsWidget
      apiKey="YOUR_API_KEY"
      toAddress={AAVE_POOL_CONTRACT}
      toAmount="100" // 100 USDC
      toChainId={8453} // Base
      toToken="USDC"
      toCalldata={depositCalldata}
      theme="auto"
    />
  )
}

Depositing variable amount into a DeFi Yield Vault

Additionally, Trails is easily integrated with virtually any ERC-4626 yield vault. For many use cases, you can simple encode the static calldata directly and pass it to Trails directly. However, for user-selected amounts where the amount is part of the calldata, you must encode it with a placeholder amount so the widget replaces it with the exact bridged/swapped output at execution time which we have a handy utility for to identify the data to replace. For example, a user can select any amount with any token to deposit into a USDC yearn vault on Katana:
import { TrailsWidget } from '0xtrails/widget'
import { TRAILS_CONTRACT_PLACEHOLDER_AMOUNT } from '0xtrails'
import { encodeFunctionData } from 'viem'
import { yearnVaultABI } from './abi.ts'

export const YearnDepositExample = () => {
  // ERC-4626 vault contract on destination chain
  const YEARN_VAULT_CONTRACT = "0x80c34BD3A3569E126e7055831036aa7b212cB159" // USDC Yearn Vault on Katana - replace with target vault address

  // Encode the deposit call with placeholder amount which will be replaced at execution time automatically
  const depositCalldata = encodeFunctionData({
    abi: yearnVaultABI,
    functionName: 'deposit',
    args: [
      TRAILS_CONTRACT_PLACEHOLDER_AMOUNT, // amount (auto-filled at runtime based on user's input)
      "0x...", // receiver address
    ],
  })

  return (
    <TrailsWidget
      apiKey="YOUR_API_KEY"
      mode="fund"
      toAddress={YEARN_VAULT_CONTRACT}
      toChainId={747474} // Katana
      toToken="USDC"
      toCalldata={depositCalldata}
      theme="auto"
    />
  )
}