fix: refresh balances and simulations after a transaction confirms
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 11
- Forks
- 26
- Avg merge
- 11h 58m
- Merged PRs (30d)
- 16
Description
Nothing in the app waits for a transaction receipt or invalidates cached state afterwards, so the UI keeps judging against pre-transaction values until something unrelated triggers a refetch.
`useHandleTransaction` calls `setLatestTransactionConfirmed()` on `useWriteContract`'s `isSuccess` (`hooks/useHandleTransaction.tsx:56-61`), which in wagmi v2 means the hash was returned, not mined. `useWaitForTransactionReceipt` is not used anywhere outside the L1 migration pages, and there are no `invalidateQueries` or SWR `mutate` calls after a write.
Two caches then go stale together:
- `useAccountBalanceData` (`hooks/useSwr.tsx:176-182`) has no `refreshInterval`. It only revalidates on focus, remount, or reconnect — and focus fires when the wallet popup closes, which is *before* the transaction is mined, so it re-reads the old value and then never tries again.
- `useSimulateContract` results are keyed on the call args, so a repeat action with the same amount reuses the result from before the previous transaction.
## Reproductions
**Allowance spent, UI unaware.** Set the allowance to 0.1, delegate 0.05. The allowance is now 0.05 on-chain, but the widget keeps accepting 0.1 and rejecting 0.15. It corrects itself only when the tab regains focus.
**Approval landed, UI unaware.** Approve, then delegate. Delegate stays disabled until the amount is retyped or the page reloaded, because the allowance the gate depends on has not refreshed.
## Proposed fix
Wait for the receipt in `useHandleTransaction`, and treat confirmation as the point where state is refreshed:
```ts
const { isSuccess: isConfirmed } = useWaitForTransactionReceipt({ hash: data });
useEffect(() => {
if (!isConfirmed) return;
setLatestTransactionConfirmed();
queryClient.invalidateQueries();
mutate(
(key) =>
typeof key === "string" &&
(key.startsWith("/account-balance/") || key.startsWith("/pending-stake/"))
);
}, [isConfirmed]);
```
Moving confirmation to the receipt also changes when the success dialog appears — currently at submission. The store already separates pending (`setLatestTransactionSummary`) from confirmed, so pending → confirmed is the honest behaviour, but it is a visible UX change and should be decided deliberately.
Since this hook backs every write, it needs testing across delegate, move-delegated-stake, undelegate, approve, and withdraw.
## Related
- #738 gates the bond simulation on the allowance, which stops a *failed* simulation from being cached forever. It does not remove the wait after approving, because the allowance value itself still does not refresh.
- #741 covers the confirmation dialog reporting a stake migration, split out from this issue.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in hooks/useHandleTransaction.tsx:56-61, then read useAccountBalanceData in hooks/useSwr.tsx:176-182 and the existing useWaitForTransactionReceipt usage in the L1 migration pages. Verify transaction state and cache behavior across delegate, move-delegated-stake, undelegate, approve, and withdraw. Done means confirmed transactions refresh the affected balance and simulation state, with the pending and confirmed dialog behavior decided and covered by tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100