Frontend: fee-discount registry outage warning for traders (#374) #893

Merged
PlasticDigits merged 3 commits from issue-374-fee-discount-registry-warning into main 2026-06-13 03:47:51 +00:00
PlasticDigits commented 2026-06-13 03:02:53 +00:00 (Migrated from gitlab.com)

Summary

Implements GitLab #374 — non-blocking Swap banner when registered traders may be charged full pair fee because the fee-discount registry LCD is unreachable or the indexer reports registry downtime.

  • feeDiscountRegistryWarning.ts: resolveFeeDiscountRegistryStatus distinguishes unregistered vs registry_unreachable vs registered; shouldShowFeeDiscountRegistryWarning gates the banner.
  • SwapPage.tsx: Amber role="status" banner (swap-fee-discount-registry-warning); polls indexer health every 30s when FEE_DISCOUNT_CONTRACT_ADDRESS is set; Hold CL8Y… CTA only for confirmed unregistered wallets.
  • Indexer GET /api/v1/health/fee-discount: narrow LCD config probe returning { configured, fee_discount_registry_ok } (no per-trader data, no raw LCD errors).
  • Docs: docs/frontend.md, skills/AGENTS_FEE_DISCOUNT_TIERS.md § Registry outage observability.

Swap submit stays enabled when the warning is shown (on-chain fail-closed to full fee unchanged).

Acceptance checklist

Criterion Verification Result
Registered trader + failing get_registration / get_discount LCD → non-blocking outage warning on Swap cd frontend-dapp && ./node_modules/.bin/vitest run src/pages/SwapPage.test.tsx -t "registration LCD fails" PASS
Registered trader + indexer fee_discount_registry_ok: false → same warning even if LCD reads succeed cd frontend-dapp && ./node_modules/.bin/vitest run src/pages/SwapPage.test.tsx -t "indexer reports registry down" PASS
Unregistered + healthy registry → no outage banner; Hold CL8Y CTA unchanged cd frontend-dapp && ./node_modules/.bin/vitest run src/pages/SwapPage.test.tsx -t "Hold CL8Y CTA" PASS
Swap submit enabled when warning shown Same SwapPage tests assert getByRole('button', { name: /^Swap$/i }) enabled PASS
Unit tests: status matrix + SwapPage banner visibility make test-frontend (or vitest on feeDiscountRegistryWarning.test.ts + SwapPage.test.tsx) PASS
Manual LocalTerra: register → pause/block registry → warning + full-fee swap Requires make setup-cloud-localterra + pause registry SKIP (not run in agent VM this session)
Manual: indexer health false with successful LCD registration curl http://127.0.0.1:3001/api/v1/health/fee-discount after indexer start SKIP (manual QA)

Third-party verification

# Unit (Node 24)
export PATH="$HOME/.nvm/versions/node/$(cat .nvmrc)/bin:$PATH"
cd frontend-dapp && ./node_modules/.bin/vitest run \
  src/utils/__tests__/feeDiscountRegistryWarning.test.ts \
  src/pages/SwapPage.test.tsx

# Indexer health endpoint (needs Postgres: make setup-indexer-postgres)
export PATH="/usr/local/cargo/bin:$PATH"
cd indexer && cargo test --test api_fee_discount_health

# Optional full frontend suite
make test-frontend

On Swap with a connected wallet: banner data-testid="swap-fee-discount-registry-warning" appears only for registry_unreachable; unregistered wallets see Hold CL8Y… instead.

  • Parent observability gap: #365 / #361 M2
  • Issue: #374 (leave open until merge)

Note

Low Risk
UX and read-path observability only; swap submit stays enabled and on-chain fee logic is unchanged.

Overview
Adds GitLab #374 observability so registered traders are not misled when fee-tier LCD or indexer health says the registry is down — on-chain behavior still fail-closes to full pair fee; Swap stays submittable.

Status logic in feeDiscountRegistryWarning.ts now separates unregistered, registered, and registry_unreachable. Indexer fee_discount_registry_ok: false or LCD errors on registration/discount only trigger the outage path when the wallet is not confirmed unregistered (successful LCD read with registered: false still shows the Hold CL8Y… CTA, not the amber banner).

Swap UI: a top-level amber role="status" banner (swap-fee-discount-registry-warning) with stable copy; fee-discount indexer health polls every 30s when the contract is configured; the cyan Hold CL8Y… link is gated on resolveFeeDiscountRegistryStatus === 'unregistered' instead of overlapping with the outage warning.

Tests & docs: expanded unit matrix in feeDiscountRegistryWarning.test.ts, new SwapPage.test.tsx scenarios, and updates to docs/frontend.md and skills/AGENTS_FEE_DISCOUNT_TIERS.md. Minor indexer client tweak: E2E outage env can shorten fetch timeout / disable retries.

Reviewed by Cursor Bugbot for commit 44d73ee47c. Bugbot is set up for automated code reviews on this repo. Configure here.

## Summary Implements GitLab #374 — non-blocking Swap banner when registered traders may be charged full pair fee because the fee-discount registry LCD is unreachable or the indexer reports registry downtime. - **`feeDiscountRegistryWarning.ts`**: `resolveFeeDiscountRegistryStatus` distinguishes `unregistered` vs `registry_unreachable` vs `registered`; `shouldShowFeeDiscountRegistryWarning` gates the banner. - **`SwapPage.tsx`**: Amber `role="status"` banner (`swap-fee-discount-registry-warning`); polls indexer health every 30s when `FEE_DISCOUNT_CONTRACT_ADDRESS` is set; **Hold CL8Y…** CTA only for confirmed unregistered wallets. - **Indexer** `GET /api/v1/health/fee-discount`: narrow LCD `config` probe returning `{ configured, fee_discount_registry_ok }` (no per-trader data, no raw LCD errors). - **Docs**: `docs/frontend.md`, `skills/AGENTS_FEE_DISCOUNT_TIERS.md` § Registry outage observability. Swap submit stays enabled when the warning is shown (on-chain fail-closed to full fee unchanged). ## Acceptance checklist | Criterion | Verification | Result | |-----------|--------------|--------| | Registered trader + failing `get_registration` / `get_discount` LCD → non-blocking outage warning on Swap | `cd frontend-dapp && ./node_modules/.bin/vitest run src/pages/SwapPage.test.tsx -t "registration LCD fails"` | **PASS** | | Registered trader + indexer `fee_discount_registry_ok: false` → same warning even if LCD reads succeed | `cd frontend-dapp && ./node_modules/.bin/vitest run src/pages/SwapPage.test.tsx -t "indexer reports registry down"` | **PASS** | | Unregistered + healthy registry → no outage banner; Hold CL8Y CTA unchanged | `cd frontend-dapp && ./node_modules/.bin/vitest run src/pages/SwapPage.test.tsx -t "Hold CL8Y CTA"` | **PASS** | | Swap submit enabled when warning shown | Same SwapPage tests assert `getByRole('button', { name: /^Swap$/i })` enabled | **PASS** | | Unit tests: status matrix + SwapPage banner visibility | `make test-frontend` (or vitest on `feeDiscountRegistryWarning.test.ts` + `SwapPage.test.tsx`) | **PASS** | | Manual LocalTerra: register → pause/block registry → warning + full-fee swap | Requires `make setup-cloud-localterra` + pause registry | **SKIP** (not run in agent VM this session) | | Manual: indexer health false with successful LCD registration | `curl http://127.0.0.1:3001/api/v1/health/fee-discount` after indexer start | **SKIP** (manual QA) | ## Third-party verification ```bash # Unit (Node 24) export PATH="$HOME/.nvm/versions/node/$(cat .nvmrc)/bin:$PATH" cd frontend-dapp && ./node_modules/.bin/vitest run \ src/utils/__tests__/feeDiscountRegistryWarning.test.ts \ src/pages/SwapPage.test.tsx # Indexer health endpoint (needs Postgres: make setup-indexer-postgres) export PATH="/usr/local/cargo/bin:$PATH" cd indexer && cargo test --test api_fee_discount_health # Optional full frontend suite make test-frontend ``` On Swap with a connected wallet: banner `data-testid="swap-fee-discount-registry-warning"` appears only for `registry_unreachable`; unregistered wallets see **Hold CL8Y…** instead. ## Related - Parent observability gap: #365 / #361 M2 - Issue: #374 (leave open until merge) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > UX and read-path observability only; swap submit stays enabled and on-chain fee logic is unchanged. > > **Overview** > Adds **GitLab #374** observability so registered traders are not misled when fee-tier LCD or indexer health says the registry is down — on-chain behavior still **fail-closes to full pair fee**; **Swap** stays submittable. > > **Status logic** in `feeDiscountRegistryWarning.ts` now separates **`unregistered`**, **`registered`**, and **`registry_unreachable`**. Indexer `fee_discount_registry_ok: false` or LCD errors on registration/discount only trigger the outage path when the wallet is not **confirmed unregistered** (successful LCD read with `registered: false` still shows the **Hold CL8Y…** CTA, not the amber banner). > > **Swap UI:** a top-level amber `role="status"` banner (`swap-fee-discount-registry-warning`) with stable copy; fee-discount indexer health polls every **30s** when the contract is configured; the cyan **Hold CL8Y…** link is gated on `resolveFeeDiscountRegistryStatus === 'unregistered'` instead of overlapping with the outage warning. > > **Tests & docs:** expanded unit matrix in `feeDiscountRegistryWarning.test.ts`, new `SwapPage.test.tsx` scenarios, and updates to `docs/frontend.md` and `skills/AGENTS_FEE_DISCOUNT_TIERS.md`. Minor indexer client tweak: E2E outage env can shorten fetch timeout / disable retries. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 44d73ee47ceefe87dd8a52fc469d5482d3f37332. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
ghost1 commented 2026-06-13 03:03:00 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-13 03:03:08 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-13 03:03:55 +00:00 (Migrated from gitlab.com)

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issue.

Reviewed by Cursor Bugbot for commit 16f31c3d35. Configure here.

<!-- BUGBOT_REVIEW --> Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue. <!-- BUGBOT_FIX_ALL --> <a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9BTExfSU5fQ1VSU09SIiwiZGF0YSI6eyJyZWRpc0tleSI6ImJ1Z2JvdC1tdWx0aTo3ZDBiYzI4OC00NzYyLTRiOTUtODhlZC05M2Q3M2FiYWVjN2IiLCJlbmNyeXB0aW9uS2V5IjoiREZIYjhMcjVZcy12WFIyVlFPaG9LRzFnSzFvRDlxUVhYUnJpcmlFX0V0QSIsImJyYW5jaCI6Imlzc3VlLTM3NC1mZWUtZGlzY291bnQtcmVnaXN0cnktd2FybmluZyIsInJlcG9Pd25lciI6IlBsYXN0aWNEaWdpdHMiLCJyZXBvTmFtZSI6ImNsOHktZGV4LXRlcnJhY2xhc3NpYyJ9fQ" target="_blank" rel="noopener noreferrer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/fix-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/fix-in-cursor-light.png"><img alt="Fix All in Cursor" width="115" height="28" src="https://cursor.com/assets/images/fix-in-cursor-dark.png"></picture></a> <!-- /BUGBOT_FIX_ALL --> <!-- BUGBOT_AUTOFIX_REVIEW_FOOTNOTE_BEGIN --> <sup>Bugbot Autofix is [ON](https://www.cursor.com/dashboard/bugbot). A cloud agent has been kicked off to fix the reported issue. <!-- BUGBOT_AUTOFIX_AGENT_LINK --></sup> <!-- BUGBOT_AUTOFIX_REVIEW_FOOTNOTE_END --> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 16f31c3d35045892550e9a1afce8f3fc98d4f8c2. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
ghost1 commented 2026-06-13 03:03:56 +00:00 (Migrated from gitlab.com)

Health query never polls

Medium Severity

feeDiscountHealthQuery sets staleTime: 30_000 but no refetchInterval. With app-wide refetchOnWindowFocus: false, indexer registry health is fetched once per mount, so the outage banner may stay after the registry recovers until the user leaves Swap.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 16f31c3d35. Configure here.

### Health query never polls **Medium Severity** <!-- DESCRIPTION START --> `feeDiscountHealthQuery` sets `staleTime: 30_000` but no `refetchInterval`. With app-wide `refetchOnWindowFocus: false`, indexer registry health is fetched once per mount, so the outage banner may stay after the registry recovers until the user leaves Swap. <!-- DESCRIPTION END --> <!-- BUGBOT_BUG_ID: acfca16c-db42-4cc6-8286-1f7a7c7fad51 --> <!-- LOCATIONS START frontend-dapp/src/pages/SwapPage.tsx#L265-L272 LOCATIONS END --> <div><a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9DVVJTT1IiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OmRiYzRiN2VmLTU2OGUtNDQxMC1hMTM2LWU5YWUwODE4MjYwZSIsImVuY3J5cHRpb25LZXkiOiJZdWJfbU14NGhHVHZOaWQ4QlU4NVhyMFgtMDZ5NnhLYkRFNnp1Z3Zlc1FRIiwiYnJhbmNoIjoiaXNzdWUtMzc0LWZlZS1kaXNjb3VudC1yZWdpc3RyeS13YXJuaW5nIiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIn19" target="_blank" rel="noopener noreferrer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/fix-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/fix-in-cursor-light.png"><img alt="Fix in Cursor" width="115" height="28" src="https://cursor.com/assets/images/fix-in-cursor-dark.png"></picture></a>&nbsp;<a href="https://cursor.com/agents?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9XRUIiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OmRiYzRiN2VmLTU2OGUtNDQxMC1hMTM2LWU5YWUwODE4MjYwZSIsImVuY3J5cHRpb25LZXkiOiJZdWJfbU14NGhHVHZOaWQ4QlU4NVhyMFgtMDZ5NnhLYkRFNnp1Z3Zlc1FRIiwiYnJhbmNoIjoiaXNzdWUtMzc0LWZlZS1kaXNjb3VudC1yZWdpc3RyeS13YXJuaW5nIiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIiwicHJOdW1iZXIiOjE3NSwiY29tbWl0U2hhIjoiMTZmMzFjM2QzNTA0NTg5MjU1MGU5YTFhZmNlOGYzZmM5OGQ0ZjhjMiIsInByb3ZpZGVyIjoiZ2l0bGFiIiwiaG9zdG5hbWUiOiJnaXRsYWIuY29tIn19" target="_blank" rel="noopener noreferrer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/fix-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/fix-in-web-light.png"><img alt="Fix in Web" width="99" height="28" src="https://cursor.com/assets/images/fix-in-web-dark.png"></picture></a></div> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 16f31c3d35045892550e9a1afce8f3fc98d4f8c2. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
ghost1 commented 2026-06-13 03:05:09 +00:00 (Migrated from gitlab.com)

added 1 commit

  • 8160805f - fix(frontend): poll fee-discount health every 30s on Swap

Compare with previous version

added 1 commit <ul><li>8160805f - fix(frontend): poll fee-discount health every 30s on Swap</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/175/diffs?diff_id=1850136163&start_sha=16f31c3d35045892550e9a1afce8f3fc98d4f8c2)
PlasticDigits commented 2026-06-13 03:05:13 +00:00 (Migrated from gitlab.com)

Security review

Commit reviewed: 16f31c3d35045892550e9a1afce8f3fc98d4f8c2
Scope: Frontend fee-discount registry outage banner (feeDiscountRegistryWarning.ts, SwapPage.tsx, indexer client), new indexer GET /api/v1/health/fee-discount, tests and docs.

Outcome: FINDINGS: 0 medium+ — Security review: no medium+ findings on this diff.

Summary

Reviewed added/modified paths for injection, authz bypass, secret leakage, SSRF/XSS, unsafe deserialization, and new attack surface. No plausible medium-or-higher exploit path was identified.

Area Assessment
Indexer health endpoint Read-only; no request parameters. LCD probe uses server-configured fee_discount_address with fixed {"config":{}} query — no user-controlled URL/contract/path. Response is two booleans only (no raw LCD errors or per-trader data). Standard API rate limit applies (rate_limit_rps).
Frontend banner Static constant copy (FEE_DISCOUNT_REGISTRY_WARNING_TEXT) rendered as text — no attacker-controlled HTML/JS sink.
Indexer client getFeeDiscountHealth() parses JSON; status logic uses strict === true / === false on known fields. Failed health fetch is ignored (indexerHealth: null), falling back to LCD error signals only.
Auth / permissions No auth or on-chain role changes; swap submit remains enabled (informational UX only).
Dependencies No new third-party packages in this diff.

Inline threads: none (no medium+ findings).

Prior security-review comments on !893: none found.

## Security review **Commit reviewed:** `16f31c3d35045892550e9a1afce8f3fc98d4f8c2` **Scope:** Frontend fee-discount registry outage banner (`feeDiscountRegistryWarning.ts`, `SwapPage.tsx`, indexer client), new indexer `GET /api/v1/health/fee-discount`, tests and docs. **Outcome:** `FINDINGS: 0` medium+ — **Security review: no medium+ findings on this diff.** ### Summary Reviewed added/modified paths for injection, authz bypass, secret leakage, SSRF/XSS, unsafe deserialization, and new attack surface. No plausible medium-or-higher exploit path was identified. | Area | Assessment | |------|------------| | **Indexer health endpoint** | Read-only; no request parameters. LCD probe uses server-configured `fee_discount_address` with fixed `{"config":{}}` query — no user-controlled URL/contract/path. Response is two booleans only (no raw LCD errors or per-trader data). Standard API rate limit applies (`rate_limit_rps`). | | **Frontend banner** | Static constant copy (`FEE_DISCOUNT_REGISTRY_WARNING_TEXT`) rendered as text — no attacker-controlled HTML/JS sink. | | **Indexer client** | `getFeeDiscountHealth()` parses JSON; status logic uses strict `=== true` / `=== false` on known fields. Failed health fetch is ignored (`indexerHealth: null`), falling back to LCD error signals only. | | **Auth / permissions** | No auth or on-chain role changes; swap submit remains enabled (informational UX only). | | **Dependencies** | No new third-party packages in this diff. | **Inline threads:** none (no medium+ findings). Prior security-review comments on !893: none found.
ghost1 commented 2026-06-13 03:05:22 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-13 03:05:27 +00:00 (Migrated from gitlab.com)

resolved all threads

resolved all threads
PlasticDigits commented 2026-06-13 03:41:34 +00:00 (Migrated from gitlab.com)

added 92 commits

  • 8160805f...cccd4cf2 - 91 commits from branch main
  • 44d73ee4 - Merge origin/main into issue-374-fee-discount-registry-warning

Compare with previous version

added 92 commits <ul><li>8160805f...cccd4cf2 - 91 commits from branch <code>main</code></li><li>44d73ee4 - Merge origin/main into issue-374-fee-discount-registry-warning</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/175/diffs?diff_id=1850148516&start_sha=8160805ff88feba48e95580ba9094bc8b3989fe3)
PlasticDigits commented 2026-06-13 03:41:36 +00:00 (Migrated from gitlab.com)

Resolved merge conflicts with main.

Conflicts resolved

  • feeDiscountRegistryWarning.ts — kept #374 nuanced status resolution (unregistered vs registry_unreachable when indexer is down) on top of main's input types and consecutive_lcd_failures health snapshot (#373).
  • SwapPage.tsx — kept #374 top-level outage banner (swap-fee-discount-registry-warning), 30s health polling, and Hold CL8Y CTA only for confirmed unregistered; removed duplicate in-card warning from main.
  • indexer/src/api/fee_discount_health.rs + tests — kept main's cached FeeDiscountRegistryHealth probe (#373).
  • client.ts — deduplicated getFeeDiscountHealth export; response includes consecutive_lcd_failures.

Sanity checks

  • vitest fee-discount / SwapPage banner tests: pass
  • Indexer integration tests need Postgres in this VM (compile OK)

Merge commit: 44d73ee

Resolved merge conflicts with `main`. **Conflicts resolved** - `feeDiscountRegistryWarning.ts` — kept #374 nuanced status resolution (unregistered vs `registry_unreachable` when indexer is down) on top of main's input types and `consecutive_lcd_failures` health snapshot (#373). - `SwapPage.tsx` — kept #374 top-level outage banner (`swap-fee-discount-registry-warning`), 30s health polling, and Hold CL8Y CTA only for confirmed `unregistered`; removed duplicate in-card warning from main. - `indexer/src/api/fee_discount_health.rs` + tests — kept main's cached `FeeDiscountRegistryHealth` probe (#373). - `client.ts` — deduplicated `getFeeDiscountHealth` export; response includes `consecutive_lcd_failures`. **Sanity checks** - `vitest` fee-discount / SwapPage banner tests: pass - Indexer integration tests need Postgres in this VM (compile OK) Merge commit: `44d73ee`
ghost1 commented 2026-06-13 03:41:47 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-13 03:43:33 +00:00 (Migrated from gitlab.com)

Security review

Commit reviewed: 44d73ee47ceefe87dd8a52fc469d5482d3f37332
Scope: MR !893 diff — feeDiscountRegistryWarning.ts, SwapPage.tsx (outage banner + 30s health poll), indexer client E2E timeout tweak, tests, docs. Consumes existing read-only GET /api/v1/health/fee-discount (added in #373, not modified here).

Outcome: FINDINGS: 0 medium+ — Security review: no medium+ findings on this diff.

Summary

Re-reviewed after merge with main and the polling fix (refetchInterval: 30_000). Traced attacker-controlled inputs through the new paths; no plausible medium-or-higher exploit was identified.

Area Assessment
XSS / HTML injection Banner renders only the static constant FEE_DISCOUNT_REGISTRY_WARNING_TEXT; no LCD errors, wallet addresses, or indexer JSON interpolated into DOM. React text escaping applies.
Indexer health consumption getFeeDiscountHealth() fetches a fixed path; response fields are used only as boolean/number predicates in status logic — never rendered. Failed health fetch yields indexerHealth: null (fail-open on ops signal only).
Auth / authz No new authenticated surfaces; health endpoint is unauthenticated read-only ops telemetry (pre-existing).
SSRF / path traversal No user-supplied URL segments; INDEXER_URL is build-time env (same trust model as other indexer calls).
Secret leakage / logging No secrets or PII added; docs explicitly forbid raw LCD errors in banner copy.
Economic / tx safety Warning is non-blocking; on-chain pair fee logic unchanged (fail-closed to full fee when registry unreachable). Suppressing the banner would require indexer MITM/compromise — same pre-existing indexer trust boundary, not a new fund-theft path.
Hold CL8Y CTA Hardcoded relative /tiers link; gated on confirmed unregistered status from successful LCD read.

Inline threads: none (no medium+ findings).

Prior review on 16f31c3d remains valid; this pass confirms the post-merge polling fix does not introduce new attack surface.

## Security review **Commit reviewed:** `44d73ee47ceefe87dd8a52fc469d5482d3f37332` **Scope:** MR !893 diff — `feeDiscountRegistryWarning.ts`, `SwapPage.tsx` (outage banner + 30s health poll), indexer client E2E timeout tweak, tests, docs. Consumes existing read-only `GET /api/v1/health/fee-discount` (added in #373, not modified here). **Outcome:** `FINDINGS: 0` medium+ — **Security review: no medium+ findings on this diff.** ### Summary Re-reviewed after merge with `main` and the polling fix (`refetchInterval: 30_000`). Traced attacker-controlled inputs through the new paths; no plausible medium-or-higher exploit was identified. | Area | Assessment | |------|------------| | **XSS / HTML injection** | Banner renders only the static constant `FEE_DISCOUNT_REGISTRY_WARNING_TEXT`; no LCD errors, wallet addresses, or indexer JSON interpolated into DOM. React text escaping applies. | | **Indexer health consumption** | `getFeeDiscountHealth()` fetches a fixed path; response fields are used only as boolean/number predicates in status logic — never rendered. Failed health fetch yields `indexerHealth: null` (fail-open on ops signal only). | | **Auth / authz** | No new authenticated surfaces; health endpoint is unauthenticated read-only ops telemetry (pre-existing). | | **SSRF / path traversal** | No user-supplied URL segments; `INDEXER_URL` is build-time env (same trust model as other indexer calls). | | **Secret leakage / logging** | No secrets or PII added; docs explicitly forbid raw LCD errors in banner copy. | | **Economic / tx safety** | Warning is non-blocking; on-chain pair fee logic unchanged (fail-closed to full fee when registry unreachable). Suppressing the banner would require indexer MITM/compromise — same pre-existing indexer trust boundary, not a new fund-theft path. | | **Hold CL8Y CTA** | Hardcoded relative `/tiers` link; gated on confirmed `unregistered` status from successful LCD read. | **Inline threads:** none (no medium+ findings). Prior review on `16f31c3d` remains valid; this pass confirms the post-merge polling fix does not introduce new attack surface.
PlasticDigits commented 2026-06-13 03:47:52 +00:00 (Migrated from gitlab.com)

mentioned in commit a17df69fcb

mentioned in commit a17df69fcbf1dbf04ae271f00572c2fc6858ded4
PlasticDigits (Migrated from gitlab.com) merged commit a17df69fcb into main 2026-06-13 03:47:52 +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!893
No description provided.