@@ -16,15 +16,17 @@ import {
1616 WalletConnectInterface ,
1717 SwitchableChain ,
1818} from "../../__types__" ;
19- import type { WalletConnectClient } from "./client" ;
2019import type { SessionTypes } from "@walletconnect/types" ;
20+ import type { WalletConnectClient } from "./client" ;
2121import { Transaction } from "ethers" ;
2222
2323type WalletConnectChangeEvent =
2424 | { type : "disconnect" }
2525 | { type : "chainChanged" ; chainId ?: string }
2626 | { type : "update" }
27- | { type : "proposalExpired" } ;
27+ | { type : "proposalExpired" }
28+ | { type : "initialized" }
29+ | { type : "failed" ; error ?: unknown } ;
2830
2931export class WalletConnectWallet implements WalletConnectInterface {
3032 readonly interfaceType = WalletInterfaceType . WalletConnect ;
@@ -37,6 +39,7 @@ export class WalletConnectWallet implements WalletConnectInterface {
3739
3840 private uri ?: string ;
3941 private isRegeneratingUri = false ;
42+ private isInitialized = false ;
4043
4144 private changeListeners = new Set <
4245 ( event ?: WalletConnectChangeEvent ) => void
@@ -60,8 +63,29 @@ export class WalletConnectWallet implements WalletConnectInterface {
6063 * updating `this.uri` so the UI can present a fresh QR/deeplink.
6164 *
6265 * @param client - The low-level WalletConnect client used for session/RPC.
66+ * @param ensureReady - Optional callback to ensure WalletConnect is initialized before operations.
67+ * @param namespaces - Optional namespace configuration to set up configured chains.
6368 */
64- constructor ( private client : WalletConnectClient ) {
69+ constructor (
70+ private client : WalletConnectClient ,
71+ private ensureReady ?: ( ) => Promise < void > ,
72+ namespaces ?: {
73+ ethereumNamespaces : string [ ] ;
74+ solanaNamespaces : string [ ] ;
75+ } ,
76+ ) {
77+ if ( namespaces ) {
78+ this . ethereumNamespaces = namespaces . ethereumNamespaces ;
79+ if ( this . ethereumNamespaces . length > 0 ) {
80+ this . ethChain = this . ethereumNamespaces [ 0 ] ! ;
81+ }
82+
83+ this . solanaNamespaces = namespaces . solanaNamespaces ;
84+ if ( this . solanaNamespaces . length > 0 ) {
85+ this . solChain = this . solanaNamespaces [ 0 ] ! ;
86+ }
87+ }
88+
6589 // session updated (actual update to the session for example adding a chain to namespaces)
6690 this . client . onSessionUpdate ( ( ) => {
6791 this . notifyChange ( { type : "update" } ) ;
@@ -112,59 +136,57 @@ export class WalletConnectWallet implements WalletConnectInterface {
112136 }
113137
114138 /**
115- * Initializes WalletConnect pairing flow with the specified namespaces .
139+ * Initializes WalletConnect pairing flow.
116140 *
117- * - Saves the requested chain namespaces (e.g., `["eip155:1", "eip155:137", "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"]`).
118141 * - If an active session already has connected accounts, pairing is skipped.
119142 * - Otherwise initiates a pairing and stores the resulting URI.
143+ * - Namespaces should be set via constructor for this to work.
120144 *
121- * @param opts.ethereumNamespaces - List of EVM CAIP IDs (e.g., "eip155:1").
122- * @param opts.solanaNamespaces - List of Solana CAIP IDs (e.g., "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp").
123- * @throws {Error } If no namespaces are provided for either chain.
145+ * @throws {Error } If no namespaces were configured in constructor.
124146 */
125- async init ( opts : {
126- ethereumNamespaces : string [ ] ;
127- solanaNamespaces : string [ ] ;
128- } ) : Promise < void > {
129- this . ethereumNamespaces = opts . ethereumNamespaces ;
130- if ( this . ethereumNamespaces . length > 0 ) {
131- this . ethChain = this . ethereumNamespaces [ 0 ] ! ;
132- }
147+ async init ( ) : Promise < void > {
148+ try {
149+ if (
150+ this . ethereumNamespaces . length === 0 &&
151+ this . solanaNamespaces . length === 0
152+ ) {
153+ throw new Error (
154+ "At least one namespace must be enabled for WalletConnect" ,
155+ ) ;
156+ }
133157
134- this . solanaNamespaces = opts . solanaNamespaces ;
135- if ( this . solanaNamespaces . length > 0 ) {
136- this . solChain = this . solanaNamespaces [ 0 ] ! ;
137- }
158+ // we don't want to create more than one active session
159+ // so we don't make a pair request if one is already active
160+ // since pairing would mean initializing a new session
161+ const session = this . client . getSession ( ) ;
162+ if ( hasConnectedAccounts ( session ) ) {
163+ this . isInitialized = true ;
164+ // we notify that initialization is complete
165+ this . notifyChange ( { type : "initialized" } ) ;
166+ return ;
167+ }
138168
139- if (
140- this . ethereumNamespaces . length === 0 &&
141- this . solanaNamespaces . length === 0
142- ) {
143- throw new Error (
144- "At least one namespace must be enabled for WalletConnect" ,
145- ) ;
146- }
169+ const namespaces = this . buildNamespaces ( ) ;
147170
148- // we don't want to create more than one active session
149- // so we don't make a pair request if one is already active
150- // since pairing would mean initializing a new session
151- const session = this . client . getSession ( ) ;
152- if ( hasConnectedAccounts ( session ) ) {
153- return ;
171+ await this . client . pair ( namespaces ) . then ( ( newUri ) => {
172+ this . uri = newUri ;
173+ this . isInitialized = true ;
174+ // we notify that initialization is complete
175+ this . notifyChange ( { type : "initialized" } ) ;
176+ } ) ;
177+ } catch ( error ) {
178+ // we emit a failed event
179+ this . notifyChange ( { type : "failed" , error } ) ;
180+ throw error ;
154181 }
155-
156- const namespaces = this . buildNamespaces ( ) ;
157-
158- await this . client . pair ( namespaces ) . then ( ( newUri ) => {
159- this . uri = newUri ;
160- } ) ;
161182 }
162183
163184 /**
164185 * Returns WalletConnect providers with associated chain/account metadata.
165186 *
166187 * - Builds an EVM provider (if Ethereum namespaces are enabled).
167188 * - Builds a Solana provider (if Solana namespaces are enabled).
189+ * - Before initialization, returns placeholder providers with isLoading: true.
168190 *
169191 * @returns A promise resolving to an array of WalletProvider objects.
170192 */
@@ -194,12 +216,18 @@ export class WalletConnectWallet implements WalletConnectInterface {
194216 *
195217 * - Calls `approve()` on the underlying client when pairing is pending.
196218 * - Throws if the approved session contains no connected accounts.
219+ * - Waits for WalletConnect initialization if still in progress.
197220 *
198221 * @param _provider - Unused (present for interface compatibility).
199222 * @returns A promise that resolves with the connected wallet's address.
200223 * @throws {Error } If the session contains no accounts.
201224 */
202225 async connectWalletAccount ( provider : WalletProvider ) : Promise < string > {
226+ // we ensure WalletConnect is fully initialized before connecting
227+ if ( this . ensureReady ) {
228+ await this . ensureReady ( ) ;
229+ }
230+
203231 const session = await this . client . approve ( ) ;
204232
205233 let address : string | undefined ;
@@ -222,14 +250,15 @@ export class WalletConnectWallet implements WalletConnectInterface {
222250 }
223251
224252 /**
225- * Switches the user’ s WalletConnect session to a new EVM chain.
253+ * Switches the user' s WalletConnect session to a new EVM chain.
226254 *
227255 * - Ethereum-only: only supported for providers on the Ethereum namespace.
228256 * - No add-then-switch: WalletConnect cannot add chains mid-session. The target chain
229257 * must be present in `ethereumNamespaces` negotiated at pairing time. To support a new chain,
230258 * you must include it in the walletConfig.
231259 * - Accepts a hex chain ID (e.g., "0x1"). If a `SwitchableChain` is passed, only its `id`
232260 * (hex chain ID) is used; metadata is ignored for WalletConnect.
261+ * - Waits for WalletConnect initialization if still in progress.
233262 *
234263 * @param provider - The WalletProvider returned by `getProviders()`.
235264 * @param chainOrId - Hex chain ID (e.g., "0x1") or a `SwitchableChain` (its `id` is used).
@@ -241,6 +270,11 @@ export class WalletConnectWallet implements WalletConnectInterface {
241270 provider : WalletProvider ,
242271 chainOrId : string | SwitchableChain ,
243272 ) : Promise < void > {
273+ // we ensure WalletConnect is fully initialized
274+ if ( this . ensureReady ) {
275+ await this . ensureReady ( ) ;
276+ }
277+
244278 if ( provider . chainInfo . namespace !== Chain . Ethereum ) {
245279 throw new Error ( "Only EVM wallets support chain switching" ) ;
246280 }
@@ -408,12 +442,18 @@ export class WalletConnectWallet implements WalletConnectInterface {
408442 *
409443 * - Ethereum: signs a fixed challenge and recovers the compressed secp256k1 public key.
410444 * - Solana: decodes the base58-encoded address to raw bytes.
445+ * - Waits for WalletConnect initialization if still in progress.
411446 *
412447 * @param provider - The WalletProvider to fetch the key from.
413448 * @returns A compressed public key as a hex string.
414449 * @throws {Error } If no account is available or the namespace is unsupported.
415450 */
416451 async getPublicKey ( provider : WalletProvider ) : Promise < string > {
452+ // we ensure WalletConnect is fully initialized
453+ if ( this . ensureReady ) {
454+ await this . ensureReady ( ) ;
455+ }
456+
417457 const session = this . client . getSession ( ) ;
418458
419459 if ( provider . chainInfo . namespace === Chain . Ethereum ) {
@@ -547,6 +587,7 @@ export class WalletConnectWallet implements WalletConnectInterface {
547587 provider : this . makeProvider ( this . ethChain ) ,
548588 connectedAddresses : address ? [ address ] : [ ] ,
549589 ...( this . uri && { uri : this . uri } ) ,
590+ isLoading : ! this . isInitialized ,
550591 } ;
551592 }
552593
@@ -574,6 +615,7 @@ export class WalletConnectWallet implements WalletConnectInterface {
574615 provider : this . makeProvider ( this . solChain ) ,
575616 connectedAddresses : address ? [ address ] : [ ] ,
576617 ...( this . uri && { uri : this . uri } ) ,
618+ isLoading : ! this . isInitialized ,
577619 } ;
578620 }
579621
0 commit comments