feat: EIP-6963 wallet discovery, per-connector errors, humanized connect failures - #151
Conversation
Every installed wallet extension now announces itself and gets its own connector row with its own name and brand icon, instead of one generic "Browser Wallet" row talking to whichever extension last hijacked window.ethereum — the usual cause of "wallet must has at least one account" (4001) failures where no popup ever appears. - createIqLoginConfig sets multiInjectedProviderDiscovery: true - new buildConnectorRows() centralizes row building for Login and useLoginFlow, and hides the generic injected() row whenever announced wallets exist - ConnectorRow.error is scoped to the connector that attempted the connection (previously the one shared useConnect error rendered under every row) - resolveConnectorMeta can now use the wallet's announced EIP-6963 icon
Raw provider failures ("wallet must has at least one account",
"User rejected the request.", -32002 already-pending, unreachable
WalletConnect relay) read as noise or blame the user for wallet-side
bugs. Map the known ones to a sentence that says what happened and what
to do about it, falling back to viem's shortMessage. Exported from
/client for consumers building custom UIs on useLoginFlow.
The built-in Login now renders rows from buildConnectorRows — so discovered wallets appear with their own name/icon, the generic row hides when wallets announce, and an error shows only under the row that attempted, formatted by humanizeConnectError. ConnectorRow moves to connector-rows.ts (re-exported, no API change). Docs + changeset for the 8.2.0 minor.
…tError fallback - match any message pairing "request" with "pending"/"processing" (word- bounded, either order), not just the literal "already pending" phrasing - treat blank shortMessage/message values as absent: trim candidates, skip whitespace-only lines, and reliably fall through to the generic copy (previously the fallback was unreachable and empty messages rendered an empty error line) - settle changeset wording on message-pattern phrasing for the humanizer
Yomzy2223
left a comment
There was a problem hiding this comment.
Reviewed at e51e3cd. Verified locally in a worktree: tsc --noEmit clean, biome check src clean.
I also read wagmi 2.16.7's createConfig.js / hydrate.js to check the discovery assumptions, and they hold up: providerDetailToConnector does injected({ target: { ...info, id: info.rdns, provider } }), so type === "injected" && id !== "injected" is a valid discriminator, connector.icon really is the announced icon, and wagmi's own dedupe is by rdns only — the generic injected() connector has no rdns, so the manual filter here genuinely is needed rather than duplicating something wagmi already does.
The diagnosis is right and the core mechanism is correct. Centralizing into buildConnectorRows is a good call, and dropping the isPending gate from attempted while keeping it on isConnecting is exactly the right split for error scoping.
Inline comments below, roughly in order of significance. The two I'd want resolved before merge are the all-or-nothing generic-row filter and the first-paint window where the generic row is the only injected option on screen.
One thing not attachable to a line: there are no tests. buildConnectorRows and humanizeConnectError are both pure functions, and they're the two pieces whose correctness can't be confirmed by reading the diff — row filtering, error scoping, and each message branch. vitest is already a dependency (though it's in dependencies rather than devDependencies, and there's no test script), so the suite would need standing up first. Given the whole PR's user-facing value rests on string matching, this feels worth the setup cost.
…ect error handling
feat(error): enhance humanizeConnectError with detailed RPC error handling
Closes https://github.com/IQIndustries/issues/issues/5205 when this PR merged
Description
Fixes multi-wallet connect conflicts and misleading error display in the login UI.
1. EIP-6963 wallet discovery
createIqLoginConfignow enablesmultiInjectedProviderDiscovery. Every installed wallet extension (MetaMask, Phantom, OKX, Rabby…) announces itself and gets its own connector row with its own name and brand icon, instead of one generic "Browser Wallet" row talking to whichever extension last hijackedwindow.ethereum. That hijack conflict is the usual cause of the"wallet must has at least one account"(4001) failures where no popup ever appears. The generic injected row still renders when no wallet announces itself.2. Per-connector error scoping
Previously the one shared
useConnecterror rendered under every row, so a single failed attempt looked like all connectors failed.ConnectorRow.error(in bothLoginanduseLoginFlow) now carries the error only on the row that attempted the connection. Row building is centralized inbuildConnectorRows().3.
humanizeConnectError(error)New
/clientexport mapping known wallet failures to actionable copy: locked-wallet 4001, pending-32002, missing provider, unreachable WalletConnect relay. The built-inLoginuses it for its error line.