From 54880d201a3629988868dcd80c87a7794876cf44 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 20 Sep 2023 12:31:54 -0700 Subject: [PATCH] feat: eagerly connect outside of react lifecycle (#7334) * feat: eagerly connect outside of react lifecycle * test: reflect selected wallet in localStorage * test: spy only on portfolio balances --- cypress/support/commands.ts | 1 - cypress/utils/user-state.ts | 12 +++--- src/components/Web3Provider/index.test.tsx | 3 -- src/components/Web3Provider/index.tsx | 2 - src/connection/eagerlyConnect.ts | 36 +++++++++++++++++ src/connection/types.ts | 10 +++++ src/hooks/useEagerlyConnect.ts | 47 ---------------------- src/index.tsx | 1 + src/state/user/reducer.ts | 6 ++- 9 files changed, 58 insertions(+), 60 deletions(-) create mode 100644 src/connection/eagerlyConnect.ts delete mode 100644 src/hooks/useEagerlyConnect.ts diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 43ca7cd043..acd5bfc04f 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -53,7 +53,6 @@ Cypress.Commands.overwrite( setInitialUserState(win, { ...initialState, - hideUniswapWalletBanner: true, ...CONNECTED_WALLET_USER_STATE, ...(options?.userState ?? {}), }) diff --git a/cypress/utils/user-state.ts b/cypress/utils/user-state.ts index 1389f47d4b..99e3c7212b 100644 --- a/cypress/utils/user-state.ts +++ b/cypress/utils/user-state.ts @@ -10,23 +10,25 @@ export const DISCONNECTED_WALLET_USER_STATE: Partial = { selectedWall * Other persisted slices are not set, so they will be filled with their respective initial values * when the app runs. */ -export function setInitialUserState(win: Cypress.AUTWindow, initialUserState: any) { +export function setInitialUserState(win: Cypress.AUTWindow, state: UserState) { + // Selected wallet should also be reflected in localStorage, so that eager connections work. + if (state.selectedWallet) { + win.localStorage.setItem('selected_wallet', state.selectedWallet) + } + win.indexedDB.deleteDatabase('redux') - const dbRequest = win.indexedDB.open('redux') - dbRequest.onsuccess = function () { const db = dbRequest.result const transaction = db.transaction('keyvaluepairs', 'readwrite') const store = transaction.objectStore('keyvaluepairs') store.put( { - user: initialUserState, + user: state, }, 'persist:interface' ) } - dbRequest.onupgradeneeded = function () { const db = dbRequest.result db.createObjectStore('keyvaluepairs') diff --git a/src/components/Web3Provider/index.test.tsx b/src/components/Web3Provider/index.test.tsx index 8830effd07..2beab7df02 100644 --- a/src/components/Web3Provider/index.test.tsx +++ b/src/components/Web3Provider/index.test.tsx @@ -5,7 +5,6 @@ import { Provider as EIP1193Provider } from '@web3-react/types' import { sendAnalyticsEvent, user } from 'analytics' import { connections, getConnection } from 'connection' import { Connection, ConnectionType } from 'connection/types' -import useEagerlyConnect from 'hooks/useEagerlyConnect' import { Provider } from 'react-redux' import { HashRouter } from 'react-router-dom' import store from 'state' @@ -33,7 +32,6 @@ jest.mock('connection', () => { return { ConnectionType, getConnection: jest.fn(), connections: [mockConnection] } }) -jest.mock('hooks/useEagerlyConnect', () => jest.fn()) jest.unmock('@web3-react/core') @@ -59,7 +57,6 @@ describe('Web3Provider', () => { await act(async () => { await result }) - expect(useEagerlyConnect).toHaveBeenCalled() expect(result).toBeTruthy() }) diff --git a/src/components/Web3Provider/index.tsx b/src/components/Web3Provider/index.tsx index 87df5629a7..231522ce06 100644 --- a/src/components/Web3Provider/index.tsx +++ b/src/components/Web3Provider/index.tsx @@ -6,7 +6,6 @@ import { connections, getConnection } from 'connection' import { isSupportedChain } from 'constants/chains' import { RPC_PROVIDERS } from 'constants/providers' import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc' -import useEagerlyConnect from 'hooks/useEagerlyConnect' import usePrevious from 'hooks/usePrevious' import { ReactNode, useEffect } from 'react' import { useLocation } from 'react-router-dom' @@ -15,7 +14,6 @@ import { getCurrentPageFromLocation } from 'utils/urlRoutes' import { getWalletMeta } from 'utils/walletMeta' export default function Web3Provider({ children }: { children: ReactNode }) { - useEagerlyConnect() const connectors = connections.map<[Connector, Web3ReactHooks]>(({ hooks, connector }) => [connector, hooks]) return ( diff --git a/src/connection/eagerlyConnect.ts b/src/connection/eagerlyConnect.ts new file mode 100644 index 0000000000..f4ad87fe3a --- /dev/null +++ b/src/connection/eagerlyConnect.ts @@ -0,0 +1,36 @@ +import { Connector } from '@web3-react/types' + +import { getConnection, gnosisSafeConnection, networkConnection } from './index' +import { ConnectionType, selectedWalletKey, toConnectionType } from './types' + +async function connect(connector: Connector, type: ConnectionType) { + performance.mark(`web3:connect:${type}:start`) + try { + if (connector.connectEagerly) { + await connector.connectEagerly() + } else { + await connector.activate() + } + return true + } catch (error) { + console.debug(`web3-react eager connection error: ${error}`) + return false + } finally { + performance.measure(`web3:connect:${type}`, `web3:connect:${type}:start`) + } +} + +connect(gnosisSafeConnection.connector, ConnectionType.GNOSIS_SAFE) +connect(networkConnection.connector, ConnectionType.NETWORK) +const selectedWallet = toConnectionType(localStorage.getItem(selectedWalletKey) ?? undefined) +if (selectedWallet) { + const selectedConnection = getConnection(selectedWallet) + if (selectedConnection) { + connect(selectedConnection.connector, selectedWallet).then((connected) => { + if (!connected) { + // only clear the persisted wallet type if it failed to connect. + localStorage.removeItem(selectedWalletKey) + } + }) + } +} diff --git a/src/connection/types.ts b/src/connection/types.ts index 536fd6f75f..ee98a9875e 100644 --- a/src/connection/types.ts +++ b/src/connection/types.ts @@ -2,6 +2,8 @@ import { ChainId } from '@uniswap/sdk-core' import { Web3ReactHooks } from '@web3-react/core' import { Connector } from '@web3-react/types' +export const selectedWalletKey = 'selected_wallet' + export enum ConnectionType { UNISWAP_WALLET_V2 = 'UNISWAP_WALLET_V2', INJECTED = 'INJECTED', @@ -11,6 +13,14 @@ export enum ConnectionType { GNOSIS_SAFE = 'GNOSIS_SAFE', } +export function toConnectionType(value = ''): ConnectionType | undefined { + if (Object.keys(ConnectionType).includes(value)) { + return value as ConnectionType + } else { + return undefined + } +} + export interface Connection { getName(): string connector: Connector diff --git a/src/hooks/useEagerlyConnect.ts b/src/hooks/useEagerlyConnect.ts deleted file mode 100644 index 1a3e94e624..0000000000 --- a/src/hooks/useEagerlyConnect.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Connector } from '@web3-react/types' -import { gnosisSafeConnection, networkConnection } from 'connection' -import { getConnection } from 'connection' -import { useEffect } from 'react' -import { useAppDispatch, useAppSelector } from 'state/hooks' -import { updateSelectedWallet } from 'state/user/reducer' - -import { useStateRehydrated } from './useStateRehydrated' - -async function connect(connector: Connector) { - try { - if (connector.connectEagerly) { - await connector.connectEagerly() - } else { - await connector.activate() - } - } catch (error) { - console.debug(`web3-react eager connection error: ${error}`) - } -} - -export default function useEagerlyConnect() { - const dispatch = useAppDispatch() - - const selectedWallet = useAppSelector((state) => state.user.selectedWallet) - const rehydrated = useStateRehydrated() - - useEffect(() => { - try { - connect(gnosisSafeConnection.connector) - connect(networkConnection.connector) - - if (!selectedWallet) return - const selectedConnection = getConnection(selectedWallet) - - if (selectedConnection) { - connect(selectedConnection.connector) - } - } catch { - // only clear the persisted wallet type if it failed to connect. - if (rehydrated) { - dispatch(updateSelectedWallet({ wallet: undefined })) - } - return - } - }, [dispatch, rehydrated, selectedWallet]) -} diff --git a/src/index.tsx b/src/index.tsx index 4582f078e5..2a44b73f1f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,6 +2,7 @@ import '@reach/dialog/styles.css' import 'inter-ui' import 'polyfills' import 'tracing' +import 'connection/eagerlyConnect' import { ApolloProvider } from '@apollo/client' import { FeatureFlagsProvider } from 'featureFlags' diff --git a/src/state/user/reducer.ts b/src/state/user/reducer.ts index 7bf0507ce6..cadc68d417 100644 --- a/src/state/user/reducer.ts +++ b/src/state/user/reducer.ts @@ -1,11 +1,12 @@ import { createSlice } from '@reduxjs/toolkit' -import { ConnectionType } from '../../connection/types' +import { ConnectionType, selectedWalletKey, toConnectionType } from '../../connection/types' import { SupportedLocale } from '../../constants/locales' import { DEFAULT_DEADLINE_FROM_NOW } from '../../constants/misc' import { RouterPreference } from '../../state/routing/types' import { SerializedPair, SerializedToken, SlippageTolerance } from './types' +const selectedWallet = toConnectionType(localStorage.getItem(selectedWalletKey) ?? undefined) const currentTimestamp = () => new Date().getTime() export interface UserState { @@ -56,7 +57,7 @@ function pairKey(token0Address: string, token1Address: string) { } export const initialState: UserState = { - selectedWallet: undefined, + selectedWallet, userLocale: null, userRouterPreference: RouterPreference.API, userHideClosedPositions: false, @@ -75,6 +76,7 @@ const userSlice = createSlice({ initialState, reducers: { updateSelectedWallet(state, { payload: { wallet } }) { + localStorage.setItem(selectedWalletKey, wallet) state.selectedWallet = wallet }, updateUserLocale(state, action) {