diff --git a/src/background/providers/MultiWalletProviderProxy.test.ts b/src/background/providers/MultiWalletProviderProxy.test.ts index eee474bc..1ba269d5 100644 --- a/src/background/providers/MultiWalletProviderProxy.test.ts +++ b/src/background/providers/MultiWalletProviderProxy.test.ts @@ -8,10 +8,14 @@ import { EIP6963ProviderInfo } from '@avalabs/vm-module-types'; jest.mock('../utils/messaging/AutoPairingPostMessageConnection'); jest.mock('@avalabs/evm-module/dist/provider', () => ({ - EVMProvider: jest.fn().mockImplementation(() => ({ - isAvalanche: true, - isMetaMask: true, + EVMProvider: jest.fn().mockImplementation(({ info, ...rest }) => ({ + isAvalanche: info.uuid ? false : true, removeAllListeners: jest.fn(), + info: { + ...info, + uuid: info.uuid ?? 'default-uuid', + }, + ...rest, })), })); @@ -51,97 +55,84 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { }); describe('addProvider', () => { - it('adds new providers from coinbase proxy', () => { + it('should add new providers', () => { const provider = new EVMProvider({ info: providerInfo }); const mwpp = new MultiWalletProviderProxy(provider); expect(mwpp.defaultProvider).toBe(provider); expect(mwpp.providers).toStrictEqual([provider]); - const mockProvider = { - providerMap: new Map([ - ['core', provider], - ['otherprovider', { isMetaMask: true }], - ['thirdprovider', { isRabby: true }], - ] as any), - }; + const provider2 = new EVMProvider({ + info: { ...providerInfo, uuid: 'uuid-2' }, + }); + const provider3 = new EVMProvider({ + info: { ...providerInfo, uuid: 'uuid-3' }, + }); - mwpp.addProvider(mockProvider); + mwpp.addProvider(provider2); + mwpp.addProvider(provider3); expect(mwpp.defaultProvider).toBe(provider); - expect(mwpp.providers).toEqual([ - mwpp, - { isMetaMask: true }, - { isRabby: true }, - ]); - expect((mwpp.providers[0] as any).isMetaMask).toBeTruthy(); + expect((mwpp.providers[0] as any).info.uuid).toBe(EVM_PROVIDER_INFO_UUID); + expect((mwpp.providers[1] as any).info.uuid).toBe('uuid-2'); + expect((mwpp.providers[2] as any).info.uuid).toBe('uuid-3'); }); - it('does not add extra coinbase proxy', () => { + it('should not add a new provider because it has been already registered', () => { const provider = new EVMProvider({ info: providerInfo }); const mwpp = new MultiWalletProviderProxy(provider); - expect(mwpp.defaultProvider).toBe(provider); - expect(mwpp.providers).toStrictEqual([provider]); - - const mockProvider = { - coinbaseWalletInstalls: {}, - }; - - mwpp.addProvider(mockProvider); - - expect(mwpp.defaultProvider).toBe(provider); - expect(mwpp.providers).toStrictEqual([provider]); - }); - - it('adds new provider', () => { - const provider = new EVMProvider({ info: providerInfo }); - const mwpp = new MultiWalletProviderProxy(provider); - - expect(mwpp.defaultProvider).toBe(provider); - expect(mwpp.providers).toStrictEqual([provider]); - - const mockProvider = { isMetaMask: true }; - - mwpp.addProvider(mockProvider); + const provider2 = new EVMProvider({ + info: { ...providerInfo, uuid: 'uuid-2' }, + }); + const provider3 = new EVMProvider({ + info: { ...providerInfo, uuid: 'uuid-2' }, + }); + mwpp.addProvider(provider2); + mwpp.addProvider(provider3); - expect(mwpp.defaultProvider).toBe(provider); - expect(mwpp.providers).toEqual([mwpp, mockProvider]); + expect(mwpp.providers.length as any).toBe(2); + expect((mwpp.providers[0] as any).info.uuid).toBe(EVM_PROVIDER_INFO_UUID); + expect((mwpp.providers[1] as any).info.uuid).toBe('uuid-2'); }); }); describe('wallet selection', () => { it('toggles wallet selection on `eth_requestAccounts` call if multiple providers', async () => { - const provider = new EVMProvider({ info: providerInfo }); - const provider2 = { isMetaMask: true, request: jest.fn() }; + const provider = new EVMProvider({ + info: providerInfo, + request: jest.fn().mockResolvedValue(1), + } as any); + const provider2 = new EVMProvider({ + info: { ...providerInfo, uuid: 'uuid-2' }, + isMetaMask: true, + request: jest.fn().mockResolvedValue(2), + } as any); + const mwpp = new MultiWalletProviderProxy(provider); mwpp.addProvider(provider2); - // user selects metamask - provider.request = jest.fn().mockResolvedValue(1); - provider2.request.mockResolvedValue(['0x000000']); - const requestAccountsCallback = jest.fn(); mwpp .request({ method: 'eth_requestAccounts' }) .then(requestAccountsCallback); expect(requestAccountsCallback).not.toHaveBeenCalled(); - expect(provider2.request).not.toHaveBeenCalled(); expect(provider.request).toHaveBeenCalledTimes(1); + expect(provider.request).toHaveBeenCalledWith({ method: 'avalanche_selectWallet', params: [ [ { index: 0, - type: 'CORE', + info: providerInfo, }, { index: 1, - type: 'METAMASK', + info: { ...providerInfo, uuid: 'uuid-2' }, }, ], ], @@ -152,10 +143,9 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { expect(provider2.request).toHaveBeenCalledWith({ method: 'eth_requestAccounts', }); - await new Promise(process.nextTick); expect(requestAccountsCallback).toHaveBeenCalledTimes(1); - expect(requestAccountsCallback).toHaveBeenCalledWith(['0x000000']); + expect(requestAccountsCallback).toHaveBeenCalledWith(2); expect(mwpp.defaultProvider).toBe(provider2); }); @@ -184,7 +174,10 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { it('does not toggle wallet selection if wallet is already selected', async () => { const provider = new EVMProvider({ info: providerInfo }); - const provider2 = { isMetaMask: true, request: jest.fn() }; + const provider2 = new EVMProvider({ + info: { ...providerInfo, uuid: 'uuid-2' }, + request: jest.fn(), + } as any); const mwpp = new MultiWalletProviderProxy(provider); mwpp.addProvider(provider2); @@ -209,11 +202,11 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { [ { index: 0, - type: 'CORE', + info: providerInfo, }, { index: 1, - type: 'METAMASK', + info: { ...providerInfo, uuid: 'uuid-2' }, }, ], ], @@ -240,13 +233,15 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { it('wallet selection works with legacy functions: enable', async () => { const provider = new EVMProvider({ info: providerInfo }); - const provider2 = { isMetaMask: true, enable: jest.fn() }; + const provider2 = new EVMProvider({ + info: { ...providerInfo, uuid: 'uuid-2' }, + enable: jest.fn().mockResolvedValue(['0x000000']), + } as any); const mwpp = new MultiWalletProviderProxy(provider); mwpp.addProvider(provider2); // user selects metamask provider.request = jest.fn().mockResolvedValue(1); - provider2.enable.mockResolvedValue(['0x000000']); const requestAccountsCallback = jest.fn(); mwpp.enable().then(requestAccountsCallback); @@ -261,11 +256,11 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { [ { index: 0, - type: 'CORE', + info: providerInfo, }, { index: 1, - type: 'METAMASK', + info: { ...providerInfo, uuid: 'uuid-2' }, }, ], ], @@ -282,13 +277,15 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { it('wallet selection works with legacy functions: sendAsync', async () => { const provider = new EVMProvider({ info: providerInfo }); - const provider2 = { isMetaMask: true, request: jest.fn() }; + const provider2 = new EVMProvider({ + info: { ...providerInfo, uuid: 'uuid-2' }, + request: jest.fn().mockResolvedValue(['0x000000']), + } as any); const mwpp = new MultiWalletProviderProxy(provider); mwpp.addProvider(provider2); // user selects metamask provider.request = jest.fn().mockResolvedValue(1); - provider2.request.mockResolvedValue(['0x000000']); const requestAccountsCallback = jest.fn(); mwpp.sendAsync( @@ -310,11 +307,11 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { [ { index: 0, - type: 'CORE', + info: providerInfo, }, { index: 1, - type: 'METAMASK', + info: { ...providerInfo, uuid: 'uuid-2' }, }, ], ], @@ -336,13 +333,15 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { it('wallet selection works with legacy functions: send with callback', async () => { const provider = new EVMProvider({ info: providerInfo }); - const provider2 = { isMetaMask: true, request: jest.fn() }; + const provider2 = new EVMProvider({ + info: { ...providerInfo, uuid: 'uuid-2' }, + request: jest.fn().mockResolvedValue(['0x000000']), + } as any); const mwpp = new MultiWalletProviderProxy(provider); mwpp.addProvider(provider2); // user selects metamask provider.request = jest.fn().mockResolvedValue(1); - provider2.request.mockResolvedValue(['0x000000']); const requestAccountsCallback = jest.fn(); mwpp.send( @@ -360,11 +359,11 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { [ { index: 0, - type: 'CORE', + info: providerInfo, }, { index: 1, - type: 'METAMASK', + info: { ...providerInfo, uuid: 'uuid-2' }, }, ], ], @@ -418,40 +417,5 @@ describe('src/background/providers/MultiWalletProviderProxy', () => { expect((mwpp as any).defaultProvider).toBe(provider); }); - - it('maintains the providers list properly', () => { - const provider = new EVMProvider({ info: providerInfo }); - const mwpp = createMultiWalletProxy(provider); - const fooMock = () => 'bar'; - const bizMock = () => 'baz'; - - const mockProvider = { - providerMap: new Map([ - ['core', provider], - ['otherprovider', { isMetaMask: true, foo: fooMock }], - ['thirdprovider', { isRabby: true, biz: bizMock }], - ] as any), - }; - - mwpp.addProvider(mockProvider); - - expect((mwpp.providers[0] as any).isMetaMask).toBe(true); - expect((mwpp.providers[0] as any).isAvalanche).toBe(true); - expect((mwpp.providers[0] as any).someUndefinedProperty).toBe(undefined); - expect((mwpp.providers[0] as any)['#isWalletSelected']).toBe(undefined); - - expect(mwpp.providers[1] as any).toEqual({ - isMetaMask: true, - isAvalanche: undefined, - foo: fooMock, - }); - - expect(mwpp.providers[2] as any).toEqual({ - isMetaMask: undefined, - isAvalanche: undefined, - isRabby: true, - biz: bizMock, - }); - }); }); }); diff --git a/src/background/providers/MultiWalletProviderProxy.ts b/src/background/providers/MultiWalletProviderProxy.ts index 7eb1ce3d..ffc1a330 100644 --- a/src/background/providers/MultiWalletProviderProxy.ts +++ b/src/background/providers/MultiWalletProviderProxy.ts @@ -4,13 +4,12 @@ import { JsonRpcRequestPayload, JsonRpcResponse, } from '../connections/dAppConnection/models'; -import { getWalletExtensionType } from './utils/getWalletExtensionType'; 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[] = []; + #_providers: EVMProvider[] = []; #isWalletSelected = false; #defaultProvider; @@ -64,27 +63,13 @@ export class MultiWalletProviderProxy extends EventEmitter { }); } - public addProvider(provider) { - // the COINBASE collects here the wallets - if (provider.providerMap) { - for (const providerProxy of provider.providerMap.values()) { - if ( - !providerProxy.isAvalanche && // we exclude Core being duplicated - !this.#_providers.includes(providerProxy) - ) { - this.#_providers.push(providerProxy); - } - } - return; - } - - // the coinbase would add another proxy which is useless for us - if (provider.coinbaseWalletInstalls) { - return; - } + public addProvider(providerDetail) { + const isProviderAdded = this.#_providers.find((provider) => { + return provider.info.uuid === providerDetail.info.uuid; + }); - if (!this.#_providers.includes(provider)) { - this.#_providers.push(provider); + if (!isProviderAdded) { + this.#_providers.push(providerDetail); } } @@ -100,11 +85,9 @@ export class MultiWalletProviderProxy extends EventEmitter { params: [ // using any since we don't really know what kind of provider they are this.#_providers.map((p: any, i) => { - const type = getWalletExtensionType(p); - return { index: i, - type, + info: p.info, }; }), ], @@ -266,7 +249,7 @@ export function createMultiWalletProxy(evmProvider: EVMProvider) { // intercept unknow calls that are meant to be handled by the current provider // and forward them if needed so that we don't have to implement all the custom // functions any given wallet provider might expose - get: (target, prop, receiver) => { + get: (_, prop, receiver) => { // if the proxy has the function call it if (proxyProvider[prop]) { return proxyProvider[prop]; @@ -280,7 +263,7 @@ export function createMultiWalletProxy(evmProvider: EVMProvider) { // check if the request param is an extension defined by the dApp return Reflect.get(walletProviderExtensions, prop, receiver); }, - set(obj, prop, value) { + set(_, prop, value) { Reflect.set(walletProviderExtensions, prop, value); return true; }, diff --git a/src/background/providers/initializeInpageProvider.test.ts b/src/background/providers/initializeInpageProvider.test.ts index 8cf42435..06cd4792 100644 --- a/src/background/providers/initializeInpageProvider.test.ts +++ b/src/background/providers/initializeInpageProvider.test.ts @@ -59,19 +59,6 @@ describe('src/background/providers/initializeInpageProvider', () => { expect(windowMock.ethereum).toBe(mockMultiWalletProxy); }); - it('adds other wallets to proxy when trying to set window.ethereum', () => { - initializeProvider(connectionMock, 10, windowMock); - - const provider2 = { isMetaMask: true }; - - windowMock.ethereum = provider2; - - expect(mockMultiWalletProxy.addProvider).toHaveBeenCalledTimes(1); - expect(mockMultiWalletProxy.addProvider).toHaveBeenCalledWith(provider2); - - expect(windowMock.ethereum).toBe(mockMultiWalletProxy); - }); - it('dispatches ethereum#initialized event', () => { initializeProvider(connectionMock, 10, windowMock); @@ -132,25 +119,6 @@ describe('src/background/providers/initializeInpageProvider', () => { }); }); - describe('EIP-5749', () => { - it('sets window.evmproviders if not defined and adds core', () => { - const provider = initializeProvider(connectionMock, 10, windowMock); - - expect(windowMock.evmproviders).toStrictEqual({ core: provider }); - }); - it('adds Core to window.evmproviders if already defined', () => { - windowMock.evmproviders = { - MetaMask: { isMetaMask: true }, - }; - const provider = initializeProvider(connectionMock, 10, windowMock); - - expect(windowMock.evmproviders).toStrictEqual({ - MetaMask: { isMetaMask: true }, - core: provider, - }); - }); - }); - describe('window.avalanche', () => { it('creates the window.avalanche object', () => { const provider = initializeProvider(connectionMock, 10, windowMock); @@ -183,31 +151,33 @@ describe('src/background/providers/initializeInpageProvider', () => { it('announces core provider with eip6963:announceProvider', () => { const provider = initializeProvider(connectionMock, 10, windowMock); - expect(windowMock.dispatchEvent).toHaveBeenCalledTimes(5); - expect(windowMock.dispatchEvent.mock.calls[3][0].type).toEqual( + expect(windowMock.dispatchEvent).toHaveBeenCalledTimes(4); + expect(windowMock.dispatchEvent.mock.calls[2][0].type).toEqual( 'eip6963:announceProvider' ); - expect(windowMock.dispatchEvent.mock.calls[3][0].detail).toEqual({ - info: provider.info, - provider: provider, - }); + expect(windowMock.dispatchEvent.mock.calls[2][0].detail).toStrictEqual( + expect.objectContaining({ + info: provider.info, + provider: provider, + }) + ); }); it('re-announces on eip6963:requestProvider', () => { initializeProvider(connectionMock, 10, windowMock); - expect(windowMock.dispatchEvent).toHaveBeenCalledTimes(5); + expect(windowMock.dispatchEvent).toHaveBeenCalledTimes(4); - expect(windowMock.addEventListener).toHaveBeenCalledTimes(2); + expect(windowMock.addEventListener).toHaveBeenCalledTimes(3); expect(windowMock.addEventListener).toHaveBeenCalledWith( 'eip6963:requestProvider', expect.anything() ); - windowMock.addEventListener.mock.calls[0][1](); + windowMock.addEventListener.mock.calls[1][1](); - expect(windowMock.dispatchEvent).toHaveBeenCalledTimes(6); + expect(windowMock.dispatchEvent).toHaveBeenCalledTimes(5); - expect(windowMock.dispatchEvent.mock.calls[3][0].type).toEqual( + expect(windowMock.dispatchEvent.mock.calls[2][0].type).toEqual( 'eip6963:announceProvider' ); }); @@ -216,14 +186,7 @@ describe('src/background/providers/initializeInpageProvider', () => { it('should announce chainagnostic provider with core-wallet:announceProvider', () => { initializeProvider(connectionMock, 10, windowMock); - expect(windowMock.dispatchEvent.mock.calls[4][0].type).toEqual( - 'core-wallet:announceProvider' - ); - }); - it('should re-announce on core-wallet:requestProvider', () => { - initializeProvider(connectionMock, 10, windowMock); - - expect(windowMock.dispatchEvent.mock.calls[4][0].type).toEqual( + expect(windowMock.dispatchEvent.mock.calls[3][0].type).toEqual( 'core-wallet:announceProvider' ); }); diff --git a/src/background/providers/initializeInpageProvider.ts b/src/background/providers/initializeInpageProvider.ts index 7cc6c835..d4b76f9d 100644 --- a/src/background/providers/initializeInpageProvider.ts +++ b/src/background/providers/initializeInpageProvider.ts @@ -44,10 +44,25 @@ export function initializeProvider( deleteProperty: () => true, } ); + const multiWalletProxy = createMultiWalletProxy(evmProvider); + + globalObject.addEventListener('eip6963:announceProvider', (event: any) => { + multiWalletProxy.addProvider( + new Proxy( + { + info: { ...event.detail.info }, + ...event.detail.provider, + }, + { + deleteProperty: () => true, + set: () => true, + } + ) + ); + }); - setGlobalProvider(evmProvider, globalObject); + setGlobalProvider(evmProvider, globalObject, multiWalletProxy); setAvalancheGlobalProvider(evmProvider, globalObject); - setEvmproviders(evmProvider, globalObject); announceWalletProvider(evmProvider, globalObject); announceChainAgnosticProvider(chainAgnosticProvider, globalObject); @@ -62,23 +77,17 @@ export function initializeProvider( */ function setGlobalProvider( providerInstance: EVMProvider, - globalObject = window + globalObject = window, + multiWalletProxy ): void { try { - const multiWalletProxy = createMultiWalletProxy(providerInstance); - - // if we already have a wallet lets add it - if (globalObject.ethereum) { - multiWalletProxy.addProvider(globalObject.ethereum); - } - Object.defineProperty(globalObject, 'ethereum', { get: () => { return multiWalletProxy; }, // in case a wallet tries to overwrite us lets add them to the list - set: (value) => { - multiWalletProxy.addProvider(value); + set: () => { + return multiWalletProxy; }, }); @@ -129,16 +138,6 @@ function setAvalancheGlobalProvider( globalObject.dispatchEvent(new Event('avalanche#initialized')); } -function setEvmproviders( - providerInstance: EVMProvider, - globalObject = window -): void { - globalObject.evmproviders = globalObject.evmproviders || {}; - globalObject.evmproviders.core = providerInstance; - - globalObject.dispatchEvent(new Event('evmproviders#initialized')); -} - function announceWalletProvider( providerInstance: EVMProvider, globalObject = window diff --git a/src/background/providers/models.ts b/src/background/providers/models.ts index f338116a..f16f8a4b 100644 --- a/src/background/providers/models.ts +++ b/src/background/providers/models.ts @@ -24,6 +24,11 @@ export interface EIP6963ProviderDetail { provider: Eip1193Provider; } +export interface EIP6963AnnounceProviderEvent extends CustomEvent { + type: 'eip6963:announceProvider'; + detail: EIP6963ProviderDetail; +} + export enum EventNames { CORE_WALLET_ANNOUNCE_PROVIDER = 'core-wallet:announceProvider', CORE_WALLET_REQUEST_PROVIDER = 'core-wallet:requestProvider', diff --git a/src/background/providers/utils/getWalletExtensionType.test.ts b/src/background/providers/utils/getWalletExtensionType.test.ts deleted file mode 100644 index 5ddd50c2..00000000 --- a/src/background/providers/utils/getWalletExtensionType.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { WalletExtensionType } from '@src/background/services/web3/models'; -import { getWalletExtensionType } from './getWalletExtensionType'; - -describe('src/background/providers/utils/getWalletExtensionType', () => { - it('returns CORE when isAvalanche is true', () => { - expect( - getWalletExtensionType({ isAvalanche: true, isMetaMask: true }) - ).toBe(WalletExtensionType.CORE); - }); - - it('returns RABBY when isRabby is true', () => { - expect(getWalletExtensionType({ isRabby: true, isMetaMask: true })).toBe( - WalletExtensionType.RABBY - ); - }); - - it('returns COINBASE when isCoinbaseWallet is true', () => { - expect( - getWalletExtensionType({ isCoinbaseWallet: true, isMetaMask: true }) - ).toBe(WalletExtensionType.COINBASE); - }); - - it('returns METAMASK when isMetamask is true', () => { - expect(getWalletExtensionType({ isMetaMask: true })).toBe( - WalletExtensionType.METAMASK - ); - }); - - it('returns UNKNOWN', () => { - expect(getWalletExtensionType({})).toBe(WalletExtensionType.UNKNOWN); - }); -}); diff --git a/src/background/providers/utils/getWalletExtensionType.ts b/src/background/providers/utils/getWalletExtensionType.ts deleted file mode 100644 index ee792659..00000000 --- a/src/background/providers/utils/getWalletExtensionType.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { WalletExtensionType } from '../../services/web3/models'; - -// using any since we don't really know what properties other wallets define -export function getWalletExtensionType(provider: any): WalletExtensionType { - if (provider.isAvalanche) { - return WalletExtensionType.CORE; - } - if (provider.isRabby) { - return WalletExtensionType.RABBY; - } - if (provider.isCoinbaseWallet) { - return WalletExtensionType.COINBASE; - } - if (provider.isMetaMask) { - return WalletExtensionType.METAMASK; - } - return WalletExtensionType.UNKNOWN; -} diff --git a/src/background/services/secrets/SecretsService.test.ts b/src/background/services/secrets/SecretsService.test.ts index 620c20f1..2097786a 100644 --- a/src/background/services/secrets/SecretsService.test.ts +++ b/src/background/services/secrets/SecretsService.test.ts @@ -542,7 +542,6 @@ describe('src/background/services/secrets/SecretsService.ts', () => { const result = await secretsService.getAccountSecrets( activeAccountData ); - // eslint-disable-next-line @typescript-eslint/no-unused-vars const { ...rest } = secrets.wallets[0]; expect(result).toEqual({ diff --git a/src/background/services/web3/handlers/avalanche_selectWallet.ts b/src/background/services/web3/handlers/avalanche_selectWallet.ts index 891fb818..249857bd 100644 --- a/src/background/services/web3/handlers/avalanche_selectWallet.ts +++ b/src/background/services/web3/handlers/avalanche_selectWallet.ts @@ -26,6 +26,7 @@ export class AvalancheSelectWalletHandler extends DAppRequestHandler { ...request, displayData: { options: availableExtensions.map((o) => o.type), + info: availableExtensions.map((extension) => extension.info), }, }, `approve/select-wallet` diff --git a/src/background/services/web3/models.ts b/src/background/services/web3/models.ts index 97a308c6..e0425f5e 100644 --- a/src/background/services/web3/models.ts +++ b/src/background/services/web3/models.ts @@ -4,4 +4,7 @@ export enum WalletExtensionType { UNKNOWN = 'UNKNOWN', RABBY = 'RABBY', COINBASE = 'COINBASE', + PHANTOM = 'PHANTOM', + ZERION = 'ZERION', + KEPLR = 'KEPLR', } diff --git a/src/localization/locales/en/translation.json b/src/localization/locales/en/translation.json index a2ef4b66..536dd092 100644 --- a/src/localization/locales/en/translation.json +++ b/src/localization/locales/en/translation.json @@ -568,6 +568,7 @@ "Operation": "Operation", "Or": "Or", "Or click Scan QR Code.": "Or click Scan QR Code.", + "Other Wallets": "Other Wallets", "Outgoing": "Outgoing", "Owner": "Owner", "Owners": "Owners", @@ -915,7 +916,6 @@ "Unknown network fee": "Unknown network fee", "Unknown sign type": "Unknown sign type", "Unknown transaction error": "Unknown transaction error", - "Unknown wallet": "Unknown wallet", "Unknown website": "Unknown website", "Unlimited": "Unlimited", "Unlock": "Unlock", diff --git a/src/pages/ApproveAction/SelectWallet.tsx b/src/pages/ApproveAction/SelectWallet.tsx index 7555b755..0efe0804 100644 --- a/src/pages/ApproveAction/SelectWallet.tsx +++ b/src/pages/ApproveAction/SelectWallet.tsx @@ -4,7 +4,10 @@ import { useApproveAction } from '@src/hooks/useApproveAction'; import { useGetRequestId } from '@src/hooks/useGetRequestId'; import { useCallback } from 'react'; import { LoadingOverlay } from '../../components/common/LoadingOverlay'; -import { WalletExtensionButton } from '../Wallet/components/WalletExtensionButton'; +import { + WalletExtensionButton, + CoreExtensionButton, +} from '../Wallet/components/WalletExtensionButton'; import { Trans } from 'react-i18next'; import { Stack, Typography, WalletIcon } from '@avalabs/core-k2-components'; @@ -46,15 +49,28 @@ export function SelectWallet() { - {request.displayData.options.map((option, i) => ( + {request.displayData.info.map((info, index) => { + if (info.rdns === 'app.core.extension') { + return ( + { + selectWallet(index); + }} + info={info} + /> + ); + } + return; + })} + {request.displayData.info.length > 1 && ( { - selectWallet(i); + onClick={(index) => { + selectWallet(index); }} - type={option} + wallets={request.displayData.info} /> - ))} + )} ); diff --git a/src/pages/Wallet/components/WalletExtensionButton.tsx b/src/pages/Wallet/components/WalletExtensionButton.tsx index 475f08b2..a6a2dca6 100644 --- a/src/pages/Wallet/components/WalletExtensionButton.tsx +++ b/src/pages/Wallet/components/WalletExtensionButton.tsx @@ -1,74 +1,177 @@ import { useTranslation } from 'react-i18next'; import { Button, - CoinbaseWalletIcon, - CoreIcon, - MetaMaskIcon, - WalletIcon, + ButtonGroup, + ChevronDownIcon, + ClickAwayListener, + Grow, + keyframes, + MenuItem, + MenuList, + Popper, + Stack, + styled, + Typography, } from '@avalabs/core-k2-components'; import { WalletExtensionType } from '@src/background/services/web3/models'; +import { EIP6963ProviderInfo } from '@avalabs/vm-module-types'; +import { useRef, useState } from 'react'; interface WalletExtensionButtonProps { - type: WalletExtensionType; - onClick: () => void; + type?: WalletExtensionType; + info?: EIP6963ProviderInfo; + onClick: (index: number) => void; + wallets?: EIP6963ProviderInfo[]; } -export function WalletExtensionButton({ - type, - onClick, -}: WalletExtensionButtonProps) { - const { t } = useTranslation(); +const flip = keyframes` + from { + transform: rotateX(0deg); + } - const getWalletDisplayName = (walletType: WalletExtensionType) => { - switch (walletType) { - case WalletExtensionType.METAMASK: - return 'Metamask'; - case WalletExtensionType.COINBASE: - return 'Coinbase'; - case WalletExtensionType.UNKNOWN: - default: - return t('Unknown wallet'); - } - }; + to { + transform: rotateX(360deg); + } +`; - const getWalletLogo = (walletType: WalletExtensionType) => { - switch (walletType) { - case WalletExtensionType.METAMASK: - return ; - case WalletExtensionType.COINBASE: - return ; - case WalletExtensionType.UNKNOWN: - default: - return ; +const StyledMenuItem = styled(MenuItem)` + img { + transition: transform 0.3s ease-in-out; + } + color: ${({ theme }) => theme.palette.text.secondary}; + &:hover { + color: ${({ theme }) => theme.palette.text.primary}; + img { + animation: ${flip} 0.5s ease-in-out; } - }; - - if (type === WalletExtensionType.CORE) { - return ( - - ); } +`; +const CoreButton = styled(Button)` + img { + transition: transform 0.3s ease-in-out; + } + color: ${({ theme }) => theme.palette.text.secondary}; + &:hover { + color: ${({ theme }) => theme.palette.text.primary}; + img { + animation: ${flip} 0.5s ease-in-out; + } + } +`; + +const StyledButtonGroup = styled(ButtonGroup)` + border-radius: 999px; +`; + +export function CoreExtensionButton({ + info, + onClick, +}: WalletExtensionButtonProps) { + const { t } = useTranslation(); return ( - + + {info?.name || t('Unknown')} + + ); +} + +export function WalletExtensionButton({ + wallets, + onClick, +}: WalletExtensionButtonProps) { + const { t } = useTranslation(); + const toggleButtonRef = useRef(); + const [isMenuOpen, setIsMenuOpen] = useState(false); + return ( + + setIsMenuOpen(false)}> + + + + + + + ); }