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
| Prop | Type | Description |
|---|
apiKey | string | Trails API key |
Source (optional)
| Prop | Type | Description |
|---|
from.currency | string | Pre-select source token symbol or address |
from.chain | ChainIdentifier | Pre-select source chain — name, ID, or viem Chain |
from.amount | string | number | Pre-fill source amount |
from.walletAddress | string | Source wallet address (defaults to connected wallet) |
Destination (optional)
| Prop | Type | Description |
|---|
to.currency | string | Pre-select or lock destination token |
to.chain | ChainIdentifier | Pre-select or lock destination chain |
to.amount | string | number | Pre-fill destination amount |
to.defaultCurrency | string | Default destination token — user can change |
to.defaultChain | ChainIdentifier | Default destination chain — user can change |
to.calldata | string | ABI-encoded calldata to execute after the swap lands |
Payment method (optional)
paymentMethod controls the source funding method. Omit to default to the connected wallet.
| Value | Method |
|---|
"CONNECTED_WALLET" | Connected wallet (default) |
"CRYPTO_TRANSFER" | QR code / address deposit |
"CREDIT_DEBIT_CARD" | Fiat on-ramp |
"EXCHANGE" | CEX transfer |
Route options (optional)
| Prop | Type | Description |
|---|
slippageTolerance | number | string | Slippage tolerance (e.g. 0.005 for 0.5%) |
swapProvider | RouteProvider | Preferred same-chain swap provider |
bridgeProvider | RouteProvider | Preferred bridge provider |
swapProviderFallback | boolean | Fall back if preferred swap provider is unavailable |
bridgeProviderFallback | boolean | Fall back if preferred bridge provider is unavailable |
Lifecycle callbacks
| Callback | Signature | When it fires |
|---|
onSwapStart | ({ sessionId }) => void | User begins the swap flow |
onSwapSuccess | ({ sessionId }) => void | Swap completes successfully |
onSwapError | ({ sessionId, error }) => void | Swap 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>
<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
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
<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