Wire post-deploy smoke and broadcast recovery E2E (#368) #877

Merged
PlasticDigits merged 7 commits from feat/368-broadcast-recovery-smoke-qa into main 2026-06-12 11:39:24 +00:00
PlasticDigits commented 2026-06-12 05:46:12 +00:00 (Migrated from gitlab.com)

Summary

GitLab #368 — wires H12 (post-sign broadcast recovery E2E) and M12 (post-deploy smoke in QA bring-up).

  • Broadcast recovery (#359): After wallet sign, capture tx hash via RpcClient.broadcastTx intercept; on broadcast timeout/network failure, enter recovering phase, poll through msg deadline without re-broadcasting, show Broadcast status unknown… retail copy.
  • E2E: terra-broadcast-recovery.spec.ts in e2e-tx (Simulated Wallet; hung broadcast_tx_sync → recovery UX → success).
  • Smoke: make start-qa runs smoke-pool-swap.sh after qa-verify-deploy using scripts/lib/smoke-deploy-env.sh (pair from .qa-deploy-stamp, offer token from pool query). QA_SKIP_SMOKE=1 skips. make smoke-pool-swap wrapper added.
  • Docs/skills: docs/testing.md, scripts/qa/README.md, AGENTS_FRONTEND_TX_BROADCAST_TIMEOUT.md, AGENTS_E2E_STRICT_CHAIN.md, AGENTS_TESTING_P2_EPIC.md.

Acceptance checklist

Criterion Command / step Result
Playwright spec asserts post-sign recovery UX (Simulated Wallet swap) CI=1 bash scripts/with-node.sh --cwd frontend-dapp -- ./node_modules/.bin/playwright test --project=e2e-tx terra-broadcast-recovery.spec.ts (after make setup-cloud-localterra + bash scripts/e2e-start-indexer.sh) PASS
make start-qa runs smoke after deploy verification scripts/qa/start-qa.sh invokes smoke after verify-deploy.sh (see [timing] smoke phase) PASS (code wired; full start-qa not re-run end-to-end on this VM after deploy)
QA_SKIP_SMOKE=1 make start-qa still works grep QA_SKIP_SMOKE scripts/qa/start-qa.sh → early skip log PASS (code path)
Smoke uses deployed pair from env make smoke-pool-swap on LocalTerra deploy stamp PASS
Keplr parity documented skills/AGENTS_FRONTEND_TX_BROADCAST_TIMEOUT.md § Post-sign broadcast recovery PASS

Verification checklist (third parties)

  1. make test-frontend — includes terraBroadcastRecovery.test.ts (935 tests passed on agent VM).
  2. make setup-cloud-localterra && bash scripts/e2e-start-indexer.sh
  3. CI=1 make test-e2e-tx or single spec: CI=1 bash scripts/with-node.sh --cwd frontend-dapp -- ./node_modules/.bin/playwright test --project=e2e-tx terra-broadcast-recovery.spec.ts
  4. make smoke-pool-swap — expect OK: smoke-pool-swap read-only checks passed.
  5. Optional QA host: make start-qa logs smoke success; QA_SKIP_SMOKE=1 make start-qa skips smoke line.

Test plan notes

  • Unit: make test-frontend PASS
  • E2E tx: terra-broadcast-recovery.spec.ts PASS (29.7s, LocalTerra + indexer)
  • Smoke: make smoke-pool-swap PASS
  • Attack/abuse: recovery poll does not re-broadcast; smoke is read-only LCD (no secrets echoed)

Note

Medium Risk
Adds safety-critical E2E around post-sign broadcast recovery (must not re-broadcast); QA smoke is read-only LCD but extends default start-qa duration and can fail bring-up on deploy mismatch.

Overview
GitLab #368 connects QA post-deploy smoke and a Playwright broadcast-recovery path to existing swap tx handling.

Post-deploy smoke: Adds make smoke-pool-swap and scripts/lib/smoke-deploy-env.sh, which load PAIR_ADDR from .qa-deploy-stamp (with fallbacks) and derive OFFER_TOKEN from an LCD pool query. make start-qa runs read-only smoke-pool-swap.sh after qa-verify-deploy; set QA_SKIP_SMOKE=1 to skip. Testing and QA docs now describe this instead of manual env exports.

Broadcast recovery E2E: Adds terra-broadcast-recovery.spec.ts to the e2e-tx project (single worker). The spec stalls broadcast_tx_sync, expects Broadcast status unknown…, disabled submit, and pending-tx UI, then release and success. Playwright dev server defaults VITE_TERRA_TX_BROADCAST_TIMEOUT_MS=3000 so recovery triggers quickly in CI.

Supporting changes: New terraMsgDeadline.ts under services/terraclassic scans execute msgs (including nested CW20 send / swap) for the latest on-chain deadline and defines the LCD poll window (msg deadline or 300s fallback). Unit test mock adds RpcClient.broadcastTx. Agent skills and P2 testing docs cross-link #368 with #359 recovery behavior.

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

## Summary GitLab #368 — wires **H12** (post-sign broadcast recovery E2E) and **M12** (post-deploy smoke in QA bring-up). - **Broadcast recovery (#359):** After wallet sign, capture tx hash via `RpcClient.broadcastTx` intercept; on broadcast timeout/network failure, enter `recovering` phase, poll through msg `deadline` without re-broadcasting, show **Broadcast status unknown…** retail copy. - **E2E:** `terra-broadcast-recovery.spec.ts` in `e2e-tx` (Simulated Wallet; hung `broadcast_tx_sync` → recovery UX → success). - **Smoke:** `make start-qa` runs `smoke-pool-swap.sh` after `qa-verify-deploy` using `scripts/lib/smoke-deploy-env.sh` (pair from `.qa-deploy-stamp`, offer token from pool query). `QA_SKIP_SMOKE=1` skips. `make smoke-pool-swap` wrapper added. - **Docs/skills:** `docs/testing.md`, `scripts/qa/README.md`, `AGENTS_FRONTEND_TX_BROADCAST_TIMEOUT.md`, `AGENTS_E2E_STRICT_CHAIN.md`, `AGENTS_TESTING_P2_EPIC.md`. ## Acceptance checklist | Criterion | Command / step | Result | |-----------|----------------|--------| | Playwright spec asserts post-sign recovery UX (Simulated Wallet swap) | `CI=1 bash scripts/with-node.sh --cwd frontend-dapp -- ./node_modules/.bin/playwright test --project=e2e-tx terra-broadcast-recovery.spec.ts` (after `make setup-cloud-localterra` + `bash scripts/e2e-start-indexer.sh`) | PASS | | `make start-qa` runs smoke after deploy verification | `scripts/qa/start-qa.sh` invokes smoke after `verify-deploy.sh` (see `[timing] smoke` phase) | PASS (code wired; full `start-qa` not re-run end-to-end on this VM after deploy) | | `QA_SKIP_SMOKE=1 make start-qa` still works | `grep QA_SKIP_SMOKE scripts/qa/start-qa.sh` → early skip log | PASS (code path) | | Smoke uses deployed pair from env | `make smoke-pool-swap` on LocalTerra deploy stamp | PASS | | Keplr parity documented | `skills/AGENTS_FRONTEND_TX_BROADCAST_TIMEOUT.md` § Post-sign broadcast recovery | PASS | ## Verification checklist (third parties) 1. `make test-frontend` — includes `terraBroadcastRecovery.test.ts` (935 tests passed on agent VM). 2. `make setup-cloud-localterra && bash scripts/e2e-start-indexer.sh` 3. `CI=1 make test-e2e-tx` or single spec: `CI=1 bash scripts/with-node.sh --cwd frontend-dapp -- ./node_modules/.bin/playwright test --project=e2e-tx terra-broadcast-recovery.spec.ts` 4. `make smoke-pool-swap` — expect `OK: smoke-pool-swap read-only checks passed.` 5. Optional QA host: `make start-qa` logs smoke success; `QA_SKIP_SMOKE=1 make start-qa` skips smoke line. ## Test plan notes - Unit: `make test-frontend` PASS - E2E tx: `terra-broadcast-recovery.spec.ts` PASS (29.7s, LocalTerra + indexer) - Smoke: `make smoke-pool-swap` PASS - Attack/abuse: recovery poll does not re-broadcast; smoke is read-only LCD (no secrets echoed) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Adds safety-critical E2E around post-sign broadcast recovery (must not re-broadcast); QA smoke is read-only LCD but extends default start-qa duration and can fail bring-up on deploy mismatch. > > **Overview** > GitLab **#368** connects **QA post-deploy smoke** and a **Playwright broadcast-recovery** path to existing swap tx handling. > > **Post-deploy smoke:** Adds `make smoke-pool-swap` and `scripts/lib/smoke-deploy-env.sh`, which load `PAIR_ADDR` from `.qa-deploy-stamp` (with fallbacks) and derive `OFFER_TOKEN` from an LCD `pool` query. **`make start-qa`** runs read-only `smoke-pool-swap.sh` after `qa-verify-deploy`; set **`QA_SKIP_SMOKE=1`** to skip. Testing and QA docs now describe this instead of manual env exports. > > **Broadcast recovery E2E:** Adds **`terra-broadcast-recovery.spec.ts`** to the **`e2e-tx`** project (single worker). The spec stalls `broadcast_tx_sync`, expects **Broadcast status unknown…**, disabled submit, and pending-tx UI, then release and success. Playwright dev server defaults **`VITE_TERRA_TX_BROADCAST_TIMEOUT_MS=3000`** so recovery triggers quickly in CI. > > **Supporting changes:** New **`terraMsgDeadline.ts`** under `services/terraclassic` scans execute msgs (including nested CW20 `send` / `swap`) for the latest on-chain **`deadline`** and defines the LCD poll window (msg deadline or 300s fallback). Unit test mock adds **`RpcClient.broadcastTx`**. Agent skills and P2 testing docs cross-link **#368** with **#359** recovery behavior. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 337f6becc2afb9c8a1e94a2cdceb8b1bed1215f8. 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-12 05:46:22 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-12 05:46:31 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-12 05:47:41 +00:00 (Migrated from gitlab.com)
Stale Security Review comment
Stale Security Review comment

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issues.

Reviewed by Cursor Bugbot for commit ab877df57f. Configure here.

<details> <summary>Stale Security Review comment</summary> <details> <summary>Stale Security Review comment</summary> <!-- BUGBOT_REVIEW --> Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.<!-- 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 issues. <!-- BUGBOT_AUTOFIX_AGENT_LINK --></sup> <!-- BUGBOT_AUTOFIX_REVIEW_FOOTNOTE_END --> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ab877df57f5b9cc0ab46171e2392cbf8be3e9840. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> </details> </details>
ghost1 commented 2026-06-12 05:47:42 +00:00 (Migrated from gitlab.com)

Recovery expiry message not preserved

Low Severity

When pollTerraTxRecovery throws TERRA_TX_RECOVERY_EXPIRED_MESSAGE, the outer catch runs handleBroadcastError, which does not pass that message through like broadcast or poll timeouts. Users get a generic Transaction failed: wrapper instead of the dedicated retry guidance copy.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ab877df57f. Configure here.

### Recovery expiry message not preserved **Low Severity** <!-- DESCRIPTION START --> When `pollTerraTxRecovery` throws `TERRA_TX_RECOVERY_EXPIRED_MESSAGE`, the outer catch runs `handleBroadcastError`, which does not pass that message through like broadcast or poll timeouts. Users get a generic `Transaction failed:` wrapper instead of the dedicated retry guidance copy. <!-- DESCRIPTION END --> <!-- BUGBOT_BUG_ID: 6fd35f3d-2398-4767-afd9-b150e801ef5c --> <!-- LOCATIONS START frontend-dapp/src/services/terraclassic/terraBroadcast.ts#L47-L54 frontend-dapp/src/services/terraclassic/terraTxRecoveryPoll.ts#L38-L39 LOCATIONS END --> <details> <summary>Additional Locations (1)</summary> - [`frontend-dapp/src/services/terraclassic/terraTxRecoveryPoll.ts#L38-L39`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-//blob/ab877df57f5b9cc0ab46171e2392cbf8be3e9840/frontend-dapp/src/services/terraclassic/terraTxRecoveryPoll.ts#L38-L39) </details> <div><a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9DVVJTT1IiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OjVjMGU2MzZlLWQxOWEtNGZiZC05MjUxLTAxNDFlMzE4ZDE5YSIsImVuY3J5cHRpb25LZXkiOiJTS2ZLSFNTUUtNMFRXSFR4SWNHLWdLQ29xZFgySk8zRVlEbnlJLVFpN2RzIiwiYnJhbmNoIjoiZmVhdC8zNjgtYnJvYWRjYXN0LXJlY292ZXJ5LXNtb2tlLXFhIiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIn19" 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=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9XRUIiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OjVjMGU2MzZlLWQxOWEtNGZiZC05MjUxLTAxNDFlMzE4ZDE5YSIsImVuY3J5cHRpb25LZXkiOiJTS2ZLSFNTUUtNMFRXSFR4SWNHLWdLQ29xZFgySk8zRVlEbnlJLVFpN2RzIiwiYnJhbmNoIjoiZmVhdC8zNjgtYnJvYWRjYXN0LXJlY292ZXJ5LXNtb2tlLXFhIiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIiwicHJOdW1iZXIiOjE1OSwiY29tbWl0U2hhIjoiYWI4NzdkZjU3ZjViOWNjMGFiNDYxNzFlMjM5MmNiZjhiZTNlOTg0MCIsInByb3ZpZGVyIjoiZ2l0bGFiIiwiaG9zdG5hbWUiOiJnaXRsYWIuY29tIn19" 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 ab877df57f5b9cc0ab46171e2392cbf8be3e9840. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
ghost1 commented 2026-06-12 05:47:42 +00:00 (Migrated from gitlab.com)

Recovery skipped after hash captured

Medium Severity

isPostSignBroadcastFailure only treats broadcast timeout and a few network strings as recoverable once installSignedTxHashCapture has a hash. Other post-sign Error values skip the recovering poll path, so the mutation fails, UI re-enables swap, and the user may submit again while the first signed tx can still land on-chain.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ab877df57f. Configure here.

### Recovery skipped after hash captured **Medium Severity** <!-- DESCRIPTION START --> `isPostSignBroadcastFailure` only treats broadcast timeout and a few network strings as recoverable once `installSignedTxHashCapture` has a hash. Other post-sign `Error` values skip the `recovering` poll path, so the mutation fails, UI re-enables swap, and the user may submit again while the first signed tx can still land on-chain. <!-- DESCRIPTION END --> <!-- BUGBOT_BUG_ID: 814844d2-2d46-498b-bc80-a3d79d1661e0 --> <!-- LOCATIONS START frontend-dapp/src/services/terraclassic/terraBroadcast.ts#L38-L45 frontend-dapp/src/services/terraclassic/terraBroadcast.ts#L144-L152 LOCATIONS END --> <details> <summary>Additional Locations (1)</summary> - [`frontend-dapp/src/services/terraclassic/terraBroadcast.ts#L144-L152`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-//blob/ab877df57f5b9cc0ab46171e2392cbf8be3e9840/frontend-dapp/src/services/terraclassic/terraBroadcast.ts#L144-L152) </details> <div><a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9DVVJTT1IiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OjMxMTZmZDEwLTUyZmYtNDk3NC04ODUyLTkxYjA2YzRjYTk4NyIsImVuY3J5cHRpb25LZXkiOiJiVk8xQWpRcDBmcnNmX0hCNldhM183TFdFUG9GN0dtY2NPRm1vUEMwWW8wIiwiYnJhbmNoIjoiZmVhdC8zNjgtYnJvYWRjYXN0LXJlY292ZXJ5LXNtb2tlLXFhIiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIn19" 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=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9XRUIiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OjMxMTZmZDEwLTUyZmYtNDk3NC04ODUyLTkxYjA2YzRjYTk4NyIsImVuY3J5cHRpb25LZXkiOiJiVk8xQWpRcDBmcnNmX0hCNldhM183TFdFUG9GN0dtY2NPRm1vUEMwWW8wIiwiYnJhbmNoIjoiZmVhdC8zNjgtYnJvYWRjYXN0LXJlY292ZXJ5LXNtb2tlLXFhIiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIiwicHJOdW1iZXIiOjE1OSwiY29tbWl0U2hhIjoiYWI4NzdkZjU3ZjViOWNjMGFiNDYxNzFlMjM5MmNiZjhiZTNlOTg0MCIsInByb3ZpZGVyIjoiZ2l0bGFiIiwiaG9zdG5hbWUiOiJnaXRsYWIuY29tIn19" 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 ab877df57f5b9cc0ab46171e2392cbf8be3e9840. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
ghost1 commented 2026-06-12 05:50:07 +00:00 (Migrated from gitlab.com)

changed this line in version 2 of the diff

changed this line in [version 2 of the diff](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/159/diffs?diff_id=1848275962&start_sha=ab877df57f5b9cc0ab46171e2392cbf8be3e9840#88a78c528e591acd067c08a37205eb540c7324f2_45_39)
ghost1 commented 2026-06-12 05:50:08 +00:00 (Migrated from gitlab.com)

added 1 commit

  • 517f66da - fix: recover on any post-sign broadcast failure (#368)

Compare with previous version

added 1 commit <ul><li>517f66da - fix: recover on any post-sign broadcast failure (#368)</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/159/diffs?diff_id=1848275962&start_sha=ab877df57f5b9cc0ab46171e2392cbf8be3e9840)
ghost1 commented 2026-06-12 05:50:21 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-12 05:50:28 +00:00 (Migrated from gitlab.com)

resolved all threads

resolved all threads
PlasticDigits commented 2026-06-12 05:50:37 +00:00 (Migrated from gitlab.com)

Severity: Medium

Issue: Post-sign recovery is gated on a narrow isPostSignBroadcastFailure allowlist. After installSignedTxHashCapture records a hash, many real RPC failures (HTTP 5xx, ABCI/mempool reject strings, JSON parse errors, etc.) do not match the timeout/network regex and skip the recovering poll path.

Impact: The mutation rejects, useTerraBroadcastMutation resets phase in onSettled, and the Swap CTA re-enables while the already-signed tx can still be accepted by the network — duplicate swap / unintended second execution (user fund loss). A hostile or flaky RPC can return a non-matching error after a successful submit to surface this window.

Evidence: Hash capture runs before original(endpoint, txRaw) returns (terraWalletSignTxRaw.ts), but recovery only runs when the caught error is the exact broadcast-timeout string or matches /failed to fetch|networkerror|network error/i (terraBroadcast.ts L39–45, L145–152). All other post-sign errors rethrow immediately.

**Severity: Medium** **Issue:** Post-sign recovery is gated on a narrow `isPostSignBroadcastFailure` allowlist. After `installSignedTxHashCapture` records a hash, many real RPC failures (HTTP 5xx, ABCI/mempool reject strings, JSON parse errors, etc.) do not match the timeout/network regex and skip the `recovering` poll path. **Impact:** The mutation rejects, `useTerraBroadcastMutation` resets phase in `onSettled`, and the Swap CTA re-enables while the already-signed tx can still be accepted by the network — duplicate swap / unintended second execution (user fund loss). A hostile or flaky RPC can return a non-matching error after a successful submit to surface this window. **Evidence:** Hash capture runs before `original(endpoint, txRaw)` returns (`terraWalletSignTxRaw.ts`), but recovery only runs when the caught error is the exact broadcast-timeout string or matches `/failed to fetch|networkerror|network error/i` (`terraBroadcast.ts` L39–45, L145–152). All other post-sign errors rethrow immediately.
PlasticDigits commented 2026-06-12 05:50:39 +00:00 (Migrated from gitlab.com)

Severity: Medium

Issue: pollTerraTxRecovery aborts the recovery window on the first pollTx error that is not a "tx not found" string, including transient network/LCD failures.

Impact: While the UI is in recovering (CTA disabled), a single poll network error ends recovery, the mutation fails, and the user can submit again even though the signed broadcast may already be in the mempool or confirmed — duplicate execution risk that this MR is meant to eliminate.

Evidence: Only not found / tx not found errors are retried until deadlineUnix; any other Error is rethrown immediately (L31–33), propagating to broadcastTerraExecuteContracts and resetting broadcast UI state via onSettled.

**Severity: Medium** **Issue:** `pollTerraTxRecovery` aborts the recovery window on the first `pollTx` error that is not a "tx not found" string, including transient network/LCD failures. **Impact:** While the UI is in `recovering` (CTA disabled), a single poll network error ends recovery, the mutation fails, and the user can submit again even though the signed broadcast may already be in the mempool or confirmed — duplicate execution risk that this MR is meant to eliminate. **Evidence:** Only `not found` / `tx not found` errors are retried until `deadlineUnix`; any other `Error` is rethrown immediately (L31–33), propagating to `broadcastTerraExecuteContracts` and resetting broadcast UI state via `onSettled`.
PlasticDigits commented 2026-06-12 05:50:46 +00:00 (Migrated from gitlab.com)

Security review — MR !877

Commit reviewed: ab877df57f5b9cc0ab46171e2392cbf8be3e9840

Scope: Post-sign Terra broadcast recovery (terraBroadcast.ts, terraWalletSignTxRaw.ts, terraTxRecoveryPoll.ts, terraMsgDeadline.ts), recovery UX (TerraBroadcastPendingLink, useTerraBroadcastMutation, terraBroadcastUi), Playwright terra-broadcast-recovery.spec.ts, QA smoke wiring (smoke-deploy-env.sh, start-qa.sh, make smoke-pool-swap), and related docs/skills.

Outcome: FINDINGS: 2 medium

Both findings concern incomplete post-sign recovery allowing a second swap submission while a signed transaction may still execute (duplicate-execution / user fund loss). No high/critical issues, injection, authz bypass, secret leakage, or SSRF/XSS paths identified in this diff. QA smoke additions are read-only LCD queries sourced from operator-controlled deploy artifacts (same trust model as existing verify-deploy.sh stamp sourcing).

Inline threads:

  1. frontend-dapp/src/services/terraclassic/terraBroadcast.ts — narrow isPostSignBroadcastFailure allowlist skips recovery for many post-sign RPC errors after hash capture.
  2. frontend-dapp/src/services/terraclassic/terraTxRecoveryPoll.ts — recovery poll aborts on transient non–not-found errors instead of holding until the msg deadline.

Label block:security applied.

## Security review — MR !877 **Commit reviewed:** `ab877df57f5b9cc0ab46171e2392cbf8be3e9840` **Scope:** Post-sign Terra broadcast recovery (`terraBroadcast.ts`, `terraWalletSignTxRaw.ts`, `terraTxRecoveryPoll.ts`, `terraMsgDeadline.ts`), recovery UX (`TerraBroadcastPendingLink`, `useTerraBroadcastMutation`, `terraBroadcastUi`), Playwright `terra-broadcast-recovery.spec.ts`, QA smoke wiring (`smoke-deploy-env.sh`, `start-qa.sh`, `make smoke-pool-swap`), and related docs/skills. **Outcome:** `FINDINGS: 2` medium Both findings concern incomplete post-sign recovery allowing a second swap submission while a signed transaction may still execute (duplicate-execution / user fund loss). No high/critical issues, injection, authz bypass, secret leakage, or SSRF/XSS paths identified in this diff. QA smoke additions are read-only LCD queries sourced from operator-controlled deploy artifacts (same trust model as existing `verify-deploy.sh` stamp sourcing). **Inline threads:** 1. `frontend-dapp/src/services/terraclassic/terraBroadcast.ts` — narrow `isPostSignBroadcastFailure` allowlist skips recovery for many post-sign RPC errors after hash capture. 2. `frontend-dapp/src/services/terraclassic/terraTxRecoveryPoll.ts` — recovery poll aborts on transient non–not-found errors instead of holding until the msg deadline. Label `block:security` applied.
ghost1 commented 2026-06-12 05:50:52 +00:00 (Migrated from gitlab.com)
Stale Security Review comment

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issue.

Reviewed by Cursor Bugbot for commit 517f66daa2. Configure here.

<details> <summary>Stale Security Review comment</summary> <!-- BUGBOT_REVIEW --> Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.<!-- 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 517f66daa24f7d49a3686db8cce8b9ad62e0c1fc. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> </details>
ghost1 commented 2026-06-12 05:50:53 +00:00 (Migrated from gitlab.com)

Over-broad post-sign recovery trigger

Medium Severity

isPostSignBroadcastFailure now treats any broadcast error as recoverable whenever signedTxHash was captured, but the hash is set as soon as RpcClient.broadcastTx runs, including immediate RPC rejections. Definite failures (e.g. sequence mismatch) enter recovering and pollTerraTxRecovery until the msg deadline instead of surfacing the original error promptly.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 517f66daa2. Configure here.

### Over-broad post-sign recovery trigger **Medium Severity** <!-- DESCRIPTION START --> `isPostSignBroadcastFailure` now treats any broadcast error as recoverable whenever `signedTxHash` was captured, but the hash is set as soon as `RpcClient.broadcastTx` runs, including immediate RPC rejections. Definite failures (e.g. sequence mismatch) enter `recovering` and `pollTerraTxRecovery` until the msg deadline instead of surfacing the original error promptly. <!-- DESCRIPTION END --> <!-- BUGBOT_BUG_ID: d7485cc0-cdfe-4146-a807-0cdac548fdb8 --> <!-- LOCATIONS START frontend-dapp/src/services/terraclassic/terraBroadcast.ts#L38-L41 LOCATIONS END --> <div><a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9DVVJTT1IiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OmNmNTYwYzkzLWFlMDAtNDQ1ZS05M2YxLWJiYjQwMWFkNGNmYyIsImVuY3J5cHRpb25LZXkiOiJQWGlBWE5WbFc5bWJBa0tmUC1RempLMU5MU2RucUFFNnB3SmJrenBxankwIiwiYnJhbmNoIjoiZmVhdC8zNjgtYnJvYWRjYXN0LXJlY292ZXJ5LXNtb2tlLXFhIiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIn19" 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=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9XRUIiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OmNmNTYwYzkzLWFlMDAtNDQ1ZS05M2YxLWJiYjQwMWFkNGNmYyIsImVuY3J5cHRpb25LZXkiOiJQWGlBWE5WbFc5bWJBa0tmUC1RempLMU5MU2RucUFFNnB3SmJrenBxankwIiwiYnJhbmNoIjoiZmVhdC8zNjgtYnJvYWRjYXN0LXJlY292ZXJ5LXNtb2tlLXFhIiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIiwicHJOdW1iZXIiOjE1OSwiY29tbWl0U2hhIjoiNTE3ZjY2ZGFhMjRmN2Q0OWEzNjg2ZGI4Y2NlOGI5YWQ2MmUwYzFmYyIsInByb3ZpZGVyIjoiZ2l0bGFiIiwiaG9zdG5hbWUiOiJnaXRsYWIuY29tIn19" 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 517f66daa24f7d49a3686db8cce8b9ad62e0c1fc. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
ghost1 commented 2026-06-12 05:53:36 +00:00 (Migrated from gitlab.com)

added 1 commit

  • b6265934 - fix: narrow post-sign broadcast recovery to timeout/network errors (#368)

Compare with previous version

added 1 commit <ul><li>b6265934 - fix: narrow post-sign broadcast recovery to timeout/network errors (#368)</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/159/diffs?diff_id=1848279382&start_sha=517f66daa24f7d49a3686db8cce8b9ad62e0c1fc)
ghost1 commented 2026-06-12 05:53:51 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
Brouie commented 2026-06-12 06:27:18 +00:00 (Migrated from gitlab.com)

mentioned in issue #368

mentioned in issue #368
PlasticDigits commented 2026-06-12 08:32:52 +00:00 (Migrated from gitlab.com)

added 1 commit

  • c02f1030 - fix: harden post-sign broadcast recovery against duplicate swap (#368)

Compare with previous version

added 1 commit <ul><li>c02f1030 - fix: harden post-sign broadcast recovery against duplicate swap (#368)</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/159/diffs?diff_id=1848543066&start_sha=b6265934579f8761af39041f27109accbdc88f5b)
PlasticDigits commented 2026-06-12 08:32:56 +00:00 (Migrated from gitlab.com)

Security fixes (commit c02f103)

Finding Severity Fix
Narrow isPostSignBroadcastFailure allowlist skipped recovery for many post-sign RPC errors after hash capture Medium Recover on any post-sign failure once hash is captured, except definite CheckTx/mempool rejections (isDefiniteBroadcastRejection: sequence mismatch, signature/chain-id/decode/fee rejections).
pollTerraTxRecovery aborted on first non–not-found poll error Medium Retry all poll errors until msg deadline; only TerraOnChainBroadcastFailure (included tx with non-zero code) exits early.
Over-broad recovery trigger (517f66d) sent definite rejections into recovery poll Medium Resolved by the CheckTx rejection gate above — sequence mismatch and similar errors fail fast without entering recovering.
Recovery expiry message not preserved through handleBroadcastError Low Already preserved in handleBroadcastError; added unit regressions for expiry copy end-to-end.

Tests: terraBroadcastRecovery.test.ts (15 passed).

Removing block:security — all low+ findings addressed.

## Security fixes (commit `c02f103`) | Finding | Severity | Fix | |---------|----------|-----| | Narrow `isPostSignBroadcastFailure` allowlist skipped recovery for many post-sign RPC errors after hash capture | Medium | Recover on any post-sign failure once hash is captured, **except** definite CheckTx/mempool rejections (`isDefiniteBroadcastRejection`: sequence mismatch, signature/chain-id/decode/fee rejections). | | `pollTerraTxRecovery` aborted on first non–not-found poll error | Medium | Retry all poll errors until msg deadline; only `TerraOnChainBroadcastFailure` (included tx with non-zero code) exits early. | | Over-broad recovery trigger (517f66d) sent definite rejections into recovery poll | Medium | Resolved by the CheckTx rejection gate above — sequence mismatch and similar errors fail fast without entering `recovering`. | | Recovery expiry message not preserved through `handleBroadcastError` | Low | Already preserved in `handleBroadcastError`; added unit regressions for expiry copy end-to-end. | **Tests:** `terraBroadcastRecovery.test.ts` (15 passed). Removing `block:security` — all low+ findings addressed.
ghost1 commented 2026-06-12 08:33:06 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-12 08:34:30 +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 c02f103081. 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=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9BTExfSU5fQ1VSU09SIiwiZGF0YSI6eyJyZWRpc0tleSI6ImJ1Z2JvdC1tdWx0aTphN2JiMzI3Yy1kYmY5LTRkZDAtOTFlMy0xZDc1Y2JhYTBhZTUiLCJlbmNyeXB0aW9uS2V5IjoiMHEyQTZfUFB4TVRvN05TbWpPcUd3MWMwQ0VEQm9wbDNDcGJza1VveXE4MCIsImJyYW5jaCI6ImZlYXQvMzY4LWJyb2FkY2FzdC1yZWNvdmVyeS1zbW9rZS1xYSIsInJlcG9Pd25lciI6IlBsYXN0aWNEaWdpdHMiLCJyZXBvTmFtZSI6ImNsOHktZGV4LXRlcnJhY2xhc3NpYyJ9fQ" 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 c02f103081663affe9adfefa9e4f1c153a164f3f. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
ghost1 commented 2026-06-12 08:34:31 +00:00 (Migrated from gitlab.com)

Cache duplicate skips recovery polling

Medium Severity

isDefiniteBroadcastRejection treats RPC errors matching tx already exists in cache as non-recoverable CheckTx failures. That response usually means the signed transaction is already in the mempool. With a captured hash, recovery is skipped, the mutation fails and the Swap CTA re-enables while the original tx may still confirm—undermining post-sign recovery and duplicate-execution protection.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c02f103081. Configure here.

### Cache duplicate skips recovery polling **Medium Severity** <!-- DESCRIPTION START --> `isDefiniteBroadcastRejection` treats RPC errors matching `tx already exists in cache` as non-recoverable CheckTx failures. That response usually means the signed transaction is already in the mempool. With a captured hash, recovery is skipped, the mutation fails and the Swap CTA re-enables while the original tx may still confirm—undermining post-sign recovery and duplicate-execution protection. <!-- DESCRIPTION END --> <!-- BUGBOT_BUG_ID: db89a3aa-6981-4d71-b9d5-7c87a54bb115 --> <!-- LOCATIONS START frontend-dapp/src/services/terraclassic/terraBroadcast.ts#L49-L50 LOCATIONS END --> <div><a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9DVVJTT1IiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90Ojg0ZDU1OWE3LTk3YjgtNDY3Ny05ZjFiLWQ1OTJmNzQ4ZGIwMyIsImVuY3J5cHRpb25LZXkiOiJFWVJ0WkhHV0F6V3FxWnFXMUVrOXh2SVlQM0dveGNoMU1lTk9EMFlMR2YwIiwiYnJhbmNoIjoiZmVhdC8zNjgtYnJvYWRjYXN0LXJlY292ZXJ5LXNtb2tlLXFhIiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIn19" 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=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9XRUIiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90Ojg0ZDU1OWE3LTk3YjgtNDY3Ny05ZjFiLWQ1OTJmNzQ4ZGIwMyIsImVuY3J5cHRpb25LZXkiOiJFWVJ0WkhHV0F6V3FxWnFXMUVrOXh2SVlQM0dveGNoMU1lTk9EMFlMR2YwIiwiYnJhbmNoIjoiZmVhdC8zNjgtYnJvYWRjYXN0LXJlY292ZXJ5LXNtb2tlLXFhIiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIiwicHJOdW1iZXIiOjE1OSwiY29tbWl0U2hhIjoiYzAyZjEwMzA4MTY2M2FmZmU5YWRmZWZhOWU0ZjFjMTUzYTE2NGYzZiIsInByb3ZpZGVyIjoiZ2l0bGFiIiwiaG9zdG5hbWUiOiJnaXRsYWIuY29tIn19" 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 c02f103081663affe9adfefa9e4f1c153a164f3f. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
PlasticDigits commented 2026-06-12 08:36:23 +00:00 (Migrated from gitlab.com)

Severity: Medium

Issue: isDefiniteBroadcastRejection treats tx already exists in cache as a non-recoverable CheckTx failure. That RPC response indicates the signed transaction is already in the mempool cache (duplicate submit), not that the tx was rejected.

Impact: After installSignedTxHashCapture records the hash, this error skips the recovering poll path. The mutation rejects, useTerraBroadcastMutation resets in onSettled, and the Swap/Limit CTA re-enables while the original signed tx may still confirm — duplicate execution / user fund loss. A flaky RPC or client rebroadcast can surface this window.

Evidence: Hash is captured before original(endpoint, txRaw) returns (terraWalletSignTxRaw.ts L36–38). isPostSignBroadcastFailure returns false when isDefiniteBroadcastRejection matches (L58–61), so recovery is skipped. This pattern is the inverse of the #368 fix intent: the tx is already submitted and should be polled, not treated as a definite rejection.

**Severity: Medium** **Issue:** `isDefiniteBroadcastRejection` treats `tx already exists in cache` as a non-recoverable CheckTx failure. That RPC response indicates the signed transaction is already in the mempool cache (duplicate submit), not that the tx was rejected. **Impact:** After `installSignedTxHashCapture` records the hash, this error skips the `recovering` poll path. The mutation rejects, `useTerraBroadcastMutation` resets in `onSettled`, and the Swap/Limit CTA re-enables while the original signed tx may still confirm — duplicate execution / user fund loss. A flaky RPC or client rebroadcast can surface this window. **Evidence:** Hash is captured before `original(endpoint, txRaw)` returns (`terraWalletSignTxRaw.ts` L36–38). `isPostSignBroadcastFailure` returns false when `isDefiniteBroadcastRejection` matches (L58–61), so recovery is skipped. This pattern is the inverse of the #368 fix intent: the tx is already submitted and should be polled, not treated as a definite rejection.
ghost1 commented 2026-06-12 08:36:28 +00:00 (Migrated from gitlab.com)

changed this line in version 5 of the diff

changed this line in [version 5 of the diff](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/159/diffs?diff_id=1848550478&start_sha=c02f103081663affe9adfefa9e4f1c153a164f3f#88a78c528e591acd067c08a37205eb540c7324f2_50_49)
ghost1 commented 2026-06-12 08:36:29 +00:00 (Migrated from gitlab.com)

changed this line in version 5 of the diff

changed this line in [version 5 of the diff](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/159/diffs?diff_id=1848550478&start_sha=c02f103081663affe9adfefa9e4f1c153a164f3f#88a78c528e591acd067c08a37205eb540c7324f2_50_49)
ghost1 commented 2026-06-12 08:36:29 +00:00 (Migrated from gitlab.com)

added 1 commit

  • 03904029 - fix: poll recovery when mempool reports tx already in cache (#368)

Compare with previous version

added 1 commit <ul><li>03904029 - fix: poll recovery when mempool reports tx already in cache (#368)</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/159/diffs?diff_id=1848550478&start_sha=c02f103081663affe9adfefa9e4f1c153a164f3f)
PlasticDigits commented 2026-06-12 08:36:30 +00:00 (Migrated from gitlab.com)

Security review — MR !877

Commit reviewed: c02f103081663affe9adfefa9e4f1c153a164f3f

Scope: Post-sign Terra broadcast recovery (terraBroadcast.ts, terraWalletSignTxRaw.ts, terraTxRecoveryPoll.ts, terraMsgDeadline.ts), recovery UX (TerraBroadcastPendingLink, useTerraBroadcastMutation, terraBroadcastUi), Playwright terra-broadcast-recovery.spec.ts, QA smoke wiring (smoke-deploy-env.sh, start-qa.sh, make smoke-pool-swap), and related docs/skills.

Outcome: FINDINGS: 1 medium

The two prior medium findings (narrow post-sign recovery allowlist; poll abort on transient errors) are resolved in c02f103 — broad recovery after hash capture with isDefiniteBroadcastRejection gate, and poll retries until msg deadline. One new medium issue remains in the rejection classifier.

Inline threads:

  1. frontend-dapp/src/services/terraclassic/terraBroadcast.ts L50 — tx already exists in cache misclassified as definite rejection; skips recovery while tx may already be in mempool.

No high/critical issues, injection, authz bypass, secret leakage, or SSRF/XSS paths identified in this diff. QA smoke additions are read-only LCD queries sourced from operator-controlled deploy artifacts (same trust model as existing verify-deploy.sh stamp sourcing).

## Security review — MR !877 **Commit reviewed:** `c02f103081663affe9adfefa9e4f1c153a164f3f` **Scope:** Post-sign Terra broadcast recovery (`terraBroadcast.ts`, `terraWalletSignTxRaw.ts`, `terraTxRecoveryPoll.ts`, `terraMsgDeadline.ts`), recovery UX (`TerraBroadcastPendingLink`, `useTerraBroadcastMutation`, `terraBroadcastUi`), Playwright `terra-broadcast-recovery.spec.ts`, QA smoke wiring (`smoke-deploy-env.sh`, `start-qa.sh`, `make smoke-pool-swap`), and related docs/skills. **Outcome:** `FINDINGS: 1` medium The two prior medium findings (narrow post-sign recovery allowlist; poll abort on transient errors) are **resolved** in `c02f103` — broad recovery after hash capture with `isDefiniteBroadcastRejection` gate, and poll retries until msg deadline. One new medium issue remains in the rejection classifier. **Inline threads:** 1. `frontend-dapp/src/services/terraclassic/terraBroadcast.ts` L50 — `tx already exists in cache` misclassified as definite rejection; skips recovery while tx may already be in mempool. No high/critical issues, injection, authz bypass, secret leakage, or SSRF/XSS paths identified in this diff. QA smoke additions are read-only LCD queries sourced from operator-controlled deploy artifacts (same trust model as existing `verify-deploy.sh` stamp sourcing).
ghost1 commented 2026-06-12 08:36:45 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-12 11:05:07 +00:00 (Migrated from gitlab.com)

added 1 commit

  • 1f36acb5 - test: regression for mempool cache duplicate recovery (#368)

Compare with previous version

added 1 commit <ul><li>1f36acb5 - test: regression for mempool cache duplicate recovery (#368)</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/159/diffs?diff_id=1848867662&start_sha=03904029f165f8c27941f859a8bb535f5fd8cf4c)
PlasticDigits commented 2026-06-12 11:05:13 +00:00 (Migrated from gitlab.com)

Security fixes (commits 0390402, 1f36acb)

Finding Severity Fix
Narrow isPostSignBroadcastFailure allowlist skipped recovery for many post-sign RPC errors after hash capture Medium Recover on any post-sign failure once hash is captured, except definite CheckTx/mempool rejections (isDefiniteBroadcastRejection: sequence mismatch, signature/chain-id/decode/fee rejections).
pollTerraTxRecovery aborted on first non–not-found poll error Medium Retry all poll errors until msg deadline; only TerraOnChainBroadcastFailure (included tx with non-zero code) exits early.
Over-broad recovery trigger sent definite rejections into recovery poll Medium Resolved by the CheckTx rejection gate — sequence mismatch and similar errors fail fast without entering recovering.
Recovery expiry message not preserved through handleBroadcastError Low Preserved in handleBroadcastError; unit regressions for expiry copy end-to-end.
tx already exists in cache misclassified as definite rejection; skipped recovery while tx may already be in mempool Medium Removed from isDefiniteBroadcastRejection (0390402); mempool duplicate now enters recovering poll. Regression test added (1f36acb).

Tests: terraBroadcastRecovery.test.ts (16 passed).

All low+ findings addressed. Removing block:security.

## Security fixes (commits `0390402`, `1f36acb`) | Finding | Severity | Fix | |---------|----------|-----| | Narrow `isPostSignBroadcastFailure` allowlist skipped recovery for many post-sign RPC errors after hash capture | Medium | Recover on any post-sign failure once hash is captured, **except** definite CheckTx/mempool rejections (`isDefiniteBroadcastRejection`: sequence mismatch, signature/chain-id/decode/fee rejections). | | `pollTerraTxRecovery` aborted on first non–not-found poll error | Medium | Retry all poll errors until msg deadline; only `TerraOnChainBroadcastFailure` (included tx with non-zero code) exits early. | | Over-broad recovery trigger sent definite rejections into recovery poll | Medium | Resolved by the CheckTx rejection gate — sequence mismatch and similar errors fail fast without entering `recovering`. | | Recovery expiry message not preserved through `handleBroadcastError` | Low | Preserved in `handleBroadcastError`; unit regressions for expiry copy end-to-end. | | `tx already exists in cache` misclassified as definite rejection; skipped recovery while tx may already be in mempool | Medium | Removed from `isDefiniteBroadcastRejection` (`0390402`); mempool duplicate now enters `recovering` poll. Regression test added (`1f36acb`). | **Tests:** `terraBroadcastRecovery.test.ts` (16 passed). All low+ findings addressed. Removing `block:security`.
ghost1 commented 2026-06-12 11:05:23 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-12 11:08:59 +00:00 (Migrated from gitlab.com)

Security review — MR !877

Commit reviewed: 1f36acb5ad484330d17bd945adb58ea761975eb0

Scope: Post-sign Terra broadcast recovery (terraBroadcast.ts, terraWalletSignTxRaw.ts, terraTxRecoveryPoll.ts, terraMsgDeadline.ts), recovery UX (TerraBroadcastPendingLink, useTerraBroadcastMutation, terraBroadcastUi), Playwright terra-broadcast-recovery.spec.ts, QA smoke wiring (smoke-deploy-env.sh, start-qa.sh, make smoke-pool-swap), and related docs/skills.

Outcome: FINDINGS: 0 medium+

Security review: no medium+ findings on this diff.

Re-checked prior threads against the current head:

Prior finding Status on 1f36acb
Narrow isPostSignBroadcastFailure allowlist skipped recovery after hash capture Resolved — broad post-sign recovery once signedTxHash is set, gated by isDefiniteBroadcastRejection (sequence/signature/chain/decode/fee rejections only).
pollTerraTxRecovery aborted on transient poll errors Resolved — retries until msg deadline; only TerraOnChainBroadcastFailure (included tx, non-zero code) exits early.
Over-broad recovery sent definite CheckTx rejections into poll Resolved — isDefiniteBroadcastRejection fast-fail; unit test for sequence mismatch.
tx already exists in cache misclassified as definite rejection Resolved (0390402, regression in 1f36acb) — mempool duplicate now enters recovering and polls.

Additional checks (no issues):

  • Recovery path does not re-broadcast signed bytes; swapMutation.isPending keeps Swap CTA disabled through recovering.
  • txHash for explorer links is derived from signed TxRaw (hex); no XSS/injection sink.
  • QA smoke additions are operator-run, read-only LCD queries; pair/token resolved from deploy stamp (same trust model as existing verify-deploy.sh).
  • No new secret logging, authz bypass, SSRF, or dependency supply-chain changes in the diff.

Inline threads: none (no current medium+ findings).

## Security review — MR !877 **Commit reviewed:** `1f36acb5ad484330d17bd945adb58ea761975eb0` **Scope:** Post-sign Terra broadcast recovery (`terraBroadcast.ts`, `terraWalletSignTxRaw.ts`, `terraTxRecoveryPoll.ts`, `terraMsgDeadline.ts`), recovery UX (`TerraBroadcastPendingLink`, `useTerraBroadcastMutation`, `terraBroadcastUi`), Playwright `terra-broadcast-recovery.spec.ts`, QA smoke wiring (`smoke-deploy-env.sh`, `start-qa.sh`, `make smoke-pool-swap`), and related docs/skills. **Outcome:** `FINDINGS: 0` medium+ Security review: **no medium+ findings** on this diff. Re-checked prior threads against the current head: | Prior finding | Status on `1f36acb` | |---------------|---------------------| | Narrow `isPostSignBroadcastFailure` allowlist skipped recovery after hash capture | **Resolved** — broad post-sign recovery once `signedTxHash` is set, gated by `isDefiniteBroadcastRejection` (sequence/signature/chain/decode/fee rejections only). | | `pollTerraTxRecovery` aborted on transient poll errors | **Resolved** — retries until msg deadline; only `TerraOnChainBroadcastFailure` (included tx, non-zero code) exits early. | | Over-broad recovery sent definite CheckTx rejections into poll | **Resolved** — `isDefiniteBroadcastRejection` fast-fail; unit test for sequence mismatch. | | `tx already exists in cache` misclassified as definite rejection | **Resolved** (`0390402`, regression in `1f36acb`) — mempool duplicate now enters `recovering` and polls. | **Additional checks (no issues):** - Recovery path does not re-broadcast signed bytes; `swapMutation.isPending` keeps Swap CTA disabled through `recovering`. - `txHash` for explorer links is derived from signed `TxRaw` (hex); no XSS/injection sink. - QA smoke additions are operator-run, read-only LCD queries; pair/token resolved from deploy stamp (same trust model as existing `verify-deploy.sh`). - No new secret logging, authz bypass, SSRF, or dependency supply-chain changes in the diff. **Inline threads:** none (no current medium+ findings).
PlasticDigits commented 2026-06-12 11:16:53 +00:00 (Migrated from gitlab.com)

changed this line in version 7 of the diff

changed this line in version 7 of the diff
PlasticDigits commented 2026-06-12 11:16:53 +00:00 (Migrated from gitlab.com)

changed this file in version 7 of the diff

changed this file in version 7 of the diff
PlasticDigits commented 2026-06-12 11:16:53 +00:00 (Migrated from gitlab.com)

changed this line in version 7 of the diff

changed this line in [version 7 of the diff](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/159/diffs?diff_id=1848890614&start_sha=1f36acb5ad484330d17bd945adb58ea761975eb0#8ec5b7817a66b52114ab84d287254f35585d818d_41_27)
PlasticDigits commented 2026-06-12 11:16:54 +00:00 (Migrated from gitlab.com)

changed this line in version 7 of the diff

changed this line in version 7 of the diff
PlasticDigits commented 2026-06-12 11:16:54 +00:00 (Migrated from gitlab.com)

added 55 commits

  • 1f36acb5...0cf04e53 - 54 commits from branch main
  • 337f6bec - Merge origin/main into feat/368-broadcast-recovery-smoke-qa

Compare with previous version

added 55 commits <ul><li>1f36acb5...0cf04e53 - 54 commits from branch <code>main</code></li><li>337f6bec - Merge origin/main into feat/368-broadcast-recovery-smoke-qa</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/159/diffs?diff_id=1848890614&start_sha=1f36acb5ad484330d17bd945adb58ea761975eb0)
PlasticDigits commented 2026-06-12 11:16:55 +00:00 (Migrated from gitlab.com)

Resolved merge conflicts with main (337f6be).

Broadcast recovery: Took main's split sign/broadcast path (#359) — signTerraTxRaw, pollTxUntilRecoveryDeadline, terraBroadcastPendingStatusMessage — replacing the branch's older RpcClient intercept approach. Mempool-cache recovery behavior is already covered on main.

#368 retained: smoke QA wiring (make smoke-pool-swap, start-qa), E2E spec, and docs/skills updates.

Sanity: 13 unit tests pass (terraBroadcastRecovery, terraBroadcastUi, TerraBroadcastPendingLink).

Resolved merge conflicts with `main` (337f6be). **Broadcast recovery:** Took main's split sign/broadcast path (#359) — `signTerraTxRaw`, `pollTxUntilRecoveryDeadline`, `terraBroadcastPendingStatusMessage` — replacing the branch's older RpcClient intercept approach. Mempool-cache recovery behavior is already covered on main. **#368 retained:** smoke QA wiring (`make smoke-pool-swap`, `start-qa`), E2E spec, and docs/skills updates. **Sanity:** 13 unit tests pass (`terraBroadcastRecovery`, `terraBroadcastUi`, `TerraBroadcastPendingLink`).
ghost1 commented 2026-06-12 11:17:14 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-12 11:19:46 +00:00 (Migrated from gitlab.com)

Security review — MR !877

Commit reviewed: 337f6becc2afb9c8a1e94a2cdceb8b1bed1215f8

Scope: Post-merge #368 wiring — QA smoke (scripts/lib/smoke-deploy-env.sh, make smoke-pool-swap, scripts/qa/start-qa.sh), Playwright terra-broadcast-recovery.spec.ts + playwright.config.ts timeout/glob, orphaned services/terraclassic/terraMsgDeadline.ts, test mock tweak, docs/skills. Broadcast-recovery implementation (terraBroadcast.ts, terraWalletSignTxRaw.ts, terraTxRecoveryPoll.ts, utils/terraMsgDeadline.ts) is on main (#359); exercised by the new E2E but not modified in this diff.

Outcome: FINDINGS: 0 medium+

Security review: no medium+ findings on this diff.

Re-check of prior threads (against current head):

Prior finding Status on 337f6be
Narrow post-sign recovery allowlist N/A in diff — resolved on main (shouldRecoverPostSignBroadcast + isDefiniteBroadcastRejection gate)
Poll abort on transient LCD errors N/A in diff — resolved on main (pollTxUntilRecoveryDeadline retries until deadline)
tx already exists in cache misclassified N/A in diff — resolved on main (pattern removed from rejection classifier)

Additional checks on this diff (no issues):

  • QA smoke: Operator-run, read-only LCD smart queries; PAIR_ADDR / OFFER_TOKEN resolved from deploy stamp or on-chain pool response (same trust model as existing verify-deploy.sh). No secrets echoed, no user-facing attack surface.
  • E2E spec: Playwright route intercept is test-only; does not ship to production.
  • VITE_TERRA_TX_BROADCAST_TIMEOUT_MS=3000: Playwright webServer env only; production builds unaffected.
  • services/terraclassic/terraMsgDeadline.ts: Dead duplicate (production uses utils/terraMsgDeadline.ts via resolveTerraTxRecoveryDeadlineUnix); docs reference wrong path — maintenance drift, not an exploitable sink.
  • No injection, authz bypass, secret leakage, SSRF/XSS, or new dependency supply-chain changes in the diff.

Inline threads: none (no current medium+ findings).

## Security review — MR !877 **Commit reviewed:** `337f6becc2afb9c8a1e94a2cdceb8b1bed1215f8` **Scope:** Post-merge #368 wiring — QA smoke (`scripts/lib/smoke-deploy-env.sh`, `make smoke-pool-swap`, `scripts/qa/start-qa.sh`), Playwright `terra-broadcast-recovery.spec.ts` + `playwright.config.ts` timeout/glob, orphaned `services/terraclassic/terraMsgDeadline.ts`, test mock tweak, docs/skills. Broadcast-recovery implementation (`terraBroadcast.ts`, `terraWalletSignTxRaw.ts`, `terraTxRecoveryPoll.ts`, `utils/terraMsgDeadline.ts`) is on `main` (#359); exercised by the new E2E but not modified in this diff. **Outcome:** `FINDINGS: 0` medium+ Security review: **no medium+ findings** on this diff. **Re-check of prior threads (against current head):** | Prior finding | Status on `337f6be` | |---------------|---------------------| | Narrow post-sign recovery allowlist | **N/A in diff** — resolved on `main` (`shouldRecoverPostSignBroadcast` + `isDefiniteBroadcastRejection` gate) | | Poll abort on transient LCD errors | **N/A in diff** — resolved on `main` (`pollTxUntilRecoveryDeadline` retries until deadline) | | `tx already exists in cache` misclassified | **N/A in diff** — resolved on `main` (pattern removed from rejection classifier) | **Additional checks on this diff (no issues):** - **QA smoke:** Operator-run, read-only LCD smart queries; `PAIR_ADDR` / `OFFER_TOKEN` resolved from deploy stamp or on-chain `pool` response (same trust model as existing `verify-deploy.sh`). No secrets echoed, no user-facing attack surface. - **E2E spec:** Playwright route intercept is test-only; does not ship to production. - **`VITE_TERRA_TX_BROADCAST_TIMEOUT_MS=3000`:** Playwright `webServer` env only; production builds unaffected. - **`services/terraclassic/terraMsgDeadline.ts`:** Dead duplicate (production uses `utils/terraMsgDeadline.ts` via `resolveTerraTxRecoveryDeadlineUnix`); docs reference wrong path — maintenance drift, not an exploitable sink. - No injection, authz bypass, secret leakage, SSRF/XSS, or new dependency supply-chain changes in the diff. **Inline threads:** none (no current medium+ findings).
PlasticDigits commented 2026-06-12 11:39:24 +00:00 (Migrated from gitlab.com)

mentioned in commit 8c641d165c

mentioned in commit 8c641d165c1ef6ad8380a18a7b28d2bb33297cdf
PlasticDigits (Migrated from gitlab.com) merged commit 8c641d165c into main 2026-06-12 11:39:24 +00:00
Brouie commented 2026-06-28 22:58:23 +00:00 (Migrated from gitlab.com)

mentioned in issue #337

mentioned in issue #337
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!877
No description provided.