Skip to main content

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

Swap mode provides a flexible interface for cross-chain token exchanges. Users control both input and output, with the widget handling optimal routing across bridges and DEXs. Trade types: EXACT_INPUT and EXACT_OUTPUT.

Quick start

import { Swap } from '0xtrails/widget'

<Swap
  apiKey="YOUR_API_KEY"
  from={{ currency: "ETH", chain: "ethereum" }}
  to={{ currency: "USDC", chain: "base" }}
/>
User picks the amount in the UI.

Props

Required

PropTypeDescription
apiKeystringTrails API key

Source (optional)

PropTypeDescription
from.currencystringPre-select source token symbol or address
from.chainChainIdentifierPre-select source chain — name, ID, or viem Chain
from.amountstring | numberPre-fill source amount
from.walletAddressstringSource wallet address (defaults to connected wallet)

Destination (optional)

PropTypeDescription
to.currencystringPre-select or lock destination token
to.chainChainIdentifierPre-select or lock destination chain
to.amountstring | numberPre-fill destination amount
to.defaultCurrencystringDefault destination token — user can change
to.defaultChainChainIdentifierDefault destination chain — user can change
to.calldatastringABI-encoded calldata to execute after the swap lands

Payment method (optional)

paymentMethod controls the source funding method. Omit to default to the connected wallet.
ValueMethod
"CONNECTED_WALLET"Connected wallet (default)
"CRYPTO_TRANSFER"QR code / address deposit
"CREDIT_DEBIT_CARD"Fiat on-ramp
"EXCHANGE"CEX transfer

Route options (optional)

PropTypeDescription
slippageTolerancenumber | stringSlippage tolerance (e.g. 0.005 for 0.5%)
swapProviderRouteProviderPreferred same-chain swap provider
bridgeProviderRouteProviderPreferred bridge provider
swapProviderFallbackbooleanFall back if preferred swap provider is unavailable
bridgeProviderFallbackbooleanFall back if preferred bridge provider is unavailable

Lifecycle callbacks

CallbackSignatureWhen it fires
onSwapStart({ sessionId }) => voidUser begins the swap flow
onSwapSuccess({ sessionId }) => voidSwap completes successfully
onSwapError({ sessionId, error }) => voidSwap encounters an error

Examples

Basic swap — user picks everything

import { Swap } from '0xtrails/widget'

<Swap
  apiKey="YOUR_API_KEY"
  onSwapSuccess={({ sessionId }) => console.log("swapped", sessionId)}
>
  <button>Swap Tokens</button>
</Swap>

Pre-configured cross-chain swap

<Swap
  apiKey="YOUR_API_KEY"
  from={{ currency: "ETH", chain: "ethereum" }}
  to={{ currency: "USDC", chain: "base" }}
  onSwapSuccess={({ sessionId }) => console.log("swapped", sessionId)}
/>

Lock destination, let user pick source

<Swap
  apiKey="YOUR_API_KEY"
  to={{ currency: "USDC", chain: "base" }}
  onSwapSuccess={({ sessionId }) => console.log("swapped", sessionId)}
/>

Cross-chain swap with a fixed output amount

<Swap
  apiKey="YOUR_API_KEY"
  from={{ currency: "USDC", chain: "ethereum" }}
  to={{ currency: "USDC", chain: "base", amount: "100" }}
  onSwapSuccess={({ sessionId }) => console.log("bridged", sessionId)}
/>

Custom bridge provider

<Swap
  apiKey="YOUR_API_KEY"
  bridgeProvider="CCTP"
  from={{ currency: "USDC", chain: "ethereum" }}
  to={{ currency: "USDC", chain: "base" }}
/>

Headless implementation

For custom UI, use the useQuote hook directly:
import { useQuote } from '0xtrails'
import { useWalletClient, useAccount } from 'wagmi'

export const CustomSwap = () => {
  const { data: walletClient } = useWalletClient()
  const { address } = useAccount()
  
  const { quote, send, isLoadingQuote, quoteError, refetchQuote } = useQuote({
    walletClient,
    from: {
      token: 'USDC',
      chain: 'ethereum',
      amount: '1', // human-readable USDC amount
    },
    to: {
      token: 'USDC',
      chain: 'base',
      recipient: address,
    },
    slippageTolerance: '0.005', // 0.5%
    onStatusUpdate: (states) => {
      console.log('Transaction status:', states)
    },
  })
  
  // Refresh quotes every 30 seconds
  useEffect(() => {
    const interval = setInterval(() => {
      refetchQuote?.()
    }, 30000)
    return () => clearInterval(interval)
  }, [refetchQuote])
  
  const handleSwap = async () => {
    if (!send) return
    
    try {
      const result = await send()
      console.log('Swap result:', result)
    } catch (error) {
      console.error('Swap failed:', error)
    }
  }
  
  if (isLoadingQuote) return <div>Loading quote...</div>
  if (!quote) return null

  return (
    <div>
      <p>From: {quote.originAmountFormatted} {quote.originToken.symbol}</p>
      <p>To: {quote.destinationAmountFormatted} {quote.destinationToken.symbol}</p>
      <button onClick={() => send?.()}>Execute Swap</button>
    </div>
  )
}

Trade Types

EXACT_INPUT

User specifies exact input amount; output amount varies:
const { quote } = useQuote({
  // ... other props
  from: { token: 'USDC', chain: 'base', amount: '100' },
})
// User sends exactly 100 USDC, receives ~0.039 ETH (varies with price)

EXACT_OUTPUT

User specifies exact output amount; input amount varies:
const { quote } = useQuote({
  // ... other props
  to: { token: 'ETH', chain: 'arbitrum', amount: '0.1' },
})
// User receives exactly 0.1 ETH, sends ~256 USDC (varies with price)

Quote Types

type UseQuoteProps = {
  walletClient?: WalletClient
  walletAddress?: string | null
  from: {
    token: string
    chain: ChainIdentifier
    amount?: string // human-readable; EXACT_INPUT when set
    decimals?: number
  }
  to: {
    token: string
    chain: ChainIdentifier
    recipient?: string | null
    amount?: string // human-readable; EXACT_OUTPUT when set
    decimals?: number
  }
  slippageTolerance?: string | number | null
  onStatusUpdate?: (txs: TransactionState[]) => void | null
}

type UseQuoteReturn = {
  quote: Quote | null
  send: (() => Promise<SwapReturn | null>) | null
  isLoadingQuote: boolean
  quoteError: unknown
  refetchQuote: (() => Promise<void>) | null
}

Quote Refresh Strategy

Quotes can become stale. Implement refresh logic:
// Refresh every 30 seconds
useEffect(() => {
  const interval = setInterval(() => {
    refetchQuote?.()
  }, 30000)
  return () => clearInterval(interval)
}, [refetchQuote])

// Refresh on window focus
useEffect(() => {
  const handleFocus = () => refetchQuote?.()
  window.addEventListener('focus', handleFocus)
  return () => window.removeEventListener('focus', handleFocus)
}, [refetchQuote])

Event Handling

Widget Events

<Swap
  apiKey="YOUR_API_KEY"
  onSwapStart={({ sessionId }) => {
    console.log('Swap started:', sessionId)
  }}
  onSwapSuccess={({ sessionId }) => {
    console.log('Swap completed:', sessionId)
  }}
  onSwapError={({ sessionId, error }) => {
    console.error('Swap failed:', error)
  }}
/>

useQuote Transaction Status

const { quote, send } = useQuote({
  // ... other props
  onStatusUpdate: (states) => {
    states.forEach(state => {
      console.log(`${state.chainId}: ${state.status}`)
      // States: 'pending', 'executing', 'completed', 'failed'
    })
  },
})

Error Handling

import { getIsUserRejectionError, InsufficientBalanceError } from '0xtrails'

const handleSwap = async () => {
  try {
    await send?.()
  } catch (error) {
    if (getIsUserRejectionError(error)) {
      console.log('User rejected transaction')
    } else if (error instanceof InsufficientBalanceError) {
      console.error('Insufficient balance')
    } else {
      console.error('Swap failed:', error)
    }
  }
}

Use Cases

  • Cross-chain Token Exchange: Swap any token to any other token across chains
  • Portfolio Rebalancing: Shift holdings between chains and tokens
  • Yield Optimization: Move assets to chains with better yields
  • Gas Token Acquisition: Get native tokens for new chains
  • Arbitrage: Take advantage of price differences across chains

Technical Notes

  • Quotes automatically factor in gas costs and fees
  • Multiple liquidity sources are queried for optimal routing
  • Supports both same-chain and cross-chain swaps
  • Native token wrapping/unwrapping is handled automatically
  • Slippage protection is built-in

See Also