From bad6607b2a4b6ef319e6c453bc2f6702f982cc21 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Tue, 10 Jun 2025 20:17:37 +1200 Subject: [PATCH 1/2] cache register user promise --- .../passport/sdk/src/zkEvm/zkEvmProvider.ts | 50 +++++++++++++++---- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/packages/passport/sdk/src/zkEvm/zkEvmProvider.ts b/packages/passport/sdk/src/zkEvm/zkEvmProvider.ts index 2955be92fc..aaed3e772a 100644 --- a/packages/passport/sdk/src/zkEvm/zkEvmProvider.ts +++ b/packages/passport/sdk/src/zkEvm/zkEvmProvider.ts @@ -77,6 +77,12 @@ export class ZkEvmProvider implements Provider { #signerInitialisationError: unknown | undefined; + /** + * Cache for the user registration promise to prevent race conditions + * when multiple eth_requestAccounts calls happen concurrently + */ + #userRegistrationPromise?: Promise | undefined; + public readonly isPassport: boolean = true; constructor({ @@ -133,9 +139,17 @@ export class ZkEvmProvider implements Provider { #handleLogout = () => { this.#ethSigner = undefined; + this.#userRegistrationPromise = undefined; this.#providerEventEmitter.emit(ProviderEvent.ACCOUNTS_CHANGED, []); }; + /** + * Clear the cached user registration promise + */ + #clearUserRegistrationCache = () => { + this.#userRegistrationPromise = undefined; + }; + /** * This method is called by `eth_requestAccounts` and asynchronously initialises the signer. * The signer is stored in a promise so that it can be retrieved by the provider @@ -247,18 +261,32 @@ export class ZkEvmProvider implements Provider { if (!isZkEvmUser(user)) { flow.addEvent('startUserRegistration'); - const ethSigner = await this.#getSigner(); - flow.addEvent('ethSignerResolved'); + // Check if registration is already in progress to prevent race conditions + if (!this.#userRegistrationPromise) { + const ethSigner = await this.#getSigner(); + flow.addEvent('ethSignerResolved'); + + // Cache the registration promise to prevent concurrent registrations + this.#userRegistrationPromise = registerZkEvmUser({ + ethSigner, + authManager: this.#authManager, + multiRollupApiClients: this.#multiRollupApiClients, + accessToken: user.accessToken, + rpcProvider: this.#rpcProvider, + flow, + }); + } - userZkEvmEthAddress = await registerZkEvmUser({ - ethSigner, - authManager: this.#authManager, - multiRollupApiClients: this.#multiRollupApiClients, - accessToken: user.accessToken, - rpcProvider: this.#rpcProvider, - flow, - }); - flow.addEvent('endUserRegistration'); + try { + userZkEvmEthAddress = await this.#userRegistrationPromise; + flow.addEvent('endUserRegistration'); + // Clear the cache after successful registration so future calls get fresh user data + this.#clearUserRegistrationCache(); + } catch (error) { + // Clear the cached promise on error so it can be retried + this.#clearUserRegistrationCache(); + throw error; + } } else { userZkEvmEthAddress = user.zkEvm.ethAddress; } From 768d86d89d220c6365ecd46c77ef5388558d3b92 Mon Sep 17 00:00:00 2001 From: Nik Ho Date: Mon, 16 Jun 2025 16:57:25 +1200 Subject: [PATCH 2/2] finally clear cache --- packages/passport/sdk/src/zkEvm/zkEvmProvider.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/passport/sdk/src/zkEvm/zkEvmProvider.ts b/packages/passport/sdk/src/zkEvm/zkEvmProvider.ts index aaed3e772a..2cc005e88b 100644 --- a/packages/passport/sdk/src/zkEvm/zkEvmProvider.ts +++ b/packages/passport/sdk/src/zkEvm/zkEvmProvider.ts @@ -280,12 +280,9 @@ export class ZkEvmProvider implements Provider { try { userZkEvmEthAddress = await this.#userRegistrationPromise; flow.addEvent('endUserRegistration'); - // Clear the cache after successful registration so future calls get fresh user data + } finally { + // Clear the cache on success or error this.#clearUserRegistrationCache(); - } catch (error) { - // Clear the cached promise on error so it can be retried - this.#clearUserRegistrationCache(); - throw error; } } else { userZkEvmEthAddress = user.zkEvm.ethAddress;