Swaps
Use this page to build a trade / swap screen: get a firm quote the user can review (amounts, slippage, costs), then execute and follow the ExecutionHandle until it settles.
routing.quote → easy.swap → ExecutionHandleSigning uses a Wallet adapter. If the handle is not yet confirmed or failed, poll settlement.track.
routing.quote
Obtain a firm quote before execution.
const quote = await sdk.routing.quote({
pair: '42-USDT',
intent: 'sell',
side: 'exactIn',
amount: '1.5',
ledgers: ['infinite-mainnet'],
slippageBps: 50,
})Parameters — QuoteRequest
Provide exactly one of pair or venue (not both, not neither). Prefer pair in UI flows.
| Field | Type | Description |
|---|---|---|
pair | string | Market label (e.g. 42-USDT). Mutually exclusive with venue. |
venue | VenueId | string | Pool id when you already know it. Mutually exclusive with pair. |
intent | 'buy' | 'sell' | Direction relative to the pair base (buy = pay quote for base; sell = pay base for quote). See Choosing intent and side. |
side | 'exactIn' | 'exactOut' | Which amount is fixed: exactIn = what you spend; exactOut = what you want to receive. |
amount | string | That fixed amount as a display decimal (e.g. "1.5"), not raw/smallest-unit. |
ledgers | LedgerScope[] | Optional. Limit to these ledgers (id or alias, e.g. infinite-mainnet). Omit to let Nutrimatic choose among configured ledgers. |
slippageBps | number | Max price movement you accept, in bps (e.g. 50 = 0.50%). Optional — default 50. The quote echoes the effective value. |
deadline | number | Optional. Unix time in milliseconds after which this quote request should not be used. |
Returns — Quote
| Field | Type | Description |
|---|---|---|
id | string | Opaque quote id. Pass it to easy.swap as quoteId. |
pairLabel | string | Resolved pair label for display (e.g. 42-USDT). |
intent | 'buy' | 'sell' | Same intent you sent — direction relative to the pair base. |
amountIn | QuoteLeg | What you spend: instrument + display amount. See legs below. |
amountOut | QuoteLeg | What you receive: instrument + display amount. See legs below. |
slippageBps | number | Effective slippage bound for this quote, in bps (from the request, or 50 if you omitted it). |
expiresAt | number | Unix time in milliseconds. Do not call easy.swap after this — you will get QUOTE_EXPIRED. |
costs | QuoteCost[] | Cost lines for this quote — see Fees and costs. |
path | Path | Optional. Ordered plan when the route is more than one hop — for your UI. After swap, poll settlement.track until confirmed or failed. Field meanings: Settlement — Path. |
ledger | LedgerId | Canonical ledger ID for this quote (never an alias). |
interface QuoteLeg {
readonly instrumentId: InstrumentId
readonly amount: string
}amountIn and amountOut are legs in the trade direction (not pair order): In = what you spend, Out = what you receive. Each leg’s amount is a display decimal string in that instrument’s units — not raw/smallest-unit. Nutrimatic handles on-chain conversion. Instrument decimals are in the catalog if your UI needs them for input masks or checks.
Costs
A successful quote includes Quote.costs — the cost breakdown for your UI (gas, trade, service; bridge when needed). Other writes can still cost money, but only swap quotes return this list before execute — Fees and costs.
Example response (sell 1.5 of 42 → receive USDT):
{
"id": "quote_01HZX…",
"pairLabel": "42-USDT",
"intent": "sell",
"amountIn": {
"instrumentId": "evm:421018:instrument:42",
"amount": "1.5"
},
"amountOut": {
"instrumentId": "evm:421018:instrument:usdt",
"amount": "3.72"
},
"slippageBps": 50,
"expiresAt": 1719799200000,
"ledger": "evm:421018",
"costs": [
{
"kind": "trade",
"amount": "0.0045",
"instrumentId": "evm:421018:instrument:42"
},
{
"kind": "gas",
"amount": "0.0007",
"instrumentId": "evm:421018:instrument:42",
"estimated": true
},
{
"kind": "service",
"amount": "0.0001",
"instrumentId": "evm:421018:instrument:42"
}
]
}Choosing intent and side
These two fields on QuoteRequest define the trade.
On a pair label like 42-USDT, the first symbol (42) is the base — the asset you are mainly buying or selling. The second (USDT) is the quote — what you usually pay with or receive. You do not have to memorize DeFi jargon: pick buy/sell for the base, then say whether the number the user typed is “I spend this much” or “I want to receive this much”.
intent—'sell'(give base, get quote) or'buy'(give quote, get base)side—'exactIn'(theamountis what you spend) or'exactOut'(theamountis what you want to receive)
| You want… | intent | side | amount is | Quote legs |
|---|---|---|---|---|
Sell a fixed amount of 42 | sell | exactIn | How much 42 you spend | amountIn = 42, amountOut = USDT |
Receive a fixed amount of USDT | sell | exactOut | How much USDT you want out | amountIn = 42, amountOut = USDT |
Spend a fixed amount of USDT | buy | exactIn | How much USDT you spend | amountIn = USDT, amountOut = 42 |
Receive a fixed amount of 42 | buy | exactOut | How much 42 you want out | amountIn = USDT, amountOut = 42 |
// 1) Sell exactly 1.5 of 42 → amountIn is 42, amountOut is USDT
await sdk.routing.quote({
pair: '42-USDT',
intent: 'sell',
side: 'exactIn',
amount: '1.5',
ledgers: ['infinite-mainnet'],
slippageBps: 50,
})
// 2) Get exactly 100 USDT by selling 42 → amountIn is 42, amountOut is USDT
await sdk.routing.quote({
pair: '42-USDT',
intent: 'sell',
side: 'exactOut',
amount: '100',
ledgers: ['infinite-mainnet'],
slippageBps: 50,
})
// 3) Spend exactly 100 USDT to buy 42 → amountIn is USDT, amountOut is 42
await sdk.routing.quote({
pair: '42-USDT',
intent: 'buy',
side: 'exactIn',
amount: '100',
ledgers: ['infinite-mainnet'],
slippageBps: 50,
})
// 4) Get exactly 1.5 of 42 by paying USDT → amountIn is USDT, amountOut is 42
await sdk.routing.quote({
pair: '42-USDT',
intent: 'buy',
side: 'exactOut',
amount: '1.5',
ledgers: ['infinite-mainnet'],
slippageBps: 50,
})Do not infer the asset from pairLabel alone — read each leg’s instrumentId and show amount as-is in the UI. In a typical swap screen: Buy/Sell maps to intent; whichever amount the user typed as fixed (spend vs receive) maps to side — that value is always display in QuoteRequest.amount.
easy.swap
Execute a swap. Provide exactly one of quoteId or request. Prefer quoteId after routing.quote so the user reviews a firm quote (including costs) first.
const handle = await sdk.easy.swap({
quoteId: quote.id,
})Parameters — SwapRequest
| Field | Type | Description |
|---|---|---|
quoteId | string | Id from a prior routing.quote. Mutually exclusive with request. |
request | QuoteRequest | Inline quote+swap in one call (same rules as QuoteRequest). Mutually exclusive with quoteId. |
slippageBps | number | Optional tighter bound in bps. Default: the quote’s slippageBps. If set, must be ≤ that value (you may only tighten; a wider bound is rejected). |
deadline | number | Optional. Unix time in milliseconds after which execution should not proceed. |
The SDK may call your WalletAdapter.sign with purpose: 'swap' before submitting.
Returns — ExecutionHandle
| Field | Type | Description |
|---|---|---|
id | string | Opaque execution id. Pass to settlement.track while status is still running (pending / submitted / in_progress). |
status | ExecutionStatus | Where the swap is now: pending | submitted | in_progress | confirmed | failed. Stop when confirmed or failed — see Settlement — Status. |
ledger | LedgerId | Canonical ledger ID where this execution is anchored. |
txRef | string | Optional. Ledger-native tx / reference when Nutrimatic already has one (e.g. EVM hash). |
receipt | ExecutionReceipt | Optional. Extra confirmation details when status is confirmed. |
Example response:
{
"id": "exec_01HZY…",
"status": "confirmed",
"ledger": "evm:421018",
"txRef": "0xabc…",
"receipt": { "txRef": "0xabc…" }
}Typical sequence
const quote = await sdk.routing.quote({
pair: '42-USDT',
intent: 'sell',
side: 'exactIn',
amount: '1.5',
ledgers: ['infinite-mainnet'],
slippageBps: 50,
})
const handle = await sdk.easy.swap({ quoteId: quote.id })
if (handle.status === 'confirmed' || handle.status === 'failed') {
// Done — refresh reads on success
} else {
// Still running — poll until confirmed or failed
const status = await sdk.settlement.track(handle.id)
}Errors
READ_ONLY_MODE, CAPABILITY_NOT_AVAILABLE, WALLET_REQUIRED, USER_REJECTED, QUOTE_EXPIRED, SLIPPAGE_EXCEEDED, INSUFFICIENT_ALLOWANCE, PAIR_NOT_FOUND, VENUE_NOT_FOUND, LEDGER_NOT_SUPPORTED.
→ Fees and costs · Write operations · Settlement · Types — Execution