diff --git a/packages/passport/sdk/src/zkEvm/zkEvmProvider.ts b/packages/passport/sdk/src/zkEvm/zkEvmProvider.ts index 2955be92fc..2cc005e88b 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,29 @@ 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'); + } finally { + // Clear the cache on success or error + this.#clearUserRegistrationCache(); + } } else { userZkEvmEthAddress = user.zkEvm.ethAddress; }