UI: validate bech32 checksum on /create token addresses (#382) #911

Merged
PlasticDigits merged 1 commit from fix/382-create-pair-bech32-checksum into main 2026-06-15 14:19:52 +00:00
PlasticDigits commented 2026-06-15 14:15:57 +00:00 (Migrated from gitlab.com)

Summary

Fixes #382: on /create, structurally valid terra1… addresses with a corrupted bech32 checksum no longer reach the chain. Client-side validation blocks submit and shows retail inline copy instead of the raw addr_validate / decoding bech32 failed contract error.

  • Added terraAddressValidation.ts with getTerraAddressInputError / isValidTerraBech32Address (format + checksum via @cosmjs/encoding).
  • Wired CreatePairPage.tsx to disable Create Pair and show the issue-specified checksum message before submission.
  • Defense in depth: tryHumanizeTerraTxMessage maps any slipped-through bech32 checksum errors to the same retail copy.
  • Trade deep links unchanged — /trade/:pairAddr still uses format-only isValidTerraAddress (no checksum on URL segments).

Acceptance checklist

Criterion Verification Result
Invalid checksum shows inline error before submit cd frontend-dapp && npm run test:unit -- src/pages/CreatePairPage.test.tsx PASS
Expected message: "Invalid address: checksum does not match…" cd frontend-dapp && npm run test:unit -- src/utils/__tests__/terraAddressValidation.test.ts PASS
Submit disabled for checksum-invalid addresses CreatePairPage.test.tsx (button disabled assertion) PASS
Raw contract error never reaches user (primary path) Inline validation blocks tx; unit test covers corrupted last-3-chars case from issue repro PASS
Raw contract error humanized if tx still fails cd frontend-dapp && npm run test:unit -- src/utils/__tests__/humanizeUserFacingError.test.ts (bech32 checksum case) PASS
Trade route deep links unaffected cd frontend-dapp && npm run test:unit -- src/utils/__tests__/tradePairRoute.test.ts PASS
Docs / agent playbooks updated docs/frontend.md § Create pair — token address validation; skills/AGENTS_FRONTEND_USER_ERRORS.md PASS
Manual repro on /create with LocalTerra + wallet Not run in this session (unit tests cover logic) SKIP

Third-party verification

  1. cd frontend-dapp && npm run test:unit -- src/utils/__tests__/terraAddressValidation.test.ts src/pages/CreatePairPage.test.tsx src/utils/__tests__/humanizeUserFacingError.test.ts
  2. Start stack (make setup-cloud-localterra or existing LocalTerra), make dev, open /create.
  3. Token A: valid Terra CW20 address. Token B: copy a valid address and change the last 3 characters.
  4. Confirm inline Invalid address: checksum does not match… appears and Create Pair stays disabled (no wallet prompt).
  5. Confirm /trade/terra1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx still shows Pair not found (format-only gate unchanged).
## Summary Fixes [#382](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/382): on `/create`, structurally valid `terra1…` addresses with a corrupted bech32 checksum no longer reach the chain. Client-side validation blocks submit and shows retail inline copy instead of the raw `addr_validate` / `decoding bech32 failed` contract error. - Added [`terraAddressValidation.ts`](frontend-dapp/src/utils/terraAddressValidation.ts) with `getTerraAddressInputError` / `isValidTerraBech32Address` (format + checksum via `@cosmjs/encoding`). - Wired [`CreatePairPage.tsx`](frontend-dapp/src/pages/CreatePairPage.tsx) to disable **Create Pair** and show the issue-specified checksum message before submission. - Defense in depth: `tryHumanizeTerraTxMessage` maps any slipped-through bech32 checksum errors to the same retail copy. - Trade deep links unchanged — `/trade/:pairAddr` still uses format-only `isValidTerraAddress` (no checksum on URL segments). ## Acceptance checklist | Criterion | Verification | Result | |-----------|--------------|--------| | Invalid checksum shows inline error before submit | `cd frontend-dapp && npm run test:unit -- src/pages/CreatePairPage.test.tsx` | PASS | | Expected message: "Invalid address: checksum does not match…" | `cd frontend-dapp && npm run test:unit -- src/utils/__tests__/terraAddressValidation.test.ts` | PASS | | Submit disabled for checksum-invalid addresses | `CreatePairPage.test.tsx` (button disabled assertion) | PASS | | Raw contract error never reaches user (primary path) | Inline validation blocks tx; unit test covers corrupted last-3-chars case from issue repro | PASS | | Raw contract error humanized if tx still fails | `cd frontend-dapp && npm run test:unit -- src/utils/__tests__/humanizeUserFacingError.test.ts` (bech32 checksum case) | PASS | | Trade route deep links unaffected | `cd frontend-dapp && npm run test:unit -- src/utils/__tests__/tradePairRoute.test.ts` | PASS | | Docs / agent playbooks updated | `docs/frontend.md` § Create pair — token address validation; `skills/AGENTS_FRONTEND_USER_ERRORS.md` | PASS | | Manual repro on `/create` with LocalTerra + wallet | Not run in this session (unit tests cover logic) | SKIP | ## Third-party verification 1. `cd frontend-dapp && npm run test:unit -- src/utils/__tests__/terraAddressValidation.test.ts src/pages/CreatePairPage.test.tsx src/utils/__tests__/humanizeUserFacingError.test.ts` 2. Start stack (`make setup-cloud-localterra` or existing LocalTerra), `make dev`, open `/create`. 3. Token A: valid Terra CW20 address. Token B: copy a valid address and change the last 3 characters. 4. Confirm inline **Invalid address: checksum does not match…** appears and **Create Pair** stays disabled (no wallet prompt). 5. Confirm `/trade/terra1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` still shows **Pair not found** (format-only gate unchanged).
PlasticDigits commented 2026-06-15 14:18:39 +00:00 (Migrated from gitlab.com)

Security review

Commit reviewed: 3f9bc007e493837178f6485d4f50388219fb6619
Scope: Client-side bech32 checksum validation on /create token contract inputs (terraAddressValidation.ts, CreatePairPage.tsx), tx-error humanization (humanizeTerraTxError.ts), and related unit tests/docs.

Outcome: FINDINGS: 0 — no medium+ severity issues on this diff.

Review notes

  • Input → sink trace: User-typed token addresses flow through getTerraAddressInputError (format regex + @cosmjs/encoding fromBech32 + terra HRP check) before submit is enabled and again in the mutation guard. Invalid checksum addresses cannot reach createPair via the UI path; on-chain addr_validate remains the authoritative boundary.
  • XSS / injection: Inline errors and humanized tx copy are static constants or library-validated strings — user input is not reflected into DOM or URLs. TxResultAlert continues to route errors through humanizeUserFacingError.
  • SSRF / path traversal: Unchanged useCodeIdCheck still queries LCD on terra1 prefix only (pre-existing); new validation tightens submit, not widens server-side reach. isValidTerraAddress charset ([a-z0-9]) blocks URL metacharacters in path segments.
  • Authz / DeFi abuse: No permission, signing, or contract-role changes. Client-side gating is UX/defense-in-depth only; bypass via devtools still fails chain validation — expected and not a new trust boundary.
  • Broad humanize regex (invalid checksum): site-wide misclassification risk is limited to confusing copy, not fund loss or privilege escalation.
  • Dependencies: Reuses existing @cosmjs/encoding from the lockfile; no new package version introduced.

Inline threads: none (zero findings).

Security review: no medium+ findings on this diff.

## Security review **Commit reviewed:** `3f9bc007e493837178f6485d4f50388219fb6619` **Scope:** Client-side bech32 checksum validation on `/create` token contract inputs (`terraAddressValidation.ts`, `CreatePairPage.tsx`), tx-error humanization (`humanizeTerraTxError.ts`), and related unit tests/docs. **Outcome:** `FINDINGS: 0` — **no medium+ severity issues** on this diff. ### Review notes - **Input → sink trace:** User-typed token addresses flow through `getTerraAddressInputError` (format regex + `@cosmjs/encoding` `fromBech32` + `terra` HRP check) before submit is enabled and again in the mutation guard. Invalid checksum addresses cannot reach `createPair` via the UI path; on-chain `addr_validate` remains the authoritative boundary. - **XSS / injection:** Inline errors and humanized tx copy are static constants or library-validated strings — user input is not reflected into DOM or URLs. `TxResultAlert` continues to route errors through `humanizeUserFacingError`. - **SSRF / path traversal:** Unchanged `useCodeIdCheck` still queries LCD on `terra1` prefix only (pre-existing); new validation tightens submit, not widens server-side reach. `isValidTerraAddress` charset (`[a-z0-9]`) blocks URL metacharacters in path segments. - **Authz / DeFi abuse:** No permission, signing, or contract-role changes. Client-side gating is UX/defense-in-depth only; bypass via devtools still fails chain validation — expected and not a new trust boundary. - **Broad humanize regex** (`invalid checksum`): site-wide misclassification risk is limited to confusing copy, not fund loss or privilege escalation. - **Dependencies:** Reuses existing `@cosmjs/encoding` from the lockfile; no new package version introduced. **Inline threads:** none (zero findings). Security review: no medium+ findings on this diff.
PlasticDigits commented 2026-06-15 14:19:53 +00:00 (Migrated from gitlab.com)

mentioned in commit bde8e3807e

mentioned in commit bde8e3807e12a2f2fb84001ad62a2377bd687b78
PlasticDigits (Migrated from gitlab.com) merged commit bde8e3807e into main 2026-06-15 14:19:53 +00:00
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!911
No description provided.