Telegram (ask questions / claim the issue here first): https://t.me/+DOylgFv1jyJlNzM0
Why this matters
In useRepaymentOperation.ts the transaction id is recomputed on every render: const transactionId = deposit-${Date.now()} (line 129) and const transactionId = withdrawal-${Date.now()} (line 220), then passed to useTransaction(transactionId). useTransaction calls useOptimisticUI() (useOptimisticUI.ts:209) which subscribes the component to the whole store, so every store write inside executeDeposit (start/sign/submit/confirm) re-renders LendPageClient and mints a NEW id. The displayed value depositOp.transaction = getTransaction(newId) is undefined while the in-flight executeDeposit closure keeps writing to the original id. Consequence: after the first network await (buildDeposit.mutateAsync) the ids diverge, OperationProgress (LendPageClient.tsx:382 and :451) hits if (!transaction) return null (OperationProgress.tsx:41) and shows nothing during signing/submitting/confirming, and depositOp.isLoading/withdrawalOp.isLoading (LendPageClient.tsx:376,431) stays false because status of an undefined tx is falsy - so the Deposit/Withdraw buttons are never disabled mid-flight, permitting a duplicate deposit/withdraw submission with a second click.
Acceptance criteria
Files to touch
frontend/src/app/hooks/useRepaymentOperation.ts
frontend/src/app/[locale]/lend/LendPageClient.tsx
frontend/src/app/hooks/useOptimisticUI.ts
frontend/src/app/components/ui/OperationProgress.tsx
Out of scope
- Reworking the simulated executeRepayment in the unused LoanRepaymentForm
Why this matters
In useRepaymentOperation.ts the transaction id is recomputed on every render: const transactionId = deposit-${Date.now()} (line 129) and const transactionId = withdrawal-${Date.now()} (line 220), then passed to useTransaction(transactionId). useTransaction calls useOptimisticUI() (useOptimisticUI.ts:209) which subscribes the component to the whole store, so every store write inside executeDeposit (start/sign/submit/confirm) re-renders LendPageClient and mints a NEW id. The displayed value depositOp.transaction = getTransaction(newId) is undefined while the in-flight executeDeposit closure keeps writing to the original id. Consequence: after the first network await (buildDeposit.mutateAsync) the ids diverge, OperationProgress (LendPageClient.tsx:382 and :451) hits if (!transaction) return null (OperationProgress.tsx:41) and shows nothing during signing/submitting/confirming, and depositOp.isLoading/withdrawalOp.isLoading (LendPageClient.tsx:376,431) stays false because status of an undefined tx is falsy - so the Deposit/Withdraw buttons are never disabled mid-flight, permitting a duplicate deposit/withdraw submission with a second click.
Acceptance criteria
Files to touch
frontend/src/app/hooks/useRepaymentOperation.tsfrontend/src/app/[locale]/lend/LendPageClient.tsxfrontend/src/app/hooks/useOptimisticUI.tsfrontend/src/app/components/ui/OperationProgress.tsxOut of scope