fix(community-tax): launcher EnableFeature path, SKU dedupe, and QA smoke (C-1, H-2, L-1) #606

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

Audit INTERNAL_KIMIK3_1787468843 C-1 / H-2 / L-1. Template #592 · Create Token #593 · invoices #595 · SKU init #605. PoCs: poc_launcher_enable_feature_always_unauthorized, poc_launcher_duplicate_sku_double_charge.

Columbus-5: token 11611, launcher 11614 (terra126pr5…ahzwze). Token/launcher code fixes need a CMM / DEX 2-of-3 migrate. A dApp-only Enable Feature payee change does not.


Current codebase

C593-4 says Enable Feature is 50 UST1 to the env launcher. The dApp follows that:

Direct-to-token Send (manager as cw20.sender) still works. That is what QA uses:

H-2: launcher create_token sets paid_skus = args.features.len() and charges 50 UST1 × len. Features::from_skus is idempotent (duplicate transfer_tax is a no-op). A crafted hook [transfer_tax, transfer_tax] pays 100 UST1 for one flag. The create UI uses unique checkboxes and does not send duplicates.

Launcher unit tests cover create only (3 tests). No launcher enable_feature happy path.


Why this is needed

Retail Manage Token Enable Feature is the only post-create SKU store. It cannot succeed today. Operators who copy the smoke script can unlock; retail cannot. That looks like a rug even though it is a revert.

Duplicate SKUs overcharge CMM on a crafted create hook. Same invoice surface; fix in the same launcher pass.

QA must exercise the official path or C-1 will regress silently.


Constraints / guardrails

  1. Do not loosen payer == manager for arbitrary addresses.
  2. Keep exact 50 UST1 invoices (T592-4). Wrong token / amount / no-op still revert; fee not kept.
  3. Do not mix EnableFeature into a settings batch.
  4. MintControl stays instantiate-only.
  5. Invoice UST1 still forwards to CMM, not the manager.
  6. If the dApp-only workaround is chosen: keep launcher for create; only Enable Feature payee becomes the token. Update C593-4 in the same MR.
  7. Unique-set or reject duplicate SKUs before multiplying. Do not enable a second flag for a duplicate name.
  8. Live 11611 / 11614: say in the MR whether this is dApp-only or a migrate.

Relevant files

  • smartcontracts/contracts/community-tax-token/src/invoice.rs
  • smartcontracts/contracts/community-token-launcher/src/contract.rs
  • smartcontracts/contracts/community-token-launcher/tests/audit_poc.rs
  • frontend-dapp/src/utils/communityTaxInvoice.ts (+ .test.ts)
  • frontend-dapp/src/pages/ManageTokenPage.tsx
  • scripts/qa/localterra-community-tax-smoke.sh
  • skills/AGENTS_FRONTEND_CREATE_TOKEN.md (C593-4)
  • skills/AGENTS_COMMUNITY_TAX_CW20.md (T592-4)

Pick one Enable Feature path (do not ship both as “supported”):

  1. Preferred if no migrate this week: dApp pays Enable Feature to the token (manager Send). Launcher enable_feature can stay unused or be gated off in the UI.
  2. Preferred long-term: token treats payer == origin.launcher as manager-authorized for EnableFeature only; keep payer == manager for direct pays and for UpdateSettings.
  3. Launcher-only execute after it already collected the invoice (splits the 50 UST1 path) — more moving parts.

Plus: unique-set / reject duplicate SKUs on create; smoke + a launcher-path unit test must use the same path as the dApp.


Acceptance criteria

  • Official Manage Token Enable Feature unlocks the SKU and CMM receives 50 UST1.
  • Non-manager cannot unlock via launcher or token.
  • Duplicate SKUs on create are rejected or charged once; Features still idempotent.
  • QA smoke (and verify-issue-601 if it calls smoke) uses the dApp Enable Feature path, not a hidden direct-to-token shortcut that would hide C-1.
  • poc_launcher_enable_feature_always_unauthorized fails or is inverted.
  • poc_launcher_duplicate_sku_double_charge fails or is inverted.
  • C593-4 / T592-4 docs match the chosen path.

Test plan — all paths

  1. Free create (0 SKU) → Manage → Enable Feature transfer_tax → feature on, 50 UST1 at CMM.
  2. Paid create with one SKU → Enable Feature a second SKU.
  3. Enable Feature MintControl → still rejected (instantiate-only).
  4. Enable Feature already-on SKU → revert, fee not kept.
  5. Wrong amount / wrong token / non-manager → revert.
  6. Settings batch still targets the token and still works.
  7. Create with [transfer_tax, transfer_tax] → reject or 50 UST1 once.
  8. Create with two distinct SKUs → 100 UST1, both flags on.
  9. Smoke script on LocalTerra follows the official path.

Test plan — attack / abuse

  1. Random address calls launcher enable_feature for someone else’s token → Unauthorized; no SKU; no UST1 taken from victim.
  2. Attacker Send to token with EnableFeature while not manager → Unauthorized.
  3. If launcher-as-payer is allowed: only origin.launcher for that token, not an arbitrary contract.
  4. Crafted duplicate SKU list cannot unlock extra features or underpay.
  5. Re-entrancy / double-forward: UST1 must not be taken twice (admin + CMM).
  6. Do not accept tax-token as invoice (T592-12).

Verification

  • cargo test -p cl8y-community-token-launcher --test audit_poc — C-1 / H-2 cases inverted.
  • cargo test -p cl8y-community-tax-token — existing invoice tests still pass.
  • Frontend communityTaxInvoice tests: payee matches the chosen path.
  • make verify-issue-593 and smoke / make verify-issue-601 after smoke change.
  • Manual: Manage Token Enable Feature on LocalTerra with Simulated Wallet.
## Parent / related Audit [`INTERNAL_KIMIK3_1787468843`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/audits/INTERNAL_KIMIK3_1787468843.md) **C-1 / H-2 / L-1**. Template [#592](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/592) · Create Token [#593](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/593) · invoices [#595](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/595) · SKU init [#605](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/605). PoCs: `poc_launcher_enable_feature_always_unauthorized`, `poc_launcher_duplicate_sku_double_charge`. Columbus-5: token **11611**, launcher **11614** (`terra126pr5…ahzwze`). Token/launcher code fixes need a CMM / DEX 2-of-3 **migrate**. A dApp-only Enable Feature payee change does **not**. --- ## Current codebase **C593-4** says Enable Feature is `50 UST1` to the **env launcher**. The dApp follows that: - [`frontend-dapp/src/utils/communityTaxInvoice.ts`](frontend-dapp/src/utils/communityTaxInvoice.ts) `buildEnableFeatureInvoice` sets `payee: launcher` and hook `{ enable_feature: { token, sku } }`. - [`community-token-launcher/src/contract.rs`](smartcontracts/contracts/community-token-launcher/src/contract.rs) `enable_feature` forwards that UST1 with `Cw20ExecuteMsg::Send` to the token. The token sees `cw20.sender = launcher`. - [`community-tax-token/src/invoice.rs`](smartcontracts/contracts/community-tax-token/src/invoice.rs) `execute_receive` requires `payer == config.manager`. Launcher ≠ manager → **`Unauthorized`**. Feature stays off. UST1 is not taken (atomic revert). Direct-to-token `Send` (manager as `cw20.sender`) still works. That is what QA uses: - [`scripts/qa/localterra-community-tax-smoke.sh`](scripts/qa/localterra-community-tax-smoke.sh) SKU unlock sends UST1 **to the token**, not the launcher. `make verify-issue-601` can stay green while the official UI path is dead. **H-2:** launcher `create_token` sets `paid_skus = args.features.len()` and charges `50 UST1 × len`. `Features::from_skus` is idempotent (duplicate `transfer_tax` is a no-op). A crafted hook `[transfer_tax, transfer_tax]` pays 100 UST1 for one flag. The create UI uses unique checkboxes and does not send duplicates. Launcher unit tests cover create only (3 tests). No launcher `enable_feature` happy path. --- ## Why this is needed Retail Manage Token **Enable Feature** is the only post-create SKU store. It cannot succeed today. Operators who copy the smoke script can unlock; retail cannot. That looks like a rug even though it is a revert. Duplicate SKUs overcharge CMM on a crafted create hook. Same invoice surface; fix in the same launcher pass. QA must exercise the **official** path or C-1 will regress silently. --- ## Constraints / guardrails 1. Do **not** loosen `payer == manager` for arbitrary addresses. 2. Keep exact **50 UST1** invoices (**T592-4**). Wrong token / amount / no-op still revert; fee not kept. 3. Do not mix `EnableFeature` into a settings batch. 4. MintControl stays instantiate-only. 5. Invoice UST1 still forwards to **CMM**, not the manager. 6. If the dApp-only workaround is chosen: keep launcher for **create**; only Enable Feature payee becomes the token. Update **C593-4** in the same MR. 7. Unique-set or **reject** duplicate SKUs before multiplying. Do not enable a second flag for a duplicate name. 8. Live **11611 / 11614**: say in the MR whether this is dApp-only or a migrate. --- ## Relevant files - `smartcontracts/contracts/community-tax-token/src/invoice.rs` - `smartcontracts/contracts/community-token-launcher/src/contract.rs` - `smartcontracts/contracts/community-token-launcher/tests/audit_poc.rs` - `frontend-dapp/src/utils/communityTaxInvoice.ts` (+ `.test.ts`) - `frontend-dapp/src/pages/ManageTokenPage.tsx` - `scripts/qa/localterra-community-tax-smoke.sh` - `skills/AGENTS_FRONTEND_CREATE_TOKEN.md` (**C593-4**) - `skills/AGENTS_COMMUNITY_TAX_CW20.md` (**T592-4**) --- ## Recommended direction Pick **one** Enable Feature path (do not ship both as “supported”): 1. **Preferred if no migrate this week:** dApp pays Enable Feature to the **token** (manager `Send`). Launcher `enable_feature` can stay unused or be gated off in the UI. 2. **Preferred long-term:** token treats `payer == origin.launcher` as manager-authorized for `EnableFeature` only; keep `payer == manager` for direct pays and for `UpdateSettings`. 3. Launcher-only execute after it already collected the invoice (splits the 50 UST1 path) — more moving parts. Plus: unique-set / reject duplicate SKUs on create; smoke + a launcher-path unit test must use the same path as the dApp. --- ## Acceptance criteria - [ ] Official Manage Token Enable Feature unlocks the SKU and CMM receives 50 UST1. - [ ] Non-manager cannot unlock via launcher or token. - [ ] Duplicate SKUs on create are rejected **or** charged once; `Features` still idempotent. - [ ] QA smoke (and `verify-issue-601` if it calls smoke) uses the **dApp** Enable Feature path, not a hidden direct-to-token shortcut that would hide C-1. - [ ] `poc_launcher_enable_feature_always_unauthorized` **fails or is inverted**. - [ ] `poc_launcher_duplicate_sku_double_charge` **fails or is inverted**. - [ ] C593-4 / T592-4 docs match the chosen path. --- ## Test plan — all paths 1. Free create (0 SKU) → Manage → Enable Feature `transfer_tax` → feature on, 50 UST1 at CMM. 2. Paid create with one SKU → Enable Feature a second SKU. 3. Enable Feature MintControl → still rejected (instantiate-only). 4. Enable Feature already-on SKU → revert, fee not kept. 5. Wrong amount / wrong token / non-manager → revert. 6. Settings batch still targets the **token** and still works. 7. Create with `[transfer_tax, transfer_tax]` → reject or 50 UST1 once. 8. Create with two distinct SKUs → 100 UST1, both flags on. 9. Smoke script on LocalTerra follows the official path. --- ## Test plan — attack / abuse 1. Random address calls launcher `enable_feature` for someone else’s token → Unauthorized; no SKU; no UST1 taken from victim. 2. Attacker Send to token with `EnableFeature` while not manager → Unauthorized. 3. If launcher-as-payer is allowed: only `origin.launcher` for **that** token, not an arbitrary contract. 4. Crafted duplicate SKU list cannot unlock extra features or underpay. 5. Re-entrancy / double-forward: UST1 must not be taken twice (admin + CMM). 6. Do not accept tax-token as invoice (**T592-12**). --- ## Verification - `cargo test -p cl8y-community-token-launcher --test audit_poc` — C-1 / H-2 cases inverted. - `cargo test -p cl8y-community-tax-token` — existing invoice tests still pass. - Frontend `communityTaxInvoice` tests: payee matches the chosen path. - `make verify-issue-593` and smoke / `make verify-issue-601` after smoke change. - Manual: Manage Token Enable Feature on LocalTerra with Simulated Wallet.
PlasticDigits commented 2026-08-23 11:49:45 +00:00 (Migrated from gitlab.com)

marked as related to #592

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

marked as related to #593

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

marked as related to #595

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

marked as related to #605

marked as related to #605
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:40 +00:00 (Migrated from gitlab.com)

mentioned in issue #605

mentioned in issue #605
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:30 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1125

mentioned in merge request !1125
PlasticDigits commented 2026-08-23 13:05:02 +00:00 (Migrated from gitlab.com)

Sanity check of !1125 after resolving conflicts with main (!1126 + #609).

Acceptance (code / automated): met. make verify-issue-606 8/8 after the merge-from-main. Official Manage path stays manager → launcher → token; non-manager blocked; duplicate SKUs reject; inverted poc_launcher_*; C593-4 / T592-4 / T606 match. Smoke script now Sends to the launcher (sku_unlock_via_launcher).

Not verified here (still open after merge):

  • LocalTerra smoke / make verify-issue-601 on a freshly redeployed stack.
  • Manual Simulated Wallet Enable Feature on LocalTerra after redeploy.
  • Columbus-5 11611 / 11614 still need CMM / DEX 2-of-3 migrate before live Enable Feature works (T606-8). Merge is not a production fix.

Conflicts resolved in !1125: AGENTS.md, Makefile, docs/testing.md, T592/C593 skills, token multitest.rs (kept #606 Enable Feature tests; reused #609 register_listed_pair). Also dropped a duplicate communityTaxHint const that !1126 landed on Swap (tsc -b TS2451).

Do not reopen this issue for the migrate/smoke remainder — track that on the post-merge ops ticket.

Sanity check of !1125 after resolving conflicts with `main` (!1126 + #609). **Acceptance (code / automated):** met. `make verify-issue-606` **8/8** after the merge-from-main. Official Manage path stays manager → launcher → token; non-manager blocked; duplicate SKUs reject; inverted `poc_launcher_*`; C593-4 / T592-4 / T606 match. Smoke script now Sends to the launcher (`sku_unlock_via_launcher`). **Not verified here (still open after merge):** - LocalTerra smoke / `make verify-issue-601` on a freshly redeployed stack. - Manual Simulated Wallet Enable Feature on LocalTerra after redeploy. - Columbus-5 **11611** / **11614** still need CMM / DEX 2-of-3 **migrate** before live Enable Feature works (T606-8). Merge is not a production fix. Conflicts resolved in !1125: `AGENTS.md`, `Makefile`, `docs/testing.md`, T592/C593 skills, token `multitest.rs` (kept #606 Enable Feature tests; reused #609 `register_listed_pair`). Also dropped a duplicate `communityTaxHint` const that !1126 landed on Swap (`tsc -b` TS2451). Do not reopen this issue for the migrate/smoke remainder — track that on the post-merge ops ticket.
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:11 +00:00 (Migrated from gitlab.com)

mentioned in commit cfaf976980

mentioned in commit cfaf9769809307a23c4f41aaf7d3de86c97171a9
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-23 13:05:11 +00:00
PlasticDigits commented 2026-08-23 13:05:30 +00:00 (Migrated from gitlab.com)

mentioned in issue #612

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

marked as related to #612

marked as related to #612
PlasticDigits commented 2026-08-23 13:06:15 +00:00 (Migrated from gitlab.com)

Post-merge remainder (migrate 11611/11614, LocalTerra Enable Feature smoke, Swap/Trade disclose): #612. !1125 is on main. Do not reopen this issue for ops.

Post-merge remainder (migrate 11611/11614, LocalTerra Enable Feature smoke, Swap/Trade disclose): #612. !1125 is on main. Do not reopen this issue for ops.
PlasticDigits commented 2026-08-26 04:20:44 +00:00 (Migrated from gitlab.com)

mentioned in issue #670

mentioned in issue #670
PlasticDigits commented 2026-08-26 04:20:45 +00:00 (Migrated from gitlab.com)

marked as related to #670

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