swap.v1.SwapService converts one Asset in an account's Trading balance into another through Polyester's spot order books. Use it to build balance conversion into an account dashboard or trading application.
Swap is available through ConnectRPC on Testnet at api.testnet.polyester.com. Mainnet is coming soon. This service has no REST resource endpoints.
The source and destination stay in the same Trading Account. There is no deposit address, external payout, or bridge. For deposit-funded, cross-chain swaps, use the separate QuickSwap documentation for swap.quickswap.v1.
Authentication and account selection
Every method accepts a session token or a signed Ed25519 API key. Follow ConnectRPC authentication and Ed25519 API keys.
Omit terms.subaccount_id to use the session owner's Main Account, or the Subaccount an API key is bound to. A session can select a Subaccount by its public ID when the caller has the required role. An API key can act only on its bound account; a Main Account key cannot select a Subaccount.
The caller needs SWAP permission. API key policies and Subaccount policies must allow the SWAP action; a Subaccount session also needs a trading role that grants it. The resulting orders remain subject to the Subaccount's spot trading policy. See API key policies and Subaccounts and roles.
Quote, create, and follow
Request a quote
Call QuoteSwap with the source and destination unified Asset IDs, a positive source amount_e18, and max_slippage_bps from 1 to 1000. One basis point is 0.01%.
All Swap amounts use 18-decimal integers, including amounts for Assets whose spot order quantities use another scale. Use the U128 representation described in Scaled integers, without floating-point conversion.
The quote returns estimated output after fees, a minimum output at the requested slippage, per-leg price and fee information, and timestamps. It expires 30 seconds after quoted_at. A quote reserves neither funds nor liquidity.
Accept the terms
Call CreateSwap with the same terms, a client-chosen idempotency_key, the accepted min_amount_out_e18, and the quote's expires_at as quote_expires_at.
Creation prices the terms again against current depth. An expired quote or an unachievable minimum is rejected before a new Swap is created. Execution begins when the Swap is accepted; a successful create response does not mean it has finished.
Read the outcome
Call GetSwap with either swap_id or idempotency_key until is_terminal is true. The response includes the orders, settled amounts, fees, status, and reason for any incomplete execution. Reading a Swap still requires permission on its funding account.
Execution and minimum output
The current route uses one direct spot Pair. It can buy or sell depending on the Pair's orientation; it does not find a route through an intermediate Asset. A buy is supported only when the market can charge its fee in the Asset received.
Execution uses immediate-or-cancel limit orders at a protection price derived from the accepted minimum after fees. The minimum defines a rate for the requested amount, rather than a promise that the whole amount will trade. Any portion that cannot trade within protection stays in the account. An amount smaller than an order step can remain even after completion.
Use executed_amount_e18, unspent_amount_e18, received_amount_e18, and fee_e18 to reconcile the outcome. These count settled fills; unspent amounts become final when is_terminal is true.
EXECUTING: orders are being placed, filled, or settled.RECONCILING: an order's outcome is still being established. Settled fills already affect balances; keep reading the same Swap.COMPLETED: the amount traded and settled, allowing a remainder smaller than an order step.PARTIALLY_COMPLETED: some of the amount traded and settled. Readreasonfor why execution stopped.FAILED: usually nothing traded. Inspect the reason and amounts before deciding what to do next.
FAILED with MINIMUM_NOT_MET means settled fills netted less than the accepted minimum rate. The Swap is reported as failed, but those fills are reflected in the returned amounts and balances. Reconcile them before submitting another conversion.Limits and fees
Source and destination Assets must differ. Sizing depends on the Pair's order steps, minimum and maximum sizes, and current depth. Read Pair rules from GetSpotConfig, as described in Pairs and trading rules.
An AMOUNT_OUT_OF_RANGE error includes min_amount_e18 and max_amount_e18 when known, in source Asset units with 18 decimals. These are current guidance, not reserved capacity. A quote will not accept a buy that exceeds the market's maximum order quantity. If depth or limits change afterward, execution can stop partially with MAX_ORDER_QUANTITY.
Swap charges the account's spot taker fee and no additional Swap fee. Quote legs report taker_fee_rate_percent, estimated fee_e18, and fee_asset_id. Fees are charged in the received Asset, and quoted output is already net of fees. price_impact_bps describes the average fill price's distance from the best book price; it is not an extra charge.
Execution can also stop for insufficient available balance, market restrictions, order-size rules, trading policy, rate limits, or expiry of the execution window. Read the returned reason and settled amounts. See Fees and VIP and Rate limits.
Retries and errors
Choose an idempotency key for each intended conversion, unique within the caller's account scope. Keys contain 1 to 128 characters from letters, digits, ., _, :, and -. Repeating a create with the same key and economic terms returns the existing Swap, including after the quote expires. Reusing it for different terms returns IDEMPOTENCY_CONFLICT with the original swap_id.
After a timeout, UNAVAILABLE, or DEADLINE_EXCEEDED, repeat the same create request with the same key and terms. For signed API keys, sign each attempt again with a fresh timestamp. A new key could create a second conversion.
Handle the stable code in swap.v1.ErrorDetail, rather than parsing message text:
- Refresh the quote for
QUOTE_EXPIREDorPROTECTION_UNACHIEVABLE. Correct the amount forAMOUNT_OUT_OF_RANGE, using returned limits when present. - Choose a supported conversion for
ROUTE_UNSUPPORTEDorDIRECTION_UNSUPPORTED. A direct market or supported fee direction is missing. - Check account access for
INVALID_SUBACCOUNT_ID,SUBACCOUNT_NOT_FOUND,SUBACCOUNT_INACTIVE, orSWAP_NOT_PERMITTED. - Back off and retry for
QUOTE_UNAVAILABLEorSERVICE_UNAVAILABLE. Preserve the create request when its outcome is unknown. - Resolve the existing Swap for
IDEMPOTENCY_CONFLICT. ForSWAP_NOT_FOUND, check the selector and authenticated account.
Request errors are distinct from a Swap's execution status and reason. The complete codes and ConnectRPC mappings are in Swap errors; see Error responses for decoding details.