Skip to main content
Read net prices with entrypoint.quoteFor and send swaps to the entrypoint. The curve book’s quote exposes the full curve output before caller-specific fees. Discover the book with entrypoint.curveBook(). All amounts are integer token atomic units: WETH uses 18 decimals and USDC uses 6. base identifies the pair; tokenIn selects its direction.

Read ABI

quote(base, tokenIn, amountIn) returns zero for the contract’s pricing failures: an unknown base or input token, zero input, an expired curve, disabled or exhausted depth, or output that rounds to zero. Treat zero as do not route. Handle RPC and execution errors separately; a failed RPC call is not a valid zero quote. The curve book quote is the gross amount; entrypoint.quoteFor with the actual caller gives the net swap amount in the same executable state. It checks neither the caller’s balance and allowance nor the custodian’s available output liquidity. Simulate the actual swap before sending it.

Check freshness at one block

Read pair(base), ttl(), validUntil(base), and quoteFor(...) at a single block number, using that block’s timestamp. A curve is live only if:
  1. pair.qUnit != 0: the base has been listed.
  2. block.timestamp <= validUntil(base): the authenticated publication deadline has not passed.
  3. ttl == 0 || block.timestamp <= pair.lastUpdateAt + ttl: the inclusion-based TTL has not passed.
  4. The required side has nonzero knotCount and depthBps, and your amount returns a nonzero quote.
ttl == 0 does not disable validUntil. At either deadline equality is allowed; expiry begins after the deadline. A quote can expire between reading and execution, even if seq and fillSeq are unchanged. seq increments on each accepted curve update. fillSeq increments on each swap for that base. Either changing can alter your quote. Use minAmountOut to bound execution. A router requiring the observed curve state can additionally read pair(base) within its transaction and reject a different (seq, fillSeq); it must still handle expiry. Keep the base token with fillSeq, because the counter is per pair.

Depth and local pricing

Each side contains up to 36 knots. knot(base, side, index) returns cumulative base size q in units of pair.qUnit, and cumulative extra quote cost in units of cUnit(). Asks add the extra cost to the spread-adjusted mid line; bids subtract it. For an enabled side, its base capacity and remaining depth are:
Check knotCount before reading the last knot. Reanchors may shrink depth below the existing fill cursor; remaining depth is then zero. On asks, this is the maximum remaining base output. On bids, it is the maximum remaining base input. Fills that exceed available depth revert; there are no partial fills. mid is expressed in quote atomic units per 10^18 base atomic units. The side’s mid line is floor(mid * (10000 + spreadBps) / 10000). For exact results, use the quote view: segment interpolation, rounding, and ask-side cost inversion are part of pricing. A spot price multiplied by size is not an executable quote.

Swap ABI and approvals

Approve the entrypoint to spend at least amountIn of tokenIn. If a router calls the entrypoint, the router is the payer and must hold the input and grant that approval. Wait for approval to confirm, then obtain a fresh quote and simulate the swap. The entrypoint checks the recipient, consumes the curve, checks minAmountOut, pulls input from msg.sender into the custodian, pays output from the custodian to to, and emits Fill. Any failure reverts every step. The recipient must be nonzero; the entrypoint is non-reentrant. The call has no deadline argument. A router can enforce its own deadline before calling. Curve expiry alone does not stop a delayed transaction from executing against a later refreshed curve that meets minAmountOut.

TypeScript example

This browser-wallet example uses viem. Pass your EIP-1193 wallet provider, Base RPC URL, and the verified entrypoint and WETH addresses from the deployment record. It buys WETH with 10 USDC and permits 30 basis points of slippage. Choose the amount and slippage for your integration.
Simulation cannot reserve a quote. Wallet confirmation, another fill, or a new block can change execution. If simulation fails because the curve moved or expired, obtain a fresh quote. If submission status is unknown, resolve that transaction’s receipt or nonce before sending another swap.

Errors and recovery

Token transfers may also revert for insufficient balance or allowance. ERC-20/SafeERC20 errors and ReentrancyGuardReentrantCall() can propagate. Do not call curveBook.consume directly: it is restricted to the entrypoint.

Receipts, fees, and gas

Decode Fill only from the configured entrypoint address. Its indexed fields are base, fillSeq, and taker; its data fields are tokenIn, amountIn, amountOut, and fee. taker is the entrypoint caller, so it is the router address when a router pays. to is not in Fill; the custodian’s PaidOut event and output token transfer identify the recipient. Keep the transaction hash, base, and fillSeq for reconciliation. Token implementations may emit additional logs, so do not assume a fixed receipt log count. Track chain reorganizations according to your integration’s confirmation policy. An owner-configured caller fee is applied to output: fee = ceil(curveOut × bps / 10000) and amountOut = curveOut - fee. The fee is retained in the custodian and credited off-chain to the caller’s ledger row. Maker attribution uses the full curve output. Curve spread, price impact, and network fees remain costs. Gas depends on how many knots the walk crosses and the current fill cursor; estimate it for the actual calldata and payer on the target chain.

Fees

Use quoteFor(base, tokenIn, amountIn, taker) on the entrypoint with the address that will call swapExactAmountIn. It returns net amountOut and fee; passing the zero address returns the raw curve quote. A router’s caller is the router itself, not its user or output recipient. takerFeeBps(taker, base) returns the effective basis points. A per-pair entry, including explicit zero, overrides the caller’s default. The maximum configured fee is 1000 bps. minAmountOut checks the net output. At atomic-unit dust sizes the rounded fee can consume the full output; require a positive minimum output to reject that case. The Fill event after the fee upgrade includes a final uint256 fee field. Indexers scanning across the upgrade must also decode the older six-argument event as fee zero. Track /v0/fees cumulative accrued amounts for billing; current balances can also reflect other account activity.