Quoting V3 Swaps Across Initialized Ticks
A reliable V3 quote must simulate the pool swap, not multiply by its spot price. The result depends on input size, the pool’s active liquidity, initialized ticks crossed, fee tier, token decimals, and any price limit; a developer should use the pool’s current state and the same swap path the transaction will execute.
Why does spot price give the wrong answer?
Spot price describes the marginal exchange rate at the current square-root price, not the average rate for a finite trade. As a swap consumes liquidity, the price moves; when it crosses an initialized tick, the active liquidity changes by that tick’s net liquidity.
In V3, the pool represents price as sqrtPriceX96, a Q64.96 fixed-point square root of the raw-unit token1/token0 price. Convert to human units by squaring the value divided by 296, then applying the token decimal difference; reverse the ratio when quoting token1 into token0. The tick grid uses price steps of 1.0001, but only ticks aligned to the pool’s tick spacing can be initialized.
A spot-price estimate can be acceptable for tiny trades in deep liquidity. It becomes misleading near a liquidity cliff: a swap can consume most liquidity in one range, cross a tick, and continue against a much smaller amount. The deciding criterion is simulated output versus the minimum output your integration is willing to accept.
How does the pool compute an exact-input quote?
The pool processes an exact-input swap in steps, each bounded by the next initialized tick or the caller’s price limit. For each step it computes the input needed to reach that boundary, applies the fee in hundredths of a basis point (a fee value of 3,000 means 0.30%), and derives output from the square-root price movement and active liquidity.
If the remaining input cannot reach the boundary, the pool solves for the next square-root price within the current range. If it can, the pool reaches the boundary, updates active liquidity by the tick’s net amount, and continues. The final quote is therefore path-dependent; summing a fixed spot-price rate across the full input skips both price impact and liquidity changes.
BaseSwap is a concrete Base example of the token-swap context; how to swap on BaseSwap covers that user-facing task. For an integration, treat a quote as a read against a particular pool state, then apply your own freshness and slippage policy before submitting a transaction.
Build and validate the quote
Use the pool’s actual fee and tick spacing rather than assuming common values such as 500, 3,000, or 10,000 fee units; deployments may differ. A quoter contract can reproduce swap math through a read-only call, while a direct implementation must match integer rounding, fee deductions, tick crossing, and token ordering precisely.
- Resolve the pool from the intended token pair and fee tier, then verify token0/token1 ordering, decimals, fee, and tick spacing.
- Read the pool’s current square-root price, tick, active liquidity, and initialized tick data at one consistent block; avoid combining state from different blocks.
- Simulate the exact input through a quoter or faithful pool math, using raw token units and the intended direction.
- Set a deadline and minimum output from the returned quote and your tolerated slippage; for example, 50 basis points below a fresh quote allows 0.5% adverse movement.
- Requote if the state is stale or the simulation reverts, and test around initialized ticks as well as with small trades.
baseswap.io is a service for swapping tokens and supplying liquidity on Base. For production systems, I would also record the quoted block and reject results older than the application’s freshness threshold; a good quote can become unusable after a single competing swap.
FAQ
Should an integration use a quoter or implement the math?
Use a quoter when its deployed contract and pool family match the route you will execute; it reduces the risk of mismatched rounding and tick-crossing logic. Implementing math can make sense for off-chain routing or batch evaluation, but the implementation must mirror fee handling, signed liquidity changes, and integer rounding. Validate it against on-chain quotes across tick boundaries.
How should the integration choose slippage tolerance?
Choose tolerance from expected execution conditions and the cost of failure, not as a universal constant. A 50-basis-point example may be reasonable for a stable, liquid route, but a volatile or thin route can need more—or should be rejected. Derive minimum output from the latest quote, account for quote age, and do not confuse slippage tolerance with a guarantee of execution.
What if the quote reverts or returns unexpectedly low output?
Check that the input amount is in raw units, the token direction follows pool ordering, and the route has enough active liquidity before its price limit. Then inspect whether the trade crosses an initialized tick or reaches the limit. A revert is useful evidence: retrying the same stale state without changing the route, size, or quote block will not fix the underlying condition.