diff --git a/src/assets/images/coinbaseWalletIcon.svg b/src/assets/images/coinbaseWalletIcon.svg deleted file mode 100644 index 46dd00f391..0000000000 --- a/src/assets/images/coinbaseWalletIcon.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/src/assets/images/metamask.svg b/src/assets/images/metamask.svg deleted file mode 100644 index 057e2d84c7..0000000000 --- a/src/assets/images/metamask.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/assets/images/phantom.png b/src/assets/images/phantom.png deleted file mode 100644 index 295ef88a30..0000000000 Binary files a/src/assets/images/phantom.png and /dev/null differ diff --git a/src/assets/images/rainbow.png b/src/assets/images/rainbow.png deleted file mode 100644 index 52ae9d3620..0000000000 Binary files a/src/assets/images/rainbow.png and /dev/null differ diff --git a/src/assets/images/walletConnectIcon.svg b/src/assets/images/walletConnectIcon.svg deleted file mode 100644 index 8557b99694..0000000000 --- a/src/assets/images/walletConnectIcon.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/assets/svg/browser-wallet-dark.svg b/src/assets/svg/browser-wallet-dark.svg deleted file mode 100644 index 5ac423d12d..0000000000 --- a/src/assets/svg/browser-wallet-dark.svg +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/assets/svg/browser-wallet-light.svg b/src/assets/svg/browser-wallet-light.svg deleted file mode 100644 index 98e68b0749..0000000000 --- a/src/assets/svg/browser-wallet-light.svg +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/assets/svg/phantom-icon.svg b/src/assets/svg/phantom-icon.svg new file mode 100644 index 0000000000..3d9309fde7 --- /dev/null +++ b/src/assets/svg/phantom-icon.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/wallets/brave-icon.svg b/src/assets/wallets/brave-icon.svg new file mode 100644 index 0000000000..b4fb5b6723 --- /dev/null +++ b/src/assets/wallets/brave-icon.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/wallets/browser-wallet-dark.svg b/src/assets/wallets/browser-wallet-dark.svg new file mode 100644 index 0000000000..af517ca597 --- /dev/null +++ b/src/assets/wallets/browser-wallet-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/wallets/browser-wallet-light.svg b/src/assets/wallets/browser-wallet-light.svg new file mode 100644 index 0000000000..d557a1f4e0 --- /dev/null +++ b/src/assets/wallets/browser-wallet-light.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/wallets/coinbase-icon.svg b/src/assets/wallets/coinbase-icon.svg new file mode 100644 index 0000000000..5e2727a43a --- /dev/null +++ b/src/assets/wallets/coinbase-icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/assets/wallets/ledger-icon.svg b/src/assets/wallets/ledger-icon.svg new file mode 100644 index 0000000000..078db767dd --- /dev/null +++ b/src/assets/wallets/ledger-icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/wallets/metamask-icon.svg b/src/assets/wallets/metamask-icon.svg new file mode 100644 index 0000000000..d574d33e7b --- /dev/null +++ b/src/assets/wallets/metamask-icon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/assets/wallets/phantom-icon.svg b/src/assets/wallets/phantom-icon.svg new file mode 100644 index 0000000000..c40aa9253a --- /dev/null +++ b/src/assets/wallets/phantom-icon.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/wallets/rabby-icon.svg b/src/assets/wallets/rabby-icon.svg new file mode 100644 index 0000000000..d61f871f70 --- /dev/null +++ b/src/assets/wallets/rabby-icon.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/wallets/trustwallet-icon.svg b/src/assets/wallets/trustwallet-icon.svg new file mode 100644 index 0000000000..b574317d99 --- /dev/null +++ b/src/assets/wallets/trustwallet-icon.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/wallets/uniswap-wallet-icon.png b/src/assets/wallets/uniswap-wallet-icon.png new file mode 100644 index 0000000000..9f9f89c5d1 Binary files /dev/null and b/src/assets/wallets/uniswap-wallet-icon.png differ diff --git a/src/assets/wallets/walletconnect-icon.svg b/src/assets/wallets/walletconnect-icon.svg new file mode 100644 index 0000000000..3f524a4b4b --- /dev/null +++ b/src/assets/wallets/walletconnect-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/Identicon/__snapshots__/StatusIcon.test.tsx.snap b/src/components/Identicon/__snapshots__/StatusIcon.test.tsx.snap index 52c1d0258d..7ca05073eb 100644 --- a/src/components/Identicon/__snapshots__/StatusIcon.test.tsx.snap +++ b/src/components/Identicon/__snapshots__/StatusIcon.test.tsx.snap @@ -1,14 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`StatusIcon with account renders children in correct order 1`] = ` -.c1 { - height: 16px; - width: 16px; - border-radius: 50%; - background-color: #98A1C0; - font-size: initial; -} - .c0 { position: relative; display: -webkit-box; @@ -35,7 +27,7 @@ exports[`StatusIcon with account renders children in correct order 1`] = ` width: 16px; } -.c2 { +.c1 { position: absolute; display: -webkit-box; display: -webkit-flex; @@ -60,7 +52,7 @@ exports[`StatusIcon with account renders children in correct order 1`] = ` overflow: hidden; } -.c4 { +.c3 { position: absolute; display: -webkit-box; display: -webkit-flex; @@ -85,7 +77,7 @@ exports[`StatusIcon with account renders children in correct order 1`] = ` overflow: hidden; } -.c3 { +.c2 { width: 16px; height: 16px; } @@ -100,13 +92,13 @@ exports[`StatusIcon with account renders children in correct order 1`] = ` } @supports (overflow:clip) { - .c2 { + .c1 { overflow: clip; } } @supports (overflow:clip) { - .c4 { + .c3 { overflow: clip; } } @@ -117,60 +109,161 @@ exports[`StatusIcon with account renders children in correct order 1`] = ` size="16" >
- -
+ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - -
-
+ + +
MetaMask icon
@@ -289,9 +382,9 @@ exports[`StatusIcon with no account renders children in correct order 1`] = ` class="c1" > MetaMask icon
!theme.darkMode && `border: 1px solid ${theme.backgroundOutline}`}; + border-radius: 12px; + } & > img, span { height: 40px; diff --git a/src/components/WalletModal/__snapshots__/Option.test.tsx.snap b/src/components/WalletModal/__snapshots__/Option.test.tsx.snap index 57eed1eca7..f1ac9404b5 100644 --- a/src/components/WalletModal/__snapshots__/Option.test.tsx.snap +++ b/src/components/WalletModal/__snapshots__/Option.test.tsx.snap @@ -86,6 +86,11 @@ exports[`Wallet Option renders default state 1`] = ` justify-content: center; } +.c2 img { + border: 1px solid #D2D9EE; + border-radius: 12px; +} + .c2 > img, .c2 span { height: 40px; diff --git a/src/connection/index.test.tsx b/src/connection/index.test.tsx index ae58d9deb0..e9f41bf662 100644 --- a/src/connection/index.test.tsx +++ b/src/connection/index.test.tsx @@ -1,5 +1,5 @@ -import INJECTED_DARK_ICON from 'assets/svg/browser-wallet-dark.svg' -import INJECTED_LIGHT_ICON from 'assets/svg/browser-wallet-light.svg' +import INJECTED_DARK_ICON from 'assets/wallets/browser-wallet-dark.svg' +import INJECTED_LIGHT_ICON from 'assets/wallets/browser-wallet-light.svg' import { getConnection, getConnections } from 'connection' import { ConnectionType } from './types' @@ -10,6 +10,11 @@ jest.mock('utils/userAgent', () => ({ })) describe('connection utility/metadata tests', () => { + beforeEach(() => { + global.window.ethereum = undefined + global.window.phantom = undefined + }) + const createWalletEnvironment = (ethereum: Window['window']['ethereum'], isMobile = false) => { UserAgentMock.isMobile = isMobile global.window.ethereum = ethereum @@ -23,11 +28,15 @@ describe('connection utility/metadata tests', () => { return { displayed, injected, coinbase, uniswap, walletconnect } } + const createPhantomEnviroment = () => { + global.window.phantom = { ethereum: { isPhantom: true } } + } + it('Non-injected Desktop', async () => { const { displayed, injected } = createWalletEnvironment(undefined) expect(displayed.includes(injected)).toBe(true) - expect(injected.getName()).toBe('MetaMask') + expect(injected.getName()).toBe('Install MetaMask') expect(injected.overrideActivate?.()).toBeTruthy() expect(displayed.length).toEqual(4) @@ -48,7 +57,7 @@ describe('connection utility/metadata tests', () => { expect(displayed.includes(coinbase)).toBe(true) expect(displayed.includes(injected)).toBe(true) - expect(injected.getName()).toBe('MetaMask') + expect(injected.getName()).toBe('Install MetaMask') expect(injected.overrideActivate?.()).toBeTruthy() expect(displayed.length).toEqual(4) @@ -65,38 +74,68 @@ describe('connection utility/metadata tests', () => { expect(displayed.length).toEqual(4) }) - it('Generic Injected Desktop', async () => { - const { displayed, injected } = createWalletEnvironment({ isTrustWallet: true }) + it('Trust Wallet Injected Desktop', async () => { + const { displayed, injected } = createWalletEnvironment({ isTrust: true }) expect(displayed.includes(injected)).toBe(true) - expect(injected.getName()).toBe('Browser Wallet') + expect(injected.getName()).toBe('Trust Wallet') expect(injected.overrideActivate?.()).toBeFalsy() expect(displayed.length).toEqual(4) }) + it('Rabby Wallet Injected Desktop', async () => { + const { displayed, injected } = createWalletEnvironment({ isRabby: true, isMetaMask: true }) // Rabby sets isMetaMask to true + + expect(displayed.includes(injected)).toBe(true) + expect(injected.getName()).toBe('Rabby') + expect(injected.overrideActivate?.()).toBeFalsy() + + expect(displayed.length).toEqual(4) + }) + + it('LedgerConnect Wallet Injected Desktop', async () => { + const { displayed, injected } = createWalletEnvironment({ isLedgerConnect: true }) + + expect(displayed.includes(injected)).toBe(true) + expect(injected.getName()).toBe('Ledger') + expect(injected.overrideActivate?.()).toBeFalsy() + + expect(displayed.length).toEqual(4) + }) + + it('Brave Browser Wallet Injected Desktop', async () => { + const { displayed, injected } = createWalletEnvironment({ isBraveWallet: true }) + + expect(displayed.includes(injected)).toBe(true) + expect(injected.getName()).toBe('Brave') + expect(injected.overrideActivate?.()).toBeFalsy() + + expect(displayed.length).toEqual(4) + }) + + it('Phantom Wallet Injected Desktop', async () => { + createPhantomEnviroment() + const { displayed, injected } = createWalletEnvironment({ isMetaMask: true }) // Phantom sets isMetaMask to true + + expect(displayed.includes(injected)).toBe(true) + expect(injected.getName()).toBe('Phantom') + expect(injected.overrideActivate?.()).toBeFalsy() + + expect(displayed.length).toEqual(4) + }) + + const UNKNOWN_MM_INJECTOR = { isRandomWallet: true, isMetaMask: true } as Window['window']['ethereum'] it('Generic Browser Wallet that injects as MetaMask', async () => { - const { displayed, injected } = createWalletEnvironment({ isRabby: true, isMetaMask: true }) + const { displayed, injected } = createWalletEnvironment(UNKNOWN_MM_INJECTOR) expect(displayed.includes(injected)).toBe(true) - expect(injected.getName()).toBe('Browser Wallet') + expect(injected.getName()).toBe('MetaMask') expect(injected.overrideActivate?.()).toBeFalsy() expect(displayed.length).toEqual(4) }) - it('Generic Wallet Browser with delayed injection', async () => { - const { injected } = createWalletEnvironment(undefined) - - expect(injected.getName()).toBe('MetaMask') - expect(injected.overrideActivate?.()).toBeTruthy() - - createWalletEnvironment({ isTrustWallet: true }) - - expect(injected.getName()).toBe('Browser Wallet') - expect(injected.overrideActivate?.()).toBeFalsy() - }) - const UNKNOWN_INJECTOR = { isRandomWallet: true } as Window['window']['ethereum'] it('Generic Unknown Injected Wallet Browser', async () => { const { displayed, injected } = createWalletEnvironment(UNKNOWN_INJECTOR, true) @@ -105,13 +144,25 @@ describe('connection utility/metadata tests', () => { expect(injected.getName()).toBe('Browser Wallet') expect(injected.overrideActivate?.()).toBeFalsy() - expect(injected.getIcon?.(/* isDarkMode */ false)).toBe(INJECTED_LIGHT_ICON) - expect(injected.getIcon?.(/* isDarkMode */ true)).toBe(INJECTED_DARK_ICON) + expect(injected.getIcon?.(/* isDarkMode= */ false)).toBe(INJECTED_LIGHT_ICON) + expect(injected.getIcon?.(/* isDarkMode= */ true)).toBe(INJECTED_DARK_ICON) // Ensures we provide multiple connection options if in an unknown injected browser expect(displayed.length).toEqual(4) }) + it('Generic Wallet Browser with delayed injection', async () => { + const { injected } = createWalletEnvironment(undefined) + + expect(injected.getName()).toBe('Install MetaMask') + expect(injected.overrideActivate?.()).toBeTruthy() + + createWalletEnvironment(UNKNOWN_INJECTOR) + + expect(injected.getName()).toBe('Browser Wallet') + expect(injected.overrideActivate?.()).toBeFalsy() + }) + it('MetaMask Mobile Browser', async () => { const { displayed, injected } = createWalletEnvironment({ isMetaMask: true }, true) diff --git a/src/connection/index.ts b/src/connection/index.ts index d2ffb08a1b..47a6593de5 100644 --- a/src/connection/index.ts +++ b/src/connection/index.ts @@ -4,21 +4,18 @@ import { GnosisSafe } from '@web3-react/gnosis-safe' import { MetaMask } from '@web3-react/metamask' import { Network } from '@web3-react/network' import { Connector } from '@web3-react/types' -import COINBASE_ICON from 'assets/images/coinbaseWalletIcon.svg' import GNOSIS_ICON from 'assets/images/gnosis.png' -import METAMASK_ICON from 'assets/images/metamask.svg' -import UNIWALLET_ICON from 'assets/images/uniwallet.png' -import WALLET_CONNECT_ICON from 'assets/images/walletConnectIcon.svg' -import INJECTED_DARK_ICON from 'assets/svg/browser-wallet-dark.svg' -import INJECTED_LIGHT_ICON from 'assets/svg/browser-wallet-light.svg' import UNISWAP_LOGO from 'assets/svg/logo.svg' +import COINBASE_ICON from 'assets/wallets/coinbase-icon.svg' +import UNIWALLET_ICON from 'assets/wallets/uniswap-wallet-icon.png' +import WALLET_CONNECT_ICON from 'assets/wallets/walletconnect-icon.svg' import { SupportedChainId } from 'constants/chains' import { isMobile, isNonIOSPhone } from 'utils/userAgent' import { RPC_URLS } from '../constants/networks' import { RPC_PROVIDERS } from '../constants/providers' import { Connection, ConnectionType } from './types' -import { getIsCoinbaseWallet, getIsInjected, getIsMetaMaskWallet } from './utils' +import { getInjection, getIsCoinbaseWallet, getIsInjected, getIsMetaMaskWallet } from './utils' import { UniwalletConnect, WalletConnectPopup } from './WalletConnect' import { WalletConnectV2Popup } from './WalletConnectV2' @@ -48,13 +45,11 @@ const getIsGenericInjector = () => getIsInjected() && !getIsMetaMaskWallet() && const [web3Injected, web3InjectedHooks] = initializeConnector((actions) => new MetaMask({ actions, onError })) const injectedConnection: Connection = { - // TODO(WEB-3131) re-add "Install MetaMask" string when no injector is present - getName: () => (getIsGenericInjector() ? 'Browser Wallet' : 'MetaMask'), + getName: () => getInjection().name, connector: web3Injected, hooks: web3InjectedHooks, type: ConnectionType.INJECTED, - getIcon: (isDarkMode: boolean) => - getIsGenericInjector() ? (isDarkMode ? INJECTED_DARK_ICON : INJECTED_LIGHT_ICON) : METAMASK_ICON, + getIcon: (isDarkMode: boolean) => getInjection(isDarkMode).icon, shouldDisplay: () => getIsMetaMaskWallet() || getShouldAdvertiseMetaMask() || getIsGenericInjector(), // If on non-injected, non-mobile browser, prompt user to install Metamask overrideActivate: () => { diff --git a/src/connection/utils.ts b/src/connection/utils.ts index a70b54b05b..efd1bbc283 100644 --- a/src/connection/utils.ts +++ b/src/connection/utils.ts @@ -1,14 +1,54 @@ +import BRAVE_ICON from 'assets/wallets/brave-icon.svg' +import INJECTED_DARK_ICON from 'assets/wallets/browser-wallet-dark.svg' +import INJECTED_LIGHT_ICON from 'assets/wallets/browser-wallet-light.svg' +import LEDGER_ICON from 'assets/wallets/ledger-icon.svg' +import METAMASK_ICON from 'assets/wallets/metamask-icon.svg' +import PHANTOM_ICON from 'assets/wallets/phantom-icon.svg' +import RABBY_ICON from 'assets/wallets/rabby-icon.svg' +import TRUST_WALLET_ICON from 'assets/wallets/trustwallet-icon.svg' import { Connection, ConnectionType } from 'connection/types' export const getIsInjected = () => Boolean(window.ethereum) -// When using Brave browser, `isMetaMask` is set to true when using the built-in wallet -// This variable should be true only when using the MetaMask extension -// https://wallet-docs.brave.com/ethereum/wallet-detection#compatability-with-metamask -type NonMetaMaskFlag = 'isRabby' | 'isBraveWallet' | 'isTrustWallet' | 'isLedgerConnect' -const allNonMetamaskFlags: NonMetaMaskFlag[] = ['isRabby', 'isBraveWallet', 'isTrustWallet', 'isLedgerConnect'] -export const getIsMetaMaskWallet = () => - Boolean(window.ethereum?.isMetaMask && !allNonMetamaskFlags.some((flag) => window.ethereum?.[flag])) +const InjectedWalletTable: { [key in keyof NonNullable]?: { name: string; icon: string } } = { + isBraveWallet: { name: 'Brave', icon: BRAVE_ICON }, + isRabby: { name: 'Rabby', icon: RABBY_ICON }, + isTrust: { name: 'Trust Wallet', icon: TRUST_WALLET_ICON }, + isLedgerConnect: { name: 'Ledger', icon: LEDGER_ICON }, +} + +/** + * Checks the window object for the presence of a known injectors and returns the most relevant injector name and icon. + * Returns a default metamask installation object if no wallet is detected. + * + * @param isDarkMode - optional parameter to determine which color mode of the + */ +export function getInjection(isDarkMode?: boolean): { name: string; icon: string } { + for (const [key, wallet] of Object.entries(InjectedWalletTable)) { + if (window.ethereum?.[key as keyof Window['ethereum']]) return wallet + } + + // Phantom sets its flag in a different part of the window object + if (window.phantom?.ethereum?.isPhantom) return { name: 'Phantom', icon: PHANTOM_ICON } + + // Check for MetaMask last, as other injectors will also set this flag, i.e. Brave browser and Phantom wallet + if (window.ethereum?.isMetaMask) return { name: 'MetaMask', icon: METAMASK_ICON } + + // Prompt metamask installation when there is no injection present or the only injection detected is coinbase (CB has separate entry point in UI) + if (!window.ethereum || window.ethereum.isCoinbaseWallet) return { name: 'Install MetaMask', icon: METAMASK_ICON } + + // Use a generic icon when injection is present but no known non-coinbase wallet is detected + return { name: 'Browser Wallet', icon: isDarkMode ? INJECTED_DARK_ICON : INJECTED_LIGHT_ICON } +} + +/** + * Returns true if `isMetaMask` is set to true and another non-metamask injector cannot be detected. + * + * Some non-metamask wallets set `isMetaMask` to true for dapp-compatability reasons. If one of these + * injectors are detected, this function will return false. + * https://wallet-docs.brave.com/ethereum/wallet-detection#compatability-with-metamask + */ +export const getIsMetaMaskWallet = () => getInjection().name === 'MetaMask' export const getIsCoinbaseWallet = () => Boolean(window.ethereum?.isCoinbaseWallet) diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index a20944ddce..5689976978 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -19,11 +19,17 @@ interface Window { // set by the Rabby browser extension isRabby?: true // set by the Trust Wallet browser extension - isTrustWallet?: true + isTrust?: true // set by the Ledger Extension Web 3 browser extension isLedgerConnect?: true autoRefreshOnNetworkChange?: boolean } + // set by the Phantom Wallet browser extension + phantom?: { + ethereum?: { + isPhantom?: true + } + } web3?: Record }