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

# Swaps

> Quote, review, and execute same-chain and cross-chain swaps from a Server Wallet, with durable progress tracking and recovery.

`WalletSwaps` runs same-chain and cross-chain swaps from a Server Wallet. A swap is quoted first, reviewed by your application, and only then confirmed, so no funds move on a quote alone.

Swaps are durable. Progress continues across restarts, which means your application supplies storage and a scheduler rather than awaiting a single call.

Import the swap surface from the SDK's `/trails` entry point:

```typescript theme={null}
import {
  WalletSwaps,
  TrailsClient,
  EvmChainReader,
  type SwapAsset,
  type SwapStore,
} from '@polygonlabs/oms-server-wallet-sdk/trails'
```

## Configure the swap client

```typescript theme={null}
declare const swapStore: SwapStore
declare const wallet: ServerWallet
declare const executor: ExclusiveExecutor

// Host-approved asset policy. Never build this from request input.
const assets: SwapAsset[] = [
  { chainId: 137, asset: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', decimals: 6, symbol: 'USDC' },
  { chainId: 8453, asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', decimals: 6, symbol: 'USDC' },
]

const swaps = new WalletSwaps({
  wallet,
  executor,
  store: swapStore,
  assets,
  enabled: true,
  trails: new TrailsClient({
    apiKey: 'YOUR_TRAILS_API_KEY',
    origin: 'https://wallet-api.example.com',
  }),
  chains: new EvmChainReader({
    137: 'https://YOUR_POLYGON_RPC',
    8453: 'https://YOUR_BASE_RPC',
  }),
})
```

Four of these deserve attention:

* **`assets`** is your own allowlist of tradable assets. A swap request naming an asset outside it is rejected, which is what keeps a caller from routing funds into an arbitrary token.
* **`executor`** must be the same exclusive executor that your ordinary transfers use. Swaps and transfers debit the same wallet, so they have to serialize against each other. See [storage and concurrency](/wallets/sdk/server/storage-and-concurrency).
* **`trails.apiKey`** is separate from your OMS publishable key and belongs only in your backend. Omitting `trails` raises `SWAPS_UNAVAILABLE`.
* **`enabled`** must be `true` to start new swaps. When it is not, `SWAPS_DISABLED` is raised and existing swaps still progress.

`EvmChainReader` accepts one HTTPS RPC URL per chain. URLs carrying credentials or a fragment are rejected.

## Quote a swap

```typescript theme={null}
const swapId = crypto.randomUUID()

const quoted = await swaps.quoteSwap(swapId, {
  originChainId: 137,
  originAsset: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',
  destinationChainId: 8453,
  destinationAsset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
  amount: '10000000',
  slippageBps: 50,
})

console.log('Phase:', quoted.phase)
console.log('Quote revision:', quoted.quote.revision)
```

`amount` is a decimal string in the origin asset's base units, so `'10000000'` is 10 USDC at 6 decimals. Never pass a floating-point token amount.

`slippageBps` accepts `10`, `50`, or `100`. A same-chain swap uses the same call with one `chainId` on both sides.

The wallet must be restored before quoting, otherwise `WALLET_PENDING` is raised.

## Review and confirm

Quoting does not move funds. The swap stays in the `quoted` phase until you confirm it with the exact quote revision you reviewed.

`quote` carries the terms your review should apply to:

| Field            | Meaning                                               |
| ---------------- | ----------------------------------------------------- |
| `revision`       | The identifier you pass to `confirmSwap`              |
| `expectedOutput` | Destination amount at the quoted rate, in base units  |
| `minimumOutput`  | Least the swap will deliver at the requested slippage |
| `inputAmount`    | Origin amount to be spent                             |
| `priceImpact`    | Price impact of the route                             |
| `fees`           | Route fees                                            |
| `providers`      | Route providers                                       |
| `expiresAt`      | When the quote stops being confirmable                |

```typescript theme={null}
// Apply your own authorization rules to `quoted` before this point.
const confirmed = await swaps.confirmSwap(swapId, quoted.quote.revision)
```

If the quote changed since you read it, `confirmSwap` raises `STALE_QUOTE` rather than executing against terms you did not review. Re-read the swap, apply your rules to the new quote, and confirm that revision.

Confirming is idempotent. A second call with the same revision returns the current state instead of starting another swap.

## Drive and track progress

A confirmed swap advances through activation, funding, and settlement. Your application drives that work:

```typescript theme={null}
// Call on a schedule: a persistent timer in Node, or an alarm per wallet on Workers.
await swaps.tick()

const view = await swaps.getSwap(swapId)
console.log(view.phase, view.nextAt)

const recent = await swaps.listSwaps()
```

`tick(limit)` performs the work that is due and returns. `nextAt` on a swap view tells you when that swap next needs attention, or is `null` when it needs none. Funding a swap uses a sponsored transfer, so the wallet pays no gas.

Use `reconcileSwap(id)` when a swap's upstream result is uncertain. As with transfers, never start a second swap because a response was lost.

## Phases

| Phase        | Meaning                                      |
| ------------ | -------------------------------------------- |
| `quoted`     | Quoted, awaiting your confirmation           |
| `preparing`  | Preparing the funding transfer               |
| `activating` | Activating the swap intent                   |
| `funding`    | Funding in progress                          |
| `settling`   | Awaiting settlement on the destination chain |
| `succeeded`  | Complete                                     |
| `expired`    | The quote expired before confirmation        |
| `failed`     | Terminal failure                             |
| `attention`  | Requires operator review                     |
| `recovering` | Recovery in progress                         |
| `refunded`   | Funds returned to the wallet                 |

`attention` is not a failure. It means the swap cannot proceed without a decision from you, so surface it rather than retrying it.

## Recovery

When a swap leaves funds short of the destination, `prepareRecovery` builds a recovery that returns them to the wallet, and `confirmRecovery` authorizes it. Recovery follows the same review-then-confirm shape as a swap, and it is restricted to the wallet's own address and your reviewed assets.

Recovery is available even when `enabled` is `false`, so turning off new swaps does not strand funds from existing ones.

## Errors

| Code                | Cause                                                   |
| ------------------- | ------------------------------------------------------- |
| `SWAPS_UNAVAILABLE` | No `trails` gateway was configured                      |
| `SWAPS_DISABLED`    | `enabled` is not `true`, so new swaps are refused       |
| `STALE_QUOTE`       | The confirmed revision does not match the current quote |
| `WALLET_PENDING`    | The wallet has not been restored yet                    |
| `WALLET_DISABLED`   | The wallet is disabled                                  |
