Hybrid book leg has no slippage floor in the no-belief-price path #273

Closed
opened 2026-06-03 07:11:50 +00:00 by Brouie · 23 comments
Brouie commented 2026-06-03 07:11:50 +00:00 (Migrated from gitlab.com)

Severity: Medium
Reachability: Any taker who runs a hybrid swap without setting belief_price (and, via the router, without minimum_receive).
Affected: assert_max_spread / check_max_spread on the pair, hybrid book leg.
Root cause: in the no-belief_price branch the spread metric numerator is the pool spread only; the book leg's execution quality isn't bounded by any per-swap floor, and the pair has no minimum_receive of its own.

Summary

For a hybrid swap, slippage protection runs through check_max_spread. The belief-price branch is fine — it compares expected vs book_net + pool_net + pool_commission, so a bad book fill is caught. But the no-belief branch computes pool_spread.min(pool_gross) / total_gross_out — the numerator is purely the pool leg's spread. The book leg only shows up in the denominator, where a worse book fill actually makes the ratio smaller and easier to pass.

So a taker who omits belief_price has no floor on the book-leg price. The pair has no minimum_receive either — the only end-to-end floor is the router's minimum_receive, which is also optional. A taker who quotes, then gets the good resting orders pulled/consumed before execution, can fill the book leg at materially worse prices and the no-belief check won't reject it.

Not a drain — it's an unprotected-by-default footgun on the book leg. Calling it Medium because the taker can self-protect with belief_price or minimum_receive, but the contract shouldn't rely on that.

Current codebase

  • smartcontracts/packages/dex-common/src/max_spread.rs — no-belief branch: spread_cmp = pool_spread.min(pool_gross) over total_gross_out; book leg never constrains the numerator.
  • smartcontracts/contracts/pair/src/contract.rs — assert_max_spread is the only slippage guard in execute_swap; there is no pair-level minimum_receive.
  • smartcontracts/contracts/router/src/contract.rs — per-hop swaps are built with belief_price: None, so every router-initiated hybrid hop runs the no-belief path; only the final-hop minimum_receive bounds the whole chain, and it's optional.

Why this matters

Hybrid is the headline feature and the path most takers will use. "Default settings, no belief price" should not silently expose the book leg to unbounded slippage. Right now safety depends on the frontend always setting a guard — fine until something calls the contract directly.

  1. In the no-belief hybrid path, bound the book leg too — e.g. derive an effective book execution price and check it against max_spread, or fold a book-leg shortfall term into the numerator.
  2. Consider a pair-level min_return for hybrid so there's a hard floor independent of belief_price.
  3. At minimum, require belief_price or minimum_receive when book_input > 0.

Acceptance criteria

  • A hybrid swap with no belief_price whose book leg fills materially worse than quoted is rejected by the spread/return check.
  • The no-belief metric reflects book-leg degradation, not just pool spread.
  • Router hybrid hops cannot settle a book leg below the caller's tolerance even with minimum_receive unset.

Test plan (functional)

case expect
hybrid, belief_price set, normal book passes, output within tolerance
hybrid, no belief, book fills at quote passes

Test plan (attack / abuse)

case expect
hybrid, no belief, good orders pulled pre-exec, book fills deep rejected on slippage
router hybrid hop, minimum_receive unset, seeded bad book rejected per-hop, not only end-to-end
**Severity:** Medium **Reachability:** Any taker who runs a hybrid swap without setting `belief_price` (and, via the router, without `minimum_receive`). **Affected:** `assert_max_spread` / `check_max_spread` on the pair, hybrid book leg. **Root cause:** in the no-`belief_price` branch the spread metric numerator is the *pool* spread only; the book leg's execution quality isn't bounded by any per-swap floor, and the pair has no `minimum_receive` of its own. ## Summary For a hybrid swap, slippage protection runs through `check_max_spread`. The belief-price branch is fine — it compares expected vs `book_net + pool_net + pool_commission`, so a bad book fill is caught. But the **no-belief branch** computes `pool_spread.min(pool_gross) / total_gross_out` — the numerator is purely the pool leg's spread. The book leg only shows up in the denominator, where a worse book fill actually makes the ratio *smaller* and easier to pass. So a taker who omits `belief_price` has no floor on the book-leg price. The pair has no `minimum_receive` either — the only end-to-end floor is the router's `minimum_receive`, which is also optional. A taker who quotes, then gets the good resting orders pulled/consumed before execution, can fill the book leg at materially worse prices and the no-belief check won't reject it. Not a drain — it's an unprotected-by-default footgun on the book leg. Calling it Medium because the taker can self-protect with `belief_price` or `minimum_receive`, but the contract shouldn't rely on that. ## Current codebase - `smartcontracts/packages/dex-common/src/max_spread.rs` — no-belief branch: `spread_cmp = pool_spread.min(pool_gross)` over `total_gross_out`; book leg never constrains the numerator. - `smartcontracts/contracts/pair/src/contract.rs` — `assert_max_spread` is the only slippage guard in `execute_swap`; there is no pair-level `minimum_receive`. - `smartcontracts/contracts/router/src/contract.rs` — per-hop swaps are built with `belief_price: None`, so every router-initiated hybrid hop runs the no-belief path; only the final-hop `minimum_receive` bounds the whole chain, and it's optional. ## Why this matters Hybrid is the headline feature and the path most takers will use. "Default settings, no belief price" should not silently expose the book leg to unbounded slippage. Right now safety depends on the frontend always setting a guard — fine until something calls the contract directly. ## Recommended direction 1. In the no-belief hybrid path, bound the book leg too — e.g. derive an effective book execution price and check it against `max_spread`, or fold a book-leg shortfall term into the numerator. 2. Consider a pair-level `min_return` for hybrid so there's a hard floor independent of `belief_price`. 3. At minimum, require `belief_price` or `minimum_receive` when `book_input > 0`. ## Acceptance criteria - [ ] A hybrid swap with no `belief_price` whose book leg fills materially worse than quoted is rejected by the spread/return check. - [ ] The no-belief metric reflects book-leg degradation, not just pool spread. - [ ] Router hybrid hops cannot settle a book leg below the caller's tolerance even with `minimum_receive` unset. ## Test plan (functional) | case | expect | |---|---| | hybrid, belief_price set, normal book | passes, output within tolerance | | hybrid, no belief, book fills at quote | passes | ## Test plan (attack / abuse) | case | expect | |---|---| | hybrid, no belief, good orders pulled pre-exec, book fills deep | rejected on slippage | | router hybrid hop, minimum_receive unset, seeded bad book | rejected per-hop, not only end-to-end |
PlasticDigits commented 2026-06-03 11:07:30 +00:00 (Migrated from gitlab.com)

All 3 approved

All 3 approved
Brouie commented 2026-06-04 06:10:10 +00:00 (Migrated from gitlab.com)

Shipped direction 1 — the no-belief metric now reflects book-leg degradation. This is the actual security fix and it's non-breaking (contract internals only, no Swap schema change, no frontend impact). Directions 2 & 3 flagged below as the frontend-coordinated follow-up, on purpose.

PoC first (you asked): added limit_order_tests::hybrid_no_belief_book_far_below_pool_rejected — a no-belief hybrid whose book leg fills ~50% below the pool fair rate. It FAILS on current code (the swap is accepted — the gap), and passes after the fix.

Fix (net-vs-net): I ran an adversarial design pass on the formula (3 independent approaches, numerically stress-tested). Winner = reference the book leg against the pool's realized NET rate, not gross — gross would phantom-reject a legit book that's only short by its own fee on deep pools. In max_spread.rs no-belief branch:
book_shortfall = max(0, pool_net_return * book_input / pool_input - book_net_return) folded into the numerator (checked_multiply_ratio, overflow-safe; gated to exactly 0 unless both legs present → #197 pool-only metric byte-identical). At the call site I plumb pool_input_amount and offer_consumed_by_book (NOT h.book_input — the book's unfilled remainder spills into the pool, so that keeps pool_input + book_input == offer).

Numbers: PoC drain case → metric 0.83 > 1% reject (today: 0.00017 pass). Legit same-price hybrid (book short only by ~10bps fee) → 0.0036 pass. Pool-only → byte-identical to #197. Verified zero false-rejects across 18 legit splits/fees.

Meets AC1/AC2/AC3. Contract suite 415/0 (incl. new max_spread reject + accept unit tests + the PoC).

Flagged follow-up (directions 2 & 3 — frontend-coordinated):

  • Direction 3 (require belief_price/min_receive when book_input>0) is the only thing that protects a pure-book hybrid (pool_input==0, no pool reference rate — this metric can't bound it). But making it MANDATORY breaks every book swap in the dapp until the frontend + router are updated to send a floor (router hops currently set belief_price: None). So it needs to land WITH a frontend/router change, not before.
  • Direction 2 (min_return on the Swap hook) is backward-compatible but inert until the frontend adopts it — belongs with the direction-3 rollout.

I scoped this MR to the non-breaking security fix so it can merge without breaking book trading; happy to do 2 & 3 once the frontend change is sequenced. Branch qa/273-hybrid-noblief-slippage-floor, MR fork→main (no closing keyword). @PlasticDigits

Shipped **direction 1** — the no-belief metric now reflects book-leg degradation. This is the actual security fix and it's **non-breaking** (contract internals only, no `Swap` schema change, no frontend impact). Directions 2 & 3 flagged below as the frontend-coordinated follow-up, on purpose. **PoC first (you asked):** added `limit_order_tests::hybrid_no_belief_book_far_below_pool_rejected` — a no-belief hybrid whose book leg fills ~50% below the pool fair rate. It FAILS on current code (the swap is accepted — the gap), and passes after the fix. **Fix (net-vs-net):** I ran an adversarial design pass on the formula (3 independent approaches, numerically stress-tested). Winner = reference the book leg against the pool's realized **NET** rate, not gross — gross would phantom-reject a legit book that's only short by its own fee on deep pools. In `max_spread.rs` no-belief branch: `book_shortfall = max(0, pool_net_return * book_input / pool_input - book_net_return)` folded into the numerator (`checked_multiply_ratio`, overflow-safe; gated to exactly 0 unless both legs present → #197 pool-only metric byte-identical). At the call site I plumb `pool_input_amount` and `offer_consumed_by_book` (NOT `h.book_input` — the book's unfilled remainder spills into the pool, so that keeps `pool_input + book_input == offer`). Numbers: PoC drain case → metric 0.83 > 1% **reject** (today: 0.00017 pass). Legit same-price hybrid (book short only by ~10bps fee) → 0.0036 **pass**. Pool-only → byte-identical to #197. Verified zero false-rejects across 18 legit splits/fees. **Meets AC1/AC2/AC3.** Contract suite 415/0 (incl. new max_spread reject + accept unit tests + the PoC). **Flagged follow-up (directions 2 & 3 — frontend-coordinated):** - **Direction 3** (require `belief_price`/`min_receive` when `book_input>0`) is the only thing that protects a **pure-book hybrid** (`pool_input==0`, no pool reference rate — this metric can't bound it). But making it MANDATORY breaks every book swap in the dapp until the frontend + router are updated to send a floor (router hops currently set `belief_price: None`). So it needs to land WITH a frontend/router change, not before. - **Direction 2** (`min_return` on the `Swap` hook) is backward-compatible but inert until the frontend adopts it — belongs with the direction-3 rollout. I scoped this MR to the non-breaking security fix so it can merge without breaking book trading; happy to do 2 & 3 once the frontend change is sequenced. Branch `qa/273-hybrid-noblief-slippage-floor`, MR fork→main (no closing keyword). @PlasticDigits
Brouie commented 2026-06-04 06:10:13 +00:00 (Migrated from gitlab.com)

mentioned in merge request !746

mentioned in merge request !746
PlasticDigits commented 2026-06-04 08:09:30 +00:00 (Migrated from gitlab.com)

mentioned in commit 016972a77b

mentioned in commit 016972a77bc962c04d83f16ea3f525285876a81f
Brouie commented 2026-06-05 01:21:08 +00:00 (Migrated from gitlab.com)

The direction-1 fix is solid and I'd ship it, but the adversarial pass turned up a reachable corner the metric still doesn't cover, so I don't think this closes yet — flagging it for the direction-2/3 follow-up.

Verified: the no-belief metric now folds the book-leg shortfall against the pool's realized net rate into the numerator (max_spread.rs), gated to zero unless both legs are present so the #197 pool-only path is byte-identical. The PoC hybrid_no_belief_book_far_below_pool_rejected (book leg ~50% below pool fair) fails on the old code and passes now; contract suite 415/0; I re-derived the three metric cases by hand and they match. For a sensibly-split hybrid this closes the footgun.

The corner it doesn't cover: the guard leans on pool_net_return / pool_input being a meaningful reference, and the split is fully caller-controlled (pool_input + book_input == input_amount is the only check, no minimum pool leg — contract.rs:812). Set pool_input = 1 against a deep balanced pool: the AMM ceil_div floors gross_output to 0, so pool_net_return = 0, book_shortfall = 0, and spread_cmp = min(pool_spread, pool_gross=0) = 0 — the whole numerator is 0 and any book fill, however far below market, passes the no-belief check. The router passes hybrid params through unchanged, so a router hop can hit it too. It's the same class as the pure-book (pool_input == 0) case the fix already leaves to belief/min_receive — the max_spread.rs comment carves out only pool_input == 0, but the real boundary is "pool leg small enough that the AMM output rounds to 0", which is a bit wider.

Same Medium severity (taker omitting belief_price and minimum_receive on a degenerate split) — the fix shrinks the footgun but doesn't eliminate it. I'd fold a material-pool-leg floor (require pool_net_return > 0, or a min pool_input when book_input > 0 on the no-belief path) into the direction-2/3 work where you're already planning the belief/min_receive requirement. Happy to implement once the frontend/router floor is sequenced. @PlasticDigits

The direction-1 fix is solid and I'd ship it, but the adversarial pass turned up a reachable corner the metric still doesn't cover, so I don't think this closes yet — flagging it for the direction-2/3 follow-up. Verified: the no-belief metric now folds the book-leg shortfall against the pool's realized net rate into the numerator (max_spread.rs), gated to zero unless both legs are present so the #197 pool-only path is byte-identical. The PoC `hybrid_no_belief_book_far_below_pool_rejected` (book leg ~50% below pool fair) fails on the old code and passes now; contract suite 415/0; I re-derived the three metric cases by hand and they match. For a sensibly-split hybrid this closes the footgun. The corner it doesn't cover: the guard leans on `pool_net_return / pool_input` being a meaningful reference, and the split is fully caller-controlled (`pool_input + book_input == input_amount` is the only check, no minimum pool leg — contract.rs:812). Set `pool_input = 1` against a deep balanced pool: the AMM `ceil_div` floors `gross_output` to 0, so `pool_net_return = 0`, `book_shortfall = 0`, and `spread_cmp = min(pool_spread, pool_gross=0) = 0` — the whole numerator is 0 and any book fill, however far below market, passes the no-belief check. The router passes hybrid params through unchanged, so a router hop can hit it too. It's the same class as the pure-book (`pool_input == 0`) case the fix already leaves to belief/min_receive — the max_spread.rs comment carves out only `pool_input == 0`, but the real boundary is "pool leg small enough that the AMM output rounds to 0", which is a bit wider. Same Medium severity (taker omitting belief_price and minimum_receive on a degenerate split) — the fix shrinks the footgun but doesn't eliminate it. I'd fold a material-pool-leg floor (require `pool_net_return > 0`, or a min `pool_input` when `book_input > 0` on the no-belief path) into the direction-2/3 work where you're already planning the belief/min_receive requirement. Happy to implement once the frontend/router floor is sequenced. @PlasticDigits
PlasticDigits commented 2026-06-05 04:08:27 +00:00 (Migrated from gitlab.com)

marked as related to #307

marked as related to #307
PlasticDigits commented 2026-06-05 04:08:27 +00:00 (Migrated from gitlab.com)

mentioned in issue #307

mentioned in issue #307
ghost1 commented 2026-06-05 09:58:48 +00:00 (Migrated from gitlab.com)

mentioned in commit 6cf3d7a4dd

mentioned in commit 6cf3d7a4dd4fe2e25826578186103539e7b59651
PlasticDigits commented 2026-06-05 09:59:19 +00:00 (Migrated from gitlab.com)

mentioned in merge request !785

mentioned in merge request !785
ghost1 commented 2026-06-05 10:02:56 +00:00 (Migrated from gitlab.com)

mentioned in commit 9ac09ab1c1

mentioned in commit 9ac09ab1c178c6c808b27eb60241189c84df642d
ghost1 commented 2026-06-05 10:21:11 +00:00 (Migrated from gitlab.com)

mentioned in commit c1f6903039cb226a7550f4397637e7c99ac4e6ac

mentioned in commit c1f6903039cb226a7550f4397637e7c99ac4e6ac
ghost1 commented 2026-06-05 10:26:24 +00:00 (Migrated from gitlab.com)

mentioned in commit e1ae059653

mentioned in commit e1ae059653f509cf6682b3f23101cf6f1e0da4d7
ghost1 commented 2026-06-05 12:30:51 +00:00 (Migrated from gitlab.com)

mentioned in commit 845ea37c19

mentioned in commit 845ea37c1980db451537e47aece1ead056d0aceb
PlasticDigits commented 2026-06-05 12:31:08 +00:00 (Migrated from gitlab.com)

mentioned in merge request !801

mentioned in merge request !801
PlasticDigits commented 2026-06-05 12:31:16 +00:00 (Migrated from gitlab.com)

Implementation verify — GitLab #273

Issue: https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/273

The contract security fix is already merged to main:

  • 58092d1 — no-belief book_shortfall vs pool net rate (#273)
  • 6cf3d7a — material declared pool leg + pool_net > 0 when book leg present (#307)
  • e1ae059 — frontend swapMaxSpread preflight aligned with execute legs

Doc sync MR: https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/83

Acceptance criteria

Item How verified Result
Toxic no-belief hybrid (book ~50% below pool) rejected cargo test -p cl8y-dex-tests hybrid_no_belief_book_far_below_pool_rejected PASS
No-belief metric includes book degradation cargo test -p dex-common max_spread (no_belief_rejects_book_far_below_pool_net_rate, no_belief_accepts_book_at_pool_net_rate) PASS
Router hybrid per-hop without minimum_receive validate_declared_hybrid_pool_leg_for_no_belief on router + pair; dust leg hybrid_no_belief_dust_pool_leg_rejected PASS
Full contract suite make test-contracts (374 integration + unit) PASS
Frontend preflight mirror npm test -- src/utils/swapMaxSpread.test.ts (9 tests) PASS

Out of scope (documented follow-up)

Pure-book hybrid (declared_pool_input = 0) still needs belief_price or minimum_receive — see issue discussion (directions 2 & 3).

Removing agent:implement / agent:verify labels.

## Implementation verify — GitLab #273 **Issue:** https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/273 The contract security fix is **already merged to `main`**: - `58092d1` — no-belief `book_shortfall` vs pool net rate (#273) - `6cf3d7a` — material declared pool leg + `pool_net > 0` when book leg present (#307) - `e1ae059` — frontend `swapMaxSpread` preflight aligned with execute legs Doc sync MR: https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/83 ### Acceptance criteria | Item | How verified | Result | |------|----------------|--------| | Toxic no-belief hybrid (book ~50% below pool) rejected | `cargo test -p cl8y-dex-tests hybrid_no_belief_book_far_below_pool_rejected` | **PASS** | | No-belief metric includes book degradation | `cargo test -p dex-common max_spread` (`no_belief_rejects_book_far_below_pool_net_rate`, `no_belief_accepts_book_at_pool_net_rate`) | **PASS** | | Router hybrid per-hop without `minimum_receive` | `validate_declared_hybrid_pool_leg_for_no_belief` on router + pair; dust leg `hybrid_no_belief_dust_pool_leg_rejected` | **PASS** | | Full contract suite | `make test-contracts` (374 integration + unit) | **PASS** | | Frontend preflight mirror | `npm test -- src/utils/swapMaxSpread.test.ts` (9 tests) | **PASS** | ### Out of scope (documented follow-up) Pure-book hybrid (`declared_pool_input = 0`) still needs `belief_price` or `minimum_receive` — see issue discussion (directions 2 & 3). Removing `agent:implement` / `agent:verify` labels.
PlasticDigits commented 2026-06-05 12:33:36 +00:00 (Migrated from gitlab.com)

mentioned in commit b9121d8392

mentioned in commit b9121d839230a62ecb449988372067d5b9a61bd5
PlasticDigits commented 2026-06-05 13:43:11 +00:00 (Migrated from gitlab.com)

Verification — GitLab #273

Issue: https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/273

Independent re-verification on branch main (no repo changes).

Acceptance criteria

Item How verified Result
Toxic no-belief hybrid (book ~50% below pool) rejected cd smartcontracts && cargo test -p cl8y-dex-tests hybrid_no_belief_book_far_below_pool_rejected PASS
No-belief metric includes book degradation cd smartcontracts && cargo test -p dex-common max_spread — no_belief_rejects_book_far_below_pool_net_rate, no_belief_accepts_book_at_pool_net_rate (10/10 unit tests) PASS
Router hybrid per-hop without minimum_receive validate_declared_hybrid_pool_leg_for_no_belief in pair/router; hybrid_no_belief_dust_pool_leg_rejected integration test PASS
Full contract suite make test-contracts — 374 integration + 41 pair unit + 19 dex-common unit PASS
Frontend preflight mirror npm test -- src/utils/swapMaxSpread.test.ts (9/9) PASS

Functional test plan

Case How verified Result
hybrid, belief_price set, normal book cargo test -p cl8y-dex-tests hybrid_belief_price_max_spread_rejects_shortfall_on_total_output + hybrid_max_spread_exact_tolerance_succeeds PASS
hybrid, no belief, book fills at quote no_belief_accepts_book_at_pool_net_rate (dex-common) PASS

Attack / abuse test plan

Case How verified Result
hybrid, no belief, book fills deep hybrid_no_belief_book_far_below_pool_rejected PASS
router hybrid hop, minimum_receive unset, dust/bad pool leg hybrid_no_belief_dust_pool_leg_rejected + no_belief_rejects_dust_pool_leg_with_book PASS

Implementation on main

  • 58092d1 — no-belief book_shortfall vs pool net rate (#273)
  • 6cf3d7a — material declared pool leg + pool_net > 0 when book leg present (#307)
  • e1ae059 — frontend swapMaxSpread preflight aligned with execute legs
  • Docs/invariant L9 synced in docs/contracts-security-audit.md and skills/AGENTS_MAX_SPREAD_HYBRID.md

Out of scope (documented follow-up)

Pure-book hybrid (declared_pool_input = 0) still requires belief_price or minimum_receive — directions 2 & 3 from issue discussion. Not a blocker for closing #273.

Verdict: All acceptance criteria PASS. Closing.

## Verification — GitLab #273 **Issue:** https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/273 Independent re-verification on branch `main` (no repo changes). ### Acceptance criteria | Item | How verified | Result | |------|----------------|--------| | Toxic no-belief hybrid (book ~50% below pool) rejected | `cd smartcontracts && cargo test -p cl8y-dex-tests hybrid_no_belief_book_far_below_pool_rejected` | **PASS** | | No-belief metric includes book degradation | `cd smartcontracts && cargo test -p dex-common max_spread` — `no_belief_rejects_book_far_below_pool_net_rate`, `no_belief_accepts_book_at_pool_net_rate` (10/10 unit tests) | **PASS** | | Router hybrid per-hop without `minimum_receive` | `validate_declared_hybrid_pool_leg_for_no_belief` in pair/router; `hybrid_no_belief_dust_pool_leg_rejected` integration test | **PASS** | | Full contract suite | `make test-contracts` — 374 integration + 41 pair unit + 19 dex-common unit | **PASS** | | Frontend preflight mirror | `npm test -- src/utils/swapMaxSpread.test.ts` (9/9) | **PASS** | ### Functional test plan | Case | How verified | Result | |------|----------------|--------| | hybrid, `belief_price` set, normal book | `cargo test -p cl8y-dex-tests hybrid_belief_price_max_spread_rejects_shortfall_on_total_output` + `hybrid_max_spread_exact_tolerance_succeeds` | **PASS** | | hybrid, no belief, book fills at quote | `no_belief_accepts_book_at_pool_net_rate` (dex-common) | **PASS** | ### Attack / abuse test plan | Case | How verified | Result | |------|----------------|--------| | hybrid, no belief, book fills deep | `hybrid_no_belief_book_far_below_pool_rejected` | **PASS** | | router hybrid hop, `minimum_receive` unset, dust/bad pool leg | `hybrid_no_belief_dust_pool_leg_rejected` + `no_belief_rejects_dust_pool_leg_with_book` | **PASS** | ### Implementation on `main` - `58092d1` — no-belief `book_shortfall` vs pool net rate (#273) - `6cf3d7a` — material declared pool leg + `pool_net > 0` when book leg present (#307) - `e1ae059` — frontend `swapMaxSpread` preflight aligned with execute legs - Docs/invariant L9 synced in `docs/contracts-security-audit.md` and `skills/AGENTS_MAX_SPREAD_HYBRID.md` ### Out of scope (documented follow-up) Pure-book hybrid (`declared_pool_input = 0`) still requires `belief_price` or `minimum_receive` — directions 2 & 3 from issue discussion. Not a blocker for closing #273. **Verdict:** All acceptance criteria **PASS**. Closing.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-05 13:43:11 +00:00
PlasticDigits commented 2026-06-05 13:44:36 +00:00 (Migrated from gitlab.com)

mentioned in issue #334

mentioned in issue #334
PlasticDigits commented 2026-06-05 13:44:36 +00:00 (Migrated from gitlab.com)

marked as related to #334

marked as related to #334
PlasticDigits commented 2026-06-05 14:09:13 +00:00 (Migrated from gitlab.com)

mentioned in merge request !819

mentioned in merge request !819
PlasticDigits commented 2026-06-05 14:27:54 +00:00 (Migrated from gitlab.com)

mentioned in commit 6e2ebbe1d0

mentioned in commit 6e2ebbe1d05d0bb30969ebada1b1275e8fc77b29
PlasticDigits commented 2026-06-08 13:42:28 +00:00 (Migrated from gitlab.com)

mentioned in commit 05ee14db17

mentioned in commit 05ee14db17272ba4b982f75df859491565454067
PlasticDigits commented 2026-06-13 07:09:04 +00:00 (Migrated from gitlab.com)

mentioned in issue #376

mentioned in issue #376
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic#273
No description provided.