BTA CBDE study materials
BTA CBDE study resources

Wallet Connection and Stale Reads in a DApp

BTA CBDE · Put your knowledge to work in context.

All case studies

Wallet Connection and Stale Reads in a DApp

A DApp shows a user's token balance and a pending reward. It reads the balance with a JSON-RPC call to a public endpoint and reads the reward from a contract event listener. Users report that after a successful transaction the UI still shows the old balance for several seconds, and sometimes indefinitely if they switch networks in the wallet. The app also lets users sign a message to log in, but the backend verifies the signature against the connected address without checking the chain ID or a nonce. A security reviewer flags the login flow. The team wants to ship a fix this sprint.

1. Diagnose the stale-balance problem and propose a fix that handles network switches.

Read the worked answer

There are two separate causes. First, the UI reads from a public RPC that may lag the chain tip, and it likely caches or reads once on mount. After a transaction is mined, the app should refetch after the receipt confirms, not optimistically assume success. Second, the event listener may be attached to the wrong provider or not re-subscribed after a network change, so updates stop arriving.

Fix: after sending a transaction, await the receipt, then invalidate and refetch the balance and reward from the provider bound to the current chain. Subscribe to chainChanged and accountsChanged events from the wallet and rebuild the provider and contract instances on each change; do not reuse a provider created for a previous chain. Poll as a fallback with a short interval, and show a clear pending state so users do not mistake lag for failure. Pin the expected chain ID and warn or block when the wallet is on an unsupported network, since a contract address valid on one chain is meaningless on another. For the event listener, re-subscribe after every chain change and keep a cancellation handle so old listeners do not leak.

2. What is wrong with the signature login, and what should the verification include?

Read the worked answer

Verifying a signature only against the connected address is insufficient. A signature is valid for whatever message was signed, so without binding to a nonce, chain, and domain, it can be replayed. An attacker who observes a valid signature can reuse it later, on another chain, or in another application that accepts the same format.

The fix is a structured login message that includes a server-issued random nonce, the chain ID, the verifying contract or domain name, an expiration timestamp, and the user's address. The backend stores the nonce, marks it used on first verification, and rejects reuse. It checks the recovered address matches the claimed address, the nonce matches and is unused, the timestamp has not expired, and the chain ID and domain match expectations. This is the same reasoning behind EIP-712 typed data, which also makes the message readable in the wallet and harder to phish.

Additionally, do not trust the address the client sends; recover it from the signature and compare. Use a constant-time comparison for any secret checks. Log failed attempts. For this sprint, the minimum is nonce plus expiry plus chain ID plus one-time use; typed data can follow if time allows, but the replay protection cannot wait.