Repository navigation
Widget, dapp+Safe: app says 'connect wallet' when it is connected #7777
Description
Update 2026-10-20: not what Lido hit. Their disconnect is the ConnectorAlreadyConnectedError race fixed by #8002 (their pin, v3.24.0, predates it; see FE-451). This retry gap is still real but only reachable when the host provider fails or returns no accounts on the first request, so it's moved to Backlog as hardening.
Follow-up to FE-451 (Lido, high: "sometimes, when the ETH Widget reloads, the wallet disconnects inside the embedded CowSwap widget").
Status of the original race
- The
ConnectorAlreadyConnectedErrorrace from Lido's gist was fixed in fix(wallet): swallow ConnectorAlreadyConnectedError in widget dapp-mode connect race (first shipped in v3.26.0). - But stake.lido.fi loads the widget from
https://cow-ipfs.lido.fi(cowswapWidgetBaseUrlin their/runtime/window-env.js), which serves v3.24.0. They don't have the fix. On v3.24.0 the error does show up, but the widget still ends up connected.Corrected 2026-10-20: with Lido's mount pattern (no provider → provider a few ms later), their v3.24.0 pin ended disconnected in 12 of 33 automated attempts; prod v3.31.1 in 0 of 11. Details in FE-451.
Remaining gap
If the host provider's first eth_accounts / eth_requestAccounts fails or comes back empty, and no accountsChanged follows, the widget stays disconnected for good.
WidgetStandaloneModeUpdater(libs/wallet/src/updaters/WidgetStandaloneMode.updater.tsx) connects exactly once perisDappModechange and only logs on failure. wagmi's reconnect gives up too.- Reproduced on v3.24.0 and on prod: the host wallet was ready at ~4.6s, and the widget never asked again (2 requests in total). A later
accountsChangeddoes recover it.
Lido's provider makes this reachable:
- its synthetic
connectevent fires viasetTimeout(0)when our bridge subscribes. That happens increateCowSwapWidget, before the iframe has loaded, so the event is lost; accountsChangedonly fires on a wagmichange;- their request wrapper uses
retryCount: 0.
Fix idea
- In dapp mode, retry the widget connector with a bounded backoff when connect fails or returns no accounts.
- Have
IframeRpcProviderBridgere-send the current accounts/chainId once the iframe postsACTIVATE/READY, so events emitted before load aren't lost. - Treat a connect with zero accounts as not connected (see the "Signer is missing account" crash issue).
For Lido
Update the cow-ipfs.lido.fi pin to current prod. Mount the widget only once the provider exists and keep it stable. Emit accountsChanged when the wallet becomes ready.
Related: GitHub #7777 (open), where dapp+Safe shows "connect wallet" while connected. A comment there notes that useIsRestoringConnection returns false in dapp mode.
Activity
[-]Widget, dapp mode: app says 'connect wallet', however, it is connected[/-][+]Widget, dapp+Safe: app says 'connect wallet', however, it is connected[/+][-]Widget, dapp+Safe: app says 'connect wallet', however, it is connected[/-][+]Widget, dapp+Safe: app says 'connect wallet' when it is connected[/+]tenderdeve commentedon Jul 1, 2026
@elena-zh I'd like to work on this.
tenderdeve commentedon Jul 1, 2026
@elena-zh looked into this. The disabled 'Connect Wallet' comes from
validateTradeFormpushingWalletNotConnectedwheneveraccountis empty; in dapp modeuseIsRestoringConnectionreturnsfalse, so there's no 'restoring' grace state and an unpopulated widget-connector account shows immediately as a hard Connect Wallet.Two candidate causes, and I'd like your read before patching wallet-connector internals:
config.tschangedenableReconnect: true→enableReconnect: hasRecentConnector(in fix(wallet): guard mobile injected provider #7757, touched again in fix(wallet): keep injected provider isolated #7766). A fresh widget iframe inside Safe has norecentConnectorId, so AppKit no longer reconnects on mount.getIsSafeAppIframe()/getAncestorOrigin()only inspectancestorOrigins[0](the dapp), never the top-levelapp.safe.global, so the widget never knows it's ultimately inside Safe and the instant Safe auto-connect never runs.This one really needs the exact nested topology (Safe → dapp → widget) to repro and confirm which is the culprit — do you have a repro dapp/config, or can you confirm whether this regressed with #7757? I'd rather not blind-patch
enableReconnectgiven the blast radius.elena-zh commentedon Jul 1, 2026
@tenderdeve , the issue was originally introduced in #7757
In order to reproduce it, you'll need to create your own Safe account, and then open this link
https://app.safe.global/apps/open?safe=**your_account**&appUrl=https%3A%2F%2Fdev.widget.cow.filinear-code commentedon Oct 16, 2026
FE-947