From f9e608e1607627ee8b46f31e28f12345d06ccdbf Mon Sep 17 00:00:00 2001 From: vvava <94397450+vvava@users.noreply.github.com> Date: Mon, 26 Aug 2024 19:00:45 +0200 Subject: [PATCH] feat: Cp-8361 add EVM provider (#32) Co-authored-by: Gergely Lovas --- package.json | 10 +- src/background/providers/CoreProvider.test.ts | 1398 ----------------- src/background/providers/CoreProvider.ts | 310 ---- .../MultiWalletProviderProxy.test.ts | 45 +- .../providers/MultiWalletProviderProxy.ts | 16 +- .../initializeInpageProvider.test.ts | 10 +- .../providers/initializeInpageProvider.ts | 42 +- src/background/vmModules/ModuleManager.ts | 11 + src/background/vmModules/mocks/avm.ts | 2 + src/background/vmModules/mocks/coreEth.ts | 1 + src/background/vmModules/mocks/evm.ts | 1 + src/background/vmModules/mocks/pvm.ts | 1 + src/types/globals.d.ts | 6 +- yarn.lock | 144 +- 14 files changed, 177 insertions(+), 1820 deletions(-) delete mode 100644 src/background/providers/CoreProvider.test.ts delete mode 100644 src/background/providers/CoreProvider.ts diff --git a/package.json b/package.json index a4b033acd..8ed8f8166 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ }, "dependencies": { "@avalabs/avalanchejs": "4.0.5", - "@avalabs/bitcoin-module": "0.1.4", "@avalabs/bridge-unified": "2.1.0", "@avalabs/core-bridge-sdk": "3.1.0-alpha.4", "@avalabs/core-chains-sdk": "3.1.0-alpha.4", @@ -39,7 +38,9 @@ "@avalabs/hw-app-avalanche": "0.14.1", "@avalabs/core-k2-components": "4.18.0-alpha.47", "@avalabs/types": "3.1.0-alpha.3", - "@avalabs/vm-module-types": "0.1.4", + "@avalabs/vm-module-types": "0.3.0", + "@avalabs/bitcoin-module": "0.3.0", + "@avalabs/evm-module": "0.3.0", "@blockaid/client": "0.10.0", "@coinbase/cbpay-js": "1.6.0", "@cubist-labs/cubesigner-sdk": "0.3.28", @@ -68,7 +69,7 @@ "date-fns": "2.28.0", "eth-json-rpc-middleware": "8.0.1", "eth-rpc-errors": "4.0.3", - "ethers": "6.7.1", + "ethers": "6.8.1", "events": "3.3.0", "fireblocks-sdk": "5.20.0", "i18next": "21.9.2", @@ -242,7 +243,8 @@ "@avalabs/core-bridge-sdk>@avalabs/core-wallets-sdk>hdkey>secp256k1": false, "@avalabs/bitcoin-module>@avalabs/core-wallets-sdk>@avalabs/hw-app-avalanche>@ledgerhq/hw-app-eth>@ledgerhq/domain-service>eip55>keccak": false, "@avalabs/bitcoin-module>@avalabs/core-wallets-sdk>@ledgerhq/hw-app-btc>bitcoinjs-lib>bip32>tiny-secp256k1": false, - "@avalabs/bitcoin-module>@avalabs/core-wallets-sdk>hdkey>secp256k1": false + "@avalabs/bitcoin-module>@avalabs/core-wallets-sdk>hdkey>secp256k1": false, + "@avalabs/evm-module": false } } } diff --git a/src/background/providers/CoreProvider.test.ts b/src/background/providers/CoreProvider.test.ts deleted file mode 100644 index 12dc55fb2..000000000 --- a/src/background/providers/CoreProvider.test.ts +++ /dev/null @@ -1,1398 +0,0 @@ -import { ethErrors } from 'eth-rpc-errors'; -import { CoreProvider } from './CoreProvider'; -import { DAppProviderRequest } from '../connections/dAppConnection/models'; -import AutoPairingPostMessageConnection from '../utils/messaging/AutoPairingPostMessageConnection'; -import { EventNames } from './models'; -import { matchingPayload } from './ChainAgnosticProvider.test'; - -jest.mock('../utils/messaging/AutoPairingPostMessageConnection', () => { - const mocks = { - connect: jest.fn().mockResolvedValue(undefined), - on: jest.fn(), - request: jest.fn().mockResolvedValue({}), - }; - return jest.fn().mockReturnValue(mocks); -}); - -jest.mock('./utils/onDomReady'); - -const channelMockResolvedValue = { - isUnlocked: true, - chainId: '0x1', - networkVersion: '1', - accounts: ['0x00000'], -}; -describe('src/background/providers/CoreProvider', () => { - const channelMock = new AutoPairingPostMessageConnection(false); - const addEventListenerSpy = jest.spyOn(window, 'addEventListener'); - - beforeEach(() => { - jest.mocked(channelMock.connect).mockResolvedValueOnce(undefined); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - describe('EIP-5749', () => { - it('sets the ProviderInfo', () => { - const provider = new CoreProvider(); - expect(provider.info).toEqual({ - description: 'EVM_PROVIDER_INFO_DESCRIPTION', - icon: 'EVM_PROVIDER_INFO_ICON', - name: 'EVM_PROVIDER_INFO_NAME', - uuid: 'EVM_PROVIDER_INFO_UUID', - rdns: 'EVM_PROVIDER_INFO_RDNS', - }); - }); - }); - - describe('EIP-1193', () => { - describe('request', () => { - it('collects pending requests till the dom is ready', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - // wait for init to finish - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(1); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: DAppProviderRequest.INIT_DAPP_STATE, - }) - ); - - // response for 'some-method' - (channelMock.request as jest.Mock).mockResolvedValue('success'); - const rpcResultCallback = jest.fn(); - provider - .request({ - method: 'some-method', - params: [{ param1: 1 }], - }) - .then(rpcResultCallback); - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(2); - - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(2); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: 'some-method', - params: [{ param1: 1 }], - }) - ); - - expect(rpcResultCallback).toHaveBeenCalledWith('success'); - }); - - it('always returns JSON RPC-compatible error', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - // wait for init to finish - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(1); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: DAppProviderRequest.INIT_DAPP_STATE, - }) - ); - - // response for 'eth_requestAccounts' - (channelMock.request as jest.Mock).mockRejectedValueOnce( - new Error('non RPC error') - ); - const callCallback = jest.fn(); - provider - .request({ - method: 'eth_requestAccounts', - }) - .catch(callCallback); - await new Promise(process.nextTick); - - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(2); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: 'eth_requestAccounts', - }) - ); - - expect(callCallback).toHaveBeenCalledWith(new Error('non RPC error')); - }); - - it('does not double wraps JSON RPC errors', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - // wait for init to finish - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(1); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: DAppProviderRequest.INIT_DAPP_STATE, - }) - ); - - // response for 'eth_requestAccounts' - (channelMock.request as jest.Mock).mockRejectedValueOnce({ - code: 4902, - message: - 'Unrecognized chain ID "0x3FF82". Try adding the chain using wallet_addEthereumChain first.', - stack: 'Error: Unrecognized chain ID "0x3FF82"', - }); - const callCallback = jest.fn(); - provider - .request({ - method: 'eth_requestAccounts', - }) - .catch(callCallback); - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(2); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: 'eth_requestAccounts', - }) - ); - - expect(callCallback).toHaveBeenCalledWith({ - code: 4902, - message: - 'Unrecognized chain ID "0x3FF82". Try adding the chain using wallet_addEthereumChain first.', - stack: 'Error: Unrecognized chain ID "0x3FF82"', - }); - }); - }); - - describe('events', () => { - describe(`connect`, () => { - it('should emit `connect` when chainId first set', async () => { - (channelMock.request as jest.Mock).mockResolvedValueOnce( - channelMockResolvedValue - ); - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const connectSubscription = jest.fn(); - provider.addListener('connect', connectSubscription); - - // // wait for init to finish - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(1); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: DAppProviderRequest.INIT_DAPP_STATE, - }) - ); - - expect(connectSubscription).toHaveBeenCalledTimes(1); - expect(connectSubscription).toHaveBeenCalledWith({ chainId: '0x1' }); - }); - - it('should not emit connect if chain is still loading', async () => { - (channelMock.request as jest.Mock).mockReset(); - (channelMock.request as jest.Mock).mockResolvedValue({ - isUnlocked: true, - chainId: '0x0', - networkVersion: 'loading', - accounts: ['0x00000'], - }); - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const connectSubscription = jest.fn(); - provider.addListener('connect', connectSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(1); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: DAppProviderRequest.INIT_DAPP_STATE, - }) - ); - - expect(connectSubscription).not.toHaveBeenCalled(); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'chainChanged', - params: { chainId: '0x1', networkVersion: '1' }, - }); - expect(connectSubscription).toHaveBeenCalledTimes(1); - expect(connectSubscription).toHaveBeenCalledWith({ - chainId: '0x1', - }); - }); - - it('should emit connect on re-connect after disconnected', async () => { - const provider = new CoreProvider(); - (channelMock.request as jest.Mock).mockResolvedValue( - channelMockResolvedValue - ); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const connectSubscription = jest.fn(); - const disconnectSubscription = jest.fn(); - provider.addListener('connect', connectSubscription); - provider.addListener('disconnect', disconnectSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(connectSubscription).toHaveBeenCalledTimes(1); - expect(connectSubscription).toHaveBeenCalledWith({ - chainId: '0x1', - }); - expect(disconnectSubscription).not.toHaveBeenCalled(); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'disconnect', - }); - - expect(disconnectSubscription).toHaveBeenCalledTimes(1); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'chainChanged', - params: { chainId: '0x2', networkVersion: '2' }, - }); - - expect(connectSubscription).toHaveBeenCalledTimes(2); - expect(connectSubscription).toHaveBeenCalledWith({ - chainId: '0x2', - }); - }); - }); - - describe('disconnect', () => { - it('emits disconnect event with error', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const disconnectSubscription = jest.fn(); - provider.addListener('disconnect', disconnectSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(provider._isConnected).toBe(true); - expect(provider._state.isConnected).toBe(true); - expect(provider._state.accounts).toStrictEqual(['0x00000']); - expect(provider.selectedAddress).toStrictEqual('0x00000'); - - expect(disconnectSubscription).not.toHaveBeenCalled(); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'disconnect', - }); - - expect(provider._isConnected).toBe(false); - expect(provider._state.isConnected).toBe(false); - expect(provider._state.accounts).toBe(null); - expect(provider.selectedAddress).toBe(null); - - expect(disconnectSubscription).toHaveBeenCalledTimes(1); - expect(disconnectSubscription).toHaveBeenCalledWith( - ethErrors.provider.disconnected() - ); - }); - }); - - describe('chainChanged', () => { - it('should not emit `chainChanged` on initialization', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const chainChangedSubscription = jest.fn(); - provider.addListener('chainChanged', chainChangedSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(chainChangedSubscription).not.toHaveBeenCalled(); - }); - - it('emits `chainChanged` when gets connected to a chain later', async () => { - (channelMock.request as jest.Mock).mockReset(); - (channelMock.request as jest.Mock).mockResolvedValue({ - isUnlocked: true, - chainId: '0x0', - networkVersion: 'loading', - accounts: ['0x00000'], - }); - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const chainChangedSubscription = jest.fn(); - provider.addListener('chainChanged', chainChangedSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(chainChangedSubscription).not.toHaveBeenCalled(); - (channelMock.on as jest.Mock).mock.calls[0]?.[1]({ - method: 'chainChanged', - params: { chainId: '0x1', networkVersion: '1' }, - }); - expect(chainChangedSubscription).toHaveBeenCalledTimes(1); - expect(chainChangedSubscription).toHaveBeenCalledWith('0x1'); - }); - - it('should not emit `chainChanged` when chain is set to the same value', async () => { - const provider = new CoreProvider(); - - (channelMock.request as jest.Mock).mockResolvedValueOnce( - channelMockResolvedValue - ); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const chainChangedSubscription = jest.fn(); - provider.addListener('chainChanged', chainChangedSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(chainChangedSubscription).not.toHaveBeenCalled(); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'chainChanged', - params: { chainId: '0x1', networkVersion: '1' }, - }); - expect(chainChangedSubscription).not.toHaveBeenCalled(); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'chainChanged', - params: { chainId: '0x1', networkVersion: '1' }, - }); - expect(chainChangedSubscription).not.toHaveBeenCalled(); - }); - - it('emits `chainChanged` when chain is set to new value', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const chainChangedSubscription = jest.fn(); - provider.addListener('chainChanged', chainChangedSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(chainChangedSubscription).not.toHaveBeenCalled(); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'chainChanged', - params: { chainId: '0x2', networkVersion: '1' }, - }); - expect(chainChangedSubscription).toHaveBeenCalledTimes(1); - expect(chainChangedSubscription).toHaveBeenCalledWith('0x2'); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'chainChanged', - params: { chainId: '0x1', networkVersion: '1' }, - }); - expect(chainChangedSubscription).toHaveBeenCalledTimes(2); - expect(chainChangedSubscription).toHaveBeenCalledWith('0x1'); - }); - }); - - describe('accountsChanged', () => { - it('emits `accountsChanged` on initialization', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const accountsChangedSubscription = jest.fn(); - provider.addListener('accountsChanged', accountsChangedSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(accountsChangedSubscription).toHaveBeenCalledTimes(1); - expect(accountsChangedSubscription).toHaveBeenCalledWith(['0x00000']); - expect(provider._state.accounts).toStrictEqual(['0x00000']); - expect(provider.selectedAddress).toStrictEqual('0x00000'); - }); - - it('emits `accountsChanged` on initialization with empty array if no accounts', async () => { - (channelMock.request as jest.Mock).mockReset(); - (channelMock.request as jest.Mock).mockResolvedValue({ - isUnlocked: true, - chainId: '0x1', - networkVersion: '1', - accounts: undefined, - }); - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const accountsChangedSubscription = jest.fn(); - provider.addListener('accountsChanged', accountsChangedSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(accountsChangedSubscription).toHaveBeenCalledTimes(1); - expect(accountsChangedSubscription).toHaveBeenCalledWith([]); - }); - - it('should not emit `accountsChanged` when account is set to the same value', async () => { - const provider = new CoreProvider(); - - (channelMock.request as jest.Mock).mockResolvedValueOnce( - channelMockResolvedValue - ); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const accountsChangedSubscription = jest.fn(); - provider.addListener('accountsChanged', accountsChangedSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(accountsChangedSubscription).toHaveBeenCalledTimes(1); - expect(accountsChangedSubscription).toHaveBeenCalledWith(['0x00000']); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'accountsChanged', - params: ['0x00000'], - }); - expect(accountsChangedSubscription).toHaveBeenCalledTimes(1); - }); - - it('should emit `accountsChanged` when account is set to new value', async () => { - const provider = new CoreProvider(); - - (channelMock.request as jest.Mock).mockResolvedValueOnce( - channelMockResolvedValue - ); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const accountsChangedSubscription = jest.fn(); - provider.addListener('accountsChanged', accountsChangedSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(accountsChangedSubscription).toHaveBeenCalledTimes(1); - expect(accountsChangedSubscription).toHaveBeenCalledWith(['0x00000']); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'accountsChanged', - params: ['0x10000'], - }); - expect(accountsChangedSubscription).toHaveBeenCalledTimes(2); - expect(accountsChangedSubscription).toHaveBeenCalledWith(['0x10000']); - expect(provider._state.accounts).toStrictEqual(['0x10000']); - expect(provider.selectedAddress).toStrictEqual('0x10000'); - }); - }); - }); - - describe('legacy', () => { - describe('sendAsync', () => { - it('should call the requests correctly', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - // wait for init to finish - await new Promise(process.nextTick); - - // response for 'some-method' - (channelMock.request as jest.Mock).mockResolvedValueOnce('success'); - // response for domain metadata send - (channelMock.request as jest.Mock).mockResolvedValueOnce(undefined); - const rpcResultCallback = jest.fn(); - provider.sendAsync( - { - method: 'some-method', - params: [{ param1: 1 }], - }, - rpcResultCallback - ); - await new Promise(process.nextTick); - - // no domReady happened yet, still only one call sent - expect(channelMock.request).toHaveBeenCalledTimes(2); - - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: 'some-method', - params: [{ param1: 1 }], - }) - ); - - expect(rpcResultCallback).toHaveBeenCalledWith(null, { - method: 'some-method', - result: 'success', - }); - }); - - it('should support batched requets', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - // wait for init to finish - await new Promise(process.nextTick); - - // response for 'some-method' - (channelMock.request as jest.Mock).mockResolvedValue('success'); - const rpcResultCallback = jest.fn(); - provider.sendAsync( - [ - { - method: 'some-method', - params: [{ param1: 1 }], - }, - { - method: 'some-method2', - params: [{ param1: 2 }], - }, - ], - rpcResultCallback - ); - - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(3); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: 'some-method', - params: [{ param1: 1 }], - }) - ); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: 'some-method2', - params: [{ param1: 2 }], - }) - ); - - expect(rpcResultCallback).toHaveBeenCalledWith(null, [ - { method: 'some-method', result: 'success' }, - { method: 'some-method2', result: 'success' }, - ]); - }); - }); - - describe('send', () => { - it('should call the requests properly', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - // wait for init to finish - await new Promise(process.nextTick); - - // response for 'some-method' - (channelMock.request as jest.Mock).mockResolvedValueOnce('success'); - const rpcResultCallback = jest.fn(); - provider.send( - { - method: 'some-method', - params: [{ param1: 1 }], - }, - rpcResultCallback - ); - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(2); - - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: 'some-method', - params: [{ param1: 1 }], - }) - ); - - expect(rpcResultCallback).toHaveBeenCalledWith(null, { - method: 'some-method', - result: 'success', - }); - }); - - it('should support batched requets', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - // wait for init to finish - await new Promise(process.nextTick); - - // response for 'some-method' - (channelMock.request as jest.Mock).mockResolvedValue('success'); - const rpcResultCallback = jest.fn(); - provider.send( - [ - { - method: 'some-method', - params: [{ param1: 1 }], - }, - { - method: 'some-method2', - params: [{ param1: 2 }], - }, - ], - rpcResultCallback - ); - - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(3); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: 'some-method', - params: [{ param1: 1 }], - }) - ); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: 'some-method2', - params: [{ param1: 2 }], - }) - ); - - expect(rpcResultCallback).toHaveBeenCalledWith(null, [ - { method: 'some-method', result: 'success' }, - { method: 'some-method2', result: 'success' }, - ]); - }); - - it('should support method as the only param', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - // wait for init to finish - await new Promise(process.nextTick); - - // response for 'some-method' - (channelMock.request as jest.Mock).mockResolvedValue('success'); - const rpcResultCallback = jest.fn(); - const promise = provider.send('some-method'); - expect(typeof (promise as Promise).then).toBe('function'); - - (promise as Promise).then(rpcResultCallback); - - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(2); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: 'some-method', - params: undefined, - }) - ); - expect(rpcResultCallback).toHaveBeenCalledWith({ - id: undefined, - jsonrpc: '2.0', - result: 'success', - }); - }); - - it('should support method with params', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - // wait for init to finish - await new Promise(process.nextTick); - - // response for 'some-method' - (channelMock.request as jest.Mock).mockResolvedValue('success'); - const rpcResultCallback = jest.fn(); - const promise = provider.send('some-method', [{ someparam: 1 }]); - expect(typeof (promise as Promise).then).toBe('function'); - - (promise as Promise).then(rpcResultCallback); - - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(2); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: 'some-method', - params: [{ someparam: 1 }], - }) - ); - expect(rpcResultCallback).toHaveBeenCalledWith({ - id: undefined, - jsonrpc: '2.0', - result: 'success', - }); - }); - - it('should return eth_accounts response syncronously', async () => { - const provider = new CoreProvider(); - - (channelMock.request as jest.Mock).mockResolvedValueOnce( - channelMockResolvedValue - ); - - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - // wait for init to finish - await new Promise(process.nextTick); - - // response for 'some-method' - (channelMock.request as jest.Mock).mockResolvedValue('success'); - const result = provider.send({ method: 'eth_accounts' }); - expect(result).toStrictEqual({ - id: undefined, - jsonrpc: undefined, - result: ['0x00000'], - }); - }); - - it('should return eth_coinbase response syncronously', async () => { - const provider = new CoreProvider(); - - (channelMock.request as jest.Mock).mockResolvedValueOnce( - channelMockResolvedValue - ); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - // wait for init to finish - await new Promise(process.nextTick); - - // response for 'some-method' - (channelMock.request as jest.Mock).mockResolvedValue('success'); - const result = provider.send({ method: 'eth_coinbase', id: 1 }); - expect(result).toStrictEqual({ - id: 1, - jsonrpc: undefined, - result: '0x00000', - }); - }); - - it('throws error if method not supported syncronously', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - // wait for init to finish - await new Promise(process.nextTick); - - // response for 'some-method' - (channelMock.request as jest.Mock).mockResolvedValue('success'); - try { - const result = provider.send({ - method: 'some-unsupported-method', - id: 1, - }); - expect(result).not.toBeDefined(); - } catch (error) { - expect(error).toStrictEqual( - new Error( - 'Sync method not supported. Please provide a callback or use the `request` function.' - ) - ); - } - }); - }); - - describe('enable', () => { - it('should call the requests properly', async () => { - const provider = new CoreProvider(); - - (channelMock.request as jest.Mock).mockResolvedValueOnce( - channelMockResolvedValue - ); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - // wait for init to finish - await new Promise(process.nextTick); - - // response for 'some-method' - (channelMock.request as jest.Mock).mockResolvedValueOnce(['0x0000']); - const rpcResultCallback = jest.fn(); - provider.enable().then(rpcResultCallback); - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(2); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: 'eth_requestAccounts', - }) - ); - - expect(rpcResultCallback).toHaveBeenCalledWith(['0x0000']); - }); - }); - - describe('net_version', () => { - it('supports net_version call', async () => { - const provider = new CoreProvider(); - - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - // wait for init to finish - await new Promise(process.nextTick); - - // response for 'some-method' - (channelMock.request as jest.Mock).mockResolvedValueOnce('1'); - const rpcResultCallback = jest.fn(); - provider.net_version().then(rpcResultCallback); - await new Promise(process.nextTick); - - expect(channelMock.request).toHaveBeenCalledTimes(2); - expect(channelMock.request).toHaveBeenCalledWith( - matchingPayload({ - method: 'net_version', - }) - ); - - expect(rpcResultCallback).toHaveBeenCalledWith('1'); - }); - }); - - describe('close event', () => { - it('should emit close event with error', async () => { - const provider = new CoreProvider(); - - (channelMock.request as jest.Mock).mockResolvedValueOnce( - channelMockResolvedValue - ); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const closeSubscription = jest.fn(); - provider.on('close', closeSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(provider._isConnected).toBe(true); - expect(provider._state.isConnected).toBe(true); - expect(provider._state.accounts).toStrictEqual(['0x00000']); - expect(provider.selectedAddress).toStrictEqual('0x00000'); - - expect(closeSubscription).not.toHaveBeenCalled(); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'disconnect', - }); - - expect(provider._isConnected).toBe(false); - expect(provider._state.isConnected).toBe(false); - expect(provider._state.accounts).toBe(null); - expect(provider.selectedAddress).toBe(null); - - expect(closeSubscription).toHaveBeenCalledTimes(1); - expect(closeSubscription).toHaveBeenCalledWith( - ethErrors.provider.disconnected() - ); - }); - }); - - describe('networkChanged event', () => { - it('should not emit `networkChanged` on initialization', async () => { - const provider = new CoreProvider(); - - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const networkChangedSubscription = jest.fn(); - provider.addListener('networkChanged', networkChangedSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(networkChangedSubscription).not.toHaveBeenCalled(); - }); - - it('emits `networkChanged` when gets connected to a chain later', async () => { - (channelMock.request as jest.Mock).mockReset(); - (channelMock.request as jest.Mock).mockResolvedValue({ - isUnlocked: true, - chainId: '0x0', - networkVersion: 'loading', - accounts: ['0x00000'], - }); - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const networkChangedSubscription = jest.fn(); - provider.addListener('networkChanged', networkChangedSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(networkChangedSubscription).not.toHaveBeenCalled(); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'chainChanged', - params: { chainId: '0x1', networkVersion: '1' }, - }); - expect(networkChangedSubscription).toHaveBeenCalledTimes(1); - expect(networkChangedSubscription).toHaveBeenCalledWith('1'); - }); - - it('should not emit `networkChanged` when chain is set to the same value', async () => { - const provider = new CoreProvider(); - - (channelMock.request as jest.Mock).mockResolvedValueOnce( - channelMockResolvedValue - ); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const networkChangedSubscription = jest.fn(); - provider.addListener('networkChanged', networkChangedSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(networkChangedSubscription).not.toHaveBeenCalled(); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'chainChanged', - params: { chainId: '0x1', networkVersion: '1' }, - }); - expect(networkChangedSubscription).not.toHaveBeenCalled(); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'chainChanged', - params: { chainId: '0x1', networkVersion: '1' }, - }); - expect(networkChangedSubscription).not.toHaveBeenCalled(); - }); - - it('emits `chainChanged` when chain is set to new value', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const networkChangedSubscription = jest.fn(); - provider.addListener('networkChanged', networkChangedSubscription); - - // wait for init to finish - await new Promise(process.nextTick); - - expect(networkChangedSubscription).not.toHaveBeenCalled(); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'chainChanged', - params: { chainId: '0x2', networkVersion: '2' }, - }); - expect(networkChangedSubscription).toHaveBeenCalledTimes(1); - expect(networkChangedSubscription).toHaveBeenCalledWith('2'); - - (channelMock.on as jest.Mock).mock.calls[0][1]({ - method: 'chainChanged', - params: { chainId: '0x1', networkVersion: '1' }, - }); - expect(networkChangedSubscription).toHaveBeenCalledTimes(2); - expect(networkChangedSubscription).toHaveBeenCalledWith('1'); - }); - }); - }); - }); - - describe('init', () => { - it('should call the event listener with the right event name', async () => { - new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - expect(addEventListenerSpy).toHaveBeenCalledTimes(1); - - expect(addEventListenerSpy).toHaveBeenCalledWith( - EventNames.CORE_WALLET_ANNOUNCE_PROVIDER, - expect.any(Function) - ); - }); - it('loads provider state from the background', async () => { - const mockedChannel = new AutoPairingPostMessageConnection(false); - - jest.mocked(mockedChannel.connect).mockResolvedValueOnce(undefined); - (mockedChannel.request as jest.Mock).mockResolvedValueOnce({ - isUnlocked: true, - chainId: '0x1', - networkVersion: '1', - accounts: ['0x00000'], - }); - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - const initializedSubscription = jest.fn(); - provider.addListener('_initialized', initializedSubscription); - await new Promise(process.nextTick); - - expect(mockedChannel.request).toHaveBeenCalledTimes(1); - expect(mockedChannel.request).toHaveBeenCalledWith( - matchingPayload({ - method: DAppProviderRequest.INIT_DAPP_STATE, - }) - ); - - await new Promise(process.nextTick); - expect(provider._isUnlocked).toBe(true); - expect(provider._state.isUnlocked).toBe(true); - expect(provider._state.accounts).toStrictEqual(['0x00000']); - expect(provider.chainId).toBe('0x1'); - expect(provider.networkVersion).toBe('1'); - expect(provider._initialized).toBe(true); - expect(provider._state.initialized).toBe(true); - expect(provider._isReady).toBe(true); - expect(initializedSubscription).toHaveBeenCalledTimes(1); - }); - }); - - describe('Metamask compatibility', () => { - it('supports _metamask.isUnlocked', async () => { - const provider = new CoreProvider(); - (addEventListenerSpy.mock.calls[0]?.[1] as any)({ - detail: { - provider: { - subscribeToMessage: jest.fn((callback) => { - return channelMock.on('message', callback); - }), - request: jest.fn((params) => { - return channelMock.request(params as any); - }), - }, - }, - }); - expect(await provider._metamask.isUnlocked()).toBe(false); - - // wait for init to finish - await new Promise(process.nextTick); - expect(await provider._metamask.isUnlocked()).toBe(true); - }); - it('isMetamask is true', () => { - const provider = new CoreProvider(); - expect(provider.isMetaMask).toBe(true); - }); - it('isAvalanche is true', async () => { - const provider = new CoreProvider(); - expect(provider.isAvalanche).toBe(true); - }); - }); -}); diff --git a/src/background/providers/CoreProvider.ts b/src/background/providers/CoreProvider.ts deleted file mode 100644 index 4e497da0f..000000000 --- a/src/background/providers/CoreProvider.ts +++ /dev/null @@ -1,310 +0,0 @@ -import { ethErrors } from 'eth-rpc-errors'; -import EventEmitter from 'events'; -import { - EventNames, - type AccountsChangedEventData, - type ChainChangedEventData, - type UnlockStateChangedEventData, -} from './models'; -import { - InitializationStep, - ProviderReadyPromise, -} from './utils/ProviderReadyPromise'; -import { - DAppProviderRequest, - type JsonRpcRequestPayload, -} from '../connections/dAppConnection/models'; -import type { PartialBy, ProviderInfo } from '../models'; -import { ChainAgnosticProvider } from './ChainAgnosticProvider'; - -interface ProviderState { - accounts: string[] | null; - isConnected: boolean; - isUnlocked: boolean; - initialized: boolean; -} - -export class CoreProvider extends EventEmitter { - #providerReadyPromise = new ProviderReadyPromise([ - InitializationStep.PROVIDER_STATE_LOADED, - ]); - #chainagnosticProvider?: ChainAgnosticProvider; - - readonly info: ProviderInfo = { - name: EVM_PROVIDER_INFO_NAME, - uuid: EVM_PROVIDER_INFO_UUID, - icon: EVM_PROVIDER_INFO_ICON, - description: EVM_PROVIDER_INFO_DESCRIPTION, - rdns: EVM_PROVIDER_INFO_RDNS, - }; - - chainId: string | null = null; - selectedAddress: string | null = null; - /** - * The network ID of the currently connected Ethereum chain. - * @deprecated - */ - networkVersion: string | null = null; - isAvalanche = true; - isMetaMask = true; - - _isReady = false; - _isConnected = false; - _initialized = false; - _isUnlocked = false; - _sessionId = crypto.randomUUID(); - - _state: ProviderState = { - accounts: null, - isConnected: false, - isUnlocked: false, - initialized: false, - }; - - // Metamask compatibility - _metamask = { - isUnlocked: () => Promise.resolve(this._isUnlocked), - }; - - constructor(maxListeners: number = 100) { - super(); - this.setMaxListeners(maxListeners); - this.#subscribe(); - } - - #subscribe() { - window.addEventListener( - EventNames.CORE_WALLET_ANNOUNCE_PROVIDER, - (event) => { - if (this.#chainagnosticProvider) { - return; - } - this.#chainagnosticProvider = (event).detail.provider; - - this.#chainagnosticProvider?.subscribeToMessage( - this.#handleBackgroundMessage - ); - - this.#init(); - } - ); - - window.dispatchEvent(new Event(EventNames.CORE_WALLET_REQUEST_PROVIDER)); - } - /** - * Initializes provider state, and collects dApp information - */ - #init = async () => { - try { - const response = await this.#request({ - method: DAppProviderRequest.INIT_DAPP_STATE, - }); - - const { chainId, accounts, networkVersion, isUnlocked } = - (response as { - isUnlocked: boolean; - chainId: string | null; - networkVersion: string | null; - accounts: string[]; - }) ?? {}; - - if (isUnlocked) { - this._isUnlocked = true; - this._state.isUnlocked = true; - } - - if ( - chainId && - chainId !== '0x0' && - networkVersion && - networkVersion !== 'loading' - ) { - this.chainId = chainId; - this.networkVersion = networkVersion; - this.#connect({ chainId }); - this.#chainChanged({ - chainId, - networkVersion, - }); - } - - this.#accountsChanged(accounts || []); - - this.#providerReadyPromise.check( - InitializationStep.PROVIDER_STATE_LOADED - ); - } catch (e) { - // the provider will have a partial state, but still should be able to function - } finally { - this._initialized = true; - this._state.initialized = true; - this._isReady = true; - this.emit('_initialized'); - } - }; - - #request = async ( - data: PartialBy - ) => { - return this.#chainagnosticProvider?.request({ - data, - chainId: this.chainId, - sessionId: this._sessionId, - }); - }; - - #getEventHandler = (method: string): ((params: any) => void) => { - const handlerMap = { - connect: this.#connect, - disconnect: this.#disconnect, - accountsChanged: this.#accountsChanged, - chainChanged: this.#chainChanged, - avalanche_unlockStateChanged: this.#unlockStateChanged, - }; - return handlerMap[method]; - }; - - #handleBackgroundMessage = ({ method, params }) => { - const eventHandler = this.#getEventHandler(method); - if (eventHandler) { - return eventHandler(params); - } - - this.emit(method, params); - }; - - isConnected = () => { - return true; - }; - - request = async (data: PartialBy) => { - return this.#providerReadyPromise.call(() => { - return this.#request(data); - }); - }; - - // shim to matamask legacy api - sendAsync = (payload, callback) => { - if (Array.isArray(payload)) { - return Promise.all( - payload.map( - (item) => - new Promise((resolve) => { - this.sendAsync(item, (err, res) => { - // ignore error - resolve(res); - }); - }) - ) - ).then((result) => callback(null, result)); - } - const { method, params, ...rest } = payload; - this.request({ method, params }) - .then((result) => callback(null, { ...rest, method, result })) - .catch((error) => callback(error, { ...rest, method, error })); - }; - - send = (payload, callback?) => { - if (typeof payload === 'string' && (!callback || Array.isArray(callback))) { - return this.request({ - method: payload, - params: callback, - }).then((result) => ({ - id: undefined, - jsonrpc: '2.0', - result, - })); - } - - if (typeof payload === 'object' && typeof callback === 'function') { - return this.sendAsync(payload, callback); - } - - let result; - switch (payload.method) { - case 'eth_accounts': - result = this.selectedAddress ? [this.selectedAddress] : []; - break; - - case 'eth_coinbase': - result = this.selectedAddress || null; - break; - - default: - throw new Error( - 'Sync method not supported. Please provide a callback or use the `request` function.' - ); - } - - return { - id: payload.id, - jsonrpc: payload.jsonrpc, - result, - }; - }; - - enable = () => { - return this.request({ method: 'eth_requestAccounts' }); - }; - - net_version = () => { - return this.request({ method: 'net_version' }); - }; - - #connect = (data: { chainId: string }) => { - if (!this._isConnected) { - this._isConnected = true; - this._state.isConnected = true; - this.emit('connect', data); - } - }; - - #unlockStateChanged = ({ - accounts, - isUnlocked, - }: UnlockStateChangedEventData) => { - if (isUnlocked) { - this._isUnlocked = true; - this._state.isUnlocked = true; - this.#accountsChanged(accounts); - } else { - this._isUnlocked = false; - } - }; - - #disconnect = () => { - this._isConnected = false; - this._state.isConnected = false; - this._state.accounts = null; - this.selectedAddress = null; - const disconnectError = ethErrors.provider.disconnected(); - - this.emit('accountsChanged', []); - this.emit('disconnect', disconnectError); - this.emit('close', disconnectError); - }; - - #accountsChanged = (accounts: AccountsChangedEventData) => { - if (accounts?.[0] === this.selectedAddress) { - return; - } - - this.selectedAddress = accounts?.[0] ?? null; - this._state.accounts = accounts; - this.emit('accountsChanged', accounts); - }; - - #chainChanged = ({ chainId, networkVersion }: ChainChangedEventData) => { - this.#connect({ chainId }); - - if (chainId !== this.chainId) { - this.chainId = chainId; - this.emit('chainChanged', chainId); - } - - if (networkVersion !== this.networkVersion) { - this.networkVersion = networkVersion; - this.emit('networkChanged', networkVersion); - } - }; -} diff --git a/src/background/providers/MultiWalletProviderProxy.test.ts b/src/background/providers/MultiWalletProviderProxy.test.ts index 63b8820a4..eee474bc3 100644 --- a/src/background/providers/MultiWalletProviderProxy.test.ts +++ b/src/background/providers/MultiWalletProviderProxy.test.ts @@ -1,23 +1,32 @@ import { EventEmitter } from 'stream'; -import { CoreProvider } from './CoreProvider'; import { MultiWalletProviderProxy, createMultiWalletProxy, } from './MultiWalletProviderProxy'; +import { EVMProvider } from '@avalabs/evm-module/dist/provider'; +import { EIP6963ProviderInfo } from '@avalabs/vm-module-types'; jest.mock('../utils/messaging/AutoPairingPostMessageConnection'); -jest.mock('./CoreProvider', () => ({ - CoreProvider: jest.fn().mockImplementation(() => ({ +jest.mock('@avalabs/evm-module/dist/provider', () => ({ + EVMProvider: jest.fn().mockImplementation(() => ({ isAvalanche: true, isMetaMask: true, removeAllListeners: jest.fn(), })), })); +const providerInfo = { + description: 'EVM_PROVIDER_INFO_DESCRIPTION', + icon: 'EVM_PROVIDER_INFO_ICON', + name: 'EVM_PROVIDER_INFO_NAME', + uuid: 'EVM_PROVIDER_INFO_UUID', + rdns: 'EVM_PROVIDER_INFO_RDNS', +} as unknown as EIP6963ProviderInfo; + describe('src/background/providers/MultiWalletProviderProxy', () => { describe('init', () => { it('initializes with the default provider', () => { - const provider = new CoreProvider(); + const provider = new EVMProvider({ info: providerInfo }); const mwpp = new MultiWalletProviderProxy(provider); @@ -27,7 +36,7 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { it('relays events from the deafult provider', () => { const provider = new EventEmitter(); - const mwpp = new MultiWalletProviderProxy(provider as CoreProvider); + const mwpp = new MultiWalletProviderProxy(provider as EVMProvider); const eventSub = jest.fn(); mwpp.on('chainChanged', eventSub); @@ -43,7 +52,7 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { describe('addProvider', () => { it('adds new providers from coinbase proxy', () => { - const provider = new CoreProvider(); + const provider = new EVMProvider({ info: providerInfo }); const mwpp = new MultiWalletProviderProxy(provider); expect(mwpp.defaultProvider).toBe(provider); @@ -70,7 +79,7 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { }); it('does not add extra coinbase proxy', () => { - const provider = new CoreProvider(); + const provider = new EVMProvider({ info: providerInfo }); const mwpp = new MultiWalletProviderProxy(provider); expect(mwpp.defaultProvider).toBe(provider); @@ -87,7 +96,7 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { }); it('adds new provider', () => { - const provider = new CoreProvider(); + const provider = new EVMProvider({ info: providerInfo }); const mwpp = new MultiWalletProviderProxy(provider); expect(mwpp.defaultProvider).toBe(provider); @@ -104,7 +113,7 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { describe('wallet selection', () => { it('toggles wallet selection on `eth_requestAccounts` call if multiple providers', async () => { - const provider = new CoreProvider(); + const provider = new EVMProvider({ info: providerInfo }); const provider2 = { isMetaMask: true, request: jest.fn() }; const mwpp = new MultiWalletProviderProxy(provider); mwpp.addProvider(provider2); @@ -151,7 +160,7 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { }); it('does not toggle wallet selection if only core is available', async () => { - const provider = new CoreProvider(); + const provider = new EVMProvider({ info: providerInfo }); const mwpp = new MultiWalletProviderProxy(provider); provider.request = jest.fn().mockResolvedValueOnce(['0x000000']); @@ -174,7 +183,7 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { }); it('does not toggle wallet selection if wallet is already selected', async () => { - const provider = new CoreProvider(); + const provider = new EVMProvider({ info: providerInfo }); const provider2 = { isMetaMask: true, request: jest.fn() }; const mwpp = new MultiWalletProviderProxy(provider); mwpp.addProvider(provider2); @@ -230,7 +239,7 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { }); it('wallet selection works with legacy functions: enable', async () => { - const provider = new CoreProvider(); + const provider = new EVMProvider({ info: providerInfo }); const provider2 = { isMetaMask: true, enable: jest.fn() }; const mwpp = new MultiWalletProviderProxy(provider); mwpp.addProvider(provider2); @@ -272,7 +281,7 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { }); it('wallet selection works with legacy functions: sendAsync', async () => { - const provider = new CoreProvider(); + const provider = new EVMProvider({ info: providerInfo }); const provider2 = { isMetaMask: true, request: jest.fn() }; const mwpp = new MultiWalletProviderProxy(provider); mwpp.addProvider(provider2); @@ -326,7 +335,7 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { }); it('wallet selection works with legacy functions: send with callback', async () => { - const provider = new CoreProvider(); + const provider = new EVMProvider({ info: providerInfo }); const provider2 = { isMetaMask: true, request: jest.fn() }; const mwpp = new MultiWalletProviderProxy(provider); mwpp.addProvider(provider2); @@ -378,7 +387,7 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { describe('createMultiWalletProxy', () => { it('creates proxy with property deletion disabled', () => { - const provider = new CoreProvider(); + const provider = new EVMProvider({ info: providerInfo }); const mwpp = createMultiWalletProxy(provider); expect(mwpp.defaultProvider).toBe(provider); @@ -387,7 +396,7 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { }); it('allows setting extra params without changing the provider', () => { - const provider = new CoreProvider(); + const provider = new EVMProvider({ info: providerInfo }); const mwpp = createMultiWalletProxy(provider); (mwpp as any).somePromerty = true; @@ -397,7 +406,7 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { it('does not overwrite provider properties', () => { const provider = { isAvalanche: true }; - const mwpp = createMultiWalletProxy(provider as CoreProvider); + const mwpp = createMultiWalletProxy(provider as EVMProvider); (mwpp as any).isAvalanche = false; expect((mwpp as any).isAvalanche).toBe(true); @@ -411,7 +420,7 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { }); it('maintains the providers list properly', () => { - const provider = new CoreProvider(); + const provider = new EVMProvider({ info: providerInfo }); const mwpp = createMultiWalletProxy(provider); const fooMock = () => 'bar'; const bizMock = () => 'baz'; diff --git a/src/background/providers/MultiWalletProviderProxy.ts b/src/background/providers/MultiWalletProviderProxy.ts index 0042edc36..7eb1ce3da 100644 --- a/src/background/providers/MultiWalletProviderProxy.ts +++ b/src/background/providers/MultiWalletProviderProxy.ts @@ -5,9 +5,9 @@ import { JsonRpcResponse, } from '../connections/dAppConnection/models'; import { getWalletExtensionType } from './utils/getWalletExtensionType'; -import { CoreProvider } from './CoreProvider'; import { Maybe } from '@avalabs/core-utils-sdk'; import EventEmitter from 'events'; +import { EVMProvider } from '@avalabs/evm-module/dist/provider'; export class MultiWalletProviderProxy extends EventEmitter { #_providers: unknown[] = []; @@ -18,18 +18,18 @@ export class MultiWalletProviderProxy extends EventEmitter { return this.#defaultProvider; } get providers() { - // When the user decides to select the providers and there is more than one provider and CoreProvider is selected, we want to trigger the wallet selection. So we replace CoreProvider with this. + // When the user decides to select the providers and there is more than one provider and EVMProvider is selected, we want to trigger the wallet selection. So we replace EVMProvider with this. if (this.#_providers.length > 1) { return [...this.#_providers].map((provider) => { - if ((provider as CoreProvider)?.isAvalanche) { + if ((provider as EVMProvider)?.isAvalanche) { return new Proxy(this, { get(target, prop) { // eslint-disable-next-line no-prototype-builtins if (target.hasOwnProperty(prop)) { return target[prop]; // eslint-disable-next-line no-prototype-builtins - } else if ((provider as CoreProvider).hasOwnProperty(prop)) { - return (provider as CoreProvider)[prop]; + } else if ((provider as EVMProvider).hasOwnProperty(prop)) { + return (provider as EVMProvider)[prop]; } }, }); @@ -41,7 +41,7 @@ export class MultiWalletProviderProxy extends EventEmitter { return [...this.#_providers]; } - constructor(private coreProvider: CoreProvider) { + constructor(private coreProvider: EVMProvider) { super(); this.addProvider = this.addProvider.bind(this); @@ -253,8 +253,8 @@ export class MultiWalletProviderProxy extends EventEmitter { } } -export function createMultiWalletProxy(coreProvider: CoreProvider) { - const proxyProvider = new MultiWalletProviderProxy(coreProvider); +export function createMultiWalletProxy(evmProvider: EVMProvider) { + const proxyProvider = new MultiWalletProviderProxy(evmProvider); // Some dApps like Pangolin likes to define helper methods on the window.ethereum object. // Store them separately to prevent them from altering the inpage provider behaviour const walletProviderExtensions = {}; diff --git a/src/background/providers/initializeInpageProvider.test.ts b/src/background/providers/initializeInpageProvider.test.ts index 259bfd796..8cf42435a 100644 --- a/src/background/providers/initializeInpageProvider.test.ts +++ b/src/background/providers/initializeInpageProvider.test.ts @@ -1,7 +1,7 @@ import AutoPairingPostMessageConnection from '../utils/messaging/AutoPairingPostMessageConnection'; -import { CoreProvider } from './CoreProvider'; import { createMultiWalletProxy } from './MultiWalletProviderProxy'; import { initializeProvider } from './initializeInpageProvider'; +import { EVMProvider } from '@avalabs/evm-module/dist/provider'; jest.mock('../utils/messaging/AutoPairingPostMessageConnection', () => { const mocks = { @@ -12,8 +12,8 @@ jest.mock('../utils/messaging/AutoPairingPostMessageConnection', () => { return jest.fn().mockReturnValue(mocks); }); -jest.mock('./CoreProvider', () => ({ - CoreProvider: jest +jest.mock('@avalabs/evm-module/dist/provider', () => ({ + EVMProvider: jest .fn() .mockImplementation(() => ({ isAvalanche: true, info: { name: 'name' } })), })); @@ -35,7 +35,9 @@ describe('src/background/providers/initializeInpageProvider', () => { it('initializes CoreProvider with the correct channel name', () => { const provider = initializeProvider(connectionMock, 10, windowMock); - expect(CoreProvider).toHaveBeenCalledWith(10); + expect(EVMProvider).toHaveBeenCalledWith( + expect.objectContaining({ maxListeners: 10 }) + ); expect(provider.isAvalanche).toBe(true); }); diff --git a/src/background/providers/initializeInpageProvider.ts b/src/background/providers/initializeInpageProvider.ts index f57466e6f..61d6e5e18 100644 --- a/src/background/providers/initializeInpageProvider.ts +++ b/src/background/providers/initializeInpageProvider.ts @@ -1,8 +1,8 @@ import type AbstractConnection from '../utils/messaging/AbstractConnection'; import { ChainAgnosticProvider } from './ChainAgnosticProvider'; -import { CoreProvider } from './CoreProvider'; import { createMultiWalletProxy } from './MultiWalletProviderProxy'; import { EventNames, type EIP6963ProviderDetail } from './models'; +import { EVMProvider } from '@avalabs/evm-module/dist/provider'; /** * Initializes a CoreProvide and assigns it as window.ethereum. @@ -16,7 +16,7 @@ export function initializeProvider( connection: AbstractConnection, maxListeners = 100, globalObject = window -): CoreProvider { +): EVMProvider { const chainAgnosticProvider = new Proxy( new ChainAgnosticProvider(connection), { @@ -24,18 +24,30 @@ export function initializeProvider( } ); - const provider = new Proxy(new CoreProvider(maxListeners), { - // some common libraries, e.g. web3@1.x, mess with our API - deleteProperty: () => true, - }); + const evmProvider = new Proxy( + new EVMProvider({ + maxListeners, + info: { + name: EVM_PROVIDER_INFO_NAME, + uuid: EVM_PROVIDER_INFO_UUID, + icon: EVM_PROVIDER_INFO_ICON, + description: EVM_PROVIDER_INFO_DESCRIPTION, + rdns: EVM_PROVIDER_INFO_RDNS, + }, + }), + { + // some common libraries, e.g. web3@1.x, mess with our API + deleteProperty: () => true, + } + ); - setGlobalProvider(provider, globalObject); - setAvalancheGlobalProvider(provider, globalObject); - setEvmproviders(provider, globalObject); - announceWalletProvider(provider, globalObject); + setGlobalProvider(evmProvider, globalObject); + setAvalancheGlobalProvider(evmProvider, globalObject); + setEvmproviders(evmProvider, globalObject); + announceWalletProvider(evmProvider, globalObject); announceChainAgnosticProvider(chainAgnosticProvider, globalObject); - return provider; + return evmProvider; } /** @@ -45,7 +57,7 @@ export function initializeProvider( * @param providerInstance - The provider instance. */ function setGlobalProvider( - providerInstance: CoreProvider, + providerInstance: EVMProvider, globalObject = window ): void { try { @@ -103,7 +115,7 @@ function setGlobalProvider( * @param providerInstance - The provider instance. */ function setAvalancheGlobalProvider( - providerInstance: CoreProvider, + providerInstance: EVMProvider, globalObject = window ): void { Object.defineProperty(globalObject, 'avalanche', { @@ -114,7 +126,7 @@ function setAvalancheGlobalProvider( } function setEvmproviders( - providerInstance: CoreProvider, + providerInstance: EVMProvider, globalObject = window ): void { globalObject.evmproviders = globalObject.evmproviders || {}; @@ -124,7 +136,7 @@ function setEvmproviders( } function announceWalletProvider( - providerInstance: CoreProvider, + providerInstance: EVMProvider, globalObject = window ): void { const announceEvent = new CustomEvent( diff --git a/src/background/vmModules/ModuleManager.ts b/src/background/vmModules/ModuleManager.ts index af2dda97f..d37c3db6b 100644 --- a/src/background/vmModules/ModuleManager.ts +++ b/src/background/vmModules/ModuleManager.ts @@ -41,6 +41,17 @@ class ModuleManager { new EVMModule(), new BitcoinModule({ environment, + approvalController: { + requestApproval: () => { + throw new Error('not implemented'); + }, + onTransactionConfirmed: () => { + throw new Error('not implemented'); + }, + onTransactionReverted: () => { + throw new Error('not implemented'); + }, + }, }), new AVMModule(), new CoreEthModule(), diff --git a/src/background/vmModules/mocks/avm.ts b/src/background/vmModules/mocks/avm.ts index 04255f738..c62af070d 100644 --- a/src/background/vmModules/mocks/avm.ts +++ b/src/background/vmModules/mocks/avm.ts @@ -57,4 +57,6 @@ export class AVMModule implements Module { }) as any, // TODO: fix it }; } + + getProvider; } diff --git a/src/background/vmModules/mocks/coreEth.ts b/src/background/vmModules/mocks/coreEth.ts index a26fc1bf8..5b0bf91ca 100644 --- a/src/background/vmModules/mocks/coreEth.ts +++ b/src/background/vmModules/mocks/coreEth.ts @@ -57,4 +57,5 @@ export class CoreEthModule implements Module { }) as any, // TODO: fix it }; } + getProvider; } diff --git a/src/background/vmModules/mocks/evm.ts b/src/background/vmModules/mocks/evm.ts index f75b7856d..d0ce6c242 100644 --- a/src/background/vmModules/mocks/evm.ts +++ b/src/background/vmModules/mocks/evm.ts @@ -56,4 +56,5 @@ export class EVMModule implements Module { }) as any, // TODO: fix it }; } + getProvider; } diff --git a/src/background/vmModules/mocks/pvm.ts b/src/background/vmModules/mocks/pvm.ts index a48ce466c..6bee26586 100644 --- a/src/background/vmModules/mocks/pvm.ts +++ b/src/background/vmModules/mocks/pvm.ts @@ -56,4 +56,5 @@ export class PVMModule implements Module { }) as any, // TODO: fix it }; } + getProvider; } diff --git a/src/types/globals.d.ts b/src/types/globals.d.ts index f95b80129..279bf7705 100644 --- a/src/types/globals.d.ts +++ b/src/types/globals.d.ts @@ -1,10 +1,10 @@ -import { CoreProvider } from '@src/background/providers/CoreProvider'; +import { EVMProvider } from '@avalabs/evm-module/dist/provider'; import { MultiWalletProviderProxy } from '@src/background/providers/MultiWalletProviderProxy'; declare global { interface Window { - avalanche?: CoreProvider; - ethereum?: MultiWalletProviderProxy | CoreProvider; + avalanche?: EVMProvider; + ethereum?: MultiWalletProviderProxy | EVMProvider; evmproviders?: Record; } diff --git a/yarn.lock b/yarn.lock index 3e00915e5..daafb44f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,11 +7,6 @@ resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== -"@adraffy/ens-normalize@1.9.2": - version "1.9.2" - resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.9.2.tgz#60111a5d9db45b2e5cbb6231b0bb8d97e8659316" - integrity sha512-0h+FrQDqe2Wn+IIGFkTCd4aAwTJ+7834Ek1COohCyV26AXhwQ7WQaz+4F/nLOeVl/3BtWHOHLPsq46V8YB46Eg== - "@ampproject/remapping@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" @@ -45,15 +40,15 @@ "@scure/base" "1.1.5" micro-eth-signer "0.7.2" -"@avalabs/bitcoin-module@0.1.4": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@avalabs/bitcoin-module/-/bitcoin-module-0.1.4.tgz#69e5b0bde655385f737db7394d05f31ffb73752a" - integrity sha512-XDjElNILR8Wpn0RL1ngiYfnyAtjp/eWPn3nC4e2mNMNjZWmWgGHa0Y/flR5vWTWpmd1WNtDxEug2n6l0pTZIlg== +"@avalabs/bitcoin-module@0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@avalabs/bitcoin-module/-/bitcoin-module-0.3.0.tgz#3ecd74fad7e645bceecfa643559fe9cf87d83558" + integrity sha512-q6YPSEcdNAE1F/cmflYIsAmgOrYTEPg+eG2hmO9epwpdmfHNr4C4V5CeWASk/IDVy3pbr+gFX57rZ68F2xMeqw== dependencies: - "@avalabs/core-coingecko-sdk" "3.0.1-alpha.1" - "@avalabs/core-utils-sdk" "3.0.1-alpha.1" - "@avalabs/core-wallets-sdk" "3.0.1-alpha.1" - "@avalabs/vm-module-types" "0.1.4" + "@avalabs/core-coingecko-sdk" "3.1.0-alpha.1" + "@avalabs/core-utils-sdk" "3.1.0-alpha.1" + "@avalabs/core-wallets-sdk" "3.1.0-alpha.1" + "@avalabs/vm-module-types" "0.3.0" "@metamask/rpc-errors" "6.3.0" "@zodios/core" "10.9.6" big.js "6.2.1" @@ -79,12 +74,12 @@ "@avalabs/core-utils-sdk" "3.1.0-alpha.4" "@avalabs/core-wallets-sdk" "3.1.0-alpha.4" -"@avalabs/core-chains-sdk@3.0.1-alpha.1": - version "3.0.1-alpha.1" - resolved "https://registry.yarnpkg.com/@avalabs/core-chains-sdk/-/core-chains-sdk-3.0.1-alpha.1.tgz#c24c2981eb8460a304949472bfa07581d925f17a" - integrity sha512-9scLLf3b1DUp03IUW66OwyX73MBcU2JO+NleDVxTp4Qh4ENcdDKKmkHLEcx1dkpl94prda8wITv0COS2q6ZRPw== +"@avalabs/core-chains-sdk@3.1.0-alpha.1": + version "3.1.0-alpha.1" + resolved "https://registry.yarnpkg.com/@avalabs/core-chains-sdk/-/core-chains-sdk-3.1.0-alpha.1.tgz#5fc89d097ca6644e3e79cc5abb93d49731f84c0e" + integrity sha512-JYNmlrqNMoEL/Tgw0MVqYMHTbiq+XXRyHv4QDvuG0al7v1IkHXQ+TAh5rGlSSAXY+39/1SQbVLMRIYpMTUo6Eg== dependencies: - "@avalabs/core-utils-sdk" "3.0.1-alpha.1" + "@avalabs/core-utils-sdk" "3.1.0-alpha.1" "@avalabs/core-chains-sdk@3.1.0-alpha.4": version "3.1.0-alpha.4" @@ -93,12 +88,12 @@ dependencies: "@avalabs/core-utils-sdk" "3.1.0-alpha.4" -"@avalabs/core-coingecko-sdk@3.0.1-alpha.1": - version "3.0.1-alpha.1" - resolved "https://registry.yarnpkg.com/@avalabs/core-coingecko-sdk/-/core-coingecko-sdk-3.0.1-alpha.1.tgz#24d1584c91290722462397e80888fd0d10f8d2c7" - integrity sha512-V0fx3mVXBJaqyhKTYDGSki51TM17XB+3fHQjVnsMplT0VGOkEW2DtXLVi2/6fTDAeKU6/LUitZaySniZC8NNJg== +"@avalabs/core-coingecko-sdk@3.1.0-alpha.1": + version "3.1.0-alpha.1" + resolved "https://registry.yarnpkg.com/@avalabs/core-coingecko-sdk/-/core-coingecko-sdk-3.1.0-alpha.1.tgz#43bc6d8b2d4d0cd8aabc520ff2ca52e565de64d8" + integrity sha512-zgDEm8Zaw3Ka9fF8lHSxSWXepPXzbGqy4fWXHwSTXs5n9Sy5bd03n5ArlTASPhXG4srMXFi2lVt2XdRg6YBfGw== dependencies: - "@avalabs/core-utils-sdk" "3.0.1-alpha.1" + "@avalabs/core-utils-sdk" "3.1.0-alpha.1" "@avalabs/core-coingecko-sdk@3.1.0-alpha.4": version "3.1.0-alpha.4" @@ -114,6 +109,13 @@ dependencies: "@avalabs/core-utils-sdk" "3.1.0-alpha.4" +"@avalabs/core-etherscan-sdk@3.1.0-alpha.1": + version "3.1.0-alpha.1" + resolved "https://registry.yarnpkg.com/@avalabs/core-etherscan-sdk/-/core-etherscan-sdk-3.1.0-alpha.1.tgz#df84e4d71863538e008216133f7a57e71ed8b690" + integrity sha512-FfCLwNNck0R0C+73YgT37mOzeOriHeWl2WAvDEpt3etgtkBayxE7aF7DRLajGhvFt5SQ1uiXFA+JGW63uTyZlQ== + dependencies: + "@avalabs/core-utils-sdk" "3.1.0-alpha.1" + "@avalabs/core-etherscan-sdk@3.1.0-alpha.4": version "3.1.0-alpha.4" resolved "https://registry.yarnpkg.com/@avalabs/core-etherscan-sdk/-/core-etherscan-sdk-3.1.0-alpha.4.tgz#e9a44a00e45205678d35c0c00498066f9c1fb6e1" @@ -159,10 +161,10 @@ "@avalabs/core-coingecko-sdk" "3.1.0-alpha.4" "@avalabs/core-utils-sdk" "3.1.0-alpha.4" -"@avalabs/core-utils-sdk@3.0.1-alpha.1": - version "3.0.1-alpha.1" - resolved "https://registry.yarnpkg.com/@avalabs/core-utils-sdk/-/core-utils-sdk-3.0.1-alpha.1.tgz#c29bdcf778118868bf7d319bd5f83175cc9b4a0b" - integrity sha512-+Wr7JA6dZgeTSuwoh0KcM8AK7CbJZnIdxZ2arm5piSkaTPZzuAkYUOE/sVDxGqrzPWZQptDclfvJxeIhfuijIw== +"@avalabs/core-utils-sdk@3.1.0-alpha.1": + version "3.1.0-alpha.1" + resolved "https://registry.yarnpkg.com/@avalabs/core-utils-sdk/-/core-utils-sdk-3.1.0-alpha.1.tgz#6c154463561c39d50274073d12502e58638acf97" + integrity sha512-amqIGN+4P5qqIMKze1Jq5pa3GQGMzAvlX4wlNQ7t510A3kMG9FDOZxDNl9PsXk+Ah3843GnFK0o+FbsQcOYVYw== dependencies: "@avalabs/avalanchejs" "4.0.5" "@hpke/core" "1.2.5" @@ -177,14 +179,14 @@ "@hpke/core" "1.2.5" is-ipfs "6.0.2" -"@avalabs/core-wallets-sdk@3.0.1-alpha.1": - version "3.0.1-alpha.1" - resolved "https://registry.yarnpkg.com/@avalabs/core-wallets-sdk/-/core-wallets-sdk-3.0.1-alpha.1.tgz#6e6d1a6da6799f7753cec9549f2e166f766c45be" - integrity sha512-q4zMx7CpcKwv2PqIaBdALQ7LZts9WeOju/oOdPHHtLqVEksXkPDPyzY4zxJEb7zLAMaLz1YvpP4Cr7cGqBzgtg== +"@avalabs/core-wallets-sdk@3.1.0-alpha.1": + version "3.1.0-alpha.1" + resolved "https://registry.yarnpkg.com/@avalabs/core-wallets-sdk/-/core-wallets-sdk-3.1.0-alpha.1.tgz#015c0a8738e5ac4a1517d2e0bfed17de41f2b257" + integrity sha512-sto78LJoSQQzTJW7JYnIqZkBI7Rftm8m7zivcDJbQbEMFzk9c1+qROvkE8KUgJxHntcVeEzg64e/e0Ar3g+6xQ== dependencies: "@avalabs/avalanchejs" "4.0.5" - "@avalabs/core-chains-sdk" "3.0.1-alpha.1" - "@avalabs/glacier-sdk" "3.0.1-alpha.1" + "@avalabs/core-chains-sdk" "3.1.0-alpha.1" + "@avalabs/glacier-sdk" "3.1.0-alpha.1" "@avalabs/hw-app-avalanche" "0.14.1" "@ledgerhq/hw-app-btc" "10.2.4" "@ledgerhq/hw-app-eth" "6.36.1" @@ -225,10 +227,29 @@ ledger-bitcoin "0.2.3" xss "1.0.14" -"@avalabs/glacier-sdk@3.0.1-alpha.1": - version "3.0.1-alpha.1" - resolved "https://registry.yarnpkg.com/@avalabs/glacier-sdk/-/glacier-sdk-3.0.1-alpha.1.tgz#7cd1fe5d534beb0c5370642a8bb22ad94bc14e48" - integrity sha512-cSf4w0dW2Kin2ImclGikT6zLm8ysNI3y9Nbzbt31tJvNj3WNQwxMO+9BUp+j9UWEhMMOOizal3t4/ieir/XKmw== +"@avalabs/evm-module@0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@avalabs/evm-module/-/evm-module-0.3.0.tgz#c78debb95fae9e5d5c6e6948ecbe6cae487e6dab" + integrity sha512-JhLvzocCnm7RDgft3ujg0qqZnJADGPQGEpbn1L2YcPvCt+ZLpKpaSQpEpByp8NuBcoUIntGS03QJ0ouVN0Nr+Q== + dependencies: + "@avalabs/core-coingecko-sdk" "3.1.0-alpha.1" + "@avalabs/core-etherscan-sdk" "3.1.0-alpha.1" + "@avalabs/core-utils-sdk" "3.1.0-alpha.1" + "@avalabs/core-wallets-sdk" "3.1.0-alpha.1" + "@avalabs/glacier-sdk" "3.1.0-alpha.1" + "@avalabs/types" "3.1.0-alpha.1" + "@avalabs/vm-module-types" "0.3.0" + "@blockaid/client" "0.11.0" + "@metamask/rpc-errors" "6.3.0" + "@zodios/core" "10.9.6" + bn.js "5.2.1" + lodash.startcase "4.4.0" + zod "3.23.8" + +"@avalabs/glacier-sdk@3.1.0-alpha.1": + version "3.1.0-alpha.1" + resolved "https://registry.yarnpkg.com/@avalabs/glacier-sdk/-/glacier-sdk-3.1.0-alpha.1.tgz#e627bc8564ad2c4114a6cfc8eb04390143fe9a03" + integrity sha512-INVlu7ClJDzrIQglABGhRO5nYs+sOLL/zSEHuEcf81TMS91NHReomqH/6V5uDLyZ06x1fvWXUFRl8le2kbpIbQ== "@avalabs/glacier-sdk@3.1.0-alpha.4": version "3.1.0-alpha.4" @@ -246,17 +267,24 @@ ledger-bitcoin "^0.2.1" sha3 "2.1.4" +"@avalabs/types@3.1.0-alpha.1": + version "3.1.0-alpha.1" + resolved "https://registry.yarnpkg.com/@avalabs/types/-/types-3.1.0-alpha.1.tgz#b3b1673f4c9f4e1db623f663423d9d768c6cccad" + integrity sha512-zzmsaE51W17P6trWfWmPQJn21+z350kxYg5y6HDyUkZYPWxBpmg6NLkj5bfw06qBCjhTlp0mscEe8OOUGrU6DA== + "@avalabs/types@3.1.0-alpha.3": version "3.1.0-alpha.3" resolved "https://registry.yarnpkg.com/@avalabs/types/-/types-3.1.0-alpha.3.tgz#3b7fb8cb8e2f124e0b11406788a903142753b5a0" integrity sha512-WN6NyPsF9XpmQp2SN4UbwjBT+XYH8di8gvL9rppQxz4d43xRLI1T2WVt/LXJgYdOauhuaSelb6zB4yNqGyKutQ== -"@avalabs/vm-module-types@0.1.4": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@avalabs/vm-module-types/-/vm-module-types-0.1.4.tgz#e6c0dfc595f06404ddd513d9134433ede71fa623" - integrity sha512-zlvAvZc1dp3VaMFApMQ0clooICt955aklh6zxDqUW4wPR1mHozI0TLUMZd8NXXFhW8OjFITaNEUb802h2qtkRA== +"@avalabs/vm-module-types@0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@avalabs/vm-module-types/-/vm-module-types-0.3.0.tgz#4139cd70b9266979a5e3e783d9d6bd005e1d4279" + integrity sha512-IzVd/LnXxMIsfAJryl4HniMrfEB8FjgMm1VUVcG0tOHdFgRcqw10/O746lxbkU11ZPBbjr3tSxjn6fnbsQ6PPA== dependencies: + "@avalabs/core-wallets-sdk" "3.1.0-alpha.1" "@metamask/rpc-errors" "6.3.0" + bitcoinjs-lib "5.2.0" ethers "6.8.1" zod "3.23.8" @@ -2350,6 +2378,20 @@ node-fetch "^2.6.7" web-streams-polyfill "^3.2.1" +"@blockaid/client@0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@blockaid/client/-/client-0.11.0.tgz#1fbc894d605eae0a8c8d7192ade4f344dd34946f" + integrity sha512-Lvg+lrQTDTBr03yKdrMOerjYovxEmWM5gUToO3Bbd5Qiybq4rBxoZBFvtBrg/vu5ENIA1f+jIo+1EI8TOktI4w== + dependencies: + "@types/node" "^18.11.18" + "@types/node-fetch" "^2.6.4" + abort-controller "^3.0.0" + agentkeepalive "^4.2.1" + form-data-encoder "1.7.2" + formdata-node "^4.3.2" + node-fetch "^2.6.7" + web-streams-polyfill "^3.2.1" + "@chainsafe/as-sha256@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz#3639df0e1435cab03f4d9870cc3ac079e57a6fc9" @@ -4416,11 +4458,6 @@ dependencies: "@noble/hashes" "1.3.3" -"@noble/hashes@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" - integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== - "@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" @@ -11130,19 +11167,6 @@ ethers@5.7.2: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" -ethers@6.7.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.7.1.tgz#9c65e8b5d8e9ad77b7e8cf1c46099892cfafad49" - integrity sha512-qX5kxIFMfg1i+epfgb0xF4WM7IqapIIu50pOJ17aebkxxa4BacW5jFrQRmCJpDEg2ZK2oNtR5QjrQ1WDBF29dA== - dependencies: - "@adraffy/ens-normalize" "1.9.2" - "@noble/hashes" "1.1.2" - "@noble/secp256k1" "1.7.1" - "@types/node" "18.15.13" - aes-js "4.0.0-beta.5" - tslib "2.4.0" - ws "8.5.0" - ethers@6.8.1: version "6.8.1" resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.8.1.tgz#ee2a1a39b5f62a13678f90ccd879175391d0a2b4" @@ -14976,7 +15000,7 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== -lodash.startcase@^4.4.0: +lodash.startcase@4.4.0, lodash.startcase@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8" integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==