Skip to main content

Swap with Trails

Trails enables seamless token swapping across multiple blockchain networks in 1-click, allowing users to exchange tokens without manually bridging assets or dealing with multiple DEXs available via an integrated widget or API.

Enhanced Swapping Experience

Traditional cross-chain swapping requires users to:
  • Approve any ERC20 tokens
  • Bridge tokens to the target chain
  • Find and interact with DEXs on multiple networks
  • Pay gas fees on each chain
  • Wait for bridge confirmations
We optimize the UX flow from any wallet to enable swaps across multiple liquidity sources & chains. All interactions are executed in a single confirmation for the user. Notably, this works across any wallet or application, ERC-7702 or 4337 is not required for batching transactions.

Use Cases

Ideal for a variety of token exchange scenarios:
  • Cross-chain token swaps (e.g., ETH on Mainnet → USDC on Base)
  • Same-chain token exchanges with optimal routing
  • Portfolio rebalancing across chains
  • Converting between stablecoins with minimal slippage

Examples

Simple Cross-Chain Token Swap

Basic cross-chain swap implementation using the Trails widget with callbacks:
import { TrailsWidget } from '0xtrails/widget'

export const BasicSwap = () => {
  return (
    <TrailsWidget
      apiKey="YOUR_API_KEY"
      mode="swap"
      onCheckoutComplete={({ sessionId }) => {
        console.log('Swap completed:', sessionId)
      }}
      onCheckoutError={({ sessionId, error }) => {
        console.error('Swap failed:', sessionId, error)
      }}
    >
      <button className="swap-button">
        Swap Tokens
      </button>
    </TrailsWidget>
  )
}

Swap with Custom Theme

Customize the appearance to match your brand:
import { TrailsWidget } from '0xtrails/widget'

export const ThemedSwap = () => {
  return (
    <TrailsWidget
      apiKey="YOUR_API_KEY"
      mode="swap"
      theme="dark"
      onCheckoutComplete={({ sessionId }) => {
        console.log('Themed swap completed:', sessionId)
      }}
    >
      <div className="custom-swap-trigger">
        <span>🔄</span>
        <span>Swap Tokens</span>
      </div>
    </TrailsWidget>
  )
}

Advanced Swap with Custom Quote Provider

While Trails automatically optimizes the selected route by the user, you may opt to specify a quote or bridge provider to optimize for your use case. For example, using Circle’s CCTP V2 for bridging:
import { TrailsWidget } from '0xtrails/widget'

export const AdvancedSwap = () => {
  return (
    <TrailsWidget
      apiKey="YOUR_API_KEY"
      mode="swap"
      bridgeProvider="CCTP"
      onCheckoutComplete={({ sessionId }) => {
        console.log('Advanced swap completed:', sessionId)
        // Handle post-swap logic
      }}
      onCheckoutError={({ sessionId, error }) => {
        console.error('Swap error:', sessionId, error)
        // Handle error state
      }}
    >
      <button className="advanced-swap-button">
        Swap with a Custom Provider
      </button>
    </TrailsWidget>
  )
}

Using the useQuote Hook

For more control over the swap process for headless implementations, use the useQuote hook directly:
import { useQuote, TradeType } from '0xtrails'
import { useWalletClient, useAccount } from 'wagmi'
import { useState } from 'react'

export const CustomSwapComponent = () => {
  const { data: walletClient } = useWalletClient()
  const { address } = useAccount()
  const [fromToken, setFromToken] = useState('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48') // USDC on Ethereum
  const [toToken, setToToken] = useState('0x833589fcd6edb6e08f4c7c32d4f71b54bda02913') // USDC on Base
  const [amount, setAmount] = useState('1000000') // 1 USDC

  const { quote, swap, isLoadingQuote, quoteError } = useQuote({
    walletClient,
    fromTokenAddress: fromToken,
    fromChainId: 1, // Ethereum
    toTokenAddress: toToken,
    toChainId: 8453, // Base
    swapAmount: amount,
    tradeType: TradeType.EXACT_INPUT,
    toAddress: address,
    slippageTolerance: '0.005', // 0.5%
    onStatusUpdate: (states) => {
      console.log('Swap progress:', states)
    },
  })

  const handleSwap = async () => {
    if (!swap) return
    
    try {
      const result = await swap()
      console.log('Swap result:', result)
    } catch (error) {
      console.error('Swap failed:', error)
    }
  }

  if (isLoadingQuote) {
    return <div>Loading quote...</div>
  }

  if (quoteError) {
    return <div>Error getting quote: {String(quoteError)}</div>
  }

  if (!quote) {
    return <div>No quote available</div>
  }

  return (
    <div className="swap-component">
      <div className="quote-info">
        <h3>Swap Quote</h3>
        <p>From: {quote.originAmountFormatted} {quote.originToken.symbol} on {quote.originChain.name}</p>
        <p>To: {quote.destinationAmountFormatted} {quote.destinationToken.symbol} on {quote.destinationChain.name}</p>
        <p>Fee: {quote.totalFeeAmountUsdDisplay}</p>
        <p>Estimated time: {quote.completionEstimateSeconds}s</p>
      </div>
      
      <button onClick={handleSwap} className="execute-swap-button">
        Execute Swap
      </button>
    </div>
  )
}

Why Swap with Trails?

  • Unified Liquidity: Access tokens from the user’s wallet across all supported chains in one transaction
  • High Performance Balance Retrieval: A high-performant indexer is natively integrated to query the entirety of a user’s balance for smooth UX and price retrieval
  • Reduced Complexity: Eliminate manual bridging and multi-step swaps (Approval and Transfer)
  • Customized Branding: Brand or theme the widget however you’d like to be native to your protocol or application
  • Seamless UX: Automatic routing finds optimal swap paths for low slippage for improved UX
  • Gasless & Alt-fee token options: Support for gasless or alternative gas tokens like USDC for transactions to reduce friction
  • Real-time Quotes: Live pricing and route optimization
  • Chain Abstraction: Built from the ground up to support multichain environments and abstract underlying chains.

Next Steps

Learn more about customizing and configuring swap functionality: