> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trails.build/llms.txt
> Use this file to discover all available pages before exploring further.

# Swap

> Cross-chain token swaps with the focused Swap component

## 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

```tsx theme={null}
import { Swap } from '0xtrails/widget'

<Swap
  apiKey="YOUR_API_KEY"
  from={{ token: "ETH", chain: "ethereum" }}
  to={{ token: "USDC", chain: "base" }}
/>
```

User picks the amount in the UI.

## Props

### Required

| Prop     | Type     | Description    |
| -------- | -------- | -------------- |
| `apiKey` | `string` | Trails API key |

### Source (optional)

The `from` shape is a discriminated union on `paymentMethod`. For wallet, exchange, and crypto-transfer flows, source is crypto; for the fiat on-ramp arm, source is fiat.

**When `paymentMethod` is `"CONNECTED_WALLET"`, `"EXCHANGE"`, `"CRYPTO_TRANSFER"`, or omitted (crypto source):**

| Prop                 | Type               | Description                                                                 |
| -------------------- | ------------------ | --------------------------------------------------------------------------- |
| `from.token`         | `string`           | Pre-select source ERC-20 token — symbol (e.g. `"USDC"`) or contract 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)                        |
| `from.exchange`      | `string`           | Mesh exchange key (e.g. `"coinbase"`). `EXCHANGE` arm only.                 |

**When `paymentMethod` is `"CREDIT_DEBIT_CARD"` (fiat source):**

| Prop            | Type               | Description                                        |
| --------------- | ------------------ | -------------------------------------------------- |
| `from.currency` | `string`           | ISO 4217 fiat code, e.g. `"USD"`, `"EUR"`, `"GBP"` |
| `from.amount`   | `string \| number` | Pre-fill fiat amount                               |

### Destination (optional)

| Prop                        | Type                             | Description                                                       |
| --------------------------- | -------------------------------- | ----------------------------------------------------------------- |
| `to.token`                  | `string`                         | Pre-select or lock destination token — symbol or contract address |
| `to.chain`                  | `ChainIdentifier`                | Pre-select or lock destination chain                              |
| `to.amount`                 | `string \| number`               | Pre-fill destination amount                                       |
| `to.defaultToken`           | `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              |
| `to.supportedChains`        | `ChainIdentifier[]`              | Restrict destination chains the user can pick                     |
| `to.supportedTokensByChain` | `SupportedTokensByChainConfig[]` | Per-chain destination token allowlist                             |

### 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

```tsx theme={null}
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

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

### Lock destination, let user pick source

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

### Cross-chain swap with a fixed output amount

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

### Custom bridge provider

```tsx theme={null}
<Swap
  apiKey="YOUR_API_KEY"
  bridgeProvider="CCTP"
  from={{ token: "USDC", chain: "ethereum" }}
  to={{ token: "USDC", chain: "base" }}
/>
```

## Headless implementation

For custom UI, use the `useQuote` hook directly:

<Note>
  This example reads `walletClient` and `address` from wagmi hooks. Since [`0xtrails@0.16.0`](/sdk/changelog/0xtrails-0.16.0), wagmi is opt-in — install `@0xtrails/adapter-wagmi` and configure `wagmiAdapter` to keep using wagmi alongside Trails. See [SDK Hooks](/sdk/hooks#apps-with-wagmi).
</Note>

```tsx theme={null}
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:

```tsx theme={null}
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:

```tsx theme={null}
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

```ts theme={null}
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:

```tsx theme={null}
// 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

```tsx theme={null}
<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

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

## Error Handling

```tsx theme={null}
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

* [Swap use cases](/use-cases/swap) — portfolio rebalancing, stablecoin conversion
* [Hooks reference](/sdk/hooks) — `useQuote` details
* [Quote providers](/sdk/quote-providers) — CCTP, Relay, SushiSwap, and more
