Skip to content

Stage - #110

Merged
1kresh merged 9 commits into
mainfrom
stage
Jul 30, 2026
Merged

Stage#110
1kresh merged 9 commits into
mainfrom
stage

Conversation

@1kresh

@1kresh 1kresh commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

No description provided.

1kresh and others added 8 commits July 27, 2026 16:04
A discount leg prices off the backend's advertised `maxRate`, which is the
adapter oracle price with the discount already applied *and floored*. The
adapter rounds down in the opposite order: `getAmountOut` floors
`amountIn * price * 10^outDec / (1e18 * 10^inDec)` first, then
`swap(DiscountSwap, ...)` applies the discount and floors again.

The two nested roundings differ by at most one unit, and the difference falls
our way often -- roughly a fifth to a half of amounts at any non-zero discount.
So pricing at the raw `maxRate` predicts one unit more output than the adapter
delivers.

That is not an adapter revert: the adapter computes `amountOut` itself and
`InvalidSwapRate` cannot trigger because `discount >= minDiscount`. It reverts
in `Reactor._fill`, which pulls the order's *signed* outputs out of the Executor
after `execute()` returns. RFQ has no `priceBufferBps` and `Finalize`
distributes the full achievable output, so the slack is exactly zero whenever
the price has not moved since the quote. The fill then fails gas estimation
(no tx is broadcast, no gas burned) and the order is re-armed by the next poll,
retrying until it expires -- we win the auction and never fill it.

`NormalizeOracleInventory` now re-derives every discount candidate's rate
through the new `liquidlane.ConservativeAdvertisedRate`, which shaves one unit
off the predicted output and converts it back to a rate. That round trip
through `RateForAmountOut` floors, so every downstream `AmountOutForRate` call
site stays at or below the on-chain value without knowing about the shave --
which is why the fix lands on the rate rather than on each output site. Direct
legs are unaffected: they already re-derive from a live `getAmountOut` read.
`NormalizeOracleInventory` is RFQ-only, so LI.FI and UniswapX are untouched.

Value given up is usually zero: when `amountIn * 10^outDec <= 1e18 * 10^inDec`
the rate round trip is lossless and the candidate lands exactly on the adapter's
number.

The exact alternative -- clamp against
`AmountOutAfterDiscount(GrossAmountOut, discount)`, as
`discounts.AdvertisedFillQuotes` already does -- needs the discount ppm, which
the `/quote` request's `adapters[]` entries do not carry. Left as a follow-up
noted in the plan rather than a backend contract change.

Tests: a regression case pinning the concrete one-unit overprediction, and a
12,500-case sweep (5 decimal pairs x 5 discounts x 500 sizes) asserting the
invariant. The sweep was verified to have teeth -- 2,351 of those cases violate
it at the raw rate. Two existing tests that asserted the raw rate passing
through verbatim were updated to the conservative expectation.

Docs: the rate-rounding rule is recorded in LIQUIDLANE-CONVENTIONS.md (it is a
LiquidLane-wide fact, not an RFQ quirk) and the full mechanics plus the
follow-up in RFQ-PLAN.md Section 5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fix(rfq): price discount legs below the adapter's nested rounding
…-lifecycle

fix: lifi/uniswapx correct capacity and fill lifecycle
@1kresh
1kresh merged commit 0dc2930 into main Jul 30, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants