fix(community-tax): launch-guard cooldown is pair-wide and max_wallet bricks provide (H-3, H-4) #608

Closed
opened 2026-08-23 11:49:52 +00:00 by PlasticDigits · 14 comments
PlasticDigits commented 2026-08-23 11:49:52 +00:00 (Migrated from gitlab.com)

Audit INTERNAL_KIMIK3_1787468843 H-3 / H-4. Template #592 T592-11. Launch-guard editors / SKU init #605. Identity #604 is unrelated. PoCs: poc_cooldown_bricks_pair, poc_max_wallet_bricks_provide.

H-5 (trading_enabled=false locks pair→EOA) is documented T592-11 / 11611 A9/E8 — out of scope; do not “fix” pause-both-sides here.

Columbus-5 token 11611 is factory-listed. This is a token migrate.


Current codebase

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

  • On Buy and Sell, check_cooldown runs for both from and to, then LAST_TRADE_BLOCK is written for both.
  • LAST_TRADE_BLOCK is documented as “last taxed swap block per wallet” (state.rs).
  • The listed pair is to on every sell and from on every buy. After the first trade, the pair’s timestamp blocks every other wallet until cooldown_blocks elapse. Not per-wallet anti-snipe — a global pair halt.
  • 11611 REPORT D11 only recorded same-sender in-block batch residual. Pair-wide cooldown is stronger and new.

max_wallet:

  • T592-11: sell to a listed pair bypasses max_wallet (so exits are not bricked by the cap).
  • Provide is TransferFrom → Honest, so the bypass does not apply. After sells grow pair_balance > max_wallet, every ProvideLiquidity (TransferFrom to the pair) reverts MaxWallet.
  • Existing multitest launch_guards_block_both_sides_and_sell_bypasses_max_wallet covers sell bypass only — not cooldown, not provide.

Why this is needed

LaunchGuards is a paid SKU marketed as max wallet / cooldown / trading on-off. As implemented, cooldown DoSes the pair after one trade, and max wallet DoSes new LP after organic sells. That is not usable as anti-snipe; it is a liveness hole (and a honeypot lever if a manager sets a tight cap + cooldown).


Constraints / guardrails

  1. Keep T592-11 sell-to-listed-pair max_wallet bypass (exits).
  2. Do not change trading_enabled exit behavior in this ticket (H-5 is accepted residual).
  3. Cooldown subjects are EOA / non-pair wallets only. Do not record or check listed pairs, router, factory, this token, or AutoLP.
  4. max_wallet must not apply when to is a listed pair or other protocol-exempt address (provide / router / AutoLP / self).
  5. max_wallet still applies to ordinary wallets on Buy / Transfer (non-exempt).
  6. cooldown_blocks == 0 remains “off.”
  7. 11611 migrate + F6 pin story if live instances exist by then (catalog was empty at audit).

Relevant files

  • smartcontracts/contracts/community-tax-token/src/tax.rs
  • smartcontracts/contracts/community-tax-token/src/state.rs (LAST_TRADE_BLOCK comment)
  • smartcontracts/contracts/community-tax-token/src/multitest.rs
  • smartcontracts/contracts/community-token-launcher/tests/audit_poc.rs
  • skills/AGENTS_COMMUNITY_TAX_CW20.md (T592-11)
  • cw20-codeid-audits/codeids/11611/REPORT.md (D11 / A11 — update after fix)

  1. H-3: check_cooldown / record_trade_blocks only for addresses that are not listed pairs and not protocol-exempt. Typically: the trader wallet on Sell (from) and the recipient wallet on Buy (to) when that recipient is a user.
  2. H-4: skip max_wallet when to is a listed pair or protocol-exempt (same set as inbound 1:1). Keep the check for user wallets.

Acceptance criteria

  • Two different wallets can sell (or buy) in the same block when cooldown_blocks > 0.
  • The same wallet still hits cooldown until cooldown_blocks elapse.
  • Pair balance may exceed max_wallet after sells; a later TransferFrom provide succeeds.
  • A user wallet still cannot receive above max_wallet on Buy / Transfer.
  • Sell to listed pair still bypasses max_wallet (T592-11).
  • poc_cooldown_bricks_pair and poc_max_wallet_bricks_provide fail or are inverted.
  • T592-11 text updated: cooldown is per wallet; max_wallet skips protocol / listed pair as to.

Test plan — all paths

  1. Cooldown 0: unchanged, multiple trades same block.
  2. Cooldown N: wallet A sells, wallet B sells next block — ok; wallet A sells again before N — Cooldown.
  3. Buy after a sell (pair is from): must not use the pair’s timestamp to block B.
  4. Provide TransferFrom after pair balance > max_wallet — ok.
  5. Buy to a user that would exceed max_wallet — MaxWallet.
  6. TransferTax + max_wallet on an EOA — still enforced.
  7. LaunchGuards SKU off — no cooldown / max_wallet.
  8. Protocol-exempt to (router / AutoLP / self) — no max_wallet brick.

Test plan — attack / abuse

  1. Pair-wide halt: two wallets, cooldown 10, second trade must not revert after this fix (current PoC).
  2. Self-trade / same wallet two hops: still rate-limited (anti-snipe intent).
  3. Provide brick / LP lock: sells inflate pair above cap; honest LP cannot add — current PoC; must not remain.
  4. max_wallet as sell lock: sell-to-pair must still bypass (do not “fix” H-4 by applying max_wallet to sells).
  5. Manager sets cooldown = 1 and max_wallet dust after users buy — still possible (M-4 / product); this ticket only removes the unintended pair/LP DoS.
  6. Batch / ladder (11611 D11): second transfer from the same wallet in-block may still cooldown — acceptable; document.

Verification

  • cargo test -p cl8y-community-token-launcher --test audit_poc — H-3 / H-4 inverted.
  • cargo test -p cl8y-community-tax-token — extend launch-guard multitest beyond sell bypass.
  • After migrate: LocalTerra LaunchGuards token, two wallets swap in one window; provide after pair > cap.
  • Update 11611 REPORT D11 / A11 notes if the pin is remigrated.
## Parent / related Audit [`INTERNAL_KIMIK3_1787468843`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/audits/INTERNAL_KIMIK3_1787468843.md) **H-3 / H-4**. Template [#592](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/592) **T592-11**. Launch-guard editors / SKU init [#605](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/605). Identity [#604](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/604) is unrelated. PoCs: `poc_cooldown_bricks_pair`, `poc_max_wallet_bricks_provide`. **H-5** (`trading_enabled=false` locks pair→EOA) is **documented T592-11 / 11611 A9/E8** — out of scope; do not “fix” pause-both-sides here. Columbus-5 token **11611** is factory-listed. This is a **token migrate**. --- ## Current codebase [`community-tax-token/src/tax.rs`](smartcontracts/contracts/community-tax-token/src/tax.rs) `apply_launch_guards` / `record_trade_blocks`: - On **Buy** and **Sell**, `check_cooldown` runs for **both** `from` and `to`, then `LAST_TRADE_BLOCK` is written for **both**. - `LAST_TRADE_BLOCK` is documented as “last taxed swap block **per wallet**” ([`state.rs`](smartcontracts/contracts/community-tax-token/src/state.rs)). - The listed pair is `to` on every sell and `from` on every buy. After the first trade, the pair’s timestamp blocks **every other wallet** until `cooldown_blocks` elapse. Not per-wallet anti-snipe — a **global pair halt**. - 11611 REPORT **D11** only recorded same-sender in-block batch residual. Pair-wide cooldown is stronger and new. `max_wallet`: - **T592-11:** sell to a listed pair **bypasses** `max_wallet` (so exits are not bricked by the cap). - Provide is `TransferFrom` → **Honest**, so the bypass does **not** apply. After sells grow `pair_balance > max_wallet`, every `ProvideLiquidity` (`TransferFrom` to the pair) reverts `MaxWallet`. - Existing multitest `launch_guards_block_both_sides_and_sell_bypasses_max_wallet` covers sell bypass only — not cooldown, not provide. --- ## Why this is needed LaunchGuards is a paid SKU marketed as max wallet / cooldown / trading on-off. As implemented, cooldown DoSes the pair after one trade, and max wallet DoSes **new LP** after organic sells. That is not usable as anti-snipe; it is a liveness hole (and a honeypot lever if a manager sets a tight cap + cooldown). --- ## Constraints / guardrails 1. Keep **T592-11** sell-to-listed-pair `max_wallet` bypass (exits). 2. Do **not** change `trading_enabled` exit behavior in this ticket (H-5 is accepted residual). 3. Cooldown subjects are **EOA / non-pair wallets only**. Do not record or check listed pairs, router, factory, this token, or AutoLP. 4. `max_wallet` must not apply when `to` is a listed pair or other protocol-exempt address (provide / router / AutoLP / self). 5. `max_wallet` still applies to ordinary wallets on Buy / Transfer (non-exempt). 6. `cooldown_blocks == 0` remains “off.” 7. 11611 migrate + F6 pin story if live instances exist by then (catalog was empty at audit). --- ## Relevant files - `smartcontracts/contracts/community-tax-token/src/tax.rs` - `smartcontracts/contracts/community-tax-token/src/state.rs` (`LAST_TRADE_BLOCK` comment) - `smartcontracts/contracts/community-tax-token/src/multitest.rs` - `smartcontracts/contracts/community-token-launcher/tests/audit_poc.rs` - `skills/AGENTS_COMMUNITY_TAX_CW20.md` (T592-11) - `cw20-codeid-audits/codeids/11611/REPORT.md` (D11 / A11 — update after fix) --- ## Recommended direction 1. **H-3:** `check_cooldown` / `record_trade_blocks` only for addresses that are **not** listed pairs and **not** protocol-exempt. Typically: the trader wallet on Sell (`from`) and the recipient wallet on Buy (`to`) when that recipient is a user. 2. **H-4:** skip `max_wallet` when `to` is a listed pair **or** protocol-exempt (same set as inbound 1:1). Keep the check for user wallets. --- ## Acceptance criteria - [ ] Two different wallets can sell (or buy) in the same block when `cooldown_blocks > 0`. - [ ] The **same** wallet still hits cooldown until `cooldown_blocks` elapse. - [ ] Pair balance may exceed `max_wallet` after sells; a later `TransferFrom` provide **succeeds**. - [ ] A user wallet still cannot **receive** above `max_wallet` on Buy / Transfer. - [ ] Sell to listed pair still bypasses `max_wallet` (T592-11). - [ ] `poc_cooldown_bricks_pair` and `poc_max_wallet_bricks_provide` **fail or are inverted**. - [ ] T592-11 text updated: cooldown is per wallet; `max_wallet` skips protocol / listed pair as `to`. --- ## Test plan — all paths 1. Cooldown 0: unchanged, multiple trades same block. 2. Cooldown N: wallet A sells, wallet B sells next block — **ok**; wallet A sells again before N — **Cooldown**. 3. Buy after a sell (pair is `from`): must not use the pair’s timestamp to block B. 4. Provide `TransferFrom` after pair balance > `max_wallet` — **ok**. 5. Buy to a user that would exceed `max_wallet` — **MaxWallet**. 6. TransferTax + max_wallet on an EOA — still enforced. 7. LaunchGuards SKU off — no cooldown / max_wallet. 8. Protocol-exempt `to` (router / AutoLP / self) — no max_wallet brick. --- ## Test plan — attack / abuse 1. **Pair-wide halt:** two wallets, cooldown 10, second trade must not revert after this fix (current PoC). 2. **Self-trade / same wallet two hops:** still rate-limited (anti-snipe intent). 3. **Provide brick / LP lock:** sells inflate pair above cap; honest LP cannot add — current PoC; must not remain. 4. **max_wallet as sell lock:** sell-to-pair must still bypass (do not “fix” H-4 by applying max_wallet to sells). 5. **Manager sets cooldown = 1 and max_wallet dust** after users buy — still possible (M-4 / product); this ticket only removes the **unintended pair/LP DoS**. 6. **Batch / ladder (11611 D11):** second transfer from the **same** wallet in-block may still cooldown — acceptable; document. --- ## Verification - `cargo test -p cl8y-community-token-launcher --test audit_poc` — H-3 / H-4 inverted. - `cargo test -p cl8y-community-tax-token` — extend launch-guard multitest beyond sell bypass. - After migrate: LocalTerra LaunchGuards token, two wallets swap in one window; provide after pair > cap. - Update 11611 REPORT D11 / A11 notes if the pin is remigrated.
PlasticDigits commented 2026-08-23 11:49:53 +00:00 (Migrated from gitlab.com)

marked as related to #592

marked as related to #592
PlasticDigits commented 2026-08-23 11:49:54 +00:00 (Migrated from gitlab.com)

marked as related to #605

marked as related to #605
PlasticDigits commented 2026-08-23 12:00:40 +00:00 (Migrated from gitlab.com)

mentioned in commit bcd58a3bff

mentioned in commit bcd58a3bfff1dccfcefb0808a722bbdadb43749e
PlasticDigits commented 2026-08-23 12:00:50 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1123

mentioned in merge request !1123
PlasticDigits commented 2026-08-23 12:20:39 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1122

mentioned in merge request !1122
PlasticDigits commented 2026-08-23 12:20:41 +00:00 (Migrated from gitlab.com)

mentioned in issue #605

mentioned in issue #605
PlasticDigits commented 2026-08-23 12:34:08 +00:00 (Migrated from gitlab.com)

mentioned in commit 33e4153f87

mentioned in commit 33e4153f87ef075614781734950f0cfa211695c2
PlasticDigits commented 2026-08-23 12:35:52 +00:00 (Migrated from gitlab.com)

mentioned in commit 709a2694d5

mentioned in commit 709a2694d562d32dd2d6cff7dbfc8c3b307010dc
PlasticDigits commented 2026-08-23 12:35:54 +00:00 (Migrated from gitlab.com)

mentioned in commit e437cdfcc3

mentioned in commit e437cdfcc3881625a997c772f63b24d606c4628b
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-23 12:35:54 +00:00
PlasticDigits commented 2026-08-23 12:40:21 +00:00 (Migrated from gitlab.com)

mentioned in commit c5d00f67be

mentioned in commit c5d00f67be4ef5304c92a06b8d59e21429c13b1a
PlasticDigits commented 2026-08-23 12:41:00 +00:00 (Migrated from gitlab.com)

mentioned in issue #611

mentioned in issue #611
PlasticDigits commented 2026-08-23 12:41:01 +00:00 (Migrated from gitlab.com)

marked as related to #611

marked as related to #611
PlasticDigits commented 2026-08-23 12:41:13 +00:00 (Migrated from gitlab.com)

Merge !1123 — H-3 / H-4 and H608-1–H608-8 passed after integrating !1122 (make verify-issue-608 6/6).

Merge problems found and fixed on this branch before merge:

  • launch_guards_cooldown_zero_allows_same_block_trades hit Trading is not enabled because #605 instantiate defaults trading_enabled=false.
  • audit_poc failed to compile (initial_exempt missing) then failed M-1 (max_* headroom without VariableRates) and H-1 (AutoLP now binds).

Still ops, not a code defect: columbus-5 11611 bytes remain pre-#608 until store + token migrate. LocalTerra two-wallet + provide-after-cap smoke is on #611.

**Merge !1123** — H-3 / H-4 and H608-1–H608-8 passed after integrating !1122 (`make verify-issue-608` 6/6). **Merge problems found and fixed on this branch before merge:** - `launch_guards_cooldown_zero_allows_same_block_trades` hit `Trading is not enabled` because #605 instantiate defaults `trading_enabled=false`. - `audit_poc` failed to compile (`initial_exempt` missing) then failed M-1 (`max_*` headroom without VariableRates) and H-1 (AutoLP now binds). **Still ops, not a code defect:** columbus-5 **11611** bytes remain pre-#608 until store + token migrate. LocalTerra two-wallet + provide-after-cap smoke is on #611.
PlasticDigits commented 2026-08-23 12:41:14 +00:00 (Migrated from gitlab.com)

mentioned in issue #609

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