fix(community-tax): ExemptionDirectory skips buy, sell, and transfer tax (M-5) #609

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

Audit INTERNAL_KIMIK3_1787468843 M-5. Template #592. Create Token copy #593 SKU hint “Manager-chosen wallets skip tax.” SKU init #605.

Product decision (2026-08-23): exempted users skip transfer, buy, and sell — not transfer-only.

Columbus-5 11611 migrate.


Current codebase

ExemptionDirectory SKU unlocks MANAGER_EXEMPT. Settings batch add_exempt / remove_exempt (50 UST1). Manager cannot remove protocol-exempt / listed pairs / router / AutoLP / factory.

tax.rs:

  • is_manager_exempt is used only via is_transfer_exempt in the Transfer branch.
  • Sell / Buy classification never consults MANAGER_EXEMPT.
  • Retail hint in communityTaxSku.ts: “Manager-chosen wallets skip tax.”

Protocol exemptions (pair, router, factory, self, AutoLP) stay separate and are not removable (T592-9).


Why this is needed

The paid SKU and UI promise “skip tax.” Holders and market-makers who are added to the directory still pay buy/sell extra-debit / outbound split. That is a billing/integrity miss and breaks the obvious use (treasury ops, market-maker wallet, airdrop claimer) unless they only ever Transfer.


Constraints / guardrails

  1. Manager-directory exemption applies to Transfer, Buy, and Sell tax only.
  2. Do not skip launch-guard trading_enabled, cooldown, or max_wallet unless a later spec says so.
  3. Do not let the manager exempt an address out of protocol rules (listed pair / router stay protocol-exempt; manager cannot remove them).
  4. Adding exempt does not make an address a listed pair or allow spoof RegisterListedPair.
  5. remove_exempt still cannot target protocol entries (CannotRemoveProtocolExempt).
  6. SKU still required to mutate the directory. Invoice still 50 UST1.
  7. Inbound 1:1 to pair/router/escrow/AutoLP unchanged (T592-1).

Relevant files

  • smartcontracts/contracts/community-tax-token/src/tax.rs (classify, is_manager_exempt, is_transfer_exempt)
  • smartcontracts/contracts/community-tax-token/src/invoice.rs (add_exempt / remove_exempt)
  • frontend-dapp/src/utils/communityTaxSku.ts (hint — already matches the new behavior)
  • frontend-dapp/src/pages/ManageTokenPage.tsx
  • Token multitest + audit_poc.rs (add coverage; no existing M-5 PoC)

In classify:

  • If from or to is is_manager_exempt and the would-be kind is Sell / Buy / Transfer → Honest (0 bps), same as transfer exemption today.
  • Keep protocol-exempt checks as they are (router/pair still Honest for other reasons).
  • Document in T592-7 / T592-9 that manager directory is a full tax skip for those three kinds, not a transfer-only list.

Acceptance criteria

  • Exempt wallet Send+Swap to a listed pair pays no sell extra-debit; pair still credited amount.
  • Pair Transfer to an exempt wallet pays no buy split; user credited amount.
  • Exempt ↔ non-exempt wallet Transfer pays no transfer tax (either side exempt is enough, matching current transfer rule).
  • Non-exempt wallets still pay buy/sell/transfer as configured.
  • Launch guards still apply to exempt wallets (trading off / cooldown / max_wallet).
  • Hint / docs say skip buy, sell, and transfer tax.
  • Multitest (or new PoC inverted to the new rule) covers all three kinds.

Test plan — all paths

  1. SKU off → add_exempt rejected.
  2. SKU on → add wallet A → A sell / A buy / A transfer: 0 tax; events / TaxPreview Honest.
  3. Remove A → taxes return.
  4. Both sides exempt; one side exempt; neither exempt.
  5. Exempt + protocol-exempt counterparty (pair/router) — still 1:1 inbound, no double-count.
  6. Settings no-op / wrong invoice still revert.
  7. TaxPreview matches execute for exempt sell/buy.

Test plan — attack / abuse

  1. Self-exempt to evade retail tax: manager can already change buy_bps/sell_bps (M-4). Exemption is an explicit list — acceptable. Do not allow permissionless self-add.
  2. Exempt the pair / router / factory via add_exempt then remove_exempt to strip protocol status — must still hit CannotRemoveProtocolExempt / ignore as protocol.
  3. Exempt to bypass launch guards (trade while trading_enabled=false, skip cooldown, exceed max_wallet) — must fail. Guards stay on.
  4. Spoof directory via event / catalog — indexer must not treat an event as an on-chain exempt without the token already in catalog (existing #594 rule).
  5. Quote vs execute: dApp sell extra-debit max must use TaxPreview so an exempt manager connected wallet is not over-debited.

Verification

  • New token multitest rows for exempt sell / buy / transfer + guards still on.
  • cargo test -p cl8y-community-tax-token.
  • Manage Token: add exemption, LocalTerra pair swap both directions, balances match amount.
  • Docs T592 + SKU hint.
## Parent / related Audit [`INTERNAL_KIMIK3_1787468843`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/audits/INTERNAL_KIMIK3_1787468843.md) **M-5**. Template [#592](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/592). Create Token copy [#593](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/593) SKU hint “Manager-chosen wallets skip tax.” SKU init [#605](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/605). **Product decision (2026-08-23):** exempted users skip **transfer, buy, and sell** — not transfer-only. Columbus-5 **11611** migrate. --- ## Current codebase `ExemptionDirectory` SKU unlocks `MANAGER_EXEMPT`. Settings batch `add_exempt` / `remove_exempt` (50 UST1). Manager cannot remove protocol-exempt / listed pairs / router / AutoLP / factory. [`tax.rs`](smartcontracts/contracts/community-tax-token/src/tax.rs): - `is_manager_exempt` is used only via `is_transfer_exempt` in the **Transfer** branch. - **Sell** / **Buy** classification never consults `MANAGER_EXEMPT`. - Retail hint in [`communityTaxSku.ts`](frontend-dapp/src/utils/communityTaxSku.ts): “Manager-chosen wallets skip tax.” Protocol exemptions (pair, router, factory, self, AutoLP) stay separate and are **not** removable (**T592-9**). --- ## Why this is needed The paid SKU and UI promise “skip tax.” Holders and market-makers who are added to the directory still pay buy/sell extra-debit / outbound split. That is a billing/integrity miss and breaks the obvious use (treasury ops, market-maker wallet, airdrop claimer) unless they only ever `Transfer`. --- ## Constraints / guardrails 1. Manager-directory exemption applies to **Transfer, Buy, and Sell** tax only. 2. Do **not** skip launch-guard `trading_enabled`, cooldown, or `max_wallet` unless a later spec says so. 3. Do **not** let the manager exempt an address out of protocol rules (listed pair / router stay protocol-exempt; manager cannot remove them). 4. Adding exempt does not make an address a listed pair or allow spoof `RegisterListedPair`. 5. `remove_exempt` still cannot target protocol entries (`CannotRemoveProtocolExempt`). 6. SKU still required to mutate the directory. Invoice still 50 UST1. 7. Inbound 1:1 to pair/router/escrow/AutoLP unchanged (**T592-1**). --- ## Relevant files - `smartcontracts/contracts/community-tax-token/src/tax.rs` (`classify`, `is_manager_exempt`, `is_transfer_exempt`) - `smartcontracts/contracts/community-tax-token/src/invoice.rs` (`add_exempt` / `remove_exempt`) - `frontend-dapp/src/utils/communityTaxSku.ts` (hint — already matches the new behavior) - `frontend-dapp/src/pages/ManageTokenPage.tsx` - Token multitest + `audit_poc.rs` (add coverage; no existing M-5 PoC) --- ## Recommended direction In `classify`: - If `from` or `to` is `is_manager_exempt` **and** the would-be kind is Sell / Buy / Transfer → **Honest** (0 bps), same as transfer exemption today. - Keep protocol-exempt checks as they are (router/pair still Honest for other reasons). - Document in T592-7 / T592-9 that manager directory is a **full tax skip** for those three kinds, not a transfer-only list. --- ## Acceptance criteria - [ ] Exempt wallet `Send+Swap` to a listed pair pays **no** sell extra-debit; pair still credited `amount`. - [ ] Pair `Transfer` to an exempt wallet pays **no** buy split; user credited `amount`. - [ ] Exempt ↔ non-exempt wallet Transfer pays **no** transfer tax (either side exempt is enough, matching current transfer rule). - [ ] Non-exempt wallets still pay buy/sell/transfer as configured. - [ ] Launch guards still apply to exempt wallets (trading off / cooldown / max_wallet). - [ ] Hint / docs say skip **buy, sell, and transfer** tax. - [ ] Multitest (or new PoC inverted to the new rule) covers all three kinds. --- ## Test plan — all paths 1. SKU off → `add_exempt` rejected. 2. SKU on → add wallet A → A sell / A buy / A transfer: 0 tax; events / `TaxPreview` Honest. 3. Remove A → taxes return. 4. Both sides exempt; one side exempt; neither exempt. 5. Exempt + protocol-exempt counterparty (pair/router) — still 1:1 inbound, no double-count. 6. Settings no-op / wrong invoice still revert. 7. `TaxPreview` matches execute for exempt sell/buy. --- ## Test plan — attack / abuse 1. **Self-exempt to evade retail tax:** manager can already change `buy_bps`/`sell_bps` (M-4). Exemption is an explicit list — acceptable. Do not allow **permissionless** self-add. 2. **Exempt the pair / router / factory** via `add_exempt` then `remove_exempt` to strip protocol status — must still hit `CannotRemoveProtocolExempt` / ignore as protocol. 3. **Exempt to bypass launch guards** (trade while `trading_enabled=false`, skip cooldown, exceed max_wallet) — must **fail**. Guards stay on. 4. **Spoof directory via event / catalog** — indexer must not treat an event as an on-chain exempt without the token already in catalog (existing #594 rule). 5. **Quote vs execute:** dApp sell extra-debit max must use `TaxPreview` so an exempt manager connected wallet is not over-debited. --- ## Verification - New token multitest rows for exempt sell / buy / transfer + guards still on. - `cargo test -p cl8y-community-tax-token`. - Manage Token: add exemption, LocalTerra pair swap both directions, balances match `amount`. - Docs T592 + SKU hint.
PlasticDigits commented 2026-08-23 11:49:55 +00:00 (Migrated from gitlab.com)

marked as related to #592

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

marked as related to #593

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

marked as related to #605

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

mentioned in commit 391af48f53

mentioned in commit 391af48f5378041473a35510ff1bb757bfefe825
PlasticDigits commented 2026-08-23 12:03:35 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1124

mentioned in merge request !1124
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:40:21 +00:00 (Migrated from gitlab.com)

mentioned in commit c5d00f67be

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

mentioned in commit 084123b5c4

mentioned in commit 084123b5c46ba03dcba4c6a79363f9310a4cb042
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-23 12:40:24 +00:00
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:02 +00:00 (Migrated from gitlab.com)

marked as related to #611

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

Merge !1124 — E609-1–E609-7 and #609 acceptance passed after integrating !1122+!1123 (make verify-issue-609 6/6).

Merge problems found and fixed on this branch before merge:

  • tax.rs needed both is_manager_directory_tax_skip (#609) and is_cooldown_subject (#608); record_trade_blocks must use economic trade_kind plus self_addr.
  • TransferTax SKU now instantiates at 200 bps (#605), so clean() pair-seed Transfer taxed treasury 2e6 until the pair was listed first.
  • add_exempt of a listed pair is now rejected as protocol (#605); the old add-then-remove test was updated.

Still ops, not a code defect: live 11611 instances still charge buy/sell on manager-exempt wallets until CMM migrate. LocalTerra Manage Token swap QA is on #611.

**Merge !1124** — E609-1–E609-7 and #609 acceptance passed after integrating !1122+!1123 (`make verify-issue-609` 6/6). **Merge problems found and fixed on this branch before merge:** - `tax.rs` needed both `is_manager_directory_tax_skip` (#609) and `is_cooldown_subject` (#608); `record_trade_blocks` must use **economic** `trade_kind` plus `self_addr`. - TransferTax SKU now instantiates at 200 bps (#605), so `clean()` pair-seed Transfer taxed treasury 2e6 until the pair was listed first. - `add_exempt` of a listed pair is now rejected as protocol (#605); the old add-then-remove test was updated. **Still ops, not a code defect:** live **11611** instances still charge buy/sell on manager-exempt wallets until CMM migrate. LocalTerra Manage Token swap QA is on #611.
PlasticDigits commented 2026-08-23 13:05:02 +00:00 (Migrated from gitlab.com)

mentioned in issue #606

mentioned in issue #606
PlasticDigits commented 2026-08-23 13:05:05 +00:00 (Migrated from gitlab.com)

mentioned in commit 666e985db3

mentioned in commit 666e985db32b6105780c6d2d3d4e61300d87ca4d
PlasticDigits commented 2026-08-23 13:05:31 +00:00 (Migrated from gitlab.com)

mentioned in issue #612

mentioned in issue #612
PlasticDigits commented 2026-08-24 05:54:15 +00:00 (Migrated from gitlab.com)

mentioned in issue #622

mentioned in issue #622
PlasticDigits commented 2026-08-24 07:02:09 +00:00 (Migrated from gitlab.com)

mentioned in issue #623

mentioned in issue #623
PlasticDigits commented 2026-08-24 07:02:37 +00:00 (Migrated from gitlab.com)

mentioned in issue #625

mentioned in issue #625
PlasticDigits commented 2026-08-24 11:45:21 +00:00 (Migrated from gitlab.com)

mentioned in issue #603

mentioned in issue #603
PlasticDigits commented 2026-08-24 11:49:44 +00:00 (Migrated from gitlab.com)

mentioned in issue #626

mentioned in issue #626
PlasticDigits commented 2026-08-25 02:04:15 +00:00 (Migrated from gitlab.com)

mentioned in issue #633

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

mentioned in merge request !1137

mentioned in merge request !1137
PlasticDigits commented 2026-08-25 02:29:31 +00:00 (Migrated from gitlab.com)

mentioned in issue #635

mentioned in issue #635
PlasticDigits commented 2026-08-25 02:29:32 +00:00 (Migrated from gitlab.com)

marked as related to #635

marked as related to #635
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#609
No description provided.