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

# Swap wallet assets

> Choose a destination asset, quote a Trails route, and execute the intent through an active OMS Wallet session.

Use Trails to choose a destination for an owned wallet asset, quote the selected pair, and prepare the required origin transaction. OMS Wallet submits that transaction, then Trails executes and tracks the intent to its destination.

<Note>
  For a prebuilt React interface, render the [Trails Swap component](https://docs.trails.build/use-cases/swap). The widget can manage wallet connections itself or share an app-owned wallet through [Trails adapters](https://docs.trails.build/sdk/adapters/overview). For example, you can share an OMS Wallet session through the [OMS Wallet connector](/wallets/sdk/typescript/using-with-wagmi-connector) and the [Trails wagmi adapter](https://docs.trails.build/sdk/adapters/wagmi). This guide instead uses OMS Wallet directly and implements the Trails intent lifecycle without the widget.
</Note>

## Prerequisites

* An active OMS Wallet session from the [TypeScript quickstart](/wallets/sdk/typescript/quickstart)
* A [Trails API key](https://dashboard.trails.build/landing)
* A positive balance of the selected source asset and enough balance in an available fee token when the origin transaction is not sponsored

Use the OMS Indexer to populate the source-asset picker from the wallet's actual balances. See [balances and history](/wallets/sdk/typescript/balances-and-history).

<Warning>
  This guide submits mainnet transactions with real funds. Show the user the route, expected and minimum output, fees, price impact, and expiry before requesting confirmation.
</Warning>

## Install the dependencies

```bash theme={null}
pnpm add @polygonlabs/oms-wallet @0xtrails/api viem
```

## Choose a destination asset

The example starts with native POL on Polygon PoS as the source asset. Use the zero address for a native token or the contract address for an ERC-20 token.

Use `getTokenList` to populate the destination picker, then filter the result to networks supported by OMS Wallet. This example searches for USDC on Polygon PoS to keep the flow focused:

```typescript theme={null}
import {
  FundMethod,
  IntentMode,
  IntentStatus,
  TradeType,
  TrailsApi,
} from '@0xtrails/api'
import {
  FeeOptionSelector,
  Networks,
  TransactionStatus,
  findNetworkById,
} from '@polygonlabs/oms-wallet'
import {
  formatUnits,
  parseUnits,
  zeroAddress,
  type Address,
  type Hex,
} from 'viem'

const trailsClient = new TrailsApi('YOUR_TRAILS_API_KEY')
const walletAddress = omsWallet.wallet.walletAddress as Address

const source = {
  chainId: Networks.polygon.id,
  tokenAddress: zeroAddress,
  decimals: 18,
  amount: '0.5',
}

const { tokens: destinationTokens } = await trailsClient.getTokenList({
  chainIds: [Networks.polygon.id],
  searchQuery: 'USDC',
  limit: 20,
  includeAllListed: true,
  includeExternal: false,
})

const destinations = destinationTokens.filter((token) =>
  findNetworkById(token.chainId),
)

console.table(
  destinations.map((token) => ({
    network: findNetworkById(token.chainId)?.name,
    symbol: token.symbol,
    address: token.address,
  })),
)
```

Add other OMS-supported chain IDs to `chainIds` when your picker supports cross-chain destinations. The token catalog provides destination candidates; `quoteIntent` confirms whether Trails has a live route for the selected pair and amount.

This guide selects native USDC on Polygon from the catalog. In your application, replace this selection with the token chosen in your asset picker:

```typescript theme={null}
const polygonUsdc =
  '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359'

const destination = destinations.find(
  (token) =>
    token.chainId === Networks.polygon.id &&
    token.address.toLowerCase() === polygonUsdc.toLowerCase(),
)

if (!destination) {
  throw new Error('The selected destination is not in the Trails token catalog.')
}
```

## Quote the selected route

Request an exact-input quote for the selected amount. A successful quote confirms the live route, and Trails selects the swap and bridge providers automatically:

```typescript theme={null}
const amountInRaw = parseUnits(source.amount, source.decimals)

const { intent } = await trailsClient.quoteIntent({
  ownerAddress: walletAddress,
  originChainId: source.chainId,
  originTokenAddress: source.tokenAddress,
  originTokenAmount: amountInRaw,
  destinationChainId: destination.chainId,
  destinationTokenAddress: destination.address,
  destinationToAddress: walletAddress,
  tradeType: TradeType.EXACT_INPUT,
  fundMethod: FundMethod.WALLET,
  mode: IntentMode.SWAP,
  options: {
    slippageTolerance: 0.01,
  },
})

const confirmation = {
  routeProviders: intent.quote.routeProviders,
  expectedOutput: formatUnits(
    intent.quote.toAmount,
    destination.decimals,
  ),
  minimumOutput: formatUnits(
    intent.quote.toAmountMin,
    destination.decimals,
  ),
  totalFeeUsd: intent.fees.totalFeeUsd,
  priceImpactPercent: intent.quote.priceImpact,
  estimatedDurationSeconds: intent.quote.estimatedDuration,
  expiresAt: intent.expiresAt,
}

console.log('Swap confirmation:', confirmation)
```

The 1% `slippageTolerance` applies to this quote. Request a new quote when the amount or either asset changes, or when `expiresAt` passes. Do not commit the intent until the user confirms the current values.

## Commit and submit the origin transaction

Commit the quoted intent, then submit the exact deposit transaction returned by that intent through OMS Wallet:

```typescript theme={null}
if (Date.parse(intent.expiresAt) <= Date.now()) {
  throw new Error('The Trails quote expired. Request a new quote.')
}

const committed = await trailsClient.commitIntent({ intent })

if (committed.intentId !== intent.intentId) {
  throw new Error('Trails committed a different intent.')
}

const deposit = intent.depositTransaction
const depositNetwork = findNetworkById(deposit.chainId)

if (
  deposit.chainId !== source.chainId ||
  deposit.tokenAddress.toLowerCase() !==
    source.tokenAddress.toLowerCase() ||
  deposit.amount !== amountInRaw
) {
  throw new Error('The deposit transaction does not match the quote.')
}

if (!depositNetwork) {
  throw new Error('OMS Wallet does not support the deposit network.')
}

const sent = await omsWallet.wallet.sendTransaction({
  network: depositNetwork,
  to: deposit.to as Address,
  data: deposit.data as Hex,
  value: deposit.value,
  selectFeeOption: FeeOptionSelector.firstAvailable,
  statusPolling: {
    timeoutMs: 120_000,
  },
})

if (
  sent.status !== TransactionStatus.Executed ||
  sent.txnHash === undefined
) {
  throw new Error(`The origin transaction ended with ${sent.status}.`)
}
```

Validate that the deposit network, token, and amount still match the quote the user confirmed before submitting it. Preserve both the OMS `txnId` and the Trails `intentId` so your application can recover an uncertain status without submitting the origin transaction again.

## Execute and monitor the intent

Give Trails the confirmed origin transaction hash, then wait for the intent to reach a terminal state:

```typescript theme={null}
await trailsClient.executeIntent({
  intentId: committed.intentId,
  depositTransactionHash: sent.txnHash,
})

let receiptResult = await trailsClient.waitIntentReceipt({
  intentId: committed.intentId,
})

while (!receiptResult.done) {
  receiptResult = await trailsClient.waitIntentReceipt({
    intentId: committed.intentId,
    lastReceiptStates: receiptResult.receiptStates,
  })
}

if (receiptResult.intentReceipt.status !== IntentStatus.SUCCEEDED) {
  throw new Error(
    `Swap ended with ${receiptResult.intentReceipt.status}.`,
  )
}

console.log('Swap receipt:', receiptResult.intentReceipt)
```

Refresh the source and destination balances through the OMS Indexer after the receipt succeeds. A completed origin transaction does not by itself mean the destination asset has arrived.

Bound receipt waiting in your application. If it times out, resume with the stored `intentId` instead of submitting the origin transaction again.

For provider overrides and other advanced configuration, see [route providers](https://docs.trails.build/sdk/quote-providers). For details about quoting, committing, executing, and monitoring intents, see the [Trails API SDK reference](https://docs.trails.build/api-reference/trails-api-sdk).
