fix(wallet): drop a dead socket instead of asking it to close, and make the hand-off measurable - #38
Merged
Conversation
…ke the hand-off measurable The reporter still sees about five seconds of "Connecting Wallet..." after approving in MetaMask, with #36 deployed. Two things in response. 1. The reconnect no longer waits for a dead socket to say goodbye. #36 replaced a socket that only claimed to be OPEN by calling `restartTransport()`. Read further, that is the polite path: `resetTransport` -> `transportDisconnect` -> `provider.disconnect()` under a 2 s timeout -- two seconds spent waiting for a close handshake that a TCP connection Android suspended will never complete, before a single byte of the reconnect is sent. The relayer has a faster path, and it is the one IT takes when a socket's `close` event fires on its own: `onProviderDisconnect()` -- stop the subscriber and dial a FRESH socket 100 ms later. The dead socket is abandoned rather than closed; `createProvider()` detaches its listeners first, so its eventual close reaches nobody. That is the truthful treatment of a socket that is, in fact, gone. The polite restart stays as the fallback if the fresh socket is not up within WAKE_TIMEOUT_MS, and the log says which path ran. (Checked while here: `batchFetchMessages`, with its 1 s sleep, has no call site in the shipped core. The relay pushes queued messages on `irn_batchSubscribe`, so nothing else on the path waits on purpose.) 2. The hand-off is now measurable from the phone. Every theory about the delay and the splash-screen hang has been argued from code reading, because the only evidence was console.log on a phone nobody had a debugger on. `wallet/diag.ts` routes the same lines into the clientLogger ring buffer, timestamped "+Nms since return" -- the moment every one of those seconds is counted from -- and the debug-mode banner now shares the buffer (plus the build sha) with one tap. Logged: tab hidden/visible with the hidden duration and whether a relay provider was even present; which reconnect path ran and how long it took; every AppKit connected/connecting/account/provider flip; on the sign tap, the relay state, the stored deep-link choice and Chrome's user-activation flag; when the request reached the relay; the exact URL of each hop and the activation flag at that moment; when the signature arrived. Always on -- the ring is 500 lines in memory -- so the one report that matters is not taken with it off. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013BpqXrkEPA9odTyRdK5Mnx
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
The reporter still sees about five seconds of Connecting Wallet… after approving in MetaMask, with #36 deployed. Two things in response.
1. The reconnect no longer waits for a dead socket to say goodbye
#36 replaced a socket that only claimed to be OPEN by calling
restartTransport(). Read further, that is the polite path:resetTransport→transportDisconnect→provider.disconnect()under a 2 s timeout — two seconds spent waiting for a close handshake that a TCP connection Android suspended will never complete, before a single byte of the reconnect is sent.The relayer has a faster path, and it is the one it takes when a socket's
closeevent fires on its own:onProviderDisconnect()— stop the subscriber and dial a fresh socket 100 ms later. The dead socket is abandoned rather than closed;createProvider()detaches its listeners first, so its eventual close reaches nobody. That is the truthful treatment of a socket that is, in fact, gone. The polite restart stays as the fallback if the fresh socket is not up withinWAKE_TIMEOUT_MS, and the log says which path ran.Checked while here:
batchFetchMessages, with its 1 s sleep, has no call site in the shipped core. The relay pushes queued messages onirn_batchSubscribe, so nothing else on the path waits on purpose.2. The hand-off is now measurable from the phone
Every theory about the delay and the splash-screen hang so far has been argued from code reading, because the only evidence was
console.logon a phone nobody had a debugger attached to.wallet/diag.tsroutes the same lines into theclientLoggerring buffer, timestamped "+N ms since return" — the moment every one of those seconds is counted from — and the debug-mode banner now shares that buffer (plus the build sha) with one tap.Logged: tab hidden/visible with the hidden duration and whether a relay provider was even present; which reconnect path ran and how long it took; every AppKit
connected/connecting/account/providerflip; on the sign tap, the relay state, the stored deep-link choice and Chrome's user-activation flag; when the request reached the relay; the exact URL of each hop and the activation flag at that moment; when the signature arrived. Always on — the ring is 500 lines in memory — so the one report that matters is not taken with it off.How to capture a report
Testing
npm test --workspaces— 951 passing, no unhandled errors. New: fast path preferred whenonProviderDisconnectexists and the fresh socket comes up; polite restart as fallback when it does not; polite restart when the fast path is absent.npm run typecheck --workspaces,npm run lintclean.🤖 Generated with Claude Code
https://claude.ai/code/session_013BpqXrkEPA9odTyRdK5Mnx