fix: allow wallets w/o mainnet to connect to WCv2 (#6854)
* fix: re-initialize wc connector with active chain * test(e2e): fix universal-search flake
This commit is contained in:
parent
1c4a383a49
commit
e5591e8f06
@ -1,13 +1,7 @@
|
||||
import { getTestSelector } from '../utils'
|
||||
|
||||
describe('Universal search bar', () => {
|
||||
before(() => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/')
|
||||
cy.get('[data-cy="magnifying-icon"]')
|
||||
.parent()
|
||||
.then(($navIcon) => {
|
||||
$navIcon.click()
|
||||
})
|
||||
cy.get('[data-cy="magnifying-icon"]').parent().eq(1).click()
|
||||
})
|
||||
|
||||
it('should yield clickable result for regular token or nft collection search term', () => {
|
||||
@ -19,20 +13,7 @@ describe('Universal search bar', () => {
|
||||
.and('contain.text', '$')
|
||||
.and('contain.text', '%')
|
||||
cy.get('[data-cy="searchbar-token-row-UNI"]').first().click()
|
||||
|
||||
cy.get('div').contains('Uniswap').should('exist')
|
||||
// Stats should have: TVL, 24H Volume, 52W low, 52W high.
|
||||
cy.get(getTestSelector('token-details-stats')).should('exist')
|
||||
cy.get(getTestSelector('token-details-stats')).within(() => {
|
||||
cy.get('[data-cy="tvl"]').should('include.text', '$')
|
||||
cy.get('[data-cy="volume-24h"]').should('include.text', '$')
|
||||
cy.get('[data-cy="52w-low"]').should('include.text', '$')
|
||||
cy.get('[data-cy="52w-high"]').should('include.text', '$')
|
||||
})
|
||||
|
||||
// About section should have description of token.
|
||||
cy.get(getTestSelector('token-details-about-section')).should('exist')
|
||||
cy.contains('UNI is the governance token for Uniswap').should('exist')
|
||||
cy.location('hash').should('equal', '#/tokens/ethereum/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984')
|
||||
})
|
||||
|
||||
it.skip('should show recent tokens and popular tokens with empty search term', () => {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { TraceEvent } from '@uniswap/analytics'
|
||||
import { BrowserEvent, InterfaceElementName, InterfaceEventName } from '@uniswap/analytics-events'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { useAccountDrawer } from 'components/AccountDrawer'
|
||||
import { ButtonEmphasis, ButtonSize, ThemeButton } from 'components/Button'
|
||||
import Loader from 'components/Icons/LoadingSpinner'
|
||||
@ -173,7 +174,8 @@ export default function Option({ connection }: OptionProps) {
|
||||
const { activationState, tryActivation } = useActivationState()
|
||||
const [wCPopoverOpen, setWCPopoverOpen] = useState(false)
|
||||
const [accountDrawerOpen, toggleAccountDrawerOpen] = useAccountDrawer()
|
||||
const activate = () => tryActivation(connection, toggleAccountDrawerOpen)
|
||||
const { chainId } = useWeb3React()
|
||||
const activate = () => tryActivation(connection, toggleAccountDrawerOpen, chainId)
|
||||
|
||||
useEffect(() => {
|
||||
if (!accountDrawerOpen) setWCPopoverOpen(false)
|
||||
|
@ -10,7 +10,7 @@ import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/tra
|
||||
import useEagerlyConnect from 'hooks/useEagerlyConnect'
|
||||
import useOrderedConnections from 'hooks/useOrderedConnections'
|
||||
import usePrevious from 'hooks/usePrevious'
|
||||
import { ReactNode, useEffect, useMemo } from 'react'
|
||||
import { ReactNode, useEffect, useMemo, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { useConnectedWallets } from 'state/wallets/hooks'
|
||||
import { getCurrentPageFromLocation } from 'utils/urlRoutes'
|
||||
@ -20,7 +20,13 @@ export default function Web3Provider({ children }: { children: ReactNode }) {
|
||||
const connections = useOrderedConnections()
|
||||
const connectors: [Connector, Web3ReactHooks][] = connections.map(({ hooks, connector }) => [connector, hooks])
|
||||
|
||||
const key = useMemo(() => connections.map((connection) => connection.getName()).join('-'), [connections])
|
||||
// Force a re-render when our connection state changes.
|
||||
const [index, setIndex] = useState(0)
|
||||
useEffect(() => setIndex((index) => index + 1), [connections])
|
||||
const key = useMemo(
|
||||
() => connections.map((connection) => connection.getName()).join('-') + index,
|
||||
[connections, index]
|
||||
)
|
||||
|
||||
return (
|
||||
<Web3ReactProvider connectors={connectors} key={key}>
|
||||
|
@ -15,22 +15,21 @@ const RPC_URLS_WITHOUT_FALLBACKS = Object.entries(RPC_URLS).reduce(
|
||||
}),
|
||||
{}
|
||||
)
|
||||
const optionalChains = [...L1_CHAIN_IDS, ...L2_CHAIN_IDS].filter((x) => x !== SupportedChainId.MAINNET)
|
||||
|
||||
export class WalletConnectV2 extends WalletConnect {
|
||||
ANALYTICS_EVENT = 'Wallet Connect QR Scan'
|
||||
constructor({
|
||||
actions,
|
||||
onError,
|
||||
defaultChainId,
|
||||
qrcode = true,
|
||||
}: Omit<WalletConnectConstructorArgs, 'options'> & { qrcode?: boolean }) {
|
||||
onError,
|
||||
}: Omit<WalletConnectConstructorArgs, 'options'> & { defaultChainId: number; qrcode?: boolean }) {
|
||||
const darkmode = Boolean(window.matchMedia('(prefers-color-scheme: dark)'))
|
||||
super({
|
||||
actions,
|
||||
options: {
|
||||
projectId: process.env.REACT_APP_WALLET_CONNECT_PROJECT_ID as string,
|
||||
optionalChains,
|
||||
chains: [SupportedChainId.MAINNET],
|
||||
chains: [defaultChainId],
|
||||
optionalChains: [...L1_CHAIN_IDS, ...L2_CHAIN_IDS],
|
||||
showQrModal: qrcode,
|
||||
rpcMap: RPC_URLS_WITHOUT_FALLBACKS,
|
||||
// as of 6/16/2023 there are no docs for `optionalMethods`
|
||||
@ -70,7 +69,7 @@ export class UniwalletConnect extends WalletConnectV2 {
|
||||
|
||||
constructor({ actions, onError }: Omit<WalletConnectConstructorArgs, 'options'>) {
|
||||
// disables walletconnect's proprietary qr code modal; instead UniwalletModal will listen for events to trigger our custom modal
|
||||
super({ actions, qrcode: false, onError })
|
||||
super({ actions, defaultChainId: SupportedChainId.MAINNET, qrcode: false, onError })
|
||||
|
||||
this.events.once(URI_AVAILABLE, () => {
|
||||
this.provider?.events.on('disconnect', this.deactivate)
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { Web3ReactHooks } from '@web3-react/core'
|
||||
import { Connector } from '@web3-react/types'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { useAppDispatch, useAppSelector } from 'state/hooks'
|
||||
import { updateSelectedWallet } from 'state/user/reducer'
|
||||
import { createDeferredPromise } from 'test-utils/promise'
|
||||
|
||||
import { act, renderHook } from '../test-utils/render'
|
||||
@ -35,6 +38,7 @@ function createMockConnection(
|
||||
hooks: {} as unknown as Web3ReactHooks,
|
||||
type,
|
||||
shouldDisplay: () => true,
|
||||
overrideActivate: jest.fn(),
|
||||
connector: new MockConnector(activate, deactivate),
|
||||
}
|
||||
}
|
||||
@ -54,18 +58,25 @@ it('Should call activate function on a connection', async () => {
|
||||
const activationResponse = createDeferredPromise()
|
||||
const mockConnection = createMockConnection(jest.fn().mockImplementation(() => activationResponse.promise))
|
||||
|
||||
renderHook(() => useAppDispatch()(updateSelectedWallet({ wallet: ConnectionType.INJECTED })))
|
||||
const initialSelectedWallet = renderHook(() => useAppSelector((state) => state.user.selectedWallet))
|
||||
expect(initialSelectedWallet.result.current).toBeDefined()
|
||||
|
||||
const result = renderHook(useActivationState).result
|
||||
const onSuccess = jest.fn()
|
||||
|
||||
let activationCall: Promise<void> = new Promise(jest.fn())
|
||||
act(() => {
|
||||
activationCall = result.current.tryActivation(mockConnection, onSuccess)
|
||||
activationCall = result.current.tryActivation(mockConnection, onSuccess, SupportedChainId.OPTIMISM)
|
||||
})
|
||||
|
||||
expect(result.current.activationState).toEqual({ status: ActivationStatus.PENDING, connection: mockConnection })
|
||||
expect(mockConnection.overrideActivate).toHaveBeenCalledWith(SupportedChainId.OPTIMISM)
|
||||
expect(mockConnection.connector.activate).toHaveBeenCalledTimes(1)
|
||||
expect(console.debug).toHaveBeenLastCalledWith(`Connection activating: ${mockConnection.getName()}`)
|
||||
expect(onSuccess).toHaveBeenCalledTimes(0)
|
||||
const pendingSelectedWallet = renderHook(() => useAppSelector((state) => state.user.selectedWallet))
|
||||
expect(pendingSelectedWallet.result.current).toBeUndefined()
|
||||
|
||||
await act(async () => {
|
||||
activationResponse.resolve()
|
||||
@ -77,6 +88,8 @@ it('Should call activate function on a connection', async () => {
|
||||
expect(console.debug).toHaveBeenLastCalledWith(`Connection activated: ${mockConnection.getName()}`)
|
||||
expect(console.debug).toHaveBeenCalledTimes(2)
|
||||
expect(onSuccess).toHaveBeenCalledTimes(1)
|
||||
const finalSelectedWallet = renderHook(() => useAppSelector((state) => state.user.selectedWallet))
|
||||
expect(finalSelectedWallet.result.current).toBeDefined()
|
||||
})
|
||||
|
||||
it('Should properly deactivate pending connection attempts', async () => {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { sendAnalyticsEvent } from '@uniswap/analytics'
|
||||
import { InterfaceEventName, WalletConnectionResult } from '@uniswap/analytics-events'
|
||||
import { Connection } from 'connection/types'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { atom } from 'jotai'
|
||||
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
|
||||
import { useCallback } from 'react'
|
||||
@ -31,15 +32,16 @@ function useTryActivation() {
|
||||
const currentPage = getCurrentPageFromLocation(pathname)
|
||||
|
||||
return useCallback(
|
||||
async (connection: Connection, onSuccess: () => void) => {
|
||||
async (connection: Connection, onSuccess: () => void, chainId?: SupportedChainId) => {
|
||||
// Skips wallet connection if the connection should override the default
|
||||
// behavior, i.e. install MetaMask or launch Coinbase app
|
||||
if (connection.overrideActivate?.()) return
|
||||
if (connection.overrideActivate?.(chainId)) return
|
||||
|
||||
try {
|
||||
setActivationState({ status: ActivationStatus.PENDING, connection })
|
||||
|
||||
console.debug(`Connection activating: ${connection.getName()}`)
|
||||
dispatch(updateSelectedWallet({ wallet: undefined }))
|
||||
await connection.connector.activate()
|
||||
|
||||
console.debug(`Connection activated: ${connection.getName()}`)
|
||||
|
@ -3,7 +3,7 @@ import { initializeConnector } from '@web3-react/core'
|
||||
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 { Actions, Connector } from '@web3-react/types'
|
||||
import GNOSIS_ICON from 'assets/images/gnosis.png'
|
||||
import UNISWAP_LOGO from 'assets/svg/logo.svg'
|
||||
import COINBASE_ICON from 'assets/wallets/coinbase-icon.svg'
|
||||
@ -82,17 +82,28 @@ export const walletConnectV1Connection: Connection = {
|
||||
shouldDisplay: () => !getIsInjectedMobileBrowser(),
|
||||
}
|
||||
|
||||
const [web3WalletConnectV2, web3WalletConnectV2Hooks] = initializeConnector<WalletConnectV2>(
|
||||
(actions) => new WalletConnectV2({ actions, onError })
|
||||
)
|
||||
export const walletConnectV2Connection: Connection = {
|
||||
getName: () => 'WalletConnect',
|
||||
connector: web3WalletConnectV2,
|
||||
hooks: web3WalletConnectV2Hooks,
|
||||
type: ConnectionType.WALLET_CONNECT_V2,
|
||||
getIcon: () => WALLET_CONNECT_ICON,
|
||||
shouldDisplay: () => !getIsInjectedMobileBrowser(),
|
||||
}
|
||||
export const walletConnectV2Connection: Connection = new (class implements Connection {
|
||||
private initializer = (actions: Actions, defaultChainId = SupportedChainId.MAINNET) =>
|
||||
new WalletConnectV2({ actions, defaultChainId, onError })
|
||||
|
||||
type = ConnectionType.WALLET_CONNECT_V2
|
||||
getName = () => 'WalletConnect'
|
||||
getIcon = () => WALLET_CONNECT_ICON
|
||||
shouldDisplay = () => !getIsInjectedMobileBrowser()
|
||||
|
||||
private _connector = initializeConnector<WalletConnectV2>(this.initializer)
|
||||
overrideActivate = (chainId?: SupportedChainId) => {
|
||||
// Always re-create the connector, so that the chainId is updated.
|
||||
this._connector = initializeConnector((actions) => this.initializer(actions, chainId))
|
||||
return false
|
||||
}
|
||||
get connector() {
|
||||
return this._connector[0]
|
||||
}
|
||||
get hooks() {
|
||||
return this._connector[1]
|
||||
}
|
||||
})()
|
||||
|
||||
const [web3UniwalletConnect, web3UniwalletConnectHooks] = initializeConnector<UniwalletConnect>(
|
||||
(actions) => new UniwalletConnect({ actions, onError })
|
||||
@ -133,7 +144,6 @@ const [web3CoinbaseWallet, web3CoinbaseWalletHooks] = initializeConnector<Coinba
|
||||
onError,
|
||||
})
|
||||
)
|
||||
|
||||
const coinbaseWalletConnection: Connection = {
|
||||
getName: () => 'Coinbase Wallet',
|
||||
connector: web3CoinbaseWallet,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Web3ReactHooks } from '@web3-react/core'
|
||||
import { Connector } from '@web3-react/types'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
|
||||
export enum ConnectionType {
|
||||
UNISWAP_WALLET = 'UNISWAP_WALLET',
|
||||
@ -21,6 +22,6 @@ export interface Connection {
|
||||
type: ConnectionType
|
||||
getIcon?(isDarkMode: boolean): string
|
||||
shouldDisplay(): boolean
|
||||
overrideActivate?: () => boolean
|
||||
overrideActivate?: (chainId?: SupportedChainId) => boolean
|
||||
isNew?: boolean
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user