# Quoting the Solidly curve on-chain: Newton's method where the naive seed lies

> Canonical: https://blazephoenix.xyz/learn/solidly-solver
> License: CC BY 4.0 (attribution + link) · © 2026 BlazePhoenix
> Updated: 2026-07-27

The Solidly stable curve k = x3y + xy3 has no clean closed form, so its output is found by iteration. The naive constant-sum seed silently saturates on lopsided pools; a Newton method seeded at the opposite reserve with a half-step clamp matches the pool's own getAmountOut within 0.5%, computed on-chain.

Most automated market makers have a clean closed form: put an amount in, a single expression gives the amount out. Constant-product pools do (x*y = k). Concentrated-liquidity pools do, per tick. The Solidly family — the ve(3,3) stable curve behind Velodrome, Aerodrome and their forks — does not. Its invariant is a quartic, k = x3y + xy3, chosen because it stays nearly flat for assets that should trade near parity and only bends sharply at the extremes. That shape is exactly what a stablecoin pair wants, and exactly what has no tidy inverse.

So the output has to be found by iteration: guess an answer, measure how wrong the invariant is, correct, repeat. Do that carelessly and you get a number that looks plausible and is quietly wrong — which, for a quote that must equal execution, is the one outcome we cannot ship.

## Why the obvious starting guess fails

The intuitive way to seed the iteration is to treat the pool as a constant-sum swap — assume one token in gives roughly one token out, since the whole point of a stable pair is near-parity — and refine from there. On a balanced pool near the middle of the curve, that seed is close and the iteration converges in a step or two.

The trap is at the edges. When the pool is lopsided, or the trade is large relative to depth, the constant-sum seed lands in a region where the curve has bent hard, and the naive iteration SILENTLY SATURATES: it stops moving toward the true answer and returns a value that is confidently, invisibly off. Nothing errors. The quote is simply wrong, and it is wrong in exactly the situation — a large or imbalanced trade — where being wrong costs the most.

## The fix: seed at the opposite reserve, clamp the step

BlazePhoenix seeds Newton's method at the OPPOSITE reserve rather than at the constant-sum guess. That places the first estimate on the correct side of the curve's bend, so the iteration walks toward the true root instead of away from it, even when the pool is far from balance.

Newton's method can also overshoot — a step too large jumps past the root and oscillates. So each step is governed by a bidirectional half-step clamp: the correction is bounded in both directions, which trades a little speed for the guarantee that the iteration tightens monotonically rather than ringing. The result is an output that matches the pool's own live getAmountOut within 0.5%, across the whole curve, including the lopsided region where the naive seed gave up.

Two supporting disciplines make that robust in production: decimals are normalised before the maths so a 6-decimal token and an 18-decimal token are compared on the same scale, and the final figure is validated with an explicit under-quote margin — when the numerical answer is uncertain, the protocol quotes slightly LOW rather than slightly high, so the realised fill can only beat the quote, never miss it. That is the Fail-Closed rule applied to arithmetic: when unsure, promise less.

## Why solve it on-chain at all

An off-chain aggregator would compute this on a server and send you the result. The reason to do it inside the contract is the same reason it runs everywhere else in this protocol: the number you are quoted and the number the trade realises are then produced by the same code over the same block. A server's Solidly solver can drift from the pool's actual behaviour — different rounding, a stale reserve read, a subtly different iteration — and you would have no way to prove the gap. On-chain, the quote is a simulation of the very computation that will settle.

And where the venue's own contract exposes the truth directly, the protocol asks it rather than reproducing it: Curve's get_dy and a Solidly pool's own getAmountOut are called on the read path, because the same bytecode that will enforce the invariant at execution is the most honest source of the number. The solver above is for the cases where that call is unavailable or too costly on the scoring path — and even then, it is validated against the pool's own answer.

**Verify it yourself:** Call a Solidly/Aerodrome pool's own getAmountOut for a given input, then compare it against the quote our Quoter returns for the same pair and size — the two match within the under-quote margin, which is the claim of this article

Related: https://blazephoenix.xyz/learn/eightfold-dispatcher · https://blazephoenix.xyz/learn/on-chain-quoting · https://blazephoenix.xyz/learn/the-512-bit-multiply · https://blazephoenix.xyz/learn/capital-anchored-split
