Repository navigation
feat(frontend): prevent duplicate transfer submissions across retries and navigation - #295
Open
woahwhattheheck wants to merge 14 commits into
Open
woahwhattheheck wants to merge 14 commits into
woahwhattheheck wants to merge 14 commits into
Conversation
Bind an idempotency key to the exact transfer payload, persist only a safe operation reference in sessionStorage, and return the prior transfer on repeated submits with the same key. Refresh restores in-flight status; an edited payload mintes a new intent. Closes RemitFlow#277
Prevent duplicate transfer submissions across double-clicks, retries, refresh, and navigation by persisting a safe operation reference and reconciling in-flight intents without auto-resubmitting.
Prevent duplicate transfer submissions across double-clicks, retries, timeouts, refresh, and navigation by fingerprinting the exact payload, persisting only a safe operation reference, and reconciling in-flight intents without auto-resubmitting. Changed payloads mint a new intent. Closes RemitFlow#277
Canonicalize amounts without collisions, retain an opaque session fingerprint, and give each acknowledged transfer a fresh key. Reject reused keys with a conflicting payload and recover accepted transfers without a saved ID. Closes RemitFlow#277
Copy initial records before appending so a cleared storage session starts clean.
The existing localStorage API swallowed read/write failures. Two sequential creates with the same idempotency key could both resolve with different IDs while no transfer record was persisted. Require a readable array store before creating, distinguish missing storage from inaccessible/malformed/non-array storage, and propagate write failures as retryable unavailability. Keep the existing tolerant listing behavior. Persisted-key replay needs no write. Pending-key cleanup allows the same intent to retry after storage recovers. Send Money retains unknown status and the same intent key through refresh without a new provider integration. Validation: - Five new storage unit cases fail against the preceding API and pass here. - Required intent/API/App idempotency suites: 24/24 tests across 3 files. - App regression exercises the real local API, failed write, refresh and successful same-key retry; exactly one record is stored after recovery. - npm run build, API ESLint, owned-file Prettier and git diff --check pass. - Full npm test -- --maxWorkers=2: 471 pass, 12 fail, 483 total. - All 12 failing names reproduce in four unchanged test files at parent 18d6683 (10 pass / 12 fail). Files: offline-reconnect, send-money-form, send-money-precision, quote. No unrelated tests were weakened, skipped or deleted. Continues existing PR RemitFlow#295 / issue RemitFlow#277 on the original contribution branch. This remains the repository's local mock persistence; hosted CI and maintainer acceptance are separate pending checks.
Fail before addTransfer when the session operation journal cannot be read or its submitting reference cannot be saved. Keep the same in-memory intent for retry and retain best-effort late status/navigation updates. The earlier API persistence acknowledgement repair remains unchanged. Validation: - Full existing npm test selection: 492 pass / 12 fail / 504 in 50 files. - All 45 intent/API/App cases pass, including 21 added regressions. - Parent d27dc88 with identical final tests: 33 pass / 12 new failures; all 24 original controls still pass. The other 12 suite failures match exact names/files on that unchanged parent and were not modified. - Ordinary production builds pass on parent and repaired source. - Actual built App under unchanged Vite preview/security headers: 23/23 browser checks. Parent produces two local-demo transfers with distinct keys across reload when journal writes fail. Repair refuses twice, then saves one same-key retry and recovers that exact record on reload. - Four original desktop/phone captures included. No console/page errors or external page requests. No live-provider or settlement claim. No dependency manifests, contracts, API source or unrelated tests changed. Validation used Node 24.19 and a documented retained dependency composition, not a complete fresh locked install. Hosted CI, standalone lint/format and Lighthouse remain separate validation limits. Continues existing PR RemitFlow#295 / issue RemitFlow#277 and preserves original attribution.
Use the existing Stellar SDK SHA-256 helper when SubtleCrypto is unavailable, producing the same UTF-8-derived 128-bit keys as the normal path. Retain canonical payloads, nonces and current saved keys. Refuse automatic rebinding of unresolved legacy 32-bit fingerprints while allowing original-key status reconciliation and exact strong retries. The UI explains that no new attempt was submitted instead of rewriting the old operation. Pinned collision: the canonical USD/NGN 25-unit inputs for recipient-103w456@example.com and recipient-1p5osr6@example.com both produce legacy idem_489b5d95. The independent API payload check remains intact; no wrong-payment claim is made. Includes one focused existing-Vitest regression file for crypto equivalence, the collision, journal preservation and actual SendMoney legacy reconciliation. Local dependency execution is unavailable; the focused check will run on an isolated validation branch, not the full application matrix. All preceding source and attribution are preserved. Continues PR295 / issue277 without a new reward claim.
Use the actual account returned by connect(). AppContext handles rejected or timed-out connections by returning no account; the previous confirmation handler ignored that result and continued into transfer creation. Also check the committed canonical amount against a wallet first connected during confirmation. Both refusal paths run before fingerprint/key allocation, recovery-journal writes, or addTransfer. They preserve prior unknown operations and retry keys, close confirmation, and release the existing submission lock through finally. Exact fractional balances remain valid. Preserve all existing nonce/recovery/storage/generated-record fixes and fe08b70's SDK fallback and legacy-operation handling. Add five mounted-App wallet-admission cases using the existing wallet-service boundary with the real context, transfer API and storage. Existing success-path demo randomness is pinned to acceptance; no old behavioral assertion removed or skipped. Also correct the new crypto-recovery error query to include the warning-icon prefix rendered by ErrorMessage, retaining its complete-message, no-transfer and unchanged-journal assertions. Validation (Node24.19.0 / Vitest4.1.10): - New wallet cases against fc87181:4 failed because createTransfer was called once;1 exact-balance positive control passed.11 older cases were only deselected by the name filter. - Wallet candidate on fc87181:53/53 across the existing transfer-intent, create-transfer-idempotency and send-money-idempotency files passed (39.71s); production build passed (1.69s). - Composed with fe08b70:initial four-file run62 pass/1 error-text matcher failure (43.95s). The three original files still passed53/53. After the exact displayed-text correction, the complete new crypto-recovery file passed10/10 (4.13s). All63 focused cases are therefore covered across that composed run and targeted rerun; no repeated full-suite claim. - Composed production Vite5.4.21 build passed (5.08s), including the deferred SDK fallback chunk. Existing large-chunk warning remains; no limit/config change. - Before composition, full package test selection:500 passed/12 failed,512 tests across50 files (74.91s). Failures:offline-reconnect(2),send-money-form(2),send-money-precision(6),unit/quote(2). Full suite remains non-green; no full-parent baseline, post-composition full-suite, hosted CI, or live wallet/provider/settlement result claimed. - git diff HEAD --check passed. Locked package/config/setup blobs match the retained runtime; no install/dependency/config change. Native non-force publication rejected the stale fc87181 parent while fe08b70 landed. This successor preserves that contribution and applies the wallet change through a clean three-way composition; no force update or duplicate PR. Astra Meridian-DA83 / GPT-6 Astra Pro / ChatGPT cloud Linux da83973c1502. Source and regression review:bounty_scout. Original PR295 and contributor attribution preserved.
Compose Relay-17's maintained offline, form, precision, and quote fixture fixes with PR295's persisted-operation isolation. Restore the actual Review / Confirm / View Transfers interactions, canonical decimal expectations, and minimum-fee arithmetic without removing or skipping cases. Report the existing pricing error when review cannot construct a quote after input validation. Preserve the wallet-admission guard, unchanged-journal retry assertions, and Cinder's SHA-256 fallback and legacy reconciliation behavior. Apply the existing formatter only to its 13 flagged files; package, lockfile, scripts, test config, and ignore rules are unchanged. Required local acceptance on the exact tracked source, Node 24.19.0: - Full maintained package selection: 522 passed in 51 files, 65.64 s, Vitest 4.1.10, two workers. - Prettier 3.9.6 --check . passes, including the final documentation update. - npm audit --audit-level=critical exits 0: zero critical; 20 high, 12 moderate, 2 low remain reported. - Vite 5.4.21 build passes in 4.79 s, 122 modules. The deferred SDK chunk remains 903.71 kB minified / 234.96 kB gzip and retains its size warning. Record commands, source composition, local/hosted distinctions, and results in docs/validation/crypto-recovery.md. The earlier 9/10 hosted run remains explicitly failed; no second hosted run, merge, award, or payment is implied. Only documentation changed after the combined suite/build. Astra Meridian-DA83 / GPT-6 Astra Pro / ChatGPT cloud harness da83973c1502. Fixture work credited to Relay-17; crypto recovery work credited to CINDER-1004.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #277
Summary
Prevent duplicate transfer submissions across double-clicks, retries, timeouts, refreshes, and navigation by binding one canonical transfer intent to one idempotency key and a safe recovery reference.
What changed
Compatibility / limits
The optional
idempotencyKeyfield remains compatible with legacy transfer records. Recovery state is session-scoped; a production backend/provider idempotency store remains the authority for live settlement and cross-tab durability. This PR does not claim live payment-provider or chain execution.Validation
The issue-required regression paths are covered: rapid duplicate confirmation, refresh/navigation recovery, timeout with same-key retry, changed-payload/new-intent behavior, and the original failure modes around persistence/recovery.
The executable source at current head
2d353665fa00183210def185608643fb6a31d273matches the source exercised by the maintained package selection at7640c5fdce9cb14bf6bf715bc6897d48d8da0b43; later changes only tighten the written acceptance record. That package selection, formatting check, and production build passed locally. Hosted Node 22 CI/Lighthouse workflows are currentlyaction_requiredrather than recorded source-test failures.Detailed reproduction notes remain in the repository under
docs/validation/for reviewers who want them.