import { MultiRollupApiClients } from '@imtbl/generated-clients'; import type { PublicClient } from 'viem'; import { getEip155ChainId } from '../walletHelpers'; import { JsonRpcError, RpcErrorCode } from '../JsonRpcError'; import { signRaw } from '../../utils/crypto'; import type { WalletSigner, GetUserFunction } from '../../types'; export type RegisterZkEvmUserInput = { /** * Function to get fresh user tokens. Used for triggering background refresh after registration. * If not provided, no background refresh is performed. */ getUser?: GetUserFunction; ethSigner: WalletSigner, multiRollupApiClients: MultiRollupApiClients, accessToken: string; rpcProvider: PublicClient; }; const MESSAGE_TO_SIGN = 'Only sign this message from Immutable Passport'; export async function registerZkEvmUser({ getUser, ethSigner, multiRollupApiClients, accessToken, rpcProvider, }: RegisterZkEvmUserInput): Promise { // Parallelize the operations that can happen concurrently const getAddressPromise = ethSigner.getAddress(); const signRawPromise = signRaw(MESSAGE_TO_SIGN, ethSigner); const detectNetworkPromise = rpcProvider.getChainId(); const listChainsPromise = multiRollupApiClients.chainsApi.listChains(); const [ethereumAddress, ethereumSignature, chainId, chainListResponse] = await Promise.all([ getAddressPromise, signRawPromise, detectNetworkPromise, listChainsPromise, ]); const eipChainId = getEip155ChainId(Number(chainId)); const chainName = chainListResponse.data?.result?.find((chain) => chain.id === eipChainId)?.name; if (!chainName) { throw new JsonRpcError( RpcErrorCode.INTERNAL_ERROR, `Chain name does not exist on for chain id ${chainId}`, ); } try { const registrationResponse = await multiRollupApiClients.passportApi.createCounterfactualAddressV2({ chainName, createCounterfactualAddressRequest: { ethereum_address: ethereumAddress, ethereum_signature: ethereumSignature, }, }, { headers: { Authorization: `Bearer ${accessToken}` }, }); // Trigger a background refresh to get the updated user with zkEvm info. // This is a best-effort operation - the caller will need to get updated // user data from their session manager (e.g., NextAuth session callback // will refresh tokens and return updated user). if (getUser) { getUser().catch(() => { // Ignore errors - this is a best-effort refresh }); } return registrationResponse.data.counterfactual_address; } catch (error) { throw new JsonRpcError(RpcErrorCode.INTERNAL_ERROR, `Failed to create counterfactual address: ${error}`); } }