From 9c387de87619d3d4242ba90c1fb7e1a1e2ae4229 Mon Sep 17 00:00:00 2001 From: Hayden Fowler Date: Fri, 21 Jul 2023 10:05:56 +1000 Subject: [PATCH] ID-913 ZkEvm provider events --- .../src/components/zkevm/Request.tsx | 17 +++---- .../src/context/ImmutableProvider.tsx | 2 +- .../src/context/PassportProvider.tsx | 2 +- packages/passport/sdk/src/index.ts | 7 +++ .../passport/sdk/src/zkEvm/JsonRpcError.ts | 15 ++++-- .../sdk/src/zkEvm/rpcMethods/index.ts | 1 - .../sdk/src/zkEvm/rpcMethods/types.ts | 13 ----- ...action.test.ts => sendTransaction.test.ts} | 26 +++++----- ..._sendTransaction.ts => sendTransaction.ts} | 27 +++++++--- .../sdk/src/zkEvm/typedEventEmitter.test.ts | 33 ++++++++++++ .../sdk/src/zkEvm/typedEventEmitter.ts | 26 ++++++++++ packages/passport/sdk/src/zkEvm/types.ts | 18 +++++-- packages/passport/sdk/src/zkEvm/user/index.ts | 2 + .../loginZkEvmUser.ts} | 12 ++--- .../registerZkEvmUser.test.ts} | 12 ++--- .../registerZkEvmUser.ts} | 6 +-- .../sdk/src/zkEvm/userRegistration/index.ts | 2 - .../passport/sdk/src/zkEvm/walletHelpers.ts | 2 +- .../sdk/src/zkEvm/zkEvmProvider.test.ts | 30 +++++++++-- .../passport/sdk/src/zkEvm/zkEvmProvider.ts | 50 ++++++++++--------- 20 files changed, 206 insertions(+), 97 deletions(-) delete mode 100644 packages/passport/sdk/src/zkEvm/rpcMethods/index.ts delete mode 100644 packages/passport/sdk/src/zkEvm/rpcMethods/types.ts rename packages/passport/sdk/src/zkEvm/{rpcMethods/eth_sendTransaction.test.ts => sendTransaction.test.ts} (79%) rename packages/passport/sdk/src/zkEvm/{rpcMethods/eth_sendTransaction.ts => sendTransaction.ts} (84%) create mode 100644 packages/passport/sdk/src/zkEvm/typedEventEmitter.test.ts create mode 100644 packages/passport/sdk/src/zkEvm/typedEventEmitter.ts create mode 100644 packages/passport/sdk/src/zkEvm/user/index.ts rename packages/passport/sdk/src/zkEvm/{userRegistration/registerZkEvmUser.ts => user/loginZkEvmUser.ts} (79%) rename packages/passport/sdk/src/zkEvm/{userRegistration/createCounterfactualAddress.test.ts => user/registerZkEvmUser.test.ts} (91%) rename packages/passport/sdk/src/zkEvm/{userRegistration/createCounterfactualAddress.ts => user/registerZkEvmUser.ts} (90%) delete mode 100644 packages/passport/sdk/src/zkEvm/userRegistration/index.ts diff --git a/packages/passport/sdk-sample-app/src/components/zkevm/Request.tsx b/packages/passport/sdk-sample-app/src/components/zkevm/Request.tsx index 238966e83a..c107c6674a 100644 --- a/packages/passport/sdk-sample-app/src/components/zkevm/Request.tsx +++ b/packages/passport/sdk-sample-app/src/components/zkevm/Request.tsx @@ -16,6 +16,7 @@ enum EthereumParamType { interface EthereumParam { name: string; type?: EthereumParamType; + default?: string; } interface EthereumMethod { @@ -37,7 +38,7 @@ const EthereumMethods: EthereumMethod[] = [ name: 'eth_getBalance', params: [ { name: 'address' }, - { name: 'blockNumber/tag' }, + { name: 'blockNumber/tag', default: 'latest' }, ], }, { @@ -45,7 +46,7 @@ const EthereumMethods: EthereumMethod[] = [ params: [ { name: 'address' }, { name: 'position' }, - { name: 'blockNumber' }, + { name: 'blockNumber', default: 'latest' }, ], }, { @@ -58,7 +59,7 @@ const EthereumMethods: EthereumMethod[] = [ name: 'eth_call', params: [ { name: 'transaction' }, - { name: 'blockNumber/tag' }, + { name: 'blockNumber/tag', default: 'latest' }, ], }, { name: 'eth_blockNumber' }, @@ -93,7 +94,6 @@ const EthereumMethods: EthereumMethod[] = [ name: 'eth_getTransactionCount', params: [ { name: 'address' }, - { name: 'blockNumber' }, ], }, ]; @@ -147,10 +147,8 @@ function Request({ showRequest, setShowRequest }: RequestProps) { addMessage(selectedEthMethod?.name, result); handleClose(); } catch (err) { - if (err instanceof Error) { - addMessage('Request', err); - handleClose(); - } + addMessage('Request', err); + handleClose(); } } else { setInvalid(true); @@ -164,7 +162,7 @@ function Request({ showRequest, setShowRequest }: RequestProps) { console.error('Invalid eth method'); } else { setSelectedEthMethod(ethMethod); - setParams(Array(ethMethod.params?.length || 0).fill('')); + setParams(ethMethod.params ? ethMethod.params.map((param) => param.default || '') : []); } }; @@ -199,6 +197,7 @@ function Request({ showRequest, setShowRequest }: RequestProps) { { const newParams = [...params]; newParams[index] = e.target.value; diff --git a/packages/passport/sdk-sample-app/src/context/ImmutableProvider.tsx b/packages/passport/sdk-sample-app/src/context/ImmutableProvider.tsx index cf878cd04e..5f521c6654 100644 --- a/packages/passport/sdk-sample-app/src/context/ImmutableProvider.tsx +++ b/packages/passport/sdk-sample-app/src/context/ImmutableProvider.tsx @@ -88,7 +88,7 @@ const getPassportConfig = (environment: EnvironmentNames): PassportModuleConfigu }, }), zkEvmRpcUrl: 'https://zkevm-rpc.dev.x.immutable.com', - zkEvmChainId: 'eip155:13403', + zkEvmChainId: 'eip155:13413', relayerUrl: 'https://evm-relayer.dev.imtbl.com', indexerMrBasePath: 'https://indexer-mr.dev.imtbl.com', orderBookMrBasePath: 'https://order-book-mr.dev.imtbl.com', diff --git a/packages/passport/sdk-sample-app/src/context/PassportProvider.tsx b/packages/passport/sdk-sample-app/src/context/PassportProvider.tsx index c08447a0c8..732cb7f88f 100644 --- a/packages/passport/sdk-sample-app/src/context/PassportProvider.tsx +++ b/packages/passport/sdk-sample-app/src/context/PassportProvider.tsx @@ -125,7 +125,7 @@ export function PassportProvider({ } finally { setIsLoading(false); } - }, [passportClient, setIsLoading]); + }, [addMessage, passportClient, setIsLoading]); const providerValues = useMemo(() => ({ imxProvider, diff --git a/packages/passport/sdk/src/index.ts b/packages/passport/sdk/src/index.ts index 53309b672e..f88d1b0c9a 100644 --- a/packages/passport/sdk/src/index.ts +++ b/packages/passport/sdk/src/index.ts @@ -6,7 +6,14 @@ export { JsonRpcResponsePayload, JsonRpcRequestCallback, Provider, + ProviderEventNames, + AccountsChangedEvent, } from './zkEvm/types'; +export { + JsonRpcError, + ProviderErrorCode, + RpcErrorCode, +} from './zkEvm/JsonRpcError'; export { UserProfile, Networks, diff --git a/packages/passport/sdk/src/zkEvm/JsonRpcError.ts b/packages/passport/sdk/src/zkEvm/JsonRpcError.ts index 5f0f29d40f..5959798bca 100644 --- a/packages/passport/sdk/src/zkEvm/JsonRpcError.ts +++ b/packages/passport/sdk/src/zkEvm/JsonRpcError.ts @@ -1,19 +1,28 @@ +/** + * ProviderErrors should take priority over RpcErrorCodes + */ +export enum ProviderErrorCode { + USER_REJECTED_REQUEST = 4001, + UNAUTHORIZED = 4100, + UNSUPPORTED_METHOD = 4200, + DISCONNECTED = 4900, +} + export enum RpcErrorCode { RPC_SERVER_ERROR = -32000, INVALID_REQUEST = -32600, METHOD_NOT_FOUND = -32601, INVALID_PARAMS = -32602, INTERNAL_ERROR = -32603, - UNAUTHORIZED = -32604, PARSE_ERROR = -32700, } export class JsonRpcError extends Error { public readonly message: string; - public readonly code: RpcErrorCode; + public readonly code: ProviderErrorCode | RpcErrorCode; - constructor(code: RpcErrorCode, message: string) { + constructor(code: ProviderErrorCode | RpcErrorCode, message: string) { super(message); this.message = message; this.code = code; diff --git a/packages/passport/sdk/src/zkEvm/rpcMethods/index.ts b/packages/passport/sdk/src/zkEvm/rpcMethods/index.ts deleted file mode 100644 index 4926858eb6..0000000000 --- a/packages/passport/sdk/src/zkEvm/rpcMethods/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './eth_sendTransaction'; diff --git a/packages/passport/sdk/src/zkEvm/rpcMethods/types.ts b/packages/passport/sdk/src/zkEvm/rpcMethods/types.ts deleted file mode 100644 index 29b9f0b195..0000000000 --- a/packages/passport/sdk/src/zkEvm/rpcMethods/types.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ExternalProvider, JsonRpcProvider } from '@ethersproject/providers'; -import { PassportConfiguration } from '../../config'; -import { RelayerClient } from '../relayerClient'; -import { UserZkEvm } from '../../types'; - -export type EthMethodWithAuthParams = { - magicProvider: ExternalProvider; - jsonRpcProvider: JsonRpcProvider; - config: PassportConfiguration; - relayerClient: RelayerClient; - user: UserZkEvm; - params: Array; -}; diff --git a/packages/passport/sdk/src/zkEvm/rpcMethods/eth_sendTransaction.test.ts b/packages/passport/sdk/src/zkEvm/sendTransaction.test.ts similarity index 79% rename from packages/passport/sdk/src/zkEvm/rpcMethods/eth_sendTransaction.test.ts rename to packages/passport/sdk/src/zkEvm/sendTransaction.test.ts index 95f46f70fd..4bdd44c194 100644 --- a/packages/passport/sdk/src/zkEvm/rpcMethods/eth_sendTransaction.test.ts +++ b/packages/passport/sdk/src/zkEvm/sendTransaction.test.ts @@ -1,18 +1,18 @@ import { JsonRpcProvider, TransactionRequest } from '@ethersproject/providers'; -import { getSignedMetaTransactions } from '../walletHelpers'; -import { ethSendTransaction } from './eth_sendTransaction'; -import { mockUserZkEvm } from '../../test/mocks'; -import { RelayerClient } from '../relayerClient'; -import { PassportConfiguration } from '../../config'; -import { retryWithDelay } from '../../network/retry'; -import { RelayerTransaction, RelayerTransactionStatus } from '../types'; -import { JsonRpcError, RpcErrorCode } from '../JsonRpcError'; +import { getSignedMetaTransactions } from './walletHelpers'; +import { sendTransaction } from './sendTransaction'; +import { mockUserZkEvm } from '../test/mocks'; +import { RelayerClient } from './relayerClient'; +import { PassportConfiguration } from '../config'; +import { retryWithDelay } from '../network/retry'; +import { RelayerTransaction, RelayerTransactionStatus } from './types'; +import { JsonRpcError, RpcErrorCode } from './JsonRpcError'; jest.mock('@ethersproject/providers'); -jest.mock('../walletHelpers'); -jest.mock('../../network/retry'); +jest.mock('./walletHelpers'); +jest.mock('../network/retry'); -describe('ethSendTransaction', () => { +describe('sendTransaction', () => { const signedTransaction = 'signedTransaction123'; const signedTransactions = 'signedTransactions123'; const relayerTransactionId = 'relayerTransactionId123'; @@ -54,7 +54,7 @@ describe('ethSendTransaction', () => { hash: transactionHash, } as RelayerTransaction); - const result = await ethSendTransaction({ + const result = await sendTransaction({ params: [transactionRequest], magicProvider, jsonRpcProvider: jsonRpcProvider as JsonRpcProvider, @@ -72,7 +72,7 @@ describe('ethSendTransaction', () => { status: RelayerTransactionStatus.FAILED, } as RelayerTransaction); - await expect(ethSendTransaction({ + await expect(sendTransaction({ params: [transactionRequest], magicProvider, jsonRpcProvider: jsonRpcProvider as JsonRpcProvider, diff --git a/packages/passport/sdk/src/zkEvm/rpcMethods/eth_sendTransaction.ts b/packages/passport/sdk/src/zkEvm/sendTransaction.ts similarity index 84% rename from packages/passport/sdk/src/zkEvm/rpcMethods/eth_sendTransaction.ts rename to packages/passport/sdk/src/zkEvm/sendTransaction.ts index a5a80991c7..0c97933911 100644 --- a/packages/passport/sdk/src/zkEvm/rpcMethods/eth_sendTransaction.ts +++ b/packages/passport/sdk/src/zkEvm/sendTransaction.ts @@ -1,24 +1,37 @@ import { + ExternalProvider, + JsonRpcProvider, TransactionRequest, Web3Provider, } from '@ethersproject/providers'; -import { getNonce, getSignedMetaTransactions, chainIdNumber } from '../walletHelpers'; -import { MetaTransaction, RelayerTransactionStatus } from '../types'; -import { EthMethodWithAuthParams } from './types'; -import { JsonRpcError, RpcErrorCode } from '../JsonRpcError'; -import { retryWithDelay } from '../../network/retry'; +import { getNonce, getSignedMetaTransactions, chainIdNumber } from './walletHelpers'; +import { MetaTransaction, RelayerTransactionStatus } from './types'; +import { JsonRpcError, RpcErrorCode } from './JsonRpcError'; +import { retryWithDelay } from '../network/retry'; +import { PassportConfiguration } from '../config'; +import { RelayerClient } from './relayerClient'; +import { UserZkEvm } from '../types'; const MAX_TRANSACTION_HASH_RETRIEVAL_RETRIES = 30; const TRANSACTION_HASH_RETRIEVAL_WAIT = 1000; -export const ethSendTransaction = async ({ +export type EthSendTransactionParams = { + magicProvider: ExternalProvider; + jsonRpcProvider: JsonRpcProvider; + config: PassportConfiguration; + relayerClient: RelayerClient; + user: UserZkEvm; + params: Array; +}; + +export const sendTransaction = async ({ params, magicProvider, jsonRpcProvider, relayerClient, config, user, -}: EthMethodWithAuthParams): Promise => { +}: EthSendTransactionParams): Promise => { const transactionRequest: TransactionRequest = params[0]; if (!transactionRequest.to) { throw new JsonRpcError(RpcErrorCode.INVALID_PARAMS, 'eth_sendTransaction requires a "to" field'); diff --git a/packages/passport/sdk/src/zkEvm/typedEventEmitter.test.ts b/packages/passport/sdk/src/zkEvm/typedEventEmitter.test.ts new file mode 100644 index 0000000000..e33bf435e0 --- /dev/null +++ b/packages/passport/sdk/src/zkEvm/typedEventEmitter.test.ts @@ -0,0 +1,33 @@ +import TypedEventEmitter from './typedEventEmitter'; + +type TestEvents = { + testEvent1: [Array]; + testEvent2: [{ id: number }], +}; + +describe('TypedEventEmitter', () => { + it('should be able to emit and listen to events', () => { + const eventEmitter = new TypedEventEmitter(); + + const testEvent1Handler = jest.fn(); + const testEvent2Handler = jest.fn(); + + eventEmitter.on('testEvent1', testEvent1Handler); + eventEmitter.on('testEvent2', testEvent2Handler); + + eventEmitter.emit('testEvent1', [1, 2, 3]); + eventEmitter.emit('testEvent2', { id: 1 }); + + expect(testEvent1Handler).toHaveBeenCalledWith([1, 2, 3]); + expect(testEvent2Handler).toHaveBeenCalledWith({ id: 1 }); + + eventEmitter.removeListener('testEvent1', testEvent1Handler); + eventEmitter.removeListener('testEvent2', testEvent2Handler); + + eventEmitter.emit('testEvent1', [4, 5, 6]); + eventEmitter.emit('testEvent2', { id: 2 }); + + expect(testEvent1Handler).toHaveBeenCalledTimes(1); + expect(testEvent2Handler).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/passport/sdk/src/zkEvm/typedEventEmitter.ts b/packages/passport/sdk/src/zkEvm/typedEventEmitter.ts new file mode 100644 index 0000000000..82d1c4a462 --- /dev/null +++ b/packages/passport/sdk/src/zkEvm/typedEventEmitter.ts @@ -0,0 +1,26 @@ +import { EventEmitter } from 'events'; + +export default class TypedEventEmitter> { + private emitter = new EventEmitter(); + + emit( + eventName: TEventName, + ...eventArg: TEvents[TEventName] + ) { + this.emitter.emit(eventName, ...(eventArg as [])); + } + + on( + eventName: TEventName, + handler: (...eventArg: TEvents[TEventName]) => void, + ) { + this.emitter.on(eventName, handler as any); + } + + removeListener( + eventName: TEventName, + handler: (...eventArg: TEvents[TEventName]) => void, + ) { + this.emitter.removeListener(eventName, handler as any); + } +} diff --git a/packages/passport/sdk/src/zkEvm/types.ts b/packages/passport/sdk/src/zkEvm/types.ts index 7445711db6..2e23e0ed3a 100644 --- a/packages/passport/sdk/src/zkEvm/types.ts +++ b/packages/passport/sdk/src/zkEvm/types.ts @@ -65,14 +65,26 @@ export interface JsonRpcResponsePayload { } export type Provider = { - request: (request: RequestArguments) => Promise + request: (request: RequestArguments) => Promise; sendAsync: ( request: JsonRpcRequestPayload | JsonRpcRequestPayload[], callback: JsonRpcRequestCallback, - ) => void + ) => void; send: ( request: string | JsonRpcRequestPayload | JsonRpcRequestPayload[], callbackOrParams?: JsonRpcRequestCallback | Array, callback?: JsonRpcRequestCallback, - ) => void + ) => void; + on: (event: string, listener: (...args: any[]) => void) => void; + removeListener: (event: string, listener: (...args: any[]) => void) => void; }; + +export enum ProviderEventNames { + ACCOUNTS_CHANGED = 'accountsChanged', +} + +export type AccountsChangedEvent = Array; + +export interface ProviderEvents extends Record { + [ProviderEventNames.ACCOUNTS_CHANGED]: [AccountsChangedEvent], +} diff --git a/packages/passport/sdk/src/zkEvm/user/index.ts b/packages/passport/sdk/src/zkEvm/user/index.ts new file mode 100644 index 0000000000..985bb87313 --- /dev/null +++ b/packages/passport/sdk/src/zkEvm/user/index.ts @@ -0,0 +1,2 @@ +export * from './registerZkEvmUser'; +export * from './loginZkEvmUser'; diff --git a/packages/passport/sdk/src/zkEvm/userRegistration/registerZkEvmUser.ts b/packages/passport/sdk/src/zkEvm/user/loginZkEvmUser.ts similarity index 79% rename from packages/passport/sdk/src/zkEvm/userRegistration/registerZkEvmUser.ts rename to packages/passport/sdk/src/zkEvm/user/loginZkEvmUser.ts index f206f79f4f..be257a9ad9 100644 --- a/packages/passport/sdk/src/zkEvm/userRegistration/registerZkEvmUser.ts +++ b/packages/passport/sdk/src/zkEvm/user/loginZkEvmUser.ts @@ -1,29 +1,29 @@ import { MultiRollupApiClients } from '@imtbl/generated-clients'; import { ExternalProvider } from '@ethersproject/providers'; -import { createCounterfactualAddress } from './createCounterfactualAddress'; +import { registerZkEvmUser } from './registerZkEvmUser'; import { UserZkEvm } from '../../types'; import AuthManager from '../../authManager'; import { PassportConfiguration } from '../../config'; import MagicAdapter from '../../magicAdapter'; -type RegisterZkEvmUserInput = { +type LoginZkEvmUserInput = { authManager: AuthManager; config: PassportConfiguration; magicAdapter: MagicAdapter; multiRollupApiClients: MultiRollupApiClients; }; -type RegisterZkEvmUserOutput = { +type LoginZkEvmUserOutput = { user: UserZkEvm; magicProvider: ExternalProvider; }; -export const registerZkEvmUser = async ({ +export const loginZkEvmUser = async ({ authManager, config, magicAdapter, multiRollupApiClients, -}: RegisterZkEvmUserInput): Promise => { +}: LoginZkEvmUserInput): Promise => { const user = await authManager.getUser() || await authManager.login(); if (!user.idToken) { throw new Error('User is missing idToken'); @@ -36,7 +36,7 @@ export const registerZkEvmUser = async ({ if (!user.zkEvm) { // Generate counterfactual address and retrieve updated Auth0 user - const userZkevm = await createCounterfactualAddress({ + const userZkevm = await registerZkEvmUser({ authManager, magicProvider, multiRollupApiClients, diff --git a/packages/passport/sdk/src/zkEvm/userRegistration/createCounterfactualAddress.test.ts b/packages/passport/sdk/src/zkEvm/user/registerZkEvmUser.test.ts similarity index 91% rename from packages/passport/sdk/src/zkEvm/userRegistration/createCounterfactualAddress.test.ts rename to packages/passport/sdk/src/zkEvm/user/registerZkEvmUser.test.ts index 3811ab645c..2e09530167 100644 --- a/packages/passport/sdk/src/zkEvm/userRegistration/createCounterfactualAddress.test.ts +++ b/packages/passport/sdk/src/zkEvm/user/registerZkEvmUser.test.ts @@ -1,14 +1,14 @@ import { Web3Provider } from '@ethersproject/providers'; import { signRaw } from '@imtbl/toolkit'; import { MultiRollupApiClients } from '@imtbl/generated-clients'; -import { createCounterfactualAddress } from './createCounterfactualAddress'; +import { registerZkEvmUser } from './registerZkEvmUser'; import AuthManager from '../../authManager'; import { mockUser, mockUserZkEvm } from '../../test/mocks'; jest.mock('@ethersproject/providers'); jest.mock('@imtbl/toolkit'); -describe('createCounterFactualAddress', () => { +describe('registerZkEvmUser', () => { const getSignerMock = jest.fn(); const ethSignerMock = { getAddress: jest.fn(), @@ -42,7 +42,7 @@ describe('createCounterFactualAddress', () => { throw new Error('Internal server error'); }); - await expect(async () => createCounterfactualAddress({ + await expect(async () => registerZkEvmUser({ authManager: authManager as unknown as AuthManager, magicProvider, multiRollupApiClients: multiRollupApiClients as unknown as MultiRollupApiClients, @@ -59,7 +59,7 @@ describe('createCounterFactualAddress', () => { authManager.loginSilent.mockResolvedValue(null); - await expect(async () => createCounterfactualAddress({ + await expect(async () => registerZkEvmUser({ authManager: authManager as unknown as AuthManager, magicProvider, multiRollupApiClients: multiRollupApiClients as unknown as MultiRollupApiClients, @@ -76,7 +76,7 @@ describe('createCounterFactualAddress', () => { authManager.loginSilent.mockResolvedValue(mockUser); - await expect(async () => createCounterfactualAddress({ + await expect(async () => registerZkEvmUser({ authManager: authManager as unknown as AuthManager, magicProvider, multiRollupApiClients: multiRollupApiClients as unknown as MultiRollupApiClients, @@ -92,7 +92,7 @@ describe('createCounterFactualAddress', () => { authManager.loginSilent.mockResolvedValue(mockUserZkEvm); - const result = await createCounterfactualAddress({ + const result = await registerZkEvmUser({ authManager: authManager as unknown as AuthManager, magicProvider, multiRollupApiClients: multiRollupApiClients as unknown as MultiRollupApiClients, diff --git a/packages/passport/sdk/src/zkEvm/userRegistration/createCounterfactualAddress.ts b/packages/passport/sdk/src/zkEvm/user/registerZkEvmUser.ts similarity index 90% rename from packages/passport/sdk/src/zkEvm/userRegistration/createCounterfactualAddress.ts rename to packages/passport/sdk/src/zkEvm/user/registerZkEvmUser.ts index 726ff63908..50d56ac64f 100644 --- a/packages/passport/sdk/src/zkEvm/userRegistration/createCounterfactualAddress.ts +++ b/packages/passport/sdk/src/zkEvm/user/registerZkEvmUser.ts @@ -5,7 +5,7 @@ import { UserZkEvm } from '../../types'; import AuthManager from '../../authManager'; import { JsonRpcError, RpcErrorCode } from '../JsonRpcError'; -export type CreateCounterfactualAddressInput = { +export type RegisterZkEvmUserInput = { authManager: AuthManager; magicProvider: ExternalProvider, multiRollupApiClients: MultiRollupApiClients, @@ -14,12 +14,12 @@ export type CreateCounterfactualAddressInput = { const MESSAGE_TO_SIGN = 'Only sign this message from Immutable Passport'; -export async function createCounterfactualAddress({ +export async function registerZkEvmUser({ authManager, magicProvider, multiRollupApiClients, accessToken, -}: CreateCounterfactualAddressInput): Promise { +}: RegisterZkEvmUserInput): Promise { const web3Provider = new Web3Provider( magicProvider, ); diff --git a/packages/passport/sdk/src/zkEvm/userRegistration/index.ts b/packages/passport/sdk/src/zkEvm/userRegistration/index.ts deleted file mode 100644 index 15d224d40f..0000000000 --- a/packages/passport/sdk/src/zkEvm/userRegistration/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './createCounterfactualAddress'; -export * from './registerZkEvmUser'; diff --git a/packages/passport/sdk/src/zkEvm/walletHelpers.ts b/packages/passport/sdk/src/zkEvm/walletHelpers.ts index a217df9452..feeb9d3a60 100644 --- a/packages/passport/sdk/src/zkEvm/walletHelpers.ts +++ b/packages/passport/sdk/src/zkEvm/walletHelpers.ts @@ -44,7 +44,7 @@ export function digestOfTransactionsAndNonce(nonce: BigNumberish, normalisedTran } export const getNonce = async (jsonRpcProvider: JsonRpcProvider, smartContractWalletAddress: string) => { - const code = await jsonRpcProvider.send('eth_getCode', [smartContractWalletAddress]); + const code = await jsonRpcProvider.send('eth_getCode', [smartContractWalletAddress, 'latest']); if (code && code !== '0x') { const contract = new ethers.Contract( smartContractWalletAddress, diff --git a/packages/passport/sdk/src/zkEvm/zkEvmProvider.test.ts b/packages/passport/sdk/src/zkEvm/zkEvmProvider.test.ts index ec53b8e9d5..bc3c017e75 100644 --- a/packages/passport/sdk/src/zkEvm/zkEvmProvider.test.ts +++ b/packages/passport/sdk/src/zkEvm/zkEvmProvider.test.ts @@ -1,10 +1,10 @@ import { JsonRpcProvider } from '@ethersproject/providers'; import { ZkEvmProviderInput, ZkEvmProvider } from './zkEvmProvider'; -import { registerZkEvmUser } from './userRegistration'; +import { loginZkEvmUser } from './user'; jest.mock('@ethersproject/providers'); jest.mock('./relayerClient'); -jest.mock('./userRegistration'); +jest.mock('./user'); describe('ZkEvmProvider', () => { const getProvider = () => { @@ -182,7 +182,7 @@ describe('ZkEvmProvider', () => { }, }; const mockMagicProvider = {}; - (registerZkEvmUser as jest.Mock).mockResolvedValue({ + (loginZkEvmUser as jest.Mock).mockResolvedValue({ user: mockUser, magicProvider: mockMagicProvider, }); @@ -193,7 +193,29 @@ describe('ZkEvmProvider', () => { expect(resultOne).toEqual([mockUser.zkEvm.ethAddress]); expect(resultTwo).toEqual([mockUser.zkEvm.ethAddress]); - expect(registerZkEvmUser).toBeCalledTimes(1); + expect(loginZkEvmUser).toBeCalledTimes(1); + }); + + it('should emit accountsChanged event when user logs in', async () => { + const mockUser = { + zkEvm: { + ethAddress: '0x123', + }, + }; + const mockMagicProvider = {}; + (loginZkEvmUser as jest.Mock).mockResolvedValue({ + user: mockUser, + magicProvider: mockMagicProvider, + }); + const provider = getProvider(); + const onAccountsChanged = jest.fn(); + + provider.on('accountsChanged', onAccountsChanged); + + const result = await provider.request({ method: 'eth_requestAccounts', params: [] }); + + expect(result).toEqual([mockUser.zkEvm.ethAddress]); + expect(onAccountsChanged).toHaveBeenCalledWith([mockUser.zkEvm.ethAddress]); }); }); }); diff --git a/packages/passport/sdk/src/zkEvm/zkEvmProvider.ts b/packages/passport/sdk/src/zkEvm/zkEvmProvider.ts index 52b3fb440b..9ecccef3aa 100644 --- a/packages/passport/sdk/src/zkEvm/zkEvmProvider.ts +++ b/packages/passport/sdk/src/zkEvm/zkEvmProvider.ts @@ -1,22 +1,24 @@ import { ExternalProvider, JsonRpcProvider } from '@ethersproject/providers'; import { MultiRollupApiClients } from '@imtbl/generated-clients'; -import { ethSendTransaction } from './rpcMethods'; +import { sendTransaction } from './sendTransaction'; import { JsonRpcRequestCallback, JsonRpcRequestPayload, JsonRpcResponsePayload, Provider, + ProviderEventNames, + ProviderEvents, RequestArguments, } from './types'; import AuthManager from '../authManager'; +import MagicAdapter from '../magicAdapter'; +import TypedEventEmitter from './typedEventEmitter'; import { PassportConfiguration } from '../config'; import { ConfirmationScreen } from '../confirmation'; -import MagicAdapter from '../magicAdapter'; import { UserZkEvm } from '../types'; import { RelayerClient } from './relayerClient'; -import { EthMethodWithAuthParams } from './rpcMethods/types'; -import { JsonRpcError, RpcErrorCode } from './JsonRpcError'; -import { registerZkEvmUser } from './userRegistration'; +import { JsonRpcError, ProviderErrorCode, RpcErrorCode } from './JsonRpcError'; +import { loginZkEvmUser } from './user'; export type ZkEvmProviderInput = { authManager: AuthManager; @@ -31,7 +33,7 @@ type LoggedInZkEvmProvider = { user: UserZkEvm; }; -export class ZkEvmProvider implements Provider { +export class ZkEvmProvider extends TypedEventEmitter implements Provider { private readonly authManager: AuthManager; private readonly config: PassportConfiguration; @@ -57,6 +59,8 @@ export class ZkEvmProvider implements Provider { confirmationScreen, multiRollupApiClients, }: ZkEvmProviderInput) { + super(); + this.authManager = authManager; this.magicAdapter = magicAdapter; this.config = config; @@ -71,27 +75,12 @@ export class ZkEvmProvider implements Provider { } private async performRequest(request: RequestArguments): Promise { - const authWrapper = (fn: (params: EthMethodWithAuthParams) => Promise) => { - if (!this.isLoggedIn()) { - throw new JsonRpcError(RpcErrorCode.UNAUTHORIZED, 'Unauthorised - call eth_requestAccounts first'); - } - - return fn({ - params: request.params || [], - magicProvider: this.magicProvider, - jsonRpcProvider: this.jsonRpcProvider, - config: this.config, - relayerClient: this.relayerClient, - user: this.user, - }); - }; - switch (request.method) { case 'eth_requestAccounts': { if (this.isLoggedIn()) { return [this.user.zkEvm.ethAddress]; } - const { magicProvider, user } = await registerZkEvmUser({ + const { magicProvider, user } = await loginZkEvmUser({ authManager: this.authManager, config: this.config, magicAdapter: this.magicAdapter, @@ -101,10 +90,23 @@ export class ZkEvmProvider implements Provider { this.user = user; this.magicProvider = magicProvider; + this.emit(ProviderEventNames.ACCOUNTS_CHANGED, [this.user.zkEvm.ethAddress]); + return [this.user.zkEvm.ethAddress]; } case 'eth_sendTransaction': { - return authWrapper(ethSendTransaction); + if (!this.isLoggedIn()) { + throw new JsonRpcError(ProviderErrorCode.UNAUTHORIZED, 'Unauthorised - call eth_requestAccounts first'); + } + + return sendTransaction({ + params: request.params || [], + magicProvider: this.magicProvider, + jsonRpcProvider: this.jsonRpcProvider, + config: this.config, + relayerClient: this.relayerClient, + user: this.user, + }); } case 'eth_accounts': { return this.isLoggedIn() ? [this.user.zkEvm.ethAddress] : []; @@ -125,7 +127,7 @@ export class ZkEvmProvider implements Provider { return this.jsonRpcProvider.send(request.method, request.params || []); } default: { - throw new JsonRpcError(RpcErrorCode.METHOD_NOT_FOUND, 'Method not supported'); + throw new JsonRpcError(ProviderErrorCode.UNSUPPORTED_METHOD, 'Method not supported'); } } }