ux: /token/migrate lead must explain why — Unlock {X} CL8Y features #670

Closed
opened 2026-08-26 04:20:42 +00:00 by PlasticDigits · 12 comments
PlasticDigits commented 2026-08-26 04:20:42 +00:00 (Migrated from gitlab.com)

Summary

/token/migrate never tells a token admin why they would adopt the CL8Y community-tax template. The only always-on lead is “Move an existing token onto this template. Address stays the same.” That is a mechanics sentence, not a reason.

Community report (anonymized, 2026-08): the migrate page should sell the unlock — Unlock {X} features for your token on CL8Y Dex by migrating today — then a second paragraph with a few high-value examples of what they gain.

This is retail copy only. Adopt execute, allowlist, invoices, pair inventory, and Terraport/GDEX gates stay unchanged (M626, M634).

Related (do not regress): #626 free adopt + LP gate, #634 venue inventory, #593 Create/Manage, #606 Enable Feature, #489 copy load, #592 on-chain SKUs. Do not fold Create Token layout (#669) or Manage density into this MR.


Current codebase

Page chrome today

MigrateTokenPage.tsx (data-testid="migrate-token-page") renders:

<h2>…Migrate Token</h2>
<p>Move an existing token onto this template. Address stays the same.</p>

That is the entire why. After Load, the confirm card only adds LP / pause / wipe facts from communityTaxMigrate.ts:

  • MIGRATE_LP_CONFIRM — address stays; CL8Y pairs pause until governance refresh; Terraport/GDEX keep the token
  • MIGRATE_LP_CONFIRM_WIPE — same + leftover tax maps are cleared

No mention of buy/sell tax, SKUs, Manage, or Enable Feature.

Playbook AGENTS_FRONTEND_TOKEN_MIGRATE.md and docs/frontend.md § Create Token M626 currently require title + one lead sentence and forbid env / 50 UST1 / cw2 essays. This issue amends that lead to two short paragraphs (headline + examples). It does not reopen a protocol essay.

Create Token (CreateTokenPage.tsx) already lists the 7 paid SKUs and links Migrate here → /token/migrate. The migrate page does not reuse that catalog.

Official feature inventory (count this)

On-chain Features / dApp COMMUNITY_TAX_SKUS in communityTaxSku.ts and state.rs:

# Id Retail label After adopt
— (built-in, not a SKU) Buy/sell tax on listed CL8Y pairs Always available. Honest adopt writes tax-off zeros; leftover FoT wipe maps 4.5%/1% → buy 450 / sell 100 (M626-8).
1 mint_control Minting Not unlockable. Create-only (C593-5 / T592-6). Adopt sets Features::from_skus(&[]) and mint_revoked if the source had a minter (adopt.rs).
2 transfer_tax Wallet-to-wallet tax Enable Feature, 50 UST1 → launcher
3 split_router Split treasury Enable Feature
4 auto_v2_lp Auto liquidity Enable Feature (factory-listed pair + skim floor)
5 exemption_directory Extra exemptions Enable Feature (buy/sell/transfer skip)
6 variable_rates Change rates later Enable Feature
7 launch_guards Launch guards Enable Feature (max wallet, cooldown, trading on/off)

Canonical headline X = 7 = 1 built-in listed-pair buy/sell tax + 6 SKUs with createOnly === false.

Derive in code so a future SKU cannot drift:

1 + COMMUNITY_TAX_SKUS.filter((s) => !s.createOnly).length

Do not advertise Minting in X. Do not invent extra “features” (catalog listing, extra-debit Max, router hops, manager console) just to inflate the number — those are how the template works, not additional SKUs.

What migrate actually turns on

Adopt is access, not auto-enable:

  • Wasm moves to the current tax pin; admin → CMM; GetMigrateOrigin is written.
  • FEATURES starts empty. Manager later pays 50 UST1 per SKU on Manage → Enable Feature (T606, #606).
  • Headline “Unlock {X} features” means the template can use them, not “all SKUs flip on in the migrate tx.”

Tests / docs today

  • Vitest: MigrateTokenPage.test.tsx asserts heading + /move an existing token onto this template/i. That regex must change with the new lead.
  • communityTaxSku.test.ts locks Minting createOnly and the 7 labels — reuse for the derived count.
  • QA: make verify-issue-626 / 634 do not assert why-copy.
  • QA_TEMPLATE.md has no migrate why-copy row.

Why this is needed

Token admins reach /token/migrate from More or Create Token Migrate here. They already have a live CW20 (10184 / 8266 / 6036 / 8654). The page must answer “why bother?” in one screen: keep the same address and gain the CL8Y template’s feature set.

Without that, adopt looks like a scary wasm swap (pause, refresh, wipe) with no upside. That is how people bounce before Load.


Constraints / guardrails

  1. Copy only on /token/migrate. No wasm, indexer, allowlist, or invoice changes. Adopt payload stays honest tax-off / wipe leftover (M626-8). Do not send SKUs in MigrateMsg.
  2. X is derived, not hardcoded in three places. One helper (e.g. migrateUnlockFeatureCount()) from COMMUNITY_TAX_SKUS + built-in buy/sell. Tests lock the current value 7.
  3. Access ≠ auto-enable. Do not say features turn on for free in the migrate tx. Do not put “50 UST1” / enable_feature / SKU JSON keys on the card (#489 / M626-9). Optional human hint: they can turn features on later from Manage — one short clause max.
  4. Never advertise Minting as a migrate unlock (C593-5).
  5. Keep safety copy. Confirm MIGRATE_LP_CONFIRM / _WIPE, pause-until-governance, Terraport/GDEX 1:1, and “address stays the same” stay on the confirm/success path. The new lead replaces the mechanics-only subtitle; the confirm card still owns LP facts.
  6. Two short paragraphs, not a lecture. Headline + one examples paragraph. No env vars, code ids, cw2, 11619, allowlist, or “this is not an AMM” essays (#489). Labels stay retail (sku.label), never mint_control.
  7. Do not list every SKU in the examples paragraph. Three to five high-value examples. Full catalog stays on Create Token / Manage.
  8. Unavailable stub unchanged (migrate-token-unavailable). Query params still do not prefill (M626-2 / M634-7).
  9. Heading stays Migrate Token (C593-2 sibling). Nav label unchanged.
  10. One chrome layer (#653). No nested card-glass feature grid. Plain <p> / short list inside the existing header block.
  11. Design system (#488). Existing ink-dim lead color. Light + dark. No *-neo.
  12. Create Token / Manage out of scope except: Create Token Migrate here still works; do not rewrite Create Token’s SKU fieldset here (#669 owns Create layout).
  13. No e2e-tx. Playwright workers stay 5. On-chain adopt stays existing #626 / #634 specs.
  14. Do not append code 3 or factory-whitelist 8654 (M626-3 / M626-4).

Relevant files

Primary

  • frontend-dapp/src/pages/MigrateTokenPage.tsx — header lead
  • frontend-dapp/src/utils/communityTaxSku.ts — catalog + createOnly (source of X)
  • frontend-dapp/src/utils/communityTaxMigrate.ts — or a small communityTaxMigrateCopy.ts for the two paragraphs + derived count
  • frontend-dapp/src/pages/MigrateTokenPage.test.tsx — replace old lead regex
  • frontend-dapp/src/utils/communityTaxSku.test.ts — lock count helper if added here

Docs / skills (this issue owns the M626 “one lead sentence” amendment)

  • skills/AGENTS_FRONTEND_TOKEN_MIGRATE.md
  • docs/frontend.md § Create Token M626
  • skills/AGENTS_FRONTEND_COPY_COGNITIVE_LOAD.md — note this page is the documented exception (two short why-paragraphs)
  • QA_TEMPLATE.md — add a migrate why-copy row

Read, do not fork

  • frontend-dapp/src/pages/CreateTokenPage.tsx (SKU labels + Migrate here)
  • frontend-dapp/src/pages/ManageTokenPage.tsx (Enable Feature)
  • smartcontracts/contracts/community-tax-token/src/adopt.rs (empty FEATURES)
  • skills/AGENTS_FRONTEND_CREATE_TOKEN.md / AGENTS_COMMUNITY_TAX_ENABLE_FEATURE.md

Bundle in one MR: (1) derived count, (2) headline, (3) examples paragraph, (4) tests + docs. Do not split.

A — Copy module

Export from communityTaxMigrate.ts (or communityTaxMigrateCopy.ts):

  • migrateUnlockFeatureCount() → 1 + COMMUNITY_TAX_SKUS.filter((s) => !s.createOnly).length (today 7)
  • MIGRATE_WHY_HEADLINE — exact: Unlock ${n} features for your token on CL8Y Dex by migrating today
  • MIGRATE_WHY_EXAMPLES — one short paragraph (or a 3–5 item list) using retail labels. Suggested high-value set:
    • Buy and sell tax on every listed CL8Y pair
    • Auto liquidity into a factory pool
    • Launch guards (max wallet, cooldown, trading on/off)
    • Extra exemptions and split treasury

Keep “Address stays the same” on the confirm card (already there). Optional second sentence in the examples paragraph: the address does not change. Do not repeat the full LP/pause essay in the header.

B — Page

Replace the current single <p> under the heading with two nodes (data-testid="migrate-token-why" + migrate-token-why-examples"). Unavailable stub stays as-is.

C — Docs

Change M626 “title + one lead sentence” to: title + why headline (Unlock {X}…) + one examples paragraph. Still no env / 50 UST1 / cw2 on the card.

D — Out of scope

  • Turning SKUs on in the adopt tx
  • Minting after adopt
  • Create Token / Manage layout
  • New SKUs or price changes
  • Refresh / Register / whitelist copy beyond existing inventory card

Acceptance criteria

  • Configured /token/migrate header (not the unavailable stub) shows Unlock 7 features for your token on CL8Y Dex by migrating today (X from the helper; today 7).
  • Immediate second paragraph (or short list) names at least three high-value gains using retail labels (buy/sell listed-pair tax, Auto liquidity, Launch guards, Extra exemptions, and/or Split treasury). Does not name Minting as a migrate unlock.
  • Copy does not claim the migrate tx enables paid SKUs or is a 50 UST1 invoice. No VITE_*, code ids, cw2, or enable_feature on the card.
  • Confirm MIGRATE_LP_CONFIRM / _WIPE and inventory (M634) unchanged. Address-stays-the-same remains visible on confirm.
  • Count is derived from COMMUNITY_TAX_SKUS + built-in buy/sell; unit test locks current 7 and mint_control excluded.
  • Existing adopt gates stay: env off → unavailable; query params do not prefill; free CTA; no PayWithAnyToken on this page (M626-2).
  • Light + dark. No nested feature cards. Heading still Migrate Token.
  • Docs/skills: M626 lead rule updated; QA_TEMPLATE.md row added. make verify-issue-626 / 634 stay green.

Test plan (all paths)

Unit (MigrateTokenPage.test.tsx + copy helper)

Keep P0 unavailable, A9 query-ignore, P6 wipe CTA, Open/ALPHA inventory. Replace the old lead assertion.

  1. Why headline: configured page has migrate-token-why (or role/text) matching /Unlock 7 features for your token on CL8Y Dex by migrating today/i.
  2. Examples: migrate-token-why-examples mentions at least buy/sell (or “listed” pair tax), Auto liquidity, and Launch guards (or Extra exemptions / Split treasury). Must not match /Minting/i as an unlock.
  3. No protocol essay: still no VITE_COMMUNITY_MIGRATE_CODE_IDS, allowlisted CW20, 50 UST1, enable_feature, 11619, cw2.
  4. Unavailable: env off → migrate-token-unavailable only; no Unlock headline (do not tease a dead page).
  5. Query ignore: ?payee= / ?manager= / ?token= / ?addr= / ?pair= still do not prefill; why copy still shows.
  6. Confirm unchanged: after Load of allowlisted admin token, migrate-token-confirm still has LP/pause (and wipe when tax_map); CTA still Migrate (free).
  7. Count helper: migrateUnlockFeatureCount() === 7; equals 1 + COMMUNITY_TAX_SKUS.filter((s) => !s.createOnly).length; mint_control is createOnly.

Vitest (must stay green)

cd frontend-dapp && npm test -- --run \
  src/pages/MigrateTokenPage.test.tsx \
  src/utils/communityTaxMigrate.test.ts \
  src/utils/communityTaxSku.test.ts \
  src/pages/CreateTokenPage.test.tsx

Plus make verify-issue-626 and make verify-issue-634.

Playwright (e2e-smoke, 5 workers)

If a migrate smoke spec exists, extend it; otherwise add a short configured-page smoke (not e2e-tx):

  1. Open /token/migrate with community-tax env on.
  2. Assert heading Migrate Token, why headline with 7, examples paragraph visible in the first viewport at 1280×720.
  3. Phone 375×667: both paragraphs readable; no horizontal scrollWidth overflow; no nested cards.
  4. Do not broadcast adopt here.

Manual

  1. More → Migrate Token (and Create Token → Migrate here): why headline + examples before Load.
  2. Light + dark.
  3. Load ALPHA (wipe) and a mintable (honest): confirm LP copy still present; why copy still above the form.
  4. Env unset: unavailable stub only.
  5. After a test adopt (existing LocalTerra #634 path): Manage still shows empty SKUs + Enable Feature — headline did not imply they are already on.

Test plan (attack, hack, and abuse)

Copy sits above a wasm migrate + UpdateAdmin CTA. Treat it as a trust-boundary surface.

  1. False auto-enable. Headline/examples must not be readable as “migrate turns on all taxes/SKUs now.” Attack: social-engineer an admin into adopt expecting live 4.5% tax or Minting. Test: no “enabled”, “turned on”, or “Minting” in the why block; adopt payload tests stay tax-off / wipe leftover only.
  2. Minting bait. Must not list Minting. After adopt, Manage unlock list still omits Minting (C593-5). Unit: why-examples queryByText(/Minting/i) null.
  3. Invoice spoof. Why copy must not grow a Pay card or ?payee= retarget. Page still has no PayWithAnyToken. Query payee/manager still ignored.
  4. XSS. Count and SKU labels are compile-time strings. No dangerouslySetInnerHTML on LCD name/symbol. Token probe still text.
  5. Count lie. Helper must not hardcode 8 or include createOnly SKUs. If a SKU is added, X updates with the catalog — test the formula, not only the literal 7 in three files.
  6. Cross-page spoof. Why copy is not a second Enable Feature form. No SKU checkboxes on migrate. No features: [...] in the adopt builder.
  7. Pause / LP omission. New paragraphs must not replace confirm wipe/pause/Terraport facts. After Load, confirm still contains those sentences (existing tests).
  8. Unavailable tease. If launcher/tax env is off, do not show Unlock 7 on a page that cannot submit (avoids phishing a dead CTA).
  9. Focus / clickjack. New paragraphs z-index stays below Connect / Legal / WalletConnect. No overlay that covers the Load/Migrate buttons with a fake “unlock” control.
  10. Keyboard. Tab order: heading/why → address → Load → (verdict) → confirm CTA. No tabindex skip of confirm.

Verification criteria

Done when:

  1. Manual /token/migrate: Unlock 7 headline + examples paragraph; Minting absent; confirm LP copy intact; light + dark.
  2. MigrateTokenPage.test.tsx covers why + unavailable + query-ignore + confirm unchanged.
  3. Count helper test: 7 and formula excluding mint_control.
  4. make verify-issue-626 and make verify-issue-634 green. No e2e-tx required.
  5. Docs/skills M626 lead rule + QA row updated. No wasm / indexer diff.
# after implementation
bash scripts/with-node.sh --cwd frontend-dapp -- npm test -- --run \
  src/pages/MigrateTokenPage.test.tsx \
  src/utils/communityTaxMigrate.test.ts \
  src/utils/communityTaxSku.test.ts
make verify-issue-626
make verify-issue-634
make lint-frontend
## Summary `/token/migrate` never tells a token admin **why** they would adopt the CL8Y community-tax template. The only always-on lead is “Move an existing token onto this template. Address stays the same.” That is a mechanics sentence, not a reason. Community report (anonymized, 2026-08): the migrate page should sell the unlock — **Unlock {X} features for your token on CL8Y Dex by migrating today** — then a **second paragraph** with a few high-value examples of what they gain. This is **retail copy only**. Adopt execute, allowlist, invoices, pair inventory, and Terraport/GDEX gates stay unchanged (**M626**, **M634**). Related (do not regress): [#626](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/626) free adopt + LP gate, [#634](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/634) venue inventory, [#593](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/593) Create/Manage, [#606](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/606) Enable Feature, [#489](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/489) copy load, [#592](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/592) on-chain SKUs. Do **not** fold Create Token layout ([#669](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/669)) or Manage density into this MR. --- ## Current codebase ### Page chrome today [`MigrateTokenPage.tsx`](frontend-dapp/src/pages/MigrateTokenPage.tsx) (`data-testid="migrate-token-page"`) renders: ```tsx <h2>…Migrate Token</h2> <p>Move an existing token onto this template. Address stays the same.</p> ``` That is the entire why. After Load, the confirm card only adds LP / pause / wipe facts from [`communityTaxMigrate.ts`](frontend-dapp/src/utils/communityTaxMigrate.ts): - `MIGRATE_LP_CONFIRM` — address stays; CL8Y pairs pause until governance refresh; Terraport/GDEX keep the token - `MIGRATE_LP_CONFIRM_WIPE` — same + leftover tax maps are cleared No mention of buy/sell tax, SKUs, Manage, or Enable Feature. Playbook [`AGENTS_FRONTEND_TOKEN_MIGRATE.md`](skills/AGENTS_FRONTEND_TOKEN_MIGRATE.md) and [`docs/frontend.md`](docs/frontend.md) § Create Token **M626** currently require **title + one lead sentence** and forbid env / 50 UST1 / cw2 essays. This issue **amends** that lead to two short paragraphs (headline + examples). It does **not** reopen a protocol essay. Create Token ([`CreateTokenPage.tsx`](frontend-dapp/src/pages/CreateTokenPage.tsx)) already lists the 7 paid SKUs and links **Migrate here** → `/token/migrate`. The migrate page does not reuse that catalog. ### Official feature inventory (count this) On-chain `Features` / dApp `COMMUNITY_TAX_SKUS` in [`communityTaxSku.ts`](frontend-dapp/src/utils/communityTaxSku.ts) and [`state.rs`](smartcontracts/contracts/community-tax-token/src/state.rs): | # | Id | Retail label | After adopt | |---|----|--------------|-------------| | — | *(built-in, not a SKU)* | Buy/sell tax on listed CL8Y pairs | Always available. Honest adopt writes **tax-off zeros**; leftover FoT wipe maps 4.5%/1% → buy 450 / sell 100 (**M626-8**). | | 1 | `mint_control` | Minting | **Not unlockable.** Create-only (**C593-5** / **T592-6**). Adopt sets `Features::from_skus(&[])` and `mint_revoked` if the source had a minter ([`adopt.rs`](smartcontracts/contracts/community-tax-token/src/adopt.rs)). | | 2 | `transfer_tax` | Wallet-to-wallet tax | Enable Feature, 50 UST1 → launcher | | 3 | `split_router` | Split treasury | Enable Feature | | 4 | `auto_v2_lp` | Auto liquidity | Enable Feature (factory-listed pair + skim floor) | | 5 | `exemption_directory` | Extra exemptions | Enable Feature (buy/sell/transfer skip) | | 6 | `variable_rates` | Change rates later | Enable Feature | | 7 | `launch_guards` | Launch guards | Enable Feature (max wallet, cooldown, trading on/off) | **Canonical headline X = 7** = 1 built-in listed-pair buy/sell tax + 6 SKUs with `createOnly === false`. Derive in code so a future SKU cannot drift: `1 + COMMUNITY_TAX_SKUS.filter((s) => !s.createOnly).length` Do **not** advertise Minting in X. Do **not** invent extra “features” (catalog listing, extra-debit Max, router hops, manager console) just to inflate the number — those are how the template works, not additional SKUs. ### What migrate actually turns on Adopt is **access**, not auto-enable: - Wasm moves to the current tax pin; admin → CMM; `GetMigrateOrigin` is written. - `FEATURES` starts empty. Manager later pays **50 UST1 per SKU** on Manage → Enable Feature (**T606**, [#606](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/606)). - Headline “Unlock {X} features” means **the template can use them**, not “all SKUs flip on in the migrate tx.” ### Tests / docs today - Vitest: [`MigrateTokenPage.test.tsx`](frontend-dapp/src/pages/MigrateTokenPage.test.tsx) asserts heading + `/move an existing token onto this template/i`. That regex **must** change with the new lead. - [`communityTaxSku.test.ts`](frontend-dapp/src/utils/communityTaxSku.test.ts) locks Minting `createOnly` and the 7 labels — reuse for the derived count. - QA: `make verify-issue-626` / `634` do not assert why-copy. - [`QA_TEMPLATE.md`](QA_TEMPLATE.md) has no migrate why-copy row. --- ## Why this is needed Token admins reach `/token/migrate` from More or Create Token **Migrate here**. They already have a live CW20 (10184 / 8266 / 6036 / 8654). The page must answer “why bother?” in one screen: keep the same address **and** gain the CL8Y template’s feature set. Without that, adopt looks like a scary wasm swap (pause, refresh, wipe) with no upside. That is how people bounce before Load. --- ## Constraints / guardrails 1. **Copy only on `/token/migrate`.** No wasm, indexer, allowlist, or invoice changes. Adopt payload stays honest tax-off / wipe leftover (**M626-8**). Do not send SKUs in `MigrateMsg`. 2. **X is derived, not hardcoded in three places.** One helper (e.g. `migrateUnlockFeatureCount()`) from `COMMUNITY_TAX_SKUS` + built-in buy/sell. Tests lock the current value **7**. 3. **Access ≠ auto-enable.** Do not say features turn on for free in the migrate tx. Do not put “50 UST1” / `enable_feature` / SKU JSON keys on the card (**#489** / **M626-9**). Optional human hint: they can turn features on later from Manage — one short clause max. 4. **Never advertise Minting** as a migrate unlock (**C593-5**). 5. **Keep safety copy.** Confirm `MIGRATE_LP_CONFIRM` / `_WIPE`, pause-until-governance, Terraport/GDEX 1:1, and “address stays the same” stay on the confirm/success path. The new lead **replaces** the mechanics-only subtitle; the confirm card still owns LP facts. 6. **Two short paragraphs, not a lecture.** Headline + one examples paragraph. No env vars, code ids, cw2, 11619, allowlist, or “this is not an AMM” essays (**#489**). Labels stay retail (`sku.label`), never `mint_control`. 7. **Do not list every SKU in the examples paragraph.** Three to five high-value examples. Full catalog stays on Create Token / Manage. 8. **Unavailable stub unchanged** (`migrate-token-unavailable`). Query params still do not prefill (**M626-2** / **M634-7**). 9. **Heading stays Migrate Token** (**C593-2** sibling). Nav label unchanged. 10. **One chrome layer (#653).** No nested `card-glass` feature grid. Plain `<p>` / short list inside the existing header block. 11. **Design system (#488).** Existing ink-dim lead color. Light + dark. No `*-neo`. 12. **Create Token / Manage out of scope** except: Create Token **Migrate here** still works; do not rewrite Create Token’s SKU fieldset here ([#669](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/669) owns Create layout). 13. **No `e2e-tx`.** Playwright workers stay **5**. On-chain adopt stays existing #626 / #634 specs. 14. **Do not append code 3** or factory-whitelist 8654 (**M626-3** / **M626-4**). --- ## Relevant files **Primary** - `frontend-dapp/src/pages/MigrateTokenPage.tsx` — header lead - `frontend-dapp/src/utils/communityTaxSku.ts` — catalog + `createOnly` (source of X) - `frontend-dapp/src/utils/communityTaxMigrate.ts` — or a small `communityTaxMigrateCopy.ts` for the two paragraphs + derived count - `frontend-dapp/src/pages/MigrateTokenPage.test.tsx` — replace old lead regex - `frontend-dapp/src/utils/communityTaxSku.test.ts` — lock count helper if added here **Docs / skills (this issue owns the M626 “one lead sentence” amendment)** - `skills/AGENTS_FRONTEND_TOKEN_MIGRATE.md` - `docs/frontend.md` § Create Token **M626** - `skills/AGENTS_FRONTEND_COPY_COGNITIVE_LOAD.md` — note this page is the documented exception (two short why-paragraphs) - `QA_TEMPLATE.md` — add a migrate why-copy row **Read, do not fork** - `frontend-dapp/src/pages/CreateTokenPage.tsx` (SKU labels + Migrate here) - `frontend-dapp/src/pages/ManageTokenPage.tsx` (Enable Feature) - `smartcontracts/contracts/community-tax-token/src/adopt.rs` (empty `FEATURES`) - `skills/AGENTS_FRONTEND_CREATE_TOKEN.md` / `AGENTS_COMMUNITY_TAX_ENABLE_FEATURE.md` --- ## Recommended direction Bundle in **one** MR: (1) derived count, (2) headline, (3) examples paragraph, (4) tests + docs. Do not split. ### A — Copy module Export from `communityTaxMigrate.ts` (or `communityTaxMigrateCopy.ts`): - `migrateUnlockFeatureCount()` → `1 + COMMUNITY_TAX_SKUS.filter((s) => !s.createOnly).length` (today **7**) - `MIGRATE_WHY_HEADLINE` — exact: `Unlock ${n} features for your token on CL8Y Dex by migrating today` - `MIGRATE_WHY_EXAMPLES` — one short paragraph (or a 3–5 item list) using retail labels. Suggested high-value set: - Buy and sell tax on every listed CL8Y pair - Auto liquidity into a factory pool - Launch guards (max wallet, cooldown, trading on/off) - Extra exemptions and split treasury Keep “Address stays the same” on the confirm card (already there). Optional second sentence in the examples paragraph: the address does not change. Do not repeat the full LP/pause essay in the header. ### B — Page Replace the current single `<p>` under the heading with two nodes (`data-testid="migrate-token-why"` + `migrate-token-why-examples"`). Unavailable stub stays as-is. ### C — Docs Change **M626** “title + one lead sentence” to: title + why headline (`Unlock {X}…`) + one examples paragraph. Still no env / 50 UST1 / cw2 on the card. ### D — Out of scope - Turning SKUs on in the adopt tx - Minting after adopt - Create Token / Manage layout - New SKUs or price changes - Refresh / Register / whitelist copy beyond existing inventory card --- ## Acceptance criteria - [ ] Configured `/token/migrate` header (not the unavailable stub) shows **Unlock 7 features for your token on CL8Y Dex by migrating today** (X from the helper; today 7). - [ ] Immediate second paragraph (or short list) names **at least three** high-value gains using retail labels (buy/sell listed-pair tax, Auto liquidity, Launch guards, Extra exemptions, and/or Split treasury). Does **not** name Minting as a migrate unlock. - [ ] Copy does **not** claim the migrate tx enables paid SKUs or is a 50 UST1 invoice. No `VITE_*`, code ids, cw2, or `enable_feature` on the card. - [ ] Confirm `MIGRATE_LP_CONFIRM` / `_WIPE` and inventory (**M634**) unchanged. Address-stays-the-same remains visible on confirm. - [ ] Count is derived from `COMMUNITY_TAX_SKUS` + built-in buy/sell; unit test locks current **7** and `mint_control` excluded. - [ ] Existing adopt gates stay: env off → unavailable; query params do not prefill; free CTA; no PayWithAnyToken on this page (**M626-2**). - [ ] Light + dark. No nested feature cards. Heading still **Migrate Token**. - [ ] Docs/skills: M626 lead rule updated; `QA_TEMPLATE.md` row added. `make verify-issue-626` / `634` stay green. --- ## Test plan (all paths) ### Unit (`MigrateTokenPage.test.tsx` + copy helper) Keep P0 unavailable, A9 query-ignore, P6 wipe CTA, Open/ALPHA inventory. Replace the old lead assertion. 1. **Why headline:** configured page has `migrate-token-why` (or role/text) matching `/Unlock 7 features for your token on CL8Y Dex by migrating today/i`. 2. **Examples:** `migrate-token-why-examples` mentions at least buy/sell (or “listed” pair tax), Auto liquidity, and Launch guards (or Extra exemptions / Split treasury). Must **not** match `/Minting/i` as an unlock. 3. **No protocol essay:** still no `VITE_COMMUNITY_MIGRATE_CODE_IDS`, `allowlisted CW20`, `50 UST1`, `enable_feature`, `11619`, `cw2`. 4. **Unavailable:** env off → `migrate-token-unavailable` only; **no** Unlock headline (do not tease a dead page). 5. **Query ignore:** `?payee=` / `?manager=` / `?token=` / `?addr=` / `?pair=` still do not prefill; why copy still shows. 6. **Confirm unchanged:** after Load of allowlisted admin token, `migrate-token-confirm` still has LP/pause (and wipe when `tax_map`); CTA still **Migrate (free)**. 7. **Count helper:** `migrateUnlockFeatureCount() === 7`; equals `1 + COMMUNITY_TAX_SKUS.filter((s) => !s.createOnly).length`; `mint_control` is `createOnly`. ### Vitest (must stay green) ```text cd frontend-dapp && npm test -- --run \ src/pages/MigrateTokenPage.test.tsx \ src/utils/communityTaxMigrate.test.ts \ src/utils/communityTaxSku.test.ts \ src/pages/CreateTokenPage.test.tsx ``` Plus `make verify-issue-626` and `make verify-issue-634`. ### Playwright (`e2e-smoke`, 5 workers) If a migrate smoke spec exists, extend it; otherwise add a short configured-page smoke (not `e2e-tx`): 1. Open `/token/migrate` with community-tax env on. 2. Assert heading **Migrate Token**, why headline with **7**, examples paragraph visible in the first viewport at 1280×720. 3. Phone 375×667: both paragraphs readable; no horizontal `scrollWidth` overflow; no nested cards. 4. Do **not** broadcast adopt here. ### Manual 1. More → Migrate Token (and Create Token → Migrate here): why headline + examples before Load. 2. Light + dark. 3. Load ALPHA (wipe) and a mintable (honest): confirm LP copy still present; why copy still above the form. 4. Env unset: unavailable stub only. 5. After a test adopt (existing LocalTerra #634 path): Manage still shows empty SKUs + Enable Feature — headline did not imply they are already on. --- ## Test plan (attack, hack, and abuse) Copy sits above a **wasm migrate + UpdateAdmin** CTA. Treat it as a trust-boundary surface. 1. **False auto-enable.** Headline/examples must not be readable as “migrate turns on all taxes/SKUs now.” Attack: social-engineer an admin into adopt expecting live 4.5% tax or Minting. Test: no “enabled”, “turned on”, or “Minting” in the why block; adopt payload tests stay tax-off / wipe leftover only. 2. **Minting bait.** Must not list Minting. After adopt, Manage unlock list still omits Minting (**C593-5**). Unit: why-examples `queryByText(/Minting/i)` null. 3. **Invoice spoof.** Why copy must not grow a Pay card or `?payee=` retarget. Page still has no `PayWithAnyToken`. Query payee/manager still ignored. 4. **XSS.** Count and SKU labels are compile-time strings. No `dangerouslySetInnerHTML` on LCD name/symbol. Token probe still text. 5. **Count lie.** Helper must not hardcode `8` or include `createOnly` SKUs. If a SKU is added, X updates with the catalog — test the formula, not only the literal 7 in three files. 6. **Cross-page spoof.** Why copy is not a second Enable Feature form. No SKU checkboxes on migrate. No `features: [...]` in the adopt builder. 7. **Pause / LP omission.** New paragraphs must not replace confirm wipe/pause/Terraport facts. After Load, confirm still contains those sentences (existing tests). 8. **Unavailable tease.** If launcher/tax env is off, do not show Unlock 7 on a page that cannot submit (avoids phishing a dead CTA). 9. **Focus / clickjack.** New paragraphs `z-index` stays below Connect / Legal / WalletConnect. No overlay that covers the Load/Migrate buttons with a fake “unlock” control. 10. **Keyboard.** Tab order: heading/why → address → Load → (verdict) → confirm CTA. No `tabindex` skip of confirm. --- ## Verification criteria Done when: 1. Manual `/token/migrate`: Unlock **7** headline + examples paragraph; Minting absent; confirm LP copy intact; light + dark. 2. `MigrateTokenPage.test.tsx` covers why + unavailable + query-ignore + confirm unchanged. 3. Count helper test: **7** and formula excluding `mint_control`. 4. `make verify-issue-626` and `make verify-issue-634` green. No `e2e-tx` required. 5. Docs/skills M626 lead rule + QA row updated. No wasm / indexer diff. ```bash # after implementation bash scripts/with-node.sh --cwd frontend-dapp -- npm test -- --run \ src/pages/MigrateTokenPage.test.tsx \ src/utils/communityTaxMigrate.test.ts \ src/utils/communityTaxSku.test.ts make verify-issue-626 make verify-issue-634 make lint-frontend ```
PlasticDigits commented 2026-08-26 04:20:43 +00:00 (Migrated from gitlab.com)

marked as related to #626

marked as related to #626
PlasticDigits commented 2026-08-26 04:20:44 +00:00 (Migrated from gitlab.com)

marked as related to #634

marked as related to #634
PlasticDigits commented 2026-08-26 04:20:44 +00:00 (Migrated from gitlab.com)

marked as related to #593

marked as related to #593
PlasticDigits commented 2026-08-26 04:20:45 +00:00 (Migrated from gitlab.com)

marked as related to #606

marked as related to #606
PlasticDigits commented 2026-08-26 04:20:46 +00:00 (Migrated from gitlab.com)

marked as related to #489

marked as related to #489
PlasticDigits commented 2026-08-26 07:14:22 +00:00 (Migrated from gitlab.com)

mentioned in commit a364ea3ce2

mentioned in commit a364ea3ce2157e63f894206122ea8f976326a24e
PlasticDigits commented 2026-08-26 07:15:03 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1168

mentioned in merge request !1168
PlasticDigits commented 2026-08-26 07:15:12 +00:00 (Migrated from gitlab.com)

Implementation is in !1168 (feat/670-migrate-why).

Configured /token/migrate now shows derived Unlock 7 + examples (no Minting, no auto-enable claim). make verify-issue-670 / 626 / 634 passed in the worktree; Playwright smoke used skip-chain. Manual light/dark and post-adopt Manage empty-SKU check remain for QA.

Implementation is in !1168 (`feat/670-migrate-why`). Configured `/token/migrate` now shows derived Unlock 7 + examples (no Minting, no auto-enable claim). `make verify-issue-670` / `626` / `634` passed in the worktree; Playwright smoke used skip-chain. Manual light/dark and post-adopt Manage empty-SKU check remain for QA.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-26 08:26:26 +00:00
PlasticDigits commented 2026-08-26 08:26:27 +00:00 (Migrated from gitlab.com)

mentioned in commit 82d57dbe88

mentioned in commit 82d57dbe88c2dac5bf3e666c37f35f778daa8a67
PlasticDigits commented 2026-08-26 08:34:15 +00:00 (Migrated from gitlab.com)

mentioned in commit 2e920d29fd

mentioned in commit 2e920d29fd5541582be043b9d5f7c5fa54e23483
PlasticDigits commented 2026-08-26 09:31:01 +00:00 (Migrated from gitlab.com)

Merged to main via !1168.

Leftover: /token/migrate unlock copy still uses the derived feature count after frontend rebuild. make verify-issue-670.

Merged to `main` via !1168. Leftover: `/token/migrate` unlock copy still uses the derived feature count after frontend rebuild. `make verify-issue-670`.
PlasticDigits commented 2026-08-26 09:31:35 +00:00 (Migrated from gitlab.com)

mentioned in issue #673

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