Skip to content

Bug: Android Chrome — wallet connection stuck "awaiting confirmation" after approving in Coinbase Wallet app (mobile EOA flow) #1883

@C00K1E-dev

Description

@C00K1E-dev

Describe the bug

When connecting from a mobile browser (Android Chrome) using the EOA/WalletLink flow, the dapp tab gets stuck on "awaiting confirmation" after the user approves the connection in the Coinbase Wallet app. The user must manually return to Chrome, then attempt the connection a second time for it to succeed. Root cause: Android aggressively suspends WebSocket connections when Chrome is backgrounded (while the user is in the Coinbase Wallet app). When the tab becomes visible again, setupVisibilityChangeHandler in WalletLinkConnection.ts calls reconnectWithFreshWebSocket() which must complete a full WS handshake before fetchUnseenEventsAPI() is called. The approval event already sitting on Coinbase's server is missed during this race. On the second attempt, the event is found because the session is already cached.

Steps

  1. Open any dapp in Android Chrome (tested on Chrome 124+, Android 13/14)
  2. Select BASE chain and click "Connect Wallet"
  3. Choose "Coinbase Wallet" from the wallet selector
  4. The keys.coinbase.com relay tab opens — tap "Open Coinbase Wallet"
  5. Coinbase Wallet app opens — tap "Allow" to approve the connection
  6. Tap "Done" in the wallet app
  7. Return manually to Chrome
  8. Dapp still shows "Awaiting Confirmation" — never resolves
  9. Close the modal, try connecting again — now it works immediately

Expected behavior

After approving in the Coinbase Wallet app and returning to Chrome, the dapp should immediately show the wallet as connected without requiring a second attempt.

Version

4.x (latest master — confirmed in WalletLinkConnection.ts and WalletLinkRelay.ts)

Additional info

Two fixes are needed. Both are minimal, isolated changes:


FIX 1 — WalletLinkConnection.ts
File: packages/wallet-sdk/src/sign/walletlink/relay/connection/WalletLinkConnection.ts
Method: setupVisibilityChangeHandler()

Add an immediate fetchUnseenEventsAPI() call when the tab becomes visible, BEFORE waiting for WS reconnection. This is a plain HTTPS REST call that does not require an active WebSocket, so it can resolve the pending approval event instantly.

BEFORE:
this.visibilityChangeHandler = () => {
if (!document.hidden && !this.destroyed) {
if (!this.connected) {
this.reconnectWithFreshWebSocket();
} else {
this.heartbeat();
}
}
};

AFTER:
this.visibilityChangeHandler = () => {
if (!document.hidden && !this.destroyed) {
// Immediately poll for events that arrived while backgrounded.
// fetchUnseenEventsAPI is a plain HTTPS call — no WS needed.
// This catches the approval event Android dropped from the WS
// while Chrome was backgrounded, without waiting for reconnection.
this.fetchUnseenEventsAPI().catch(() => {});

  if (!this.connected) {
    this.reconnectWithFreshWebSocket();
  } else {
    this.heartbeat();
  }
}

};


FIX 2 — WalletLinkRelay.ts (bonus fix for transaction signing same issue)
File: packages/wallet-sdk/src/sign/walletlink/relay/WalletLinkRelay.ts
Method: openCoinbaseWalletDeeplink()

The blur/focus { once: true } listeners are consumed by intermediate UI events (the redirect dialog overlay, keys.coinbase.com tab focus) before the user returns from the wallet app. Replace with visibilitychange which fires exactly once when the tab is foregrounded.

BEFORE:
default:
window.addEventListener(
'blur',
() => {
window.addEventListener(
'focus',
() => {
this.connection.checkUnseenEvents();
},
{ once: true }
);
},
{ once: true }
);
this.ui.openCoinbaseWalletDeeplink();
break;

AFTER:
default:
const handleVisibilityChange = () => {
if (document.visibilityState === 'visible') {
document.removeEventListener('visibilitychange', handleVisibilityChange);
this.connection.checkUnseenEvents();
}
};
document.addEventListener('visibilitychange', handleVisibilityChange);
this.ui.openCoinbaseWalletDeeplink();
break;

Desktop

No response

Smartphone

  • Device: Android (reproduced on multiple devices)
  • OS: Android 13 / 14
  • Browser: Google Chrome (mobile)
  • Version: Chrome 124+

Metadata

Metadata

Assignees

No one assigned

    Labels

    type: bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions