Skip to content

fix(frontend): stabilize transactionId to fix progress UI and prevent double submission#1260

Merged
ogazboiz merged 1 commit into
LabsCrypt:mainfrom
dannyy2000:fix/issue-1105-1103-1104-1106-stable-transaction-ids
Jun 27, 2026
Merged

fix(frontend): stabilize transactionId to fix progress UI and prevent double submission#1260
ogazboiz merged 1 commit into
LabsCrypt:mainfrom
dannyy2000:fix/issue-1105-1103-1104-1106-stable-transaction-ids

Conversation

@dannyy2000

Copy link
Copy Markdown
Contributor

Summary

Problem

In useDepositOperation, useWithdrawalOperation, and useRepaymentOperation, the transaction ID was computed with Date.now() on every render:

const transactionId = `deposit-${Date.now()}`; // new id each render

Because useTransaction subscribes to the Zustand store, every state write inside executeDeposit (start → sign → submit → confirm) triggered a re-render, which minted a new ID. The in-flight closure kept writing to the original ID, but the component was reading from the new one — so transaction was always undefined. Consequences:

  • OperationProgress hit its if (!transaction) return null guard and showed nothing during signing/submitting/confirming
  • depositOp.isLoading / withdrawalOp.isLoading stayed false, so the Deposit/Withdraw buttons were never disabled mid-flight, allowing duplicate submissions with a second click

Fix

useRepaymentOperation.ts — replaced Date.now() with React's useId() in all three hooks (useRepaymentOperation, useDepositOperation, useWithdrawalOperation). useId() returns a stable value across all renders for the lifetime of the hook instance.

useOptimisticUI.ts — extended isLoading in useTransaction to cover submitted and confirming states in addition to pending and signing, so buttons stay disabled for the entire in-flight window until the operation settles.

Files changed

  • frontend/src/app/hooks/useRepaymentOperation.ts
  • frontend/src/app/hooks/useOptimisticUI.ts

… UI loss and double submission

Closes LabsCrypt#1105

Date.now() was called on every render inside useDepositOperation,
useWithdrawalOperation, and useRepaymentOperation, minting a new
transactionId each time. Because useTransaction subscribes to the
Zustand store, every store write (start/sign/submit/confirm) triggered
a re-render which produced a new id. The executeDeposit/executeWithdrawal
closure kept writing to the original id while the component read from
the new one — so transaction was undefined, OperationProgress showed
nothing, and isLoading stayed false, allowing duplicate submissions.

Fix: replace Date.now() with React's useId() so the id is stable across
all renders for the lifetime of the hook instance.

Also extend isLoading in useTransaction to cover the submitted and
confirming states so the Deposit/Withdraw buttons remain disabled for
the entire in-flight window, not just pending/signing.
@ogazboiz ogazboiz merged commit 4cf0d1b into LabsCrypt:main Jun 27, 2026
6 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment