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:
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 is an exclusive executor dedicated to WalletSwaps, separate from the one you passed to ServerWallet. WalletSwaps holds it for each swap step and for every ordinary debit, then calls the wallet, which runs its own executor. See 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.
Do not pass the ServerWallet executor to WalletSwaps. Every WalletSwaps call runs inside its executor and then calls the wallet, which waits on its own executor. When both are the same exclusive executor, the inner call waits for the outer one and neither completes.
Once a wallet uses swaps, send its ordinary transfers through swaps.prepareTransfer and swaps.executeTransfer rather than the ServerWallet methods. They take the same arguments, and they refuse a debit with DEBIT_PENDING while a swap, recovery, or earlier transfer on that chain is unresolved. Run other wallet calls, such as rotate or signing, through swaps.walletCommand(() => wallet.rotate()) so they serialize against swap work too.
EvmChainReader accepts one HTTPS RPC URL per chain. A missing URL, or one carrying credentials or a fragment, raises RPC_CONFIGURATION when the SDK first reads that chain.
Quote a swap
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:
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:
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
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.
prepareRecovery(id, recoveryId, source) takes the swap ID, a recovery ID that you own, and 'origin' or 'destination' for the side of the route that holds the funds. confirmRecovery(id, recoveryId, revision) authorizes the revision of that entry in the swap view’s recoveries. Both return the swap view, and tick then drives the confirmed recovery.
Recovery is available even when enabled is false, so turning off new swaps does not strand funds from existing ones.
Errors