Skip to content

fix: refresh balances and simulations after a transaction confirms #739

Description

@rickstaa

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:

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions