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/logic/safe/utils/mocks/remoteConfig.json b/src/logic/safe/utils/mocks/remoteConfig.json index 8b38dd0ef9..9d7108985e 100644 --- a/src/logic/safe/utils/mocks/remoteConfig.json +++ b/src/logic/safe/utils/mocks/remoteConfig.json @@ -64,6 +64,59 @@ "SPENDING_LIMIT" ] }, + { + "transactionService": "https://safe-transaction.xdai.gnosis.io", + "chainId": "100", + "chainName": "Gnosis Chain", + "shortName": "gno", + "l2": true, + "description": "Gnosis Chain", + "rpcUri": { + "authentication": "NO_AUTHENTICATION", + "value": "https://rpc.xdaichain.com/oe-only/" + }, + "safeAppsRpcUri": { + "authentication": "NO_AUTHENTICATION", + "value": "https://rpc.xdaichain.com/oe-only/" + }, + "publicRpcUri": { + "authentication": "NO_AUTHENTICATION", + "value": "https://rpc.xdaichain.com/oe-only/" + }, + "blockExplorerUriTemplate": { + "address": "https://blockscout.com/xdai/mainnet/address/{{address}}/transactions", + "txHash": "https://blockscout.com/xdai/mainnet/tx/{{txHash}}/", + "api": "https://blockscout.com/poa/xdai/api?module={{module}}&action={{action}}&address={{address}}&apiKey={{apiKey}}" + }, + "nativeCurrency": { + "name": "xDai", + "symbol": "XDAI", + "decimals": 18, + "logoUri": "https://safe-transaction-assets.staging.gnosisdev.com/chains/100/currency_logo.png" + }, + "theme": { + "textColor": "#ffffff", + "backgroundColor": "#48A9A6" + }, + "gasPrice": [ + { + "type": "FIXED", + "weiValue": "4000000000" + } + ], + "disabledWallets": [ + "metamask", + "walletConnect" + ], + "features": [ + "CONTRACT_INTERACTION", + "EIP1559", + "ERC721", + "SAFE_APPS", + "SAFE_TX_GAS_OPTIONAL", + "SPENDING_LIMIT" + ] + }, { "transactionService": "https://safe-transaction-polygon.staging.gnosisdev.com", "chainId": "137", 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',