fix(pair): skip blacklisted maker orders during limit book fills (#468) #1009

Merged
PlasticDigits merged 1 commit from fix/468-blacklisted-maker-fill-gate into main 2026-07-07 02:35:10 +00:00
PlasticDigits commented 2026-07-07 02:19:20 +00:00 (Migrated from gitlab.com)

Summary

Fixes GitLab #468: BlacklistWallet previously gated only the taker on swap entry. Resting limit orders owned by a blacklisted maker could still fill, paying offer-token CW20 into the frozen wallet.

Changes:

  • TradeBlacklistGate + wallet_is_trade_blacklisted in pair blacklist_guard.rs
  • match_bids / match_asks skip blacklisted order.owner; when park budget allows, unlink into EXPIRED_LIMIT_CLAIMS via park_limit_order_for_clean(..., force_expired=true) — no offer-token payout
  • simulate_match_* skips blacklisted makers read-only for quote/execute parity
  • Integration test blacklisted_maker_resting_limit_not_filled_taker_can_still_swap
  • Docs: invariant L19, B1 update, limit-orders.md, security-model.md, user FAQ, AGENTS_BLACKLIST_DECISION.md

Acceptance checklist

Criterion Verification Result
Blacklisted maker resting bid not filled on taker hybrid swap cd smartcontracts && cargo test -p cl8y-dex-tests blacklisted_maker_resting_limit_not_filled PASS
Maker receives no offer-token CW20 payout while blacklisted Same test (maker_token_a_after == maker_token_a_before) PASS
Order parks off-book for post-unblacklist claim Same test (ExpiredLimitRefund query for order_id 1) PASS
Clean-wallet taker can still execute swap Same test (swap succeeds) PASS
Existing blacklist gates unchanged cd smartcontracts && cargo test -p cl8y-dex-tests blacklist_tests -- --test-threads=1 PASS
Orderbook unit tests cd smartcontracts && cargo test -p cl8y-dex-pair orderbook -- --test-threads=1 PASS
Full contract suite make test-contracts PASS
Invariant L19 documented docs/contracts-security-audit.md PASS
Limit-order + security docs cross-linked docs/limit-orders.md, docs/security-model.md PASS
Agent playbook updated skills/AGENTS_BLACKLIST_DECISION.md PASS
LocalTerra E2E Not required for contract-only security fix SKIP

Third-party verification

git fetch origin fix/468-blacklisted-maker-fill-gate
git checkout fix/468-blacklisted-maker-fill-gate
make test-contracts
cd smartcontracts && cargo test -p cl8y-dex-tests blacklisted_maker_resting_limit_not_filled -v

Confirm docs/contracts-security-audit.md row L19 and B1 reference #468.

## Summary Fixes GitLab #468: `BlacklistWallet` previously gated only the taker on swap entry. Resting limit orders owned by a blacklisted maker could still fill, paying offer-token CW20 into the frozen wallet. **Changes:** - `TradeBlacklistGate` + `wallet_is_trade_blacklisted` in pair `blacklist_guard.rs` - `match_bids` / `match_asks` skip blacklisted `order.owner`; when park budget allows, unlink into `EXPIRED_LIMIT_CLAIMS` via `park_limit_order_for_clean(..., force_expired=true)` — no offer-token payout - `simulate_match_*` skips blacklisted makers read-only for quote/execute parity - Integration test `blacklisted_maker_resting_limit_not_filled_taker_can_still_swap` - Docs: invariant **L19**, B1 update, `limit-orders.md`, `security-model.md`, user FAQ, `AGENTS_BLACKLIST_DECISION.md` ## Acceptance checklist | Criterion | Verification | Result | |-----------|--------------|--------| | Blacklisted maker resting bid not filled on taker hybrid swap | `cd smartcontracts && cargo test -p cl8y-dex-tests blacklisted_maker_resting_limit_not_filled` | PASS | | Maker receives no offer-token CW20 payout while blacklisted | Same test (`maker_token_a_after == maker_token_a_before`) | PASS | | Order parks off-book for post-unblacklist claim | Same test (`ExpiredLimitRefund` query for order_id 1) | PASS | | Clean-wallet taker can still execute swap | Same test (swap succeeds) | PASS | | Existing blacklist gates unchanged | `cd smartcontracts && cargo test -p cl8y-dex-tests blacklist_tests -- --test-threads=1` | PASS | | Orderbook unit tests | `cd smartcontracts && cargo test -p cl8y-dex-pair orderbook -- --test-threads=1` | PASS | | Full contract suite | `make test-contracts` | PASS | | Invariant L19 documented | `docs/contracts-security-audit.md` | PASS | | Limit-order + security docs cross-linked | `docs/limit-orders.md`, `docs/security-model.md` | PASS | | Agent playbook updated | `skills/AGENTS_BLACKLIST_DECISION.md` | PASS | | LocalTerra E2E | Not required for contract-only security fix | SKIP | ## Third-party verification ```bash git fetch origin fix/468-blacklisted-maker-fill-gate git checkout fix/468-blacklisted-maker-fill-gate make test-contracts cd smartcontracts && cargo test -p cl8y-dex-tests blacklisted_maker_resting_limit_not_filled -v ``` Confirm `docs/contracts-security-audit.md` row **L19** and **B1** reference #468.
ghost1 commented 2026-07-07 02:19:23 +00:00 (Migrated from gitlab.com)

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the [Cursor dashboard](https://www.cursor.com/dashboard/bugbot) to get automatic reviews on future PRs.
PlasticDigits commented 2026-07-07 02:22:15 +00:00 (Migrated from gitlab.com)

Security review — MR !1009

Commit reviewed: 48bee6ae44a88c2a9e9b19946fe4f9cf4bd0b72e
Scope: Pair contract maker-side trading blacklist gate during hybrid limit-book fills (blacklist_guard.rs, orderbook.rs, contract.rs wiring), integration test, and documentation updates for GitLab #468 / invariant L19.

Outcome

FINDINGS: 0 (no medium, high, or critical issues on this diff)

Summary

This MR closes a real compliance gap: resting limit orders owned by a wallet-blacklisted maker could still fill and pay offer-token CW20 to the frozen address. The fix is sound:

  • Fill gate: skip_blacklisted_maker_order runs before fill math in match_bids / match_asks; blacklisted makers are never credited via maker_payouts.
  • Fail-closed: wallet_is_trade_blacklisted uses the same probe_factory_blacklist path as taker entry (#456); factory BlacklistCheck errors abort the swap (BlacklistGuardUnavailable), not fail-open.
  • Escrow safety: Parks reuse park_limit_order_for_clean(..., force_expired=true) (L1 path) — unlink only, no CW20 in the taker tx; claim remains pause/blacklist-gated until UnblacklistWallet.
  • Park-budget edge: When MAX_EXPIRED_PARKS_PER_SWAP is exhausted, blacklisted rows are still skipped (not filled); they may remain on-book until a later walk parks them — same pattern as expired-head handling, not a payout bypass.
  • Quote parity: simulate_match_* skips blacklisted makers read-only; execute skips (and parks when budget allows). Fill accounting aligns for the same snapshot.
  • Regression: blacklisted_maker_resting_limit_not_filled_taker_can_still_swap covers no maker payout + off-book park.

Candidate paths reviewed (not reported)

Area Result
Taker authz bypass via book_start_hint Hint only sets walk start; per-step skip_blacklisted_maker_order still applies forward — no fill bypass
Token/pair blacklist dimensions BlacklistCheck includes pair tokens + pair address; blocked makers skipped — consistent with B1
Factory query per book step Bounded by MAX_SCAN_STEPS (500); mitigated by book_start_hint / head-clog patterns (#289); capital required for deep-book griefing
Indexer db_orderbook_sim unchanged Off-chain quote drift possible but not an on-chain authz or fund-loss vector introduced here

Inline threads

None — zero medium+ findings.


Security review: no medium+ findings on this diff.

## Security review — MR !1009 **Commit reviewed:** `48bee6ae44a88c2a9e9b19946fe4f9cf4bd0b72e` **Scope:** Pair contract maker-side trading blacklist gate during hybrid limit-book fills (`blacklist_guard.rs`, `orderbook.rs`, `contract.rs` wiring), integration test, and documentation updates for GitLab #468 / invariant L19. ### Outcome **FINDINGS: 0** (no medium, high, or critical issues on this diff) ### Summary This MR closes a real compliance gap: resting limit orders owned by a wallet-blacklisted maker could still fill and pay offer-token CW20 to the frozen address. The fix is sound: - **Fill gate:** `skip_blacklisted_maker_order` runs before fill math in `match_bids` / `match_asks`; blacklisted makers are never credited via `maker_payouts`. - **Fail-closed:** `wallet_is_trade_blacklisted` uses the same `probe_factory_blacklist` path as taker entry (#456); factory `BlacklistCheck` errors abort the swap (`BlacklistGuardUnavailable`), not fail-open. - **Escrow safety:** Parks reuse `park_limit_order_for_clean(..., force_expired=true)` (L1 path) — unlink only, no CW20 in the taker tx; claim remains pause/blacklist-gated until `UnblacklistWallet`. - **Park-budget edge:** When `MAX_EXPIRED_PARKS_PER_SWAP` is exhausted, blacklisted rows are still skipped (not filled); they may remain on-book until a later walk parks them — same pattern as expired-head handling, not a payout bypass. - **Quote parity:** `simulate_match_*` skips blacklisted makers read-only; execute skips (and parks when budget allows). Fill accounting aligns for the same snapshot. - **Regression:** `blacklisted_maker_resting_limit_not_filled_taker_can_still_swap` covers no maker payout + off-book park. ### Candidate paths reviewed (not reported) | Area | Result | |------|--------| | Taker authz bypass via `book_start_hint` | Hint only sets walk start; per-step `skip_blacklisted_maker_order` still applies forward — no fill bypass | | Token/pair blacklist dimensions | `BlacklistCheck` includes pair tokens + pair address; blocked makers skipped — consistent with B1 | | Factory query per book step | Bounded by `MAX_SCAN_STEPS` (500); mitigated by `book_start_hint` / head-clog patterns (#289); capital required for deep-book griefing | | Indexer `db_orderbook_sim` unchanged | Off-chain quote drift possible but not an on-chain authz or fund-loss vector introduced here | ### Inline threads None — zero medium+ findings. --- Security review: no medium+ findings on this diff.
PlasticDigits (Migrated from gitlab.com) merged commit 529b34e1a7 into main 2026-07-07 02:35:11 +00:00
PlasticDigits commented 2026-07-07 02:35:12 +00:00 (Migrated from gitlab.com)

mentioned in commit 529b34e1a7

mentioned in commit 529b34e1a744b2ab6cb304dc076e1050e403aad9
Sign in to join this conversation.
No reviewers
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!1009
No description provided.