test: greedy pause/blacklist, AfterSwap L7, community-tax extra-debit (!480 leftover) #710

Closed
opened 2026-08-30 10:20:10 +00:00 by PlasticDigits · 10 comments
PlasticDigits commented 2026-08-30 10:20:10 +00:00 (Migrated from gitlab.com)

Summary

#708 / !1198 added an opt-in greedy book-first swap and a greedy_book_first_708 multitest module. That suite covers G1–G3/G5/G8 happy paths. It does not replay three invariants that #708 required to “behave as Pattern C hybrid”:

  1. Community-tax greedy sell extra-debit on the original trader (T592-13 / path 18).
  2. Pause (L6) and blacklist (L19) on a greedy walk, plus pair-direct trader spoof (A9 / A14).
  3. AfterSwap L7 — return_asset / commission = book net + pool net on a greedy fill (path 16).

This ticket is tests (and only the minimal product code if a test finds a real greedy-specific hole). Do not change G1 (pool-only default), the official dApp solver path, or Pattern C.

Correctness leftovers (query mutex, greedy_stop=empty, Decimal::from_ratio panic) are a separate issue. Land or coordinate with that MR so greedy execute used here is the post-fix matcher.

Related: #708, #607 (router tax option 2), #196 (L7 commission), #120 (pause), #468 (blacklist).


Current codebase

Greedy execute (what tests must drive)

On !1198, SwapHybridMode::Greedy sets pool_leg = 0, book_leg = offer, then leftover offer after match_* rolls into the AMM (G9). Hook string stays Cw20HookMsg::Swap (greedy: Some(...)). Pause is still assert_not_paused on Receive. Blacklist still skips makers in match_bids / match_asks. AfterSwap still runs after settlement with return_asset = book net + pool net (same as Pattern C). Community-tax is_swap_send_hook matches any Cw20HookMsg::Swap (including greedy) via from_json.

There is no greedy-specific pause, blacklist, AfterSwap, or tax test. Existing tests pass greedy: None:

Invariant Existing test Greedy?
Pause blocks swap (L6) pause_blocks_swap_and_place_cancel_refunds_escrow (limit_order_tests.rs ~L2968) No — pool-only / hybrid greedy: None
Blacklisted maker skipped (L19) blacklisted_maker_resting_limit_not_filled_taker_can_still_swap (blacklist_tests.rs ~L711) No — Pattern C book_input
AfterSwap commission = pool + book (L7) hybrid_hook_commission_includes_pool_and_book (limit_order_tests.rs ~L3670) No — declared hybrid
Tax sell extra-debit pair-direct sell_extra_debit_on_swap_send (community-tax-token/src/multitest.rs ~L493) greedy: None
Tax sell extra-debit official router router_sell_extra_debits_authenticated_trader (~L2356) greedy: None

make verify-issue-708 runs dex-common + pair greedy + greedy_book_first_708 + gas vitest + docs greps. It does not run community-tax, blacklist, or L7 hybrid hook tests.

Tax hook detection (why greedy should work)

community-tax-token/src/tax.rs:

  • is_swap_send_hook — from_json::<Cw20HookMsg> is Swap { .. } (any hybrid / greedy).
  • is_router_sell_hop — listed pair + swap hook + from is official router.
  • hop_trader_addr — honor Swap.trader only when from is the stamped official router; pair-direct extra-debits from.

#[serde(default)] on Cw20HookMsg::Swap.greedy keeps old JSON decoding. A greedy sell is still a Swap send. If tests fail, that is a product bug (serde / hook / trader), not “coverage optional.”

AfterSwap L7 (Pattern C reference)

hybrid_hook_commission_includes_pool_and_book quotes HybridSimulation, executes declared hybrid, and checks treasury / hook commission_amount = pool commission + book taker half, return_asset = book net + pool net. Greedy remainder (G9) is the same two legs; the hook must not see book-only or pool-only.


Why this is needed

#708 acceptance:

Pause / blacklist / fee discount / tax extra-debit / hooks L7 behave as Pattern C hybrid.

Attack table A9 / A12 / A13 / A14 and functional paths 14, 16, 18 were listed and are untested on greedy. Bots will send greedy on listed tax pairs and through the official router. A silent tax skip, a paused pair that still greedy-fills, a blacklisted maker that gets paid, or AfterSwap return_asset that omits the book (or pool) leg would be a mainnet-class failure that Pattern C tests would not catch (greedy: None).

These paths are the difference between “matcher reuse (G10) on paper” and “we proved pause/tax/hooks still bind.”


Constraints / guardrails

ID Rule
G1 Control: hybrid: None + greedy: None on a paused / tax / hooked pair still pool-only (no book fill).
G8 Greedy execute still needs belief_price or min_return. Tests must pass a floor (min_return = 1 is acceptable for invariant tests; add one tight-floor fail if cheap).
G10 Do not fork a second matcher to make tests pass. Drive existing match_* via greedy: Some(GreedySwapParams { … }).
G12 Router greedy is per-hop and explicit. Tax router test must set greedy on the sell hop, not auto-upgrade hybrid: null.
T592-13 / #607 Official-router hops extra-debit the original trader (Swap.trader stamped by router). Pair-direct extra-debits the CW20 from. Greedy must not loosen hop_trader_addr.
L6 / #120 Pause rejects swap (including greedy). Cancel/claim pause behavior is out of scope unless a greedy-only hole appears.
L19 / #468 Blacklisted makers skipped/parked, not paid. Blacklisted taker cannot greedy-swap.
L7 / #196 AfterSwap return_asset.amount = book net + pool net; commission_amount = pool commission + book taker commission (ask asset).
A9 / A14 Pair-direct trader: Some(whale) must not grant a fee tier or change tax subject.
A13 AfterSwap still after settlement; hook cannot re-enter swap and mint extra book fills. Reuse adversarial-token patterns if needed; do not weaken hook allowlists.
No dApp / indexer requirement Unless a test reveals a hook-string change (it must not).

Out of scope: G11 query mutex / greedy_stop remainder / Decimal panic (sibling issue); expired-prefix scan cap; reverse sim; columbus-5 migrate; e2e Playwright (nice-to-have, not this ticket unless already cheap).


Relevant files

Path Role
smartcontracts/tests/src/limit_order_tests.rs greedy_book_first_708, pause, hybrid_hook_commission_includes_pool_and_book
smartcontracts/tests/src/blacklist_tests.rs Maker skip + taker guard
smartcontracts/contracts/community-tax-token/src/multitest.rs sell_extra_debit_on_swap_send, router_sell_extra_debits_authenticated_trader
smartcontracts/contracts/community-tax-token/src/tax.rs is_swap_send_hook, hop_trader_addr
smartcontracts/contracts/pair/src/contract.rs AfterSwap emit (~L1296 return_asset), pause, execute_swap greedy
smartcontracts/contracts/pair/src/orderbook.rs Blacklist skip in match_*
smartcontracts/contracts/router/src/contract.rs Stamps trader; greedy hop
smartcontracts/tests/src/adversarial_token.rs AfterSwap re-entrancy patterns (greedy: None today)
scripts/qa/verify-issue-708.sh Add this iid’s tests or a new verify-issue-<iid>
skills/AGENTS_GREEDY_BOOK_FIRST.md, skills/AGENTS_COMMUNITY_TAX_ROUTER.md Crosslink

  1. Reuse fixtures, swap the hook field. Copy the Pattern C / pool-only setups (seed pool, better bid/ask so greedy actually touches the book). Replace hybrid: Some(declared) / greedy: None with hybrid: None + greedy: Some(greedy_swap_params(n, None)) and a G8 floor. Prefer extending mod greedy_book_first_708 plus tax multitest siblings named *_greedy_* so cargo test greedy / verify-issue-* can filter.

  2. Tax:

    • Pair-direct greedy sell of the listed tax CW20 into the pair: extra-debit sender, same bps as sell_extra_debit_on_swap_send.
    • Official router greedy hop: extra-debit authenticated trader, not the router (router_sell_extra_debits_authenticated_trader analogue).
    • Control: greedy: None on the same pair still extra-debits (regression).
    • Control: buy path unchanged if the existing suite distinguishes buy vs sell.
  3. Pause: factory SetPairPaused { paused: true } → greedy Send+Swap errors; unpause → greedy fill works (better bid). Query HybridSimulation greedy may still succeed while paused (pin current pause-on-execute-only behavior; do not newly pause queries unless product already does).

  4. Blacklist:

    • Blacklisted maker with a strictly better bid: greedy does not pay them; remainder/other makers/pool still work; escrow parked as Pattern C.
    • Blacklisted taker: greedy swap errors.
    • Non-blacklisted taker + live better non-blacklisted maker: fill (sanity).
  5. AfterSwap L7: Seed better bid + leftover offer so both legs exist. Assert hook return_asset and commission_amount against sim return_amount / commission_amount (or treasury delta like the Pattern C test). Query must not park or fire AfterSwap.

  6. Trader spoof (pair-direct): greedy swap with trader: Some(high-tier) from a non-router CW20 sender → fee/tax as sender, not whale.

  7. If a test fails because greedy never hits the book (G3 stop), fix the fixture (price strictly better than pool after fees), not the matcher.

  8. Add names to make verify-issue-<iid> (tax crate + cl8y-dex-tests filters). Do not require make test-contracts entire tree unless already cheap.


Acceptance criteria

  • Greedy community-tax sell pair-direct extra-debits the CW20 from (same economic extra-debit as pool-only/hybrid sell on that fixture).
  • Greedy community-tax sell via official router extra-debits the original trader (Swap.trader), not the router contract (T592-13).
  • Pair paused → greedy execute rejects; after unpause, greedy can fill a better maker (L6).
  • Blacklisted maker’s better resting order is not filled / not paid on greedy; taker can still complete via pool and/or other makers (L19).
  • Blacklisted taker greedy swap rejects.
  • Pair-direct greedy trader: Some(other) does not apply that wallet’s fee tier or tax exemption (A9 / A14).
  • Greedy fill with book + pool remainder: AfterSwap return_asset = book net + pool net; commission = pool + book taker half (L7). Query does not fire the hook / does not park.
  • G1 control: omitted greedy on the same tax/pause fixtures still does not fill the book.
  • Pattern C tests above stay green (greedy: None).
  • make verify-issue-<iid> runs the new test names (and greps G1/L7/T592-13 if docs updated).

Test plan (functional paths)

Community tax (listed pair, sell of tax CW20)

  1. Pair-direct greedy sell, better ask/bid so book is used + remainder pool → extra-debit on sender; balances vs pool-only control on same offer (extra-debit still charged).
  2. Pair-direct greedy sell, empty/worse book (100% pool) → extra-debit still charged (tax must not depend on book contact).
  3. Official router one-hop greedy sell → extra-debit on trader; router CW20 balance not the tax subject.
  4. Official router hop hybrid: null (no greedy) → unchanged extra-debit (regression).
  5. Pair-direct with trader: Some(victim) spoof → extra-debit from, not victim.
  6. Buy of tax token via greedy (if the tax suite has a buy analogue) → net/buy behavior matches hybrid/pool-only, not a sell extra-debit.

Pause / blacklist / trader

  1. Pause then greedy execute → error; IsPaused true; unpause then greedy fills better bid.
  2. Pause then G1 pool-only still errors (control).
  3. Blacklisted maker + greedy, better bid → no limit_order_fill for that id; maker token0/1 not increased from fill; taker return from pool (and/or other makers).
  4. Blacklisted taker + greedy → error; book unchanged.
  5. Pair-direct greedy + spoof trader high CL8Y tier → effective_fee_bps / treasury commission as unregistered sender (or existing pair-direct rule).

AfterSwap L7

  1. Greedy better bid + remainder: execute events / hook mock: return_asset.amount == book_return + pool_return (net); commission_amount == pool_commission + book_commission (match HybridSimulation greedy quote).
  2. Greedy empty book (100% pool): AfterSwap matches pool-only L7 (no book commission).
  3. HybridSimulation greedy on the same state: no AfterSwap message, no park, amounts equal execute when book has no expired prefix.

Optional if cheap

  1. Adversarial AfterSwap re-enter greedy swap → no extra fills (A13).

Test plan (attack, hack, abuse)

Vector Expect
A9 spoof trader Pair-direct greedy ignores trader for fee discount and for tax hop_trader_addr. Router-only stamp remains.
A12 pause during walk Pause is checked at Receive / execute entry, not mid-match. Paused pair: no greedy fills, no extra-debit success, no AfterSwap. Unpause does not retroactively fill.
A12 blacklist maker Skip/park, not pay. Greedy G3 must not treat a skipped maker as WorseThanPool in a way that stops the walk and then pays that maker via another path. Remainder → pool.
A12 blacklist taker No swap, book untouched.
A13 AfterSwap re-entrancy Hook cannot Send+Swap greedy on the same pair to mint extra fills or double extra-debit. Same allowlist as Pattern C.
A14 fee-tier grief Cannot self-set trader on pair-direct greedy to steal a whale tier (pool + book legs both use authenticated trader).
Tax bypass via greedy is_swap_send_hook must still match Swap { greedy: Some(_) }. A serde miss (deny_unknown_fields / missing default) that makes tax treat greedy as not a swap would skip extra-debit — fail this ticket.
Tax subject = router Greedy hop must not extra-debit the router contract; still original trader.
L7 hook underpay / overpay return_asset must not be pool-only (hides book) or book-only (hides AMM remainder). HookFeeExceedsReturn still guards.

Verification criteria

  • cd smartcontracts && cargo test -p cl8y-dex-community-tax-token greedy -- --test-threads=1 (or the exact new test filter) green, including pair-direct + router extra-debit.
  • cd smartcontracts && cargo test -p cl8y-dex-tests greedy_book_first_708 -- --test-threads=1 green, including new pause / L7 tests in that module or clearly named greedy_* tests in limit_order_tests / blacklist_tests.
  • Blacklist greedy tests green (cargo test -p cl8y-dex-tests greedy_blacklist or equivalent).
  • Explicit G1: tax/pause fixtures with omitted greedy do not fill the better rest.
  • Explicit: router greedy sell extra-debit target is the user, not the router.
  • Explicit: AfterSwap return_asset on greedy remainder = book net + pool net.
  • make verify-issue-<iid> lists those cargo filters (and does not claim they ran if they did not).
  • Skill AGENTS_GREEDY_BOOK_FIRST.md Tests section mentions tax / pause / L7 / blacklist commands.

No LocalTerra required. If a test finds a real greedy-specific tax/pause/hook bug, fix it in the same MR as the test (do not file-and-forget).

## Summary [#708](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/708) / [!1198](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/480) added an opt-in greedy book-first swap and a `greedy_book_first_708` multitest module. That suite covers G1–G3/G5/G8 happy paths. It does **not** replay three invariants that #708 required to “behave as Pattern C hybrid”: 1. **Community-tax greedy sell extra-debit** on the original trader (**T592-13** / path 18). 2. **Pause (L6)** and **blacklist (L19)** on a greedy walk, plus pair-direct `trader` spoof (**A9** / **A14**). 3. **AfterSwap L7** — `return_asset` / commission = book net + pool net on a greedy fill (path 16). This ticket is **tests (and only the minimal product code if a test finds a real greedy-specific hole)**. Do not change G1 (pool-only default), the official dApp solver path, or Pattern C. Correctness leftovers (query mutex, `greedy_stop=empty`, `Decimal::from_ratio` panic) are a **separate** issue. Land or coordinate with that MR so greedy execute used here is the post-fix matcher. Related: [#708](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/708), [#607](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/607) (router tax option 2), [#196](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/196) (L7 commission), [#120](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/120) (pause), [#468](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/468) (blacklist). --- ## Current codebase ### Greedy execute (what tests must drive) On !1198, `SwapHybridMode::Greedy` sets `pool_leg = 0`, `book_leg = offer`, then leftover offer after `match_*` rolls into the AMM (**G9**). Hook string stays `Cw20HookMsg::Swap` (`greedy: Some(...)`). Pause is still `assert_not_paused` on `Receive`. Blacklist still skips makers in `match_bids` / `match_asks`. AfterSwap still runs **after** settlement with `return_asset` = book net + pool net (same as Pattern C). Community-tax `is_swap_send_hook` matches **any** `Cw20HookMsg::Swap` (including greedy) via `from_json`. There is **no** greedy-specific pause, blacklist, AfterSwap, or tax test. Existing tests pass `greedy: None`: | Invariant | Existing test | Greedy? | |-----------|---------------|---------| | Pause blocks swap (**L6**) | `pause_blocks_swap_and_place_cancel_refunds_escrow` ([`limit_order_tests.rs`](smartcontracts/tests/src/limit_order_tests.rs) ~L2968) | No — pool-only / hybrid `greedy: None` | | Blacklisted maker skipped (**L19**) | `blacklisted_maker_resting_limit_not_filled_taker_can_still_swap` ([`blacklist_tests.rs`](smartcontracts/tests/src/blacklist_tests.rs) ~L711) | No — Pattern C `book_input` | | AfterSwap commission = pool + book (**L7**) | `hybrid_hook_commission_includes_pool_and_book` ([`limit_order_tests.rs`](smartcontracts/tests/src/limit_order_tests.rs) ~L3670) | No — declared hybrid | | Tax sell extra-debit pair-direct | `sell_extra_debit_on_swap_send` ([`community-tax-token/src/multitest.rs`](smartcontracts/contracts/community-tax-token/src/multitest.rs) ~L493) | `greedy: None` | | Tax sell extra-debit official router | `router_sell_extra_debits_authenticated_trader` (~L2356) | `greedy: None` | `make verify-issue-708` runs dex-common + pair `greedy` + `greedy_book_first_708` + gas vitest + docs greps. It does **not** run community-tax, blacklist, or L7 hybrid hook tests. ### Tax hook detection (why greedy *should* work) [`community-tax-token/src/tax.rs`](smartcontracts/contracts/community-tax-token/src/tax.rs): - `is_swap_send_hook` — `from_json::<Cw20HookMsg>` is `Swap { .. }` (any `hybrid` / `greedy`). - `is_router_sell_hop` — listed pair + swap hook + `from` is official router. - `hop_trader_addr` — honor `Swap.trader` **only** when `from` is the stamped official router; pair-direct extra-debits `from`. `#[serde(default)]` on `Cw20HookMsg::Swap.greedy` keeps old JSON decoding. A greedy sell is still a `Swap` send. **If tests fail, that is a product bug** (serde / hook / trader), not “coverage optional.” ### AfterSwap L7 (Pattern C reference) `hybrid_hook_commission_includes_pool_and_book` quotes `HybridSimulation`, executes declared hybrid, and checks treasury / hook `commission_amount` = pool commission + book taker half, `return_asset` = book net + pool net. Greedy remainder (**G9**) is the same two legs; the hook must not see book-only or pool-only. --- ## Why this is needed #708 acceptance: > Pause / blacklist / fee discount / tax extra-debit / hooks **L7** behave as Pattern C hybrid. Attack table **A9 / A12 / A13 / A14** and functional paths **14, 16, 18** were listed and are **untested** on greedy. Bots will send `greedy` on listed tax pairs and through the official router. A silent tax skip, a paused pair that still greedy-fills, a blacklisted maker that gets paid, or AfterSwap `return_asset` that omits the book (or pool) leg would be a **mainnet-class** failure that Pattern C tests would not catch (`greedy: None`). These paths are the difference between “matcher reuse (**G10**) on paper” and “we proved pause/tax/hooks still bind.” --- ## Constraints / guardrails | ID | Rule | |----|------| | **G1** | Control: `hybrid: None` + `greedy: None` on a paused / tax / hooked pair still pool-only (no book fill). | | **G8** | Greedy execute still needs `belief_price` or `min_return`. Tests must pass a floor (`min_return = 1` is acceptable for invariant tests; add one tight-floor fail if cheap). | | **G10** | Do **not** fork a second matcher to make tests pass. Drive existing `match_*` via `greedy: Some(GreedySwapParams { … })`. | | **G12** | Router greedy is per-hop and explicit. Tax router test must set `greedy` on the **sell hop**, not auto-upgrade `hybrid: null`. | | **T592-13 / #607** | Official-router hops extra-debit the **original trader** (`Swap.trader` stamped by router). Pair-direct extra-debits the CW20 `from`. Greedy must not loosen `hop_trader_addr`. | | **L6 / #120** | Pause rejects swap (including greedy). Cancel/claim pause behavior is out of scope unless a greedy-only hole appears. | | **L19 / #468** | Blacklisted **makers** skipped/parked, not paid. Blacklisted **taker** cannot greedy-swap. | | **L7 / #196** | AfterSwap `return_asset.amount` = book net + pool net; `commission_amount` = pool commission + book taker commission (ask asset). | | **A9 / A14** | Pair-direct `trader: Some(whale)` must **not** grant a fee tier or change tax subject. | | **A13** | AfterSwap still after settlement; hook cannot re-enter swap and mint extra book fills. Reuse adversarial-token patterns if needed; do not weaken hook allowlists. | | **No dApp / indexer requirement** | Unless a test reveals a hook-string change (it must not). | **Out of scope:** G11 query mutex / `greedy_stop` remainder / Decimal panic (sibling issue); expired-prefix scan cap; reverse sim; columbus-5 migrate; e2e Playwright (nice-to-have, not this ticket unless already cheap). --- ## Relevant files | Path | Role | |------|------| | [`smartcontracts/tests/src/limit_order_tests.rs`](smartcontracts/tests/src/limit_order_tests.rs) | `greedy_book_first_708`, pause, `hybrid_hook_commission_includes_pool_and_book` | | [`smartcontracts/tests/src/blacklist_tests.rs`](smartcontracts/tests/src/blacklist_tests.rs) | Maker skip + taker guard | | [`smartcontracts/contracts/community-tax-token/src/multitest.rs`](smartcontracts/contracts/community-tax-token/src/multitest.rs) | `sell_extra_debit_on_swap_send`, `router_sell_extra_debits_authenticated_trader` | | [`smartcontracts/contracts/community-tax-token/src/tax.rs`](smartcontracts/contracts/community-tax-token/src/tax.rs) | `is_swap_send_hook`, `hop_trader_addr` | | [`smartcontracts/contracts/pair/src/contract.rs`](smartcontracts/contracts/pair/src/contract.rs) | AfterSwap emit (~L1296 `return_asset`), pause, `execute_swap` greedy | | [`smartcontracts/contracts/pair/src/orderbook.rs`](smartcontracts/contracts/pair/src/orderbook.rs) | Blacklist skip in `match_*` | | [`smartcontracts/contracts/router/src/contract.rs`](smartcontracts/contracts/router/src/contract.rs) | Stamps `trader`; greedy hop | | [`smartcontracts/tests/src/adversarial_token.rs`](smartcontracts/tests/src/adversarial_token.rs) | AfterSwap re-entrancy patterns (`greedy: None` today) | | [`scripts/qa/verify-issue-708.sh`](scripts/qa/verify-issue-708.sh) | Add this iid’s tests or a new `verify-issue-<iid>` | | [`skills/AGENTS_GREEDY_BOOK_FIRST.md`](skills/AGENTS_GREEDY_BOOK_FIRST.md), [`skills/AGENTS_COMMUNITY_TAX_ROUTER.md`](skills/AGENTS_COMMUNITY_TAX_ROUTER.md) | Crosslink | --- ## Recommended direction 1. **Reuse fixtures, swap the hook field.** Copy the Pattern C / pool-only setups (seed pool, better bid/ask so greedy actually touches the book). Replace `hybrid: Some(declared)` / `greedy: None` with `hybrid: None` + `greedy: Some(greedy_swap_params(n, None))` and a G8 floor. Prefer extending `mod greedy_book_first_708` plus tax multitest siblings named `*_greedy_*` so `cargo test greedy` / `verify-issue-*` can filter. 2. **Tax:** - Pair-direct greedy **sell** of the listed tax CW20 into the pair: extra-debit **sender**, same bps as `sell_extra_debit_on_swap_send`. - Official router greedy hop: extra-debit **authenticated `trader`**, not the router (`router_sell_extra_debits_authenticated_trader` analogue). - Control: `greedy: None` on the same pair still extra-debits (regression). - Control: buy path unchanged if the existing suite distinguishes buy vs sell. 3. **Pause:** factory `SetPairPaused { paused: true }` → greedy `Send+Swap` errors; unpause → greedy fill works (better bid). Query `HybridSimulation` greedy may still succeed while paused (pin current pause-on-execute-only behavior; do not newly pause queries unless product already does). 4. **Blacklist:** - Blacklisted **maker** with a strictly better bid: greedy does not pay them; remainder/other makers/pool still work; escrow parked as Pattern C. - Blacklisted **taker**: greedy swap errors. - Non-blacklisted taker + live better non-blacklisted maker: fill (sanity). 5. **AfterSwap L7:** Seed better bid + leftover offer so **both** legs exist. Assert hook `return_asset` and `commission_amount` against sim `return_amount` / `commission_amount` (or treasury delta like the Pattern C test). Query must **not** park or fire AfterSwap. 6. **Trader spoof (pair-direct):** greedy swap with `trader: Some(high-tier)` from a non-router CW20 sender → fee/tax as **sender**, not whale. 7. If a test fails because greedy never hits the book (G3 stop), fix the fixture (price strictly better than pool after fees), not the matcher. 8. Add names to `make verify-issue-<iid>` (tax crate + `cl8y-dex-tests` filters). Do not require `make test-contracts` entire tree unless already cheap. --- ## Acceptance criteria - [ ] Greedy **community-tax sell** pair-direct extra-debits the CW20 `from` (same economic extra-debit as pool-only/hybrid sell on that fixture). - [ ] Greedy **community-tax sell** via official router extra-debits the **original trader** (`Swap.trader`), not the router contract (**T592-13**). - [ ] Pair paused → greedy execute **rejects**; after unpause, greedy can fill a better maker (**L6**). - [ ] Blacklisted maker’s better resting order is **not** filled / not paid on greedy; taker can still complete via pool and/or other makers (**L19**). - [ ] Blacklisted taker greedy swap **rejects**. - [ ] Pair-direct greedy `trader: Some(other)` does **not** apply that wallet’s fee tier or tax exemption (**A9** / **A14**). - [ ] Greedy fill with book + pool remainder: AfterSwap `return_asset` = book net + pool net; commission = pool + book taker half (**L7**). Query does not fire the hook / does not park. - [ ] G1 control: omitted `greedy` on the same tax/pause fixtures still does not fill the book. - [ ] Pattern C tests above stay green (`greedy: None`). - [ ] `make verify-issue-<iid>` runs the new test names (and greps G1/L7/T592-13 if docs updated). --- ## Test plan (functional paths) **Community tax (listed pair, sell of tax CW20)** 1. Pair-direct greedy sell, better ask/bid so book is used + remainder pool → extra-debit on sender; balances vs pool-only control on same offer (extra-debit still charged). 2. Pair-direct greedy sell, empty/worse book (100% pool) → extra-debit still charged (tax must not depend on book contact). 3. Official router one-hop greedy sell → extra-debit on `trader`; router CW20 balance not the tax subject. 4. Official router hop `hybrid: null` (no greedy) → unchanged extra-debit (regression). 5. Pair-direct with `trader: Some(victim)` spoof → extra-debit **from**, not victim. 6. Buy of tax token via greedy (if the tax suite has a buy analogue) → net/buy behavior matches hybrid/pool-only, not a sell extra-debit. **Pause / blacklist / trader** 7. Pause then greedy execute → error; `IsPaused` true; unpause then greedy fills better bid. 8. Pause then G1 pool-only still errors (control). 9. Blacklisted maker + greedy, better bid → no `limit_order_fill` for that id; maker token0/1 not increased from fill; taker return from pool (and/or other makers). 10. Blacklisted taker + greedy → error; book unchanged. 11. Pair-direct greedy + spoof `trader` high CL8Y tier → `effective_fee_bps` / treasury commission as unregistered sender (or existing pair-direct rule). **AfterSwap L7** 12. Greedy better bid + remainder: execute events / hook mock: `return_asset.amount` == book_return + pool_return (net); `commission_amount` == pool_commission + book_commission (match `HybridSimulation` greedy quote). 13. Greedy empty book (100% pool): AfterSwap matches pool-only L7 (no book commission). 14. `HybridSimulation` greedy on the same state: **no** AfterSwap message, **no** park, amounts equal execute when book has no expired prefix. **Optional if cheap** 15. Adversarial AfterSwap re-enter greedy swap → no extra fills (A13). --- ## Test plan (attack, hack, abuse) | Vector | Expect | |--------|--------| | **A9 spoof `trader`** | Pair-direct greedy ignores `trader` for fee discount and for tax `hop_trader_addr`. Router-only stamp remains. | | **A12 pause during walk** | Pause is checked at `Receive` / execute entry, not mid-match. Paused pair: no greedy fills, no extra-debit success, no AfterSwap. Unpause does not retroactively fill. | | **A12 blacklist maker** | Skip/park, not pay. Greedy G3 must not treat a skipped maker as `WorseThanPool` in a way that **stops** the walk and then pays that maker via another path. Remainder → pool. | | **A12 blacklist taker** | No swap, book untouched. | | **A13 AfterSwap re-entrancy** | Hook cannot `Send+Swap` greedy on the same pair to mint extra fills or double extra-debit. Same allowlist as Pattern C. | | **A14 fee-tier grief** | Cannot self-set `trader` on pair-direct greedy to steal a whale tier (pool + book legs both use authenticated trader). | | **Tax bypass via greedy** | `is_swap_send_hook` must still match `Swap { greedy: Some(_) }`. A serde miss (`deny_unknown_fields` / missing `default`) that makes tax treat greedy as **not** a swap would skip extra-debit — **fail this ticket**. | | **Tax subject = router** | Greedy hop must not extra-debit the router contract; still original trader. | | **L7 hook underpay / overpay** | `return_asset` must not be pool-only (hides book) or book-only (hides AMM remainder). `HookFeeExceedsReturn` still guards. | --- ## Verification criteria - [ ] `cd smartcontracts && cargo test -p cl8y-dex-community-tax-token greedy -- --test-threads=1` (or the exact new test filter) green, including pair-direct + router extra-debit. - [ ] `cd smartcontracts && cargo test -p cl8y-dex-tests greedy_book_first_708 -- --test-threads=1` green, including new pause / L7 tests in that module **or** clearly named `greedy_*` tests in `limit_order_tests` / `blacklist_tests`. - [ ] Blacklist greedy tests green (`cargo test -p cl8y-dex-tests greedy_blacklist` or equivalent). - [ ] Explicit G1: tax/pause fixtures with omitted greedy do not fill the better rest. - [ ] Explicit: router greedy sell extra-debit target is the user, not the router. - [ ] Explicit: AfterSwap `return_asset` on greedy remainder = book net + pool net. - [ ] `make verify-issue-<iid>` lists those cargo filters (and does not claim they ran if they did not). - [ ] Skill `AGENTS_GREEDY_BOOK_FIRST.md` Tests section mentions tax / pause / L7 / blacklist commands. No LocalTerra required. If a test finds a real greedy-specific tax/pause/hook bug, fix it in the **same** MR as the test (do not file-and-forget).
PlasticDigits commented 2026-08-30 10:20:11 +00:00 (Migrated from gitlab.com)

marked as related to #708

marked as related to #708
PlasticDigits commented 2026-08-30 11:08:15 +00:00 (Migrated from gitlab.com)

mentioned in commit 83bccc2838

mentioned in commit 83bccc28386660222aebde56a127f4403f834669
PlasticDigits commented 2026-08-30 11:09:51 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1198

mentioned in merge request !1198
PlasticDigits commented 2026-08-31 05:07:34 +00:00 (Migrated from gitlab.com)

mentioned in commit 56e4c4f513

mentioned in commit 56e4c4f51339b9bc20422a7783bf94af748d7044
PlasticDigits commented 2026-08-31 05:07:56 +00:00 (Migrated from gitlab.com)

mentioned in commit f54de4fd4b

mentioned in commit f54de4fd4b2373b8f482791c620ff34e6d47d6fa
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-31 05:07:58 +00:00
PlasticDigits commented 2026-08-31 05:09:18 +00:00 (Migrated from gitlab.com)

mentioned in issue #712

mentioned in issue #712
PlasticDigits commented 2026-08-31 05:09:19 +00:00 (Migrated from gitlab.com)

marked as related to #712

marked as related to #712
PlasticDigits commented 2026-08-31 05:09:37 +00:00 (Migrated from gitlab.com)

mentioned in issue #708

mentioned in issue #708
PlasticDigits commented 2026-08-31 05:09:39 +00:00 (Migrated from gitlab.com)

!1198 merged to main (f54de4fd). Merge-time verify: make verify-issue-710 PASS.

Covered: greedy tax sell extra-debit (pair-direct + official router T592-13), pause L6, blacklist maker/taker L19, pair-direct trader spoof A9/A14, AfterSwap book+pool L7.

Not in this MR (optional follow-up on #712): community-tax greedy buy analogue; AfterSwap re-entrancy A13 greedy-specific test. Do not reopen this issue for those.

!1198 merged to `main` (`f54de4fd`). Merge-time verify: `make verify-issue-710` **PASS**. Covered: greedy tax sell extra-debit (pair-direct + official router **T592-13**), pause **L6**, blacklist maker/taker **L19**, pair-direct `trader` spoof **A9/A14**, AfterSwap book+pool **L7**. Not in this MR (optional follow-up on #712): community-tax greedy **buy** analogue; AfterSwap re-entrancy **A13** greedy-specific test. Do **not** reopen this issue for those.
PlasticDigits commented 2026-09-01 08:15:55 +00:00 (Migrated from gitlab.com)

mentioned in issue #718

mentioned in issue #718
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#710
No description provided.