From a0ad8046926e14fd4905420fa8716e6056062488 Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Mon, 17 Jan 2022 18:01:56 +0100 Subject: [PATCH 1/4] Fix: xDai -> Gnosis Chain (#3304) * Fix: xDai -> Gnosis Chain * chore: Update deployment link * Rm "Safe" from deployment links * chore: Add /xdai to root routes * chore: Extract root /xdai redirection * build: Fixed xDai redirect + shortName root routes * chore: Fix shortName, update deploy + shortName * fix: shortName root routes + exact xDai redirect * Rm prod check Co-authored-by: iamacook --- .github/workflows/deploy.yml | 2 +- src/components/Root/index.tsx | 9 +------ src/config/cache/chains.ts | 2 +- src/config/chain.d.ts | 2 +- src/routes/index.tsx | 35 ++++++++++++++++++-------- src/routes/routes.ts | 5 ++-- src/utils/__tests__/history.test.ts | 39 +++++++++++++++++------------ src/utils/history.ts | 29 ++++++++------------- src/utils/storage/index.ts | 2 +- 9 files changed, 65 insertions(+), 60 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 35077a3797..f57619139e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -114,7 +114,7 @@ jobs: with: message: | ## Deployment links - :orange_circle: [Safe Rinkeby](${{ env.REVIEW_FEATURE_URL }}/rinkeby) | :white_circle: [Safe Mainnet](${{ env.REVIEW_FEATURE_URL }}/mainnet) | :purple_circle: [Safe Polygon](${{ env.REVIEW_FEATURE_URL }}/polygon) | :yellow_circle: [Safe BSC](${{ env.REVIEW_FEATURE_URL }}/bsc) | :black_circle: [Safe Arbitrum](${{ env.REVIEW_FEATURE_URL }}/arbitrum) | :green_circle: [Safe xDai](${{ env.REVIEW_FEATURE_URL }}/xdai) + :orange_circle: [Rinkeby](${{ env.REVIEW_FEATURE_URL }}/rin) | :white_circle: [Mainnet](${{ env.REVIEW_FEATURE_URL }}/eth) | :purple_circle: [Polygon](${{ env.REVIEW_FEATURE_URL }}/matic) | :yellow_circle: [BSC](${{ env.REVIEW_FEATURE_URL }}/bnb) | :black_circle: [Arbitrum](${{ env.REVIEW_FEATURE_URL }}/arb1) | :green_circle: [Gnosis Chain](${{ env.REVIEW_FEATURE_URL }}/gno) -|-|-|-|-|- repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token-user-login: 'github-actions[bot]' diff --git a/src/components/Root/index.tsx b/src/components/Root/index.tsx index 6581c67376..031f2f6a50 100644 --- a/src/components/Root/index.tsx +++ b/src/components/Root/index.tsx @@ -7,7 +7,7 @@ import App from 'src/components/App' import GlobalErrorBoundary from 'src/components/GlobalErrorBoundary' import AppRoutes from 'src/routes' import { store } from 'src/store' -import { history, WELCOME_ROUTE } from 'src/routes/routes' +import { history } from 'src/routes/routes' import theme from 'src/theme/mui' import { wrapInSuspense } from 'src/utils/wrapInSuspense' import Providers from '../Providers' @@ -18,9 +18,6 @@ import StoreMigrator from 'src/components/StoreMigrator' import LegacyRouteRedirection from './LegacyRouteRedirection' import { logError, Errors, CodedException } from 'src/logic/exceptions/CodedException' import { loadChains } from 'src/config/cache/chains' -import { isValidChainId, _getChainId } from 'src/config' -import { DEFAULT_CHAIN_ID } from 'src/utils/constants' -import { setChainId } from 'src/logic/config/utils' // Preloader is rendered outside of '#root' and acts as a loading spinner // for the app and then chains loading @@ -36,10 +33,6 @@ const RootConsumer = (): React.ReactElement | null => { const initChains = async () => { try { await loadChains() - if (!isValidChainId(_getChainId())) { - setChainId(DEFAULT_CHAIN_ID) - history.push(WELCOME_ROUTE) - } setHasChains(true) } catch (err) { logError(Errors._904, err.message) diff --git a/src/config/cache/chains.ts b/src/config/cache/chains.ts index d501d9b99a..6380170ad1 100644 --- a/src/config/cache/chains.ts +++ b/src/config/cache/chains.ts @@ -8,7 +8,7 @@ let chains: ChainInfo[] = [] export const getChains = (): ChainInfo[] => chains export const loadChains = async () => { - const { results = [] } = await getChainsConfig(GATEWAY_URL, { limit: 100 }) + const { results = [] } = await getChainsConfig(GATEWAY_URL) chains = results // Set the initail web3 provider after loading chains setWeb3ReadOnly() diff --git a/src/config/chain.d.ts b/src/config/chain.d.ts index 04b87d0281..b58d76d459 100644 --- a/src/config/chain.d.ts +++ b/src/config/chain.d.ts @@ -17,7 +17,7 @@ export const CHAIN_ID: Record = { OPTIMISM: '10', KOVAN: '42', BSC: '56', - XDAI: '100', + GNOSIS_CHAIN: '100', POLYGON: '137', ENERGY_WEB_CHAIN: '246', LOCAL: '4447', diff --git a/src/routes/index.tsx b/src/routes/index.tsx index b6238416c8..cd0df60745 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -23,9 +23,9 @@ import { } from './routes' import { getShortName } from 'src/config' import { setChainId } from 'src/logic/config/utils' -import { switchNetworkWithUrl } from 'src/utils/history' import { isDeeplinkedTx } from './safe/components/Transactions/TxList/utils' import { useAddressedRouteKey } from './safe/container/hooks/useAddressedRouteKey' +import { setChainIdFromUrl } from 'src/utils/history' const Welcome = React.lazy(() => import('./welcome/Welcome')) const CreateSafePage = React.lazy(() => import('./CreateSafePage/CreateSafePage')) @@ -41,6 +41,7 @@ const Routes = (): React.ReactElement => { // Component key that changes when addressed route slug changes const { key } = useAddressedRouteKey() + // Google Analytics useEffect(() => { let trackedPath = pathname @@ -59,12 +60,6 @@ const Routes = (): React.ReactElement => { } trackPage(trackedPath + search) - - // Set the initial network id from the URL. - // It depends on the chains - switchNetworkWithUrl({ pathname }) - - // Track when pathname changes }, [pathname, search, trackPage]) return ( @@ -74,12 +69,25 @@ const Routes = (): React.ReactElement => { path="/:url*(/+)" render={() => } /> + + } + /> + } + /> + { // Redirection to open network specific welcome pages - getNetworkRootRoutes().map(({ chainId, route }) => ( + getNetworkRootRoutes().map(({ chainId, route, shortName }) => ( { setChainId(chainId) return @@ -87,6 +95,7 @@ const Routes = (): React.ReactElement => { /> )) } + { return }} /> + + + { - // Rerender the container/reset its state when prefix/address changes - return + // Routes with a shortName prefix + const validShortName = setChainIdFromUrl(pathname) + return validShortName ? : }} /> diff --git a/src/routes/routes.ts b/src/routes/routes.ts index 8dc6459bd3..edac6983c1 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -64,10 +64,11 @@ export const SAFE_ROUTES = { SETTINGS_ADVANCED: `${ADDRESSED_ROUTE}/settings/advanced`, } -export const getNetworkRootRoutes = (): Array<{ chainId: ChainId; route: string }> => - getChains().map(({ chainId, chainName }) => ({ +export const getNetworkRootRoutes = (): Array<{ chainId: ChainId; route: string; shortName: string }> => + getChains().map(({ chainId, chainName, shortName }) => ({ chainId, route: `/${chainName.replaceAll(' ', '-').toLowerCase()}`, + shortName, })) export type SafeRouteParams = { shortName: ShortName; safeAddress: string } diff --git a/src/utils/__tests__/history.test.ts b/src/utils/__tests__/history.test.ts index 257eb3addb..041e681392 100644 --- a/src/utils/__tests__/history.test.ts +++ b/src/utils/__tests__/history.test.ts @@ -1,44 +1,51 @@ -import { getShortName } from 'src/config' import * as configUtils from 'src/logic/config/utils' import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' -import { history, WELCOME_ROUTE } from 'src/routes/routes' -import { switchNetworkWithUrl } from '../history' +import { history } from 'src/routes/routes' +import { setChainIdFromUrl } from '../history' -describe('switchNetworkWithUrl', () => { +describe('setChainIdFromUrl', () => { it('does not switch the network when there is no shortName in the url', () => { const setChainIdMock = jest.spyOn(configUtils, 'setChainId') + const pathname = `/welcome` - history.push(`/rin:${ZERO_ADDRESS}`) + history.push(pathname) - switchNetworkWithUrl({ pathname: '/welcome' }) + const result = setChainIdFromUrl(pathname) + expect(result).toBe(false) expect(setChainIdMock).not.toHaveBeenCalled() }) - it('does not switch the network when the shortName has not changed', () => { - // chainId defaults to RINKEBY in non-production environments if it can't be read from LS + it('does not switch the network when the chainId has not changed', () => { const setChainIdMock = jest.spyOn(configUtils, 'setChainId') + const pathname = `/rin:${ZERO_ADDRESS}` - history.push(`/rin:${ZERO_ADDRESS}`) + history.push(pathname) - switchNetworkWithUrl(history.location) + const result = setChainIdFromUrl(pathname) + expect(result).toBe(true) expect(setChainIdMock).not.toHaveBeenCalled() }) it('switches the network when the shortName changes', () => { - // chainId defaults to RINKEBY in non-production environments if it can't be read from LS const setChainIdMock = jest.spyOn(configUtils, 'setChainId') + const pathname = `/eth:${ZERO_ADDRESS}` - history.push(`/eth:${ZERO_ADDRESS}`) + history.push(pathname) - switchNetworkWithUrl(history.location) + const result = setChainIdFromUrl(pathname) + expect(result).toBe(true) expect(setChainIdMock).toHaveBeenCalled() }) it('redirects to the Welcome page when incorrect shortName is in the URL', () => { - history.push(`/fakechain:${ZERO_ADDRESS}`) + const setChainIdMock = jest.spyOn(configUtils, 'setChainId') + const pathname = `/fakechain:${ZERO_ADDRESS}` + + history.push(pathname) - switchNetworkWithUrl(history.location) + const result = setChainIdFromUrl(pathname) - expect(history.location.pathname).toBe(WELCOME_ROUTE) + expect(result).toBe(false) + expect(setChainIdMock).not.toHaveBeenCalled() }) }) diff --git a/src/utils/history.ts b/src/utils/history.ts index b659cfed6c..55dbbda433 100644 --- a/src/utils/history.ts +++ b/src/utils/history.ts @@ -1,29 +1,20 @@ -import { LocationDescriptorObject } from 'history' -import { getShortName } from 'src/config' +import { _getChainId } from 'src/config' import { getChains } from 'src/config/cache/chains' import { setChainId } from 'src/logic/config/utils' -import { hasPrefixedSafeAddressInUrl, extractPrefixedSafeAddress, history, WELCOME_ROUTE } from 'src/routes/routes' -import { DEFAULT_CHAIN_ID } from './constants' +import { hasPrefixedSafeAddressInUrl, extractPrefixedSafeAddress } from 'src/routes/routes' -export const switchNetworkWithUrl = ({ pathname }: LocationDescriptorObject): void => { - if (!hasPrefixedSafeAddressInUrl()) { - return - } +export const setChainIdFromUrl = (pathname: string): boolean => { + if (!hasPrefixedSafeAddressInUrl()) return false const { shortName } = extractPrefixedSafeAddress(pathname) - const currentShortName = getShortName() - - if (shortName === currentShortName) { - return - } - const chainId = getChains().find((chain) => chain.shortName === shortName)?.chainId - if (!chainId) { - setChainId(DEFAULT_CHAIN_ID) - history.push(WELCOME_ROUTE) - return + if (chainId) { + if (chainId !== _getChainId()) { + setChainId(chainId) + } + return true } - setChainId(chainId) + return false } diff --git a/src/utils/storage/index.ts b/src/utils/storage/index.ts index 47f8990d39..d3e41a90d0 100644 --- a/src/utils/storage/index.ts +++ b/src/utils/storage/index.ts @@ -8,7 +8,7 @@ const STORAGE_KEYS: Record = { [CHAIN_ID.ETHEREUM]: 'MAINNET', [CHAIN_ID.RINKEBY]: 'RINKEBY', [CHAIN_ID.BSC]: 'BSC', - [CHAIN_ID.XDAI]: 'XDAI', + [CHAIN_ID.GNOSIS_CHAIN]: 'XDAI', [CHAIN_ID.POLYGON]: 'POLYGON', [CHAIN_ID.ENERGY_WEB_CHAIN]: 'ENERGY_WEB_CHAIN', [CHAIN_ID.ARBITRUM]: 'ARBITRUM', From 2b67598d377602b5216c89d626e181f81112e13a Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Tue, 18 Jan 2022 18:05:46 +0100 Subject: [PATCH 2/4] Instantiate contracts via `chainId` from loaded config (#3313) * fix: Use chain id from chains config * build: init Safe contracts with chains + id set * build: Set chain id + contract init in middleware * fix: Remove unnecessary contract init * fix: Dispatch chain id setting with App init * build: Move contract init to provider watcher * build: Separate local `chainId` from store --- src/components/App/index.tsx | 14 +--- src/components/Root/index.tsx | 3 + src/logic/config/store/middleware/index.ts | 3 +- src/logic/config/utils/index.ts | 2 +- src/logic/contracts/safeContracts.ts | 10 +-- src/logic/safe/utils/safeVersion.ts | 2 +- .../store/middlewares/providerWatcher.ts | 83 +++++++++++-------- src/logic/wallets/store/reducer/provider.ts | 2 +- src/routes/CreateSafePage/CreateSafePage.tsx | 2 - src/routes/opening/index.tsx | 8 +- 10 files changed, 64 insertions(+), 65 deletions(-) diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index aba293456b..a93bfab110 100644 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -1,7 +1,7 @@ -import { useContext, useEffect } from 'react' +import { useContext } from 'react' import { makeStyles } from '@material-ui/core/styles' import { SnackbarProvider } from 'notistack' -import { useDispatch, useSelector } from 'react-redux' +import { useSelector } from 'react-redux' import styled from 'styled-components' import AlertIcon from 'src/assets/icons/alert.svg' @@ -26,8 +26,6 @@ import ReceiveModal from './ReceiveModal' import { useSidebarItems } from 'src/components/AppLayout/Sidebar/useSidebarItems' import useAddressBookSync from 'src/logic/addressBook/hooks/useAddressBookSync' import { extractSafeAddress } from 'src/routes/routes' -import loadSafesFromStorage from 'src/logic/safe/store/actions/loadSafesFromStorage' -import loadCurrentSessionFromStorage from 'src/logic/currentSession/store/actions/loadCurrentSessionFromStorage' const notificationStyles = { success: { @@ -62,7 +60,6 @@ const App: React.FC = ({ children }) => { const currentCurrency = useSelector(currentCurrencySelector) const granted = useSelector(grantedSelector) const sidebarItems = useSidebarItems() - const dispatch = useDispatch() useLoadSafe(addressFromUrl) // load initially useSafeScheduledUpdates(addressFromUrl) // load every X seconds useAddressBookSync() @@ -75,13 +72,6 @@ const App: React.FC = ({ children }) => { const onReceiveShow = () => onShow('Receive') const onReceiveHide = () => onHide('Receive') - // Load the Safes from LS just once, - // they'll be reloaded on network change - useEffect(() => { - dispatch(loadSafesFromStorage()) - dispatch(loadCurrentSessionFromStorage()) - }, [dispatch]) - return ( { } } initChains() + + // Set store chainId and init contracts/session + setChainId(_getChainId()) }, []) // Chains failed to load diff --git a/src/logic/config/store/middleware/index.ts b/src/logic/config/store/middleware/index.ts index 32dc608072..209e6f7223 100644 --- a/src/logic/config/store/middleware/index.ts +++ b/src/logic/config/store/middleware/index.ts @@ -6,9 +6,10 @@ import { clearSafeList } from 'src/logic/safe/store/actions/clearSafeList' import loadSafesFromStorage from 'src/logic/safe/store/actions/loadSafesFromStorage' import { Dispatch } from 'src/logic/safe/store/actions/types' import { CONFIG_ACTIONS } from '../actions' +import { store as reduxStore } from 'src/store' export const configMiddleware = - ({ dispatch }) => + ({ dispatch }: typeof reduxStore) => (next: Dispatch) => async (action: Action) => { const handledAction = next(action) diff --git a/src/logic/config/utils/index.ts b/src/logic/config/utils/index.ts index 869c5fde2d..9b2b2e61c2 100644 --- a/src/logic/config/utils/index.ts +++ b/src/logic/config/utils/index.ts @@ -1,7 +1,7 @@ -import { _getChainId, _setChainId } from 'src/config' import { ChainId } from 'src/config/chain.d' import { store } from 'src/store' import { setChainIdAction } from 'src/logic/config/store/actions' +import { _setChainId } from 'src/config' export const setChainId = (newChainId: ChainId) => { _setChainId(newChainId) diff --git a/src/logic/contracts/safeContracts.ts b/src/logic/contracts/safeContracts.ts index aba118a599..df6f6486e1 100644 --- a/src/logic/contracts/safeContracts.ts +++ b/src/logic/contracts/safeContracts.ts @@ -15,7 +15,7 @@ import { getChainById, _getChainId } from 'src/config' import { ChainId } from 'src/config/chain.d' import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' import { calculateGasOf, EMPTY_DATA } from 'src/logic/wallets/ethTransactions' -import { getWeb3, getChainIdFrom } from 'src/logic/wallets/getWeb3' +import { getWeb3 } from 'src/logic/wallets/getWeb3' import { GnosisSafe } from 'src/types/contracts/gnosis_safe.d' import { ProxyFactory } from 'src/types/contracts/proxy_factory.d' import { CompatibilityFallbackHandler } from 'src/types/contracts/compatibility_fallback_handler.d' @@ -194,9 +194,9 @@ export const getMasterCopyAddressFromProxyAddress = async (proxyAddress: string) return masterCopyAddress } -export const instantiateSafeContracts = async () => { +export const instantiateSafeContracts = () => { const web3 = getWeb3() - const chainId = (await getChainIdFrom(web3)).toString() as ChainId + const chainId = _getChainId() // Create ProxyFactory Master Copy proxyFactoryMaster = getProxyFactoryContractInstance(web3, chainId) @@ -211,8 +211,8 @@ export const instantiateSafeContracts = async () => { multiSend = getMultiSendContractInstance(web3, chainId) } -export const getSafeMasterContract = async () => { - await instantiateSafeContracts() +export const getSafeMasterContract = () => { + instantiateSafeContracts() return safeMaster } diff --git a/src/logic/safe/utils/safeVersion.ts b/src/logic/safe/utils/safeVersion.ts index 25725b7001..0c853357f1 100644 --- a/src/logic/safe/utils/safeVersion.ts +++ b/src/logic/safe/utils/safeVersion.ts @@ -68,7 +68,7 @@ export const checkIfSafeNeedsUpdate = async ( export const getCurrentMasterContractLastVersion = async (): Promise => { let safeMasterVersion: string try { - const safeMaster = await getSafeMasterContract() + const safeMaster = getSafeMasterContract() safeMasterVersion = await safeMaster.methods.VERSION().call() } catch (err) { // Default in case that it's not possible to obtain the version from the contract, returns a hardcoded value or an diff --git a/src/logic/wallets/store/middlewares/providerWatcher.ts b/src/logic/wallets/store/middlewares/providerWatcher.ts index 879edfcd19..bc5aadbd5d 100644 --- a/src/logic/wallets/store/middlewares/providerWatcher.ts +++ b/src/logic/wallets/store/middlewares/providerWatcher.ts @@ -1,10 +1,13 @@ +import { instantiateSafeContracts } from 'src/logic/contracts/safeContracts' import closeSnackbar from 'src/logic/notifications/store/actions/closeSnackbar' import { getProviderInfo, getWeb3 } from 'src/logic/wallets/getWeb3' import { fetchProvider } from 'src/logic/wallets/store/actions' import { ADD_PROVIDER } from 'src/logic/wallets/store/actions/addProvider' import { REMOVE_PROVIDER } from 'src/logic/wallets/store/actions/removeProvider' - import { loadFromStorage, removeFromStorage, saveToStorage } from 'src/utils/storage' +import { store as reduxStore } from 'src/store' +import { Dispatch } from 'redux' +import { ProviderState } from '../reducer/provider' const watchedActions = [ADD_PROVIDER, REMOVE_PROVIDER] @@ -16,50 +19,60 @@ export const loadLastUsedProvider = async (): Promise => { return lastUsedProvider } -let watcherInterval -const providerWatcherMware = (store) => (next) => async (action) => { - const handledAction = next(action) +type ProviderWatcherAction = { + type: string + payload: ProviderState +} - if (watchedActions.includes(action.type)) { - switch (action.type) { - case ADD_PROVIDER: { - const currentProviderProps = action.payload.toJS() +let watcherInterval: NodeJS.Timer +const providerWatcherMware = + (store: typeof reduxStore) => + (next: Dispatch) => + async (action: ProviderWatcherAction): Promise => { + const handledAction = next(action) - if (watcherInterval) { - clearInterval(watcherInterval) - } + if (watchedActions.includes(action.type)) { + switch (action.type) { + case ADD_PROVIDER: { + const currentProviderProps = action.payload.toJS() - saveToStorage(LAST_USED_PROVIDER_KEY, currentProviderProps.name) + if (watcherInterval) { + clearInterval(watcherInterval) + } - watcherInterval = setInterval(async () => { - const web3 = getWeb3() - const providerInfo = await getProviderInfo(web3) + instantiateSafeContracts() - const networkChanged = currentProviderProps.network !== providerInfo.network + saveToStorage(LAST_USED_PROVIDER_KEY, currentProviderProps.name) - if (networkChanged) { - store.dispatch(closeSnackbar({ dismissAll: true })) - } + watcherInterval = setInterval(async () => { + const web3 = getWeb3() + const providerInfo = await getProviderInfo(web3) - if (currentProviderProps.account !== providerInfo.account || networkChanged) { - store.dispatch(fetchProvider(currentProviderProps.name)) - } - }, 2000) + const networkChanged = currentProviderProps.network !== providerInfo.network - break - } - case REMOVE_PROVIDER: - clearInterval(watcherInterval) - if (!action.payload?.keepStorageKey) { - removeFromStorage(LAST_USED_PROVIDER_KEY) + if (networkChanged) { + store.dispatch(closeSnackbar({ dismissAll: true })) + } + + if (currentProviderProps.account !== providerInfo.account || networkChanged) { + store.dispatch(fetchProvider(currentProviderProps.name)) + } + }, 2000) + + break } - break - default: - break + case REMOVE_PROVIDER: + clearInterval(watcherInterval) + if (!action.payload?.keepStorageKey) { + removeFromStorage(LAST_USED_PROVIDER_KEY) + } + break + default: + break + } } - } - return handledAction -} + return handledAction + } export default providerWatcherMware diff --git a/src/logic/wallets/store/reducer/provider.ts b/src/logic/wallets/store/reducer/provider.ts index e0171db0e7..fa65c4a179 100644 --- a/src/logic/wallets/store/reducer/provider.ts +++ b/src/logic/wallets/store/reducer/provider.ts @@ -6,7 +6,7 @@ import { makeProvider, ProviderRecord, ProviderProps } from 'src/logic/wallets/s export const PROVIDER_REDUCER_ID = 'providers' -export type ProviderState = ProviderRecord +export type ProviderState = ProviderRecord & { keepStorageKey?: boolean } const providerReducer = handleActions( { diff --git a/src/routes/CreateSafePage/CreateSafePage.tsx b/src/routes/CreateSafePage/CreateSafePage.tsx index 63712f1cc1..1dc2264751 100644 --- a/src/routes/CreateSafePage/CreateSafePage.tsx +++ b/src/routes/CreateSafePage/CreateSafePage.tsx @@ -36,7 +36,6 @@ import ReviewNewSafeStep, { reviewNewSafeStepLabel } from './steps/ReviewNewSafe import { loadFromStorage, saveToStorage } from 'src/utils/storage' import SafeCreationProcess from './components/SafeCreationProcess' import SelectWalletAndNetworkStep, { selectWalletAndNetworkStepLabel } from './steps/SelectWalletAndNetworkStep' -import { instantiateSafeContracts } from 'src/logic/contracts/safeContracts' function CreateSafePage(): ReactElement { const [safePendingToBeCreated, setSafePendingToBeCreated] = useState() @@ -56,7 +55,6 @@ function CreateSafePage(): ReactElement { ) if (provider) { - await instantiateSafeContracts() setSafePendingToBeCreated(safePendingToBeCreated) } setIsLoading(false) diff --git a/src/routes/opening/index.tsx b/src/routes/opening/index.tsx index 50a6aca51a..635e255b61 100644 --- a/src/routes/opening/index.tsx +++ b/src/routes/opening/index.tsx @@ -10,7 +10,6 @@ import Button from 'src/components/layout/Button' import Heading from 'src/components/layout/Heading' import Img from 'src/components/layout/Img' import Paragraph from 'src/components/layout/Paragraph' -import { instantiateSafeContracts } from 'src/logic/contracts/safeContracts' import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' import { getWeb3, isTxPendingError } from 'src/logic/wallets/getWeb3' import { background, connected, fontColor } from 'src/theme/variables' @@ -94,13 +93,8 @@ export const SafeDeployment = ({ } useEffect(() => { - const loadContracts = async () => { - await instantiateSafeContracts() - setLoading(false) - } - if (provider) { - loadContracts() + setLoading(false) } }, [provider]) From df53139bce2c978ed58421ee64cdcc3e110513df Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Tue, 18 Jan 2022 18:06:34 +0100 Subject: [PATCH 3/4] Bump version to 3.17.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2c3048b638..b9f058b3db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "safe-react", - "version": "3.17.0", + "version": "3.17.1", "description": "Allowing crypto users manage funds in a safer way", "website": "https://github.com/gnosis/safe-react#readme", "bugs": { From 90e075ca90bab88ad81e7720afff393fb92bd5c1 Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Wed, 19 Jan 2022 10:41:24 +0100 Subject: [PATCH 4/4] Chore: remove legacy migration --- public/migrate-local-storage.html | 49 --- src/components/Root/index.tsx | 4 +- .../StoreMigrator/__test__/utils.test.ts | 313 ------------------ src/components/StoreMigrator/index.tsx | 85 ----- src/components/StoreMigrator/utils.ts | 112 ------- src/logic/addressBook/store/actions/index.ts | 2 - src/logic/addressBook/store/reducer/index.ts | 3 +- src/logic/exceptions/registry.ts | 2 - src/logic/safe/utils/mocks/remoteConfig.json | 53 +++ src/utils/storage/index.ts | 5 - 10 files changed, 56 insertions(+), 572 deletions(-) delete mode 100644 public/migrate-local-storage.html delete mode 100644 src/components/StoreMigrator/__test__/utils.test.ts delete mode 100644 src/components/StoreMigrator/index.tsx delete mode 100644 src/components/StoreMigrator/utils.ts diff --git a/public/migrate-local-storage.html b/public/migrate-local-storage.html deleted file mode 100644 index e9d150d655..0000000000 --- a/public/migrate-local-storage.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - diff --git a/src/components/Root/index.tsx b/src/components/Root/index.tsx index d8b387b4e0..4aec498fa3 100644 --- a/src/components/Root/index.tsx +++ b/src/components/Root/index.tsx @@ -14,10 +14,11 @@ import Providers from '../Providers' import './index.module.scss' import './OnboardCustom.module.scss' import './KeystoneCustom.module.scss' -import StoreMigrator from 'src/components/StoreMigrator' import LegacyRouteRedirection from './LegacyRouteRedirection' import { logError, Errors, CodedException } from 'src/logic/exceptions/CodedException' import { loadChains } from 'src/config/cache/chains' +import { setChainId } from 'src/logic/config/utils' +import { _getChainId } from 'src/config' // Preloader is rendered outside of '#root' and acts as a loading spinner // for the app and then chains loading @@ -65,7 +66,6 @@ const RootConsumer = (): React.ReactElement | null => { , )} - ) } diff --git a/src/components/StoreMigrator/__test__/utils.test.ts b/src/components/StoreMigrator/__test__/utils.test.ts deleted file mode 100644 index 721374be9a..0000000000 --- a/src/components/StoreMigrator/__test__/utils.test.ts +++ /dev/null @@ -1,313 +0,0 @@ -import * as migrationUtils from '../utils' - -jest.mock('src/logic/exceptions/CodedException', () => { - // Require the original module to not be mocked... - const originalModule = jest.requireActual('src/logic/exceptions/CodedException') - - return { - __esModule: true, // Use it when dealing with esModules - ...originalModule, - trackError: jest.fn(), - } -}) - -// Only exported functions -describe('getSubdomainUrl', () => { - // production - it('returns the correct production url', () => { - Object.defineProperty(window, 'location', { - writable: true, - value: { hostname: 'gnosis-safe.io' }, - }) - - const subdomain = migrationUtils.getSubdomainUrl('rinkeby') - expect(subdomain).toEqual('https://rinkeby.gnosis-safe.io/app') - }) - - it('returns nothing for incorrect staging url', () => { - Object.defineProperty(window, 'location', { - writable: true, - value: { hostname: 'fake.url' }, - }) - - const subdomain = migrationUtils.getSubdomainUrl('rinkeby') - expect(subdomain).toEqual('') - }) - - // staging - it('returns the correct staging url', () => { - Object.defineProperty(window, 'location', { - writable: true, - value: { hostname: 'safe-team-mainnet.staging.gnosisdev.com' }, - }) - - const subdomain = migrationUtils.getSubdomainUrl('bsc') - expect(subdomain).toEqual('https://safe-team-bsc.staging.gnosisdev.com/app') - }) -}) - -describe('getNetworksToMigrate', () => { - it('returns all networks when no localStorage value exists', () => { - Object.defineProperty(window, 'localStorage', { - writable: true, - value: { getItem: jest.fn(() => null) }, - }) - - const networks = migrationUtils.getNetworksToMigrate() - expect(networks).toEqual(['arbitrum', 'bsc', 'ewc', 'polygon', 'rinkeby', 'volta', 'xdai']) - }) - - it('returns non-migrated networks', () => { - Object.defineProperty(window, 'localStorage', { - writable: true, - value: { getItem: jest.fn(() => JSON.stringify(['bsc', 'rinkeby'])) }, - }) - - const networks = migrationUtils.getNetworksToMigrate() - expect(networks).toEqual(['arbitrum', 'ewc', 'polygon', 'volta', 'xdai']) - }) - - it('returns an empty array when all networks are migrated', () => { - Object.defineProperty(window, 'localStorage', { - writable: true, - value: { - getItem: jest.fn(() => JSON.stringify(['arbitrum', 'bsc', 'polygon', 'rinkeby', 'xdai', 'ewc', 'volta'])), - }, - }) - - const networks = migrationUtils.getNetworksToMigrate() - expect(networks).toEqual([]) - }) -}) - -describe('addMigratedNetwork', () => { - it('should not add already migrated network', () => { - Object.defineProperty(window, 'localStorage', { - writable: true, - value: { getItem: jest.fn(() => JSON.stringify(['rinkeby'])), setItem: jest.fn() }, - }) - - migrationUtils.addMigratedNetwork('rinkeby') - expect(localStorage.setItem).not.toHaveBeenCalled() - }) - - it('should add newly migrated network', () => { - Object.defineProperty(window, 'localStorage', { - writable: true, - value: { getItem: jest.fn(() => JSON.stringify(['xdai'])), setItem: jest.fn() }, - }) - - migrationUtils.addMigratedNetwork('rinkeby') - expect(localStorage.setItem).toHaveBeenCalledWith('SAFE__migratedNetworks', JSON.stringify(['xdai', 'rinkeby'])) - }) -}) - -describe('isNetworkSubdomain', () => { - it('returns true if subdomain', () => { - Object.defineProperty(window, 'location', { - writable: true, - value: { - href: 'https://rinkeby.gnosis-safe.io/app/#/safes/0xb3b83bf204C458B461de9B0CD2739DB152b4fa5A/balances', - hostname: 'rinkeby.gnosis-safe.io', - }, - }) - - expect(migrationUtils.isNetworkSubdomain()).toBe(true) - }) - - it('returns false if not subdomain', () => { - Object.defineProperty(window, 'location', { - writable: true, - value: { href: 'https://gnosis-safe.io/app/welcome', hostname: 'gnosis-safe.io' }, - }) - - expect(migrationUtils.isNetworkSubdomain()).toBe(false) - }) -}) -describe('handleMessage', () => { - let receivedCallback: () => void - let addressBookCallbackMock: () => void - let immortalDataCallbackMock: () => void - - beforeAll(() => { - receivedCallback = jest.fn() - addressBookCallbackMock = jest.fn() - immortalDataCallbackMock = jest.fn() - - Object.defineProperty(window, 'localStorage', { - writable: true, - value: { - getItem: jest.fn(), - setItem: jest.fn(), - }, - }) - }) - - afterEach(() => { - jest.clearAllMocks() - }) - - it('should return if not a valid origin', () => { - Object.defineProperty(window, 'location', { - writable: true, - value: { hostname: 'google.com' }, - }) - - const eventMock = { - data: { - payload: '', - }, - origin: 'gnosis-safe.io', - } as MessageEvent - - migrationUtils.handleMessage(eventMock, receivedCallback, addressBookCallbackMock, immortalDataCallbackMock) - - expect(receivedCallback).not.toHaveBeenCalled() - expect(addressBookCallbackMock).not.toHaveBeenCalled() - expect(immortalDataCallbackMock).not.toHaveBeenCalled() - }) - - it('should return if no payload', () => { - Object.defineProperty(window, 'location', { - writable: true, - value: { hostname: 'gnosis-safe.io' }, - }) - - Object.defineProperty(window, 'origin', { - writable: true, - value: 'https://gnosis-safe.io', - }) - - const eventMock = { - data: { - somethingElse: '', - }, - origin: 'https://rinkeby.gnosis-safe.io', - } as MessageEvent - - migrationUtils.handleMessage(eventMock, receivedCallback, addressBookCallbackMock, immortalDataCallbackMock) - - expect(receivedCallback).not.toHaveBeenCalled() - expect(addressBookCallbackMock).not.toHaveBeenCalled() - expect(immortalDataCallbackMock).not.toHaveBeenCalled() - }) - - it('should return if there is a completely malformed payload', () => { - const exceptions = require('src/logic/exceptions/CodedException') - Object.defineProperty(window, 'location', { - writable: true, - value: { hostname: 'gnosis-safe.io' }, - }) - - Object.defineProperty(window, 'origin', { - writable: true, - value: 'https://gnosis-safe.io', - }) - - const eventMock = { - data: { - payload: 'isdufhgoisdfuhglsdf', - }, - origin: 'https://rinkeby.gnosis-safe.io', - } as MessageEvent - - migrationUtils.handleMessage(eventMock, receivedCallback, addressBookCallbackMock, immortalDataCallbackMock) - - expect(receivedCallback).toHaveBeenCalled() - expect(addressBookCallbackMock).not.toHaveBeenCalled() - expect(immortalDataCallbackMock).not.toHaveBeenCalled() - expect(exceptions.trackError).toHaveBeenCalled() - }) - - it('should not try to merge the address book if the address book payload is malformed', () => { - const exceptions = require('src/logic/exceptions/CodedException') - - Object.defineProperty(window, 'location', { - writable: true, - value: { hostname: 'gnosis-safe.io' }, - }) - - Object.defineProperty(window, 'origin', { - writable: true, - value: 'https://gnosis-safe.io', - }) - - const eventMock = { - data: { - payload: JSON.stringify({ - SAFE__addressBook: 'sdfiguhfdoshgudslf', - ['_immortal|v2_RINKEBY__SAFES']: JSON.stringify({ test: 'aisfdhoilsaf' }), - ['_immortal|v2_MAINNET__SAFES']: JSON.stringify({}), - }), - }, - origin: 'https://rinkeby.gnosis-safe.io', - } as MessageEvent - - migrationUtils.handleMessage(eventMock, receivedCallback, addressBookCallbackMock, immortalDataCallbackMock) - - expect(receivedCallback).toHaveBeenCalled() - expect(addressBookCallbackMock).not.toHaveBeenCalled() - expect(exceptions.trackError).toHaveBeenCalledTimes(1) - expect(immortalDataCallbackMock).toHaveBeenCalledTimes(1) - expect(immortalDataCallbackMock).toHaveBeenCalledWith('v2_RINKEBY__SAFES', { test: 'aisfdhoilsaf' }) - }) - - it('should not save localStorage data if the localStorage payload is malformed', () => { - const exceptions = require('src/logic/exceptions/CodedException') - - Object.defineProperty(window, 'location', { - writable: true, - value: { hostname: 'gnosis-safe.io' }, - }) - - Object.defineProperty(window, 'origin', { - writable: true, - value: 'https://gnosis-safe.io', - }) - - const eventMock = { - data: { - payload: JSON.stringify({ - SAFE__addressBook: JSON.stringify([]), - ['_immortal|v2_RINKEBY__SAFES']: 'sdifughosidfghdfgs', - }), - }, - origin: 'https://rinkeby.gnosis-safe.io', - } as MessageEvent - - migrationUtils.handleMessage(eventMock, receivedCallback, addressBookCallbackMock, immortalDataCallbackMock) - - expect(receivedCallback).toHaveBeenCalled() - expect(addressBookCallbackMock).toHaveBeenCalledWith([]) - expect(immortalDataCallbackMock).not.toHaveBeenCalled() - expect(exceptions.trackError).toHaveBeenCalledTimes(1) - }) - - it('should migrate correctly formed address book/localStorage data', () => { - Object.defineProperty(window, 'location', { - writable: true, - value: { hostname: 'gnosis-safe.io' }, - }) - - Object.defineProperty(window, 'origin', { - writable: true, - value: 'https://gnosis-safe.io', - }) - - const eventMock = { - data: { - payload: JSON.stringify({ - SAFE__addressBook: JSON.stringify([]), - ['_immortal|v2_RINKEBY__SAFES']: JSON.stringify({}), - }), - }, - origin: 'https://rinkeby.gnosis-safe.io', - } as MessageEvent - - migrationUtils.handleMessage(eventMock, receivedCallback, addressBookCallbackMock, immortalDataCallbackMock) - - expect(receivedCallback).toHaveBeenCalled() - expect(addressBookCallbackMock).toHaveBeenCalledWith([]) - expect(immortalDataCallbackMock).toHaveBeenCalledWith('v2_RINKEBY__SAFES', {}) - }) -}) diff --git a/src/components/StoreMigrator/index.tsx b/src/components/StoreMigrator/index.tsx deleted file mode 100644 index ca476cfb79..0000000000 --- a/src/components/StoreMigrator/index.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { ReactElement, useEffect, useState } from 'react' -import { useDispatch } from 'react-redux' -import { addressBookMigrate } from 'src/logic/addressBook/store/actions' -import { saveMigratedKeyToStorage } from 'src/utils/storage' -import { - NETWORK_TO_MIGRATE, - isNetworkSubdomain, - getNetworksToMigrate, - handleMessage, - getSubdomainUrl, - addMigratedNetwork, -} from './utils' - -const IFRAME_PATH = '/migrate-local-storage.html' -const MAX_WAIT = 10e3 // 10 seconds - -const getIframeUrl = (network: NETWORK_TO_MIGRATE): string => `${getSubdomainUrl(network)}${IFRAME_PATH}` - -const StoreMigrator = (): ReactElement | null => { - const dispatch = useDispatch() - const [networksToMigrate, setNetworksToMigrate] = useState([]) - const [networkIndex, setNetworkIndex] = useState(0) - const currentNetwork = networksToMigrate[networkIndex] - - useEffect(() => { - if (isNetworkSubdomain()) return - const remainingNetworks = getNetworksToMigrate() - setNetworksToMigrate(remainingNetworks) - }, []) - - // Fix broken keys that start with v2_ - useEffect(() => { - try { - Object.keys(localStorage).forEach((key) => { - if (key.startsWith('v2_')) { - const val = localStorage.getItem(key) - localStorage.removeItem(key) - if (val) { - localStorage.setItem(`_immortal|${key}`, val) - } - } - }) - } catch (err) { - // ignore - } - }, []) - - // Add an event listener to receive the data to be migrated and save it into the storage - useEffect(() => { - if (!currentNetwork) return - - const nextNetwork = () => setNetworkIndex((index) => index + 1) - - const timeout = setTimeout(nextNetwork, MAX_WAIT) - - const onMessage = (event: MessageEvent) => { - handleMessage( - event, - // Move to the next network - () => { - clearTimeout(timeout) - addMigratedNetwork(currentNetwork) - nextNetwork() - }, - // Save address book - (addressBookData) => dispatch(addressBookMigrate(addressBookData)), - // Save immortal data - (key, value) => saveMigratedKeyToStorage(key, value), - ) - } - - window.addEventListener('message', onMessage, false) - - return () => { - clearTimeout(timeout) - window.removeEventListener('message', onMessage, false) - } - }, [dispatch, currentNetwork, setNetworkIndex]) - - return currentNetwork ? ( -