Swap Routing
Direct-pair splits, quote units, and protected execution
Routing splits input across Coffer pools sharing a pair; it searches neither multi-hop paths nor other exchanges.
Request and response
The route endpoint is public; no key or sign-in is needed:
curl 'https://api.coffer.so/api/pools/swap-route?tokenIn=So11111111111111111111111111111111111111112&tokenOut=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&amountIn=1000000000&decimalsIn=9'Response fields use raw integer
amounts; minReceived and each leg's minAmountOut already apply the
requested slippageBps (default 2%). Require nonempty legs, positive inputs/outputs, summed inputs equal
to the request, and valid pools, mints, vaults, and token programs. No
partial-fill flag is returned; empty routes/zero output are unusable.
Discovery and allocation
The router reads indexed candidates through RPC, allocates greedily by marginal output, and compares the split with the best single-pool quote. Failed RPC reads do not fall back to database balances. The heuristic ignores transaction capacity/network fees and does not guarantee an optimum.
Price fields and decimal conversion
Prices use raw output/input:
humanPrice = rawPrice × 10^(decimalsIn − decimalsOut)
effectivePriceRaw = totalExpectedOut / totalAmountIn
poolSpotRaw = (V_out × w_in) / (V_in × w_out)
reference = max(poolSpotRaw × (1 − poolFeeRate / 1,000,000))
priceImpact = max(0, (reference − effectivePriceRaw) / reference × 100)Thus 0.15 raw USDC/SOL is 150 USDC/SOL with decimals 6/9.
spotPrice is the best pre-fee candidate spot, while impact uses the
fee-adjusted reference above.
Quote coverage and execution checks
Refresh chain state and verify every leg:
- Pool/swaps enabled; input token active.
- Current base fee, including zero, and integer rounding.
- Sell-off capacity and output-side surge fee.
- Mint extensions and transfer compatibility.
- Gross curve output within LP-owned actual reserves; protocol fees are already separate. Reject excess output rather than capping it.
Confirm expectedOut covers applicable fees before treating it as net
received output. Simulate the complete transaction; slippage cannot bypass
flags, capacity, or incompatible tokens.
Building a protected transaction
Calculate a positive per-leg net minimum; never reuse a trade-wide floor for each leg:
function minimumOut(expectedNetOut: bigint, slippageBps: number): bigint {
if (!Number.isInteger(slippageBps) || slippageBps < 0 || slippageBps >= 10_000) {
throw new Error('Invalid slippage tolerance');
}
const minimum = expectedNetOut * BigInt(10_000 - slippageBps) / 10_000n;
if (minimum <= 0n) throw new Error('No protected output');
return minimum;
}Supply each swap's input, minimum, and token indices. Prepare ATAs under each mint's token program and wrap SOL as needed. Combine legs in one transaction for atomic execution; if size/compute limits prevent this, requote a feasible route. Separate transactions change that guarantee. Requote after state changes or blockhash expiry, retaining the user's floor.