Skip to content

Commit 246f056

Browse files
svenvoskampclaude
andcommitted
fix(controllers): scope one-click auth chains to the connector's namespace
`WalletConnectConnector.authenticate()` passed `this.chains` to `SIWXUtil.universalProviderAuthenticate`. That getter resolves to `ChainController.getCaipNetworks()` with no namespace argument, i.e. `getAllRequestedCaipNetworks()` — every registered namespace rather than the connector's own, even though the connector is constructed per namespace and already stores it as `this.chain`. `universalProviderAuthenticate` then bails early: const namespaces = new Set(chains.map(chain => chain.split(':')[0])) if (!siwx || namespaces.size !== 1 || !namespaces.has('eip155')) { return false } So registering any second namespace alongside EVM — Solana, for example — made that set `{eip155, solana}` and silently disabled one-click auth for every wallet. `connectWalletConnect()` fell back to `provider.connect()` followed by a separate `personal_sign` round trip on every login, which is markedly less reliable on mobile wallets; it reproduces as "Error signing message" on Trust Wallet. `getCaipNetworks()` already accepts a namespace, so pass `this.chain`. Single-namespace apps are unaffected: the set was already of size one. Scoped in `authenticate()` rather than in the `chains` getter because `chains` is part of the public `ChainAdapterConnector` surface — the bitcoin, tron and ton adapters read `connector.chains[0]` — so narrowing it deserves its own change. Added a regression test covering both an eip155 and a non-eip155 connector while two namespaces are registered. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 12ad5be commit 246f056

3 files changed

Lines changed: 87 additions & 1 deletion

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@reown/appkit-controllers': patch
3+
---
4+
5+
Fixed WalletConnect one-click auth being silently skipped whenever more than one chain namespace is registered.
6+
7+
`WalletConnectConnector.authenticate()` passed `this.chains` — which resolves to every registered namespace rather than the connector's own — to `SIWXUtil.universalProviderAuthenticate`, which only proceeds for a single `eip155` namespace. Any app registering a second namespace alongside EVM (for example Solana) therefore lost one-click auth for every wallet and fell back to connecting first and requesting a separate `personal_sign`. The chain list is now scoped to the connector's own namespace.

packages/controllers/src/controllers/AdapterController/WalletConnectConnector.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,13 @@ export class WalletConnectConnector<Namespace extends ChainNamespace = ChainName
5656
}
5757

5858
async authenticate(): Promise<boolean> {
59-
const chains = this.chains.map(network => network.caipNetworkId)
59+
/*
60+
* Scope to this connector's own namespace. `this.chains` resolves to every registered
61+
* namespace, while `SIWXUtil.universalProviderAuthenticate` only proceeds for a single
62+
* `eip155` namespace — so passing the unscoped list silently disabled one-click auth for
63+
* every wallet as soon as a second namespace (e.g. Solana) was registered.
64+
*/
65+
const chains = this.getCaipNetworks(this.chain).map(network => network.caipNetworkId)
6066

6167
return SIWXUtil.universalProviderAuthenticate({
6268
universalProvider: this.provider,
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import type UniversalProvider from '@walletconnect/universal-provider'
2+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
3+
4+
import { ChainController, SIWXUtil } from '../../exports/index.js'
5+
import { extendedMainnet, solanaCaipNetwork, updateChainsMap } from '../../exports/testing.js'
6+
import { WalletConnectConnector } from '../../src/controllers/AdapterController/WalletConnectConnector.js'
7+
8+
const mockProvider = {
9+
client: { core: { crypto: { getClientId: vi.fn().mockResolvedValue('client-id') } } },
10+
session: undefined
11+
} as unknown as UniversalProvider
12+
13+
describe('WalletConnectConnector', () => {
14+
beforeEach(() => {
15+
vi.restoreAllMocks()
16+
17+
updateChainsMap('eip155', {
18+
namespace: 'eip155',
19+
networkState: { requestedCaipNetworks: [extendedMainnet] }
20+
})
21+
updateChainsMap('solana', {
22+
namespace: 'solana',
23+
networkState: { requestedCaipNetworks: [solanaCaipNetwork] }
24+
})
25+
})
26+
27+
afterEach(() => {
28+
ChainController.state.chains.delete('eip155')
29+
ChainController.state.chains.delete('solana')
30+
})
31+
32+
describe('authenticate', () => {
33+
it('should only pass its own namespace chains when several namespaces are registered', async () => {
34+
const authenticateSpy = vi
35+
.spyOn(SIWXUtil, 'universalProviderAuthenticate')
36+
.mockResolvedValue(true)
37+
38+
const connector = new WalletConnectConnector({
39+
provider: mockProvider,
40+
caipNetworks: [extendedMainnet, solanaCaipNetwork],
41+
namespace: 'eip155'
42+
})
43+
44+
await expect(connector.authenticate()).resolves.toBe(true)
45+
46+
/*
47+
* `universalProviderAuthenticate` bails unless every chain shares the `eip155` namespace,
48+
* so leaking the Solana network here would silently disable one-click auth.
49+
*/
50+
expect(authenticateSpy).toHaveBeenCalledWith(
51+
expect.objectContaining({ chains: ['eip155:1'] })
52+
)
53+
})
54+
55+
it('should pass its own namespace chains for a non-eip155 connector', async () => {
56+
const authenticateSpy = vi
57+
.spyOn(SIWXUtil, 'universalProviderAuthenticate')
58+
.mockResolvedValue(false)
59+
60+
const connector = new WalletConnectConnector({
61+
provider: mockProvider,
62+
caipNetworks: [extendedMainnet, solanaCaipNetwork],
63+
namespace: 'solana'
64+
})
65+
66+
await expect(connector.authenticate()).resolves.toBe(false)
67+
68+
expect(authenticateSpy).toHaveBeenCalledWith(
69+
expect.objectContaining({ chains: [solanaCaipNetwork.caipNetworkId] })
70+
)
71+
})
72+
})
73+
})

0 commit comments

Comments
 (0)