Skip to content

Optimistic UI Updates and Rollback Management for Node Staking Adjustments #5

Description

@JamesEjembi

Problem Statement

Multi-second blockchain finality lag makes stake modification actions feel sluggish. The current implementation waits for the Soroban transaction to be confirmed (6-30 seconds) before updating the UI, leaving the user staring at a loading spinner with no feedback about the expected outcome. This creates user anxiety and increases the likelihood of duplicate submissions from impatience clicks.

Technical Bounds & Invariants

  • Soroban transaction finality: 6-30 seconds (1-5 ledger closes)
  • Optimistic update must be revertible if transaction fails
  • Staking actions: stake, unstake, restake, delegate, undelegate
  • UX rule: user must see the result of their action within 500ms of clicking
  • Optimistic state must persist across component remounts (tab navigation)

Codebase Navigation Guide

  • Primary target: /src/hooks/useSorobanStaking.ts
  • Current (synchronous) staking hook: /src/hooks/useSorobanStaking.old.ts (to be replaced)
  • Staking UI components: /src/components/staking/StakingPanel.tsx, /src/components/staking/StakingHistory.tsx
  • Transaction builder: /src/lib/stellar/transaction.ts

Step-by-Step Resolution Blueprint

  1. Restructure useSorobanStaking to use a state machine with four states per action: idle -> pending (optimistic) -> confirmed | failed (rollback). Store the optimistic state in a Zustand slice stakingStore that persists to sessionStorage so it survives tab switches
  2. On stake(amount): (a) immediately compute the new balance as currentBalance - amount and update the Zustand store, (b) generate a temporary optimisticTxId (UUID v4) to track the operation, (c) call api.staking.submitStake(amount) which returns a real transactionHash, (d) store a mapping optimisticTxId -> realTxHash in the store
  3. On successful confirmation (listening for transactionHash via Horizon SSE): mark the optimistic update as confirmed, remove the temporary ID, and show a success toast with a link to the Stellar explorer
  4. On transaction failure (timeout after 30s or HostError): (a) revert the balance by adding back the amount, (b) set error: { txId, reason } in the store, (c) show an error toast with the decoded error message, (d) log the event to Sentry with the optimisticTxId for traceability
  5. Add a pendingTransactions array to the store exposed as useSorobanStaking().pending; the UI reads this to show a "transaction in flight" indicator (animated pulse) next to each pending action in the staking history table
  6. Implement a retry(txId) method that re-submits a failed transaction with the same parameters; on re-submission, re-enter the optimistic state and repeat the lifecycle
  7. Write a test using vitest + msw that: (a) calls stake(100), (b) asserts the balance updates immediately (optimistic), (c) simulates a failed on-chain execution, (d) asserts the balance reverts to the original value, (e) calls retry() and asserts the balance updates optimistically again

Metadata

Metadata

Assignees

Labels

Complexity: HardcoreExtremely difficult, high-complexity engineering taskGrantFox OSSIssue tracked in GrantFox OSSLayer: UI-CoreCore UI layer architectural concernMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignCampaign: Official CampaignType: Web3-IntegrationWeb3 wallet and blockchain integration issue

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions