You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Completes and hardens the external-wallet login flow (PR #72), closing all four scope areas defined in #76:
Scope 1 — Account & Session Isolation: Isolates activity history and token balances per account with session-token guards; validates dApp-supplied sender address with case-insensitive EIP-55 comparison; ensures dApp sessions disconnect on all logout paths; prunes incompatible restored sessions before signer usage.
Scope 2 — Network Switching Coordination: Serializes chain switching, transaction submission, typed-data signing, and message signing through a shared mutex (chainOpLock); unblocks on failure/rejection; separates external disconnect from account change.
Scope 3 — Mempool Tracking & Accurate Outcomes: Tracks external wallet submissions through replaceableTransaction; captures startBlock before submit; rethrows on cancellation and replacement so canceled claims never report false success; preserves Divvi referral tracking on mined and repriced transactions.
Scope 4 — Connection Feedback & Compatibility: Rejects unsupported required chains with UNSUPPORTED_CHAINS error code; advertises only executable methods in approved namespaces; prunes incompatible restored sessions on startup; provides distinct account change and disconnect feedback.
Scope 1 — Keep each account's permissions and data separate
Key activity history cache by active account and purge on account change/logout (transactionHistoryStore).
Guard activity history and token balance async responses against late arrival across account switches (amountsStore, transactionHistoryStore).
Use case-insensitive address comparison (isSameAddress) in WalletWrapper so EIP-55 casing variants from dApps are accepted while mismatched senders are rejected.
Call disconnectDappSessions() on all logout paths (social, private key, external disconnect, account change) in storage.ts.
Prune incompatible restored WalletConnect sessions in initializeWalletConnect before signer usage (walletConnectStore).
Scope 2 — Avoid competing network switches
Serialize ensureChain, submitTransaction, signTypedData, signMessage, and getReownEip1193Provider through chainOpLock in reown.ts.
Release mutex in finally blocks so rejected or failed switches do not leave the interface blocked.
Split watchReownAccount into onAccountChanged and onDisconnected callbacks for distinct UI messaging.
Scope 3 — Finish transaction tracking
Enable mempool replacement tracking via replaceableTransaction(hash, startBlock) in utils.ts.
Query getBlockNumber prior to calling submitTransaction so replacement window is captured.
Branch on err.reason: use replacement receipt and proceed with Divvi tracking for "repriced"; rethrow for "cancelled" and "replaced" to prevent false success.
Retry transient RPC getTransaction failures up to 40 attempts at 1.5s intervals.
Scope 4 — Connection feedback and compatibility checks
Reject session proposals for unsupported required namespaces with UNSUPPORTED_CHAINS error.
Advertise only supported methods in session approval namespaces.
Prune incompatible restored sessions on initialization.
Provide distinct messages for account changes vs. explicit disconnections.
…dresses
- Key activity history cache by active account and purge on account change/logout
- Guard activity history and token balances against late async responses
- Preserve dApp-supplied from address in WalletWrapper and validate against connected account
… from disconnect
- Serialize ensureChain and transaction/typed-data signing via wallet-operation queue
- Release operation lock on failed or rejected switch so subsequent actions are unblocked
- Separate watchReownAccount into onAccountChanged and onDisconnected callbacks
- Ensure all logout paths disconnect active dApp sessions and clear activity history
…le restored sessions
- Reject session proposals for unsupported required namespaces with UNSUPPORTED_CHAINS
- Disconnect restored sessions incompatible with the active signer before signer is usable
- Prevent restored sessions from silently rebinding across accounts or unsupported namespaces
- Acquire chainOpLock in signMessage to prevent interleaving with switch-and-submit
- Add regression test verifying signMessage waits for in-flight transaction submission
- Restore import { config } from "@/config" in reown.ts and walletConnectStore.ts matching base branch
- Update reown.test.ts mock target to match @/config
…esolving as success
Ethers TRANSACTION_REPLACED distinguishes three replacement reasons:
- repriced: same calldata/to/value at higher gas; replacement carries the
original intent so the receipt is used and Divvi tracking proceeds.
- cancelled: zero-value self-transfer with empty calldata.
- replaced: different calldata/to/value (different intent).
Previously all replacement reasons resolved as success with the replacement
receipt. This caused cancelled transactions (e.g. user cancels a claim in
their wallet popup) to report success in ClaimView, fire false success
analytics, and trigger Divvi referral tracking with a cancel tx hash.
Branch on err.reason: rethrow for 'cancelled' and 'replaced' so caller error
paths surface the failure correctly and Divvi tracking is not reached.
Keep current behavior for 'repriced' speed-ups.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Unresolved session-isolation flaws can expose or overwrite another account’s balances and allow WalletConnect requests to use the wrong account’s signer.
A restored session can pass with the current address on an unsupported chain, or with a Solana/Bitcoin account whose network-specific signer is missing. Such sessions remain connected even though their requests cannot execute. Validate declared chains and full chain-qualified accounts against the supported namespace, preserving case-insensitive comparison for EVM addresses.
Reject stale-session propagating transactions
src/stores/transactionHistoryStore.ts:29
Advancing this token guards fetched history but not delayed addPropagatingTx calls: a claim started by A can mine after login as B, and ClaimView then inserts A's transaction into B's history. Capture the originating session token when starting send, claim, and swap operations, and reject propagating inserts when that token no longer matches.
- Guard refreshAmounts against stale callbacks adopting inactive session addresses (B7)
- Add session token guard to addPropagatingTx to prevent cross-account history leak (B8)
- Defer exposing activeSigner in WalletConnect until incompatible sessions are pruned (B9)
- Guard inFlightRequests counter decrement in amountsStore against stale requests (B10)
- Normalize chain-scoped namespace keys in WalletConnect session compatibility (B11)
- Clear cached amounts on account change in amountsStore (B12)
- Validate EVM account chain against supported chains in restored sessions (B13)
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Unresolved account-isolation flaws can expose a new signer to incompatible WalletConnect sessions and insert a prior account’s transactions into the current account’s history.
The account token is captured after await import("@lifi/sdk"), so a logout/login during that wait tags account A's retained swap operation with account B's token. Its completed transaction can then pass the guard and enter B's history. Capture the token at the start of executeSelectedRoute, before any await, and stop before executing the route if the token has changed.
Require session tokens for all asynchronous transaction inserts
src/stores/transactionHistoryStore.ts:51
The undefined-token bypass leaves the non-EVM send paths unprotected: BTCDialogDetails, SVMDialogDetails, and XRPDialogDetails still call addPropagatingTx(tx) after awaiting broadcast. If account A logs out and B logs in while that broadcast is pending, A's completion inserts its transaction into B's newly cleared history. Require a captured session token for every asynchronous insertion and update those callers as well.
Reconcile propagating sends with mined transaction outcomes
src/sections/Send/hooks/evmTransaction.ts:295
The Send path inserts the original hash but never reconciles it with the mining outcome: a speed-up leaves a stale entry alongside the fetched replacement, while cancellation leaves a transfer that never executed. Replace the propagating entry with the mined receipt's hash on success and remove or mark it failed on cancellation or replacement with different intent. Guard these updates with the captured account token.
A valid chain-scoped proposal such as requiredNamespaces: { "eip155:1": { methods: ["personal_sign"], events: [] } } receives UNSUPPORTED_CHAINS because chain collection ignores the namespace key. With no explicit chains array, supportedChains is empty despite Ethereum being supported. Extract chains from scoped keys in both required and optional namespaces before deciding to reject.
Stop advertising unsupported raw transaction signing
For social/private-key signers, this still advertises eth_sendRawTransaction as supported. approveEIP155Request has no handler for that method and falls through to INVALID_METHOD, so an approved request cannot execute; the new restored-session check also incorrectly considers that permission compatible. Exclude it for all signers until a raw-broadcast handler exists.
Initialize the account token when the session starts
src/stores/transactionHistoryStore.ts:80
On a fresh login, activeAccount remains undefined until the first history refresh. Home initially mounts only the Crypto tab, so a claim or swap can capture a token and insert a propagating transaction before Activity is opened. This first refresh then clears that same account's transaction and increments its token, also rejecting operations still in flight. Initialize the account/token when the session starts so opening Activity does not invalidate valid same-session operations.
…ty reconciliation
- Guard WalletKit initialization and signer publication with shared init promise and generation counter
- Validate session topic compatibility before opening request dialog and before signing
- Extract chains from scoped namespace keys in session proposals
- Exclude unsupported eth_sendRawTransaction from EIP155 supported methods for all signers
- Initialize activity account token on session start to avoid invalidating same-session operations
- Capture account token before awaiting SDK import in swap route execution
- Require session tokens for all asynchronous transaction insertions across all chain dialogs
- Reconcile propagating transactions with mined outcomes (hash replacement on speed-up, removal on failure)
- Ignore balance refresh callbacks from ended or non-matching sessions
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
A critical initialization race can remove valid WalletConnect sessions, while transaction tracking can lose Activity entries for uncertain outcomes or duplicate speed-up transfers.
… and deduplicate speed-ups
- Check generation before pruning restored sessions during WalletKit init to prevent superseded signers from pruning active connections
- Keep pending activity entries when transaction outcome is uncertain; remove only on confirmed cancellation, different-intent replacement, or on-chain revert
- Deduplicate speed-up replacements in activity history by dropping original propagating entry when replacement hash is already present
…duplication
- Return immediately from initializeWalletConnect when superseded before pruning or publishing (F1)
- Add dedicated test suite for handleTrxMinedOutcome covering unknown tracking errors vs cancellations (F2)
- Match duplicate replacement check per list excluding the hash being renamed in replacePropagatingTx (F3)
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Logout during WalletKit initialization can leave restored sessions connected, allowing a later login to retain permissions that logout should have revoked.
…itialization
- Record pending logout cleanup when disconnectAllWalletConnectSessions is called while WalletKit is initializing
- Execute disconnect on all active sessions when initialization resolves with pending logout cleanup
- Add regression test verifying restored sessions are disconnected and signer remains unpublished upon logout during init
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human assessment is needed for critical session-lifecycle races that can allow signing after connection revocation or execute swaps using another account’s signer.
- Add regression test verifying executeSelectedRoute captures initialAccountToken before dynamic LiFi import and aborts if token changes in-flight
- Assert executeRoute and addPropagatingTx are not called on account change
- Assert happy path executes route and records propagating tx when token is unchanged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Completes and hardens the external-wallet login flow (PR #72), closing all four scope areas defined in #76:
chainOpLock); unblocks on failure/rejection; separates external disconnect from account change.replaceableTransaction; capturesstartBlockbefore submit; rethrows on cancellation and replacement so canceled claims never report false success; preserves Divvi referral tracking on mined and repriced transactions.UNSUPPORTED_CHAINSerror code; advertises only executable methods in approved namespaces; prunes incompatible restored sessions on startup; provides distinct account change and disconnect feedback.Closes #76
Base branch:
feat-connect-wallet-reownDetailed Changes by Scope
Scope 1 — Keep each account's permissions and data separate
transactionHistoryStore).amountsStore,transactionHistoryStore).isSameAddress) inWalletWrapperso EIP-55 casing variants from dApps are accepted while mismatched senders are rejected.disconnectDappSessions()on all logout paths (social, private key, external disconnect, account change) instorage.ts.initializeWalletConnectbefore signer usage (walletConnectStore).Scope 2 — Avoid competing network switches
ensureChain,submitTransaction,signTypedData,signMessage, andgetReownEip1193ProviderthroughchainOpLockinreown.ts.finallyblocks so rejected or failed switches do not leave the interface blocked.watchReownAccountintoonAccountChangedandonDisconnectedcallbacks for distinct UI messaging.Scope 3 — Finish transaction tracking
replaceableTransaction(hash, startBlock)inutils.ts.getBlockNumberprior to callingsubmitTransactionso replacement window is captured.err.reason: use replacement receipt and proceed with Divvi tracking for"repriced"; rethrow for"cancelled"and"replaced"to prevent false success.getTransactionfailures up to 40 attempts at 1.5s intervals.Scope 4 — Connection feedback and compatibility checks
UNSUPPORTED_CHAINSerror.Verification
yarn install --immutable✅yarn biome check✅ (363 files, 0 errors)yarn tsc --noEmit✅ (0 type errors)yarn build✅ (Turbopack static build clean, 11/11 pages)yarn test✅ (19 files, 151 tests passed)yarn test:e2e✅ (10/10 Playwright tests passed)Conscious Deferrals
disconnectDappSessionsfire-and-forget race on rapid re-login (pre-existing pattern, not introduced by this PR).chainOpLockfor indefinitely pending wallet popups (requires product decision on mid-operation abort UX).lifiStore(manages own retries, out of scope for wallet-level queue).