diff --git a/.github/workflows/vercel.yml b/.github/workflows/vercel.yml index c93c8b687a..df7a85b4ad 100644 --- a/.github/workflows/vercel.yml +++ b/.github/workflows/vercel.yml @@ -46,12 +46,14 @@ jobs: REACT_APP_INFURA_KEY=${{ secrets.REACT_APP_INFURA_KEY }} REACT_APP_NETWORK_URL_1=${{ secrets.REACT_APP_NETWORK_URL_1 }} REACT_APP_NETWORK_URL_100=${{ secrets.REACT_APP_NETWORK_URL_100 }} + REACT_APP_NETWORK_URL_42161=${{ secrets.REACT_APP_NETWORK_URL_42161 }} REACT_APP_NETWORK_URL_11155111=${{ secrets.REACT_APP_NETWORK_URL_11155111 }} REACT_APP_WC_PROJECT_ID=${{ secrets.REACT_APP_WC_PROJECT_ID }} REACT_APP_IPFS_READ_URI=${{ secrets.REACT_APP_IPFS_READ_URI }} REACT_APP_EXPLORER_SENTRY_DSN=${{ secrets.EXPLORER_SENTRY_DSN }} REACT_APP_EXPLORER_GOOGLE_ANALYTICS_ID=${{ secrets.EXPLORER_GOOGLE_ANALYTICS_ID }} REACT_APP_SUBGRAPH_URL_MAINNET=${{ secrets.REACT_APP_SUBGRAPH_URL_MAINNET }} + REACT_APP_SUBGRAPH_URL_ARBITRUM_ONE=${{ secrets.REACT_APP_SUBGRAPH_URL_ARBITRUM_ONE }} REACT_APP_SUBGRAPH_URL_GNOSIS_CHAIN=${{ secrets.REACT_APP_SUBGRAPH_URL_GNOSIS_CHAIN }} vercel build -t ${{ secrets.VERCEL_TOKEN }} --prod diff --git a/README.md b/README.md index 864f056340..9e7118a69a 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,7 @@ Alternatively you can define the RPC URLs directly with the following environmen REACT_APP_NETWORK_URL_1: https://... REACT_APP_NETWORK_URL_11155111: https://... REACT_APP_NETWORK_URL_100: https://... +REACT_APP_NETWORK_URL_42161: https://... ``` Additionally, if you plan to run the integration tests locally you must define: diff --git a/apps/cow-fi/services/cms/index.ts b/apps/cow-fi/services/cms/index.ts index ce3c42b2bc..277a5c19e7 100644 --- a/apps/cow-fi/services/cms/index.ts +++ b/apps/cow-fi/services/cms/index.ts @@ -181,7 +181,7 @@ export async function getArticleBySlug(slug: string): Promise
{ * @param slug Slug of the category * * @throws Error if slug is not found - * @throws Error if multiple categorys are found with the same slug + * @throws Error if multiple categories are found with the same slug * * @returns Article with the given slug */ diff --git a/apps/cowswap-frontend/src/api/gasPrices/index.ts b/apps/cowswap-frontend/src/api/gasPrices/index.ts index 029e676757..fa90e655f3 100644 --- a/apps/cowswap-frontend/src/api/gasPrices/index.ts +++ b/apps/cowswap-frontend/src/api/gasPrices/index.ts @@ -3,7 +3,7 @@ import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' import { fetchWithRateLimit } from 'common/utils/fetch' -const fetchRateLimitted = fetchWithRateLimit({ +const fetchRateLimited = fetchWithRateLimit({ rateLimit: { tokensPerInterval: 3, interval: 'second', @@ -39,13 +39,7 @@ const priceMap: GasPrices = { class GasFeeApi { getUrl(chainId: ChainId): string { - const baseUrl = GAS_FEE_ENDPOINTS[chainId] - - if (chainId !== ChainId.GNOSIS_CHAIN) { - return `${baseUrl}?chainId=${chainId}` - } - - return baseUrl + return GAS_FEE_ENDPOINTS[chainId] } supportedChain(chainId: ChainId): boolean { @@ -54,9 +48,9 @@ class GasFeeApi { getHeaders(chainId: ChainId): { headers?: Headers } { const headers: { [key: string]: any } = {} - const apiKey = GAS_API_KEYS[chainId] || '' + const apiKey = GAS_API_KEYS[chainId] - if (chainId !== ChainId.GNOSIS_CHAIN) { + if (apiKey) { headers.headers = new Headers({ Authorization: apiKey, }) @@ -91,13 +85,8 @@ class GasFeeApi { slow: null, } - // Parse data for Gnosis chain (from Blockscout) - if (chainId === ChainId.GNOSIS_CHAIN) { - for (const key of Object.keys(priceMap)) { - output[key as keyof GasPrices] = this.toWei(json[key]) - } - } else { - // Parse data for other chains (from Blocknative) + if (this.getUrl(chainId).match(/blocknative/)) { + // Parse data from Blocknative const prices = json?.blockPrices[0]?.estimatedPrices if (prices) { @@ -106,6 +95,11 @@ class GasFeeApi { output[key as keyof GasPrices] = this.toWei(price) } } + } else { + // Parse data from Blockscout + for (const key of Object.keys(priceMap)) { + output[key as keyof GasPrices] = this.toWei(json[key]) + } } return output @@ -114,7 +108,7 @@ class GasFeeApi { async fetchData(chainId: ChainId) { const url = this.getUrl(chainId) const headers = this.getHeaders(chainId) - const response = await fetchRateLimitted(url, headers) + const response = await fetchRateLimited(url, headers) return response.json() } diff --git a/apps/cowswap-frontend/src/api/gnosisProtocol/priceApi.ts b/apps/cowswap-frontend/src/api/gnosisProtocol/priceApi.ts index 13b0b3143f..bbd1dfae9b 100644 --- a/apps/cowswap-frontend/src/api/gnosisProtocol/priceApi.ts +++ b/apps/cowswap-frontend/src/api/gnosisProtocol/priceApi.ts @@ -1,6 +1,6 @@ import { RAW_CODE_LINK } from '@cowprotocol/common-const' import { environmentName } from '@cowprotocol/common-utils' -import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { SupportedChainId, mapSupportedNetworks } from '@cowprotocol/cow-sdk' import { GpPriceStrategy } from 'legacy/state/gas/atoms' @@ -9,11 +9,7 @@ const STRATEGY_URL_BASE = RAW_CODE_LINK + '/configuration/config/' const ENV_BASE = environmentName !== 'production' ? 'barn' : environmentName const STRATEGY_URL = STRATEGY_URL_BASE + ENV_BASE + '/strategies' -const STRATEGY_API_URL: Record = { - [SupportedChainId.MAINNET]: STRATEGY_URL + '/strategy-1.json', - [SupportedChainId.GNOSIS_CHAIN]: STRATEGY_URL + '/strategy-100.json', - [SupportedChainId.SEPOLIA]: STRATEGY_URL + '/strategy-11155111.json', -} +const STRATEGY_API_URL = mapSupportedNetworks((chainId: SupportedChainId) => `${STRATEGY_URL}/strategy-${chainId}.json`) export type PriceStrategy = { primary: GpPriceStrategy diff --git a/apps/cowswap-frontend/src/common/hooks/featureFlags/useVerifiedQuotesEnabled.ts b/apps/cowswap-frontend/src/common/hooks/featureFlags/useVerifiedQuotesEnabled.ts index 3548a5b14b..1660b70658 100644 --- a/apps/cowswap-frontend/src/common/hooks/featureFlags/useVerifiedQuotesEnabled.ts +++ b/apps/cowswap-frontend/src/common/hooks/featureFlags/useVerifiedQuotesEnabled.ts @@ -4,10 +4,12 @@ import { SupportedChainId } from '@cowprotocol/cow-sdk' export function useVerifiedQuotesEnabled(chainId: SupportedChainId): boolean { const { verifyQuotesMainnet, verifyQuotesGnosis, verifyQuotesSepolia } = useFeatureFlags() + // TODO: remove completely const map: Record = { [SupportedChainId.MAINNET]: !!verifyQuotesMainnet, [SupportedChainId.GNOSIS_CHAIN]: !!verifyQuotesGnosis, [SupportedChainId.SEPOLIA]: !!verifyQuotesSepolia, + [SupportedChainId.ARBITRUM_ONE]: false, } return map[chainId] diff --git a/libs/common-hooks/src/useBlockNumber.tsx b/apps/cowswap-frontend/src/common/hooks/useBlockNumber.tsx similarity index 97% rename from libs/common-hooks/src/useBlockNumber.tsx rename to apps/cowswap-frontend/src/common/hooks/useBlockNumber.tsx index 806b523649..ba2152b8bb 100644 --- a/libs/common-hooks/src/useBlockNumber.tsx +++ b/apps/cowswap-frontend/src/common/hooks/useBlockNumber.tsx @@ -1,8 +1,8 @@ import { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState } from 'react' +import { useIsWindowVisible } from '@cowprotocol/common-hooks' import { useWalletChainId, useWalletProvider } from '@cowprotocol/wallet-provider' -import { useIsWindowVisible } from './useIsWindowVisible' const MISSING_PROVIDER = Symbol() const BlockNumberContext = createContext< diff --git a/apps/cowswap-frontend/src/common/hooks/useContract.ts b/apps/cowswap-frontend/src/common/hooks/useContract.ts index 2cdcc52614..acfe608246 100644 --- a/apps/cowswap-frontend/src/common/hooks/useContract.ts +++ b/apps/cowswap-frontend/src/common/hooks/useContract.ts @@ -84,5 +84,7 @@ export function useGP2SettlementContract(): GPv2Settlement | null { export function useVCowContract() { const { chainId } = useWalletInfo() - return useContract(chainId ? V_COW_CONTRACT_ADDRESS[chainId] : undefined, vCowAbi, true) + const vCowAddress = V_COW_CONTRACT_ADDRESS[chainId] || undefined + + return useContract(vCowAddress, vCowAbi, true) } diff --git a/libs/common-hooks/src/useGetReceipt.ts b/apps/cowswap-frontend/src/common/hooks/useGetReceipt.ts similarity index 100% rename from libs/common-hooks/src/useGetReceipt.ts rename to apps/cowswap-frontend/src/common/hooks/useGetReceipt.ts diff --git a/apps/cowswap-frontend/src/common/hooks/useIsProviderNetworkUnsupported.ts b/apps/cowswap-frontend/src/common/hooks/useIsProviderNetworkUnsupported.ts index 36d88bc964..ec3ac4353a 100644 --- a/apps/cowswap-frontend/src/common/hooks/useIsProviderNetworkUnsupported.ts +++ b/apps/cowswap-frontend/src/common/hooks/useIsProviderNetworkUnsupported.ts @@ -1,14 +1,16 @@ import { useMemo } from 'react' -import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { useAvailableChains } from '@cowprotocol/common-hooks' import { useWalletChainId } from '@cowprotocol/wallet-provider' + export function useIsProviderNetworkUnsupported(): boolean { const chainId = useWalletChainId() + const availableChains = useAvailableChains() return useMemo(() => { if (!chainId) return false - return !(chainId in SupportedChainId) - }, [chainId]) + return availableChains.indexOf(chainId) === -1 + }, [chainId, availableChains]) } diff --git a/apps/cowswap-frontend/src/common/hooks/useUnsupportedNetworksText.tsx b/apps/cowswap-frontend/src/common/hooks/useUnsupportedNetworksText.tsx index c7e5c0d015..c5bef9652b 100644 --- a/apps/cowswap-frontend/src/common/hooks/useUnsupportedNetworksText.tsx +++ b/apps/cowswap-frontend/src/common/hooks/useUnsupportedNetworksText.tsx @@ -1,14 +1,18 @@ import { getChainInfo } from '@cowprotocol/common-const' -import { ALL_SUPPORTED_CHAIN_IDS } from '@cowprotocol/cow-sdk' +import { useAvailableChains } from '@cowprotocol/common-hooks' import { Trans } from '@lingui/macro' + export function useUnsupportedNetworksText(): JSX.Element { + const availableChains = useAvailableChains() + return ( Please connect your wallet to one of our supported networks:
- {ALL_SUPPORTED_CHAIN_IDS.map((chainId) => getChainInfo(chainId)?.label) + {availableChains + .map((chainId) => getChainInfo(chainId)?.label) .filter(Boolean) .join(', ')}
diff --git a/apps/cowswap-frontend/src/common/pure/ChainPrefixWarning/index.tsx b/apps/cowswap-frontend/src/common/pure/ChainPrefixWarning/index.tsx index a9ea9a1d77..49ca0f15d9 100644 --- a/apps/cowswap-frontend/src/common/pure/ChainPrefixWarning/index.tsx +++ b/apps/cowswap-frontend/src/common/pure/ChainPrefixWarning/index.tsx @@ -36,9 +36,11 @@ const Format = styled.strong` type ChainPrefixWarningProps = { chainPrefixWarning: string chainInfo: BaseChainInfo + isDarkMode: boolean } -export default function ChainPrefixWarning({ chainPrefixWarning, chainInfo }: ChainPrefixWarningProps) { - const { label, addressPrefix, logoUrl, color } = chainInfo +export default function ChainPrefixWarning({ chainPrefixWarning, chainInfo, isDarkMode }: ChainPrefixWarningProps) { + const { label, addressPrefix, logo, color } = chainInfo + const logoUrl = isDarkMode ? logo.dark : logo.light return (

diff --git a/apps/cowswap-frontend/src/common/pure/NetworksList/index.tsx b/apps/cowswap-frontend/src/common/pure/NetworksList/index.tsx index 2209af139c..418ac3b5bb 100644 --- a/apps/cowswap-frontend/src/common/pure/NetworksList/index.tsx +++ b/apps/cowswap-frontend/src/common/pure/NetworksList/index.tsx @@ -1,6 +1,6 @@ import { getChainInfo } from '@cowprotocol/common-const' import { getExplorerBaseUrl } from '@cowprotocol/common-utils' -import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from '@cowprotocol/cow-sdk' +import { SupportedChainId } from '@cowprotocol/cow-sdk' import { ExternalLink } from '@cowprotocol/ui' import { Trans } from '@lingui/macro' @@ -9,19 +9,22 @@ import * as styledEl from './styled' export interface NetworksListProps { currentChainId: SupportedChainId | null + isDarkMode: boolean + availableChains: SupportedChainId[] onSelectChain(targetChainId: SupportedChainId): void } export function NetworksList(props: NetworksListProps) { - const { currentChainId, onSelectChain } = props + const { currentChainId, isDarkMode, availableChains, onSelectChain } = props return ( <> - {ALL_SUPPORTED_CHAIN_IDS.map((targetChainId: SupportedChainId) => { + {availableChains.map((targetChainId: SupportedChainId) => { const info = getChainInfo(targetChainId) - const { label, logoUrl, bridge, explorer, explorerTitle, helpCenterUrl } = info + const { label, logo, bridge, explorer, explorerTitle, helpCenterUrl } = info const isActive = targetChainId === currentChainId + const logoUrl = getLogo(isDarkMode, isActive, logo.dark, logo.light) const rowContent = ( onSelectChain(targetChainId)} active={isActive}> @@ -69,3 +72,11 @@ export function NetworksList(props: NetworksListProps) { ) } + +function getLogo(isDarkMode: boolean, isActive: boolean, darkLogo: string, lightLogo: string): string { + if (isDarkMode || isActive) { + return darkLogo + } + + return lightLogo +} diff --git a/apps/cowswap-frontend/src/common/services/getQuoteCurrency/index.ts b/apps/cowswap-frontend/src/common/services/getQuoteCurrency/index.ts index ac02bcfa91..e97971b8f7 100644 --- a/apps/cowswap-frontend/src/common/services/getQuoteCurrency/index.ts +++ b/apps/cowswap-frontend/src/common/services/getQuoteCurrency/index.ts @@ -1,10 +1,13 @@ import { DAI, + DAI_ARBITRUM_ONE, NATIVE_CURRENCY_ADDRESS, + USDC_ARBITRUM_ONE, USDC_GNOSIS_CHAIN, USDC_MAINNET, USDC_SEPOLIA, USDT, + USDT_ARBITRUM_ONE, USDT_GNOSIS_CHAIN, WXDAI, } from '@cowprotocol/common-const' @@ -21,6 +24,9 @@ const STABLE_COINS: Record = { .map((token) => token.address.toLowerCase()) // XDAI and WXDAI are stable-coins .concat(NATIVE_CURRENCY_ADDRESS), + [SupportedChainId.ARBITRUM_ONE]: [USDT_ARBITRUM_ONE, USDC_ARBITRUM_ONE, DAI_ARBITRUM_ONE].map((token) => + token.address.toLowerCase() + ), [SupportedChainId.SEPOLIA]: [USDC_SEPOLIA].map((token) => token.address.toLowerCase()), } diff --git a/apps/cowswap-frontend/src/common/updaters/GasUpdater.tsx b/apps/cowswap-frontend/src/common/updaters/GasUpdater.tsx index 0c8cef160d..99e07258dd 100644 --- a/apps/cowswap-frontend/src/common/updaters/GasUpdater.tsx +++ b/apps/cowswap-frontend/src/common/updaters/GasUpdater.tsx @@ -2,7 +2,6 @@ import { useSetAtom } from 'jotai/index' import { useCallback, useEffect } from 'react' import { GAS_PRICE_UPDATE_THRESHOLD } from '@cowprotocol/common-const' -import { useBlockNumber } from '@cowprotocol/common-hooks' import { gasPriceAtom } from '@cowprotocol/core' import { useWalletInfo } from '@cowprotocol/wallet' @@ -14,6 +13,8 @@ import { useGasPrices } from 'legacy/state/gas/hooks' import { gasFeeApi } from 'api/gasPrices' +import { useBlockNumber } from '../hooks/useBlockNumber' + function needsGasUpdate(now: number, lastUpdated: number, threshold: number) { return now - lastUpdated > threshold } diff --git a/apps/cowswap-frontend/src/cosmos.decorator.tsx b/apps/cowswap-frontend/src/cosmos.decorator.tsx index e0023c91e3..5f49c4c51c 100644 --- a/apps/cowswap-frontend/src/cosmos.decorator.tsx +++ b/apps/cowswap-frontend/src/cosmos.decorator.tsx @@ -1,12 +1,11 @@ import '@reach/dialog/styles.css' import './polyfills' -import React, { StrictMode, useCallback, useContext, ReactNode, useEffect } from 'react' +import React, { ReactNode, StrictMode, useCallback, useContext, useEffect } from 'react' import IMAGE_MOON from '@cowprotocol/assets/cow-swap/moon.svg' import IMAGE_SUN from '@cowprotocol/assets/cow-swap/sun.svg' -import { BlockNumberProvider } from '@cowprotocol/common-hooks' -import { WalletUpdater, injectedWalletConnection } from '@cowprotocol/wallet' +import { injectedWalletConnection, WalletUpdater } from '@cowprotocol/wallet' import { Web3ReactProvider } from '@web3-react/core' import { LanguageProvider } from 'i18n' @@ -14,13 +13,14 @@ import SVG from 'react-inlinesvg' import { Provider } from 'react-redux' import { HashRouter } from 'react-router-dom' import { Flex } from 'rebass' -import styled from 'styled-components/macro' -import { ThemeContext } from 'styled-components/macro' +import styled, { ThemeContext } from 'styled-components/macro' import { cowSwapStore } from 'legacy/state' import { useDarkModeManager } from 'legacy/state/user/hooks' import ThemeProvider, { FixedGlobalStyle, ThemedGlobalStyle } from 'legacy/theme' +import { BlockNumberProvider } from './common/hooks/useBlockNumber' + const DarkModeToggleButton = styled.button` display: flex; align-items: center; diff --git a/apps/cowswap-frontend/src/cow-react/index.tsx b/apps/cowswap-frontend/src/cow-react/index.tsx index 93c40a1440..f8f3324f84 100644 --- a/apps/cowswap-frontend/src/cow-react/index.tsx +++ b/apps/cowswap-frontend/src/cow-react/index.tsx @@ -4,11 +4,9 @@ import 'inter-ui' import '@cowprotocol/analytics' import './sentry' import { Provider as AtomProvider } from 'jotai' -import { useEffect, StrictMode, ReactNode } from 'react' +import { ReactNode, StrictMode, useEffect } from 'react' -import { BlockNumberProvider } from '@cowprotocol/common-hooks' -import { isInjectedWidget } from '@cowprotocol/common-utils' -import { nodeRemoveChildFix } from '@cowprotocol/common-utils' +import { isInjectedWidget, nodeRemoveChildFix } from '@cowprotocol/common-utils' import { jotaiStore } from '@cowprotocol/core' import { SnackbarsWidget } from '@cowprotocol/snackbars' import { Web3Provider } from '@cowprotocol/wallet' @@ -35,6 +33,7 @@ import { useInjectedWidgetParams } from 'modules/injectedWidget' import { FeatureGuard } from 'common/containers/FeatureGuard' import { WalletUnsupportedNetworkBanner } from '../common/containers/WalletUnsupportedNetworkBanner' +import { BlockNumberProvider } from '../common/hooks/useBlockNumber' // Node removeChild hackaround // based on: https://github.com/facebook/react/issues/11538#issuecomment-417504600 diff --git a/apps/cowswap-frontend/src/legacy/components/AMMsLogo/index.tsx b/apps/cowswap-frontend/src/legacy/components/AMMsLogo/index.tsx index 62811f3e7d..9a1c4fa2a6 100644 --- a/apps/cowswap-frontend/src/legacy/components/AMMsLogo/index.tsx +++ b/apps/cowswap-frontend/src/legacy/components/AMMsLogo/index.tsx @@ -62,6 +62,7 @@ const ETH_AMM_LOGOS = [SushiImage, OneInchImage, ParaSwapImage, UniswapImage, Cu const LogosPerNetwork: Record> = { [SupportedChainId.MAINNET]: ETH_AMM_LOGOS, [SupportedChainId.SEPOLIA]: ETH_AMM_LOGOS, + [SupportedChainId.ARBITRUM_ONE]: ETH_AMM_LOGOS, // TODO: review actual AMMs on arbitrum [SupportedChainId.GNOSIS_CHAIN]: [ SushiImage, BaoSwapImage, diff --git a/apps/cowswap-frontend/src/legacy/components/AddressInputPanel/index.tsx b/apps/cowswap-frontend/src/legacy/components/AddressInputPanel/index.tsx index 6abf6a7e2e..9c822d914b 100644 --- a/apps/cowswap-frontend/src/legacy/components/AddressInputPanel/index.tsx +++ b/apps/cowswap-frontend/src/legacy/components/AddressInputPanel/index.tsx @@ -7,15 +7,14 @@ import { parsePrefixedAddress, } from '@cowprotocol/common-utils' import { useENS } from '@cowprotocol/ens' -import { RowBetween } from '@cowprotocol/ui' -import { ExternalLink } from '@cowprotocol/ui' -import { UI } from '@cowprotocol/ui' +import { ExternalLink, RowBetween, UI } from '@cowprotocol/ui' import { useWalletInfo } from '@cowprotocol/wallet' import { t, Trans } from '@lingui/macro' import styled from 'styled-components/macro' import { AutoColumn } from 'legacy/components/Column' +import { useIsDarkMode } from 'legacy/state/user/hooks' import ChainPrefixWarning from 'common/pure/ChainPrefixWarning' import { autofocus } from 'common/utils/autofocus' @@ -104,6 +103,7 @@ export function AddressInputPanel({ const chainInfo = getChainInfo(chainId) const { address, loading, name } = useENS(value) const [chainPrefixWarning, setChainPrefixWarning] = useState('') + const isDarkMode = useIsDarkMode() const handleInput = useCallback( (event: ChangeEvent) => { @@ -139,7 +139,7 @@ export function AddressInputPanel({ return ( - {chainPrefixWarning && } + {chainPrefixWarning && } diff --git a/apps/cowswap-frontend/src/legacy/components/CowBalanceButton/index.tsx b/apps/cowswap-frontend/src/legacy/components/CowBalanceButton/index.tsx index 21efe4a467..721d3799b9 100644 --- a/apps/cowswap-frontend/src/legacy/components/CowBalanceButton/index.tsx +++ b/apps/cowswap-frontend/src/legacy/components/CowBalanceButton/index.tsx @@ -1,7 +1,6 @@ import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' import { Command } from '@cowprotocol/types' -import { UI } from '@cowprotocol/ui' -import { TokenAmount } from '@cowprotocol/ui' +import { TokenAmount, UI } from '@cowprotocol/ui' import { transparentize } from 'color2k' import styled, { css } from 'styled-components/macro' diff --git a/apps/cowswap-frontend/src/legacy/components/CowSubsidyModal/SubsidyTable.tsx b/apps/cowswap-frontend/src/legacy/components/CowSubsidyModal/SubsidyTable.tsx index cecf5414e8..5af291c911 100644 --- a/apps/cowswap-frontend/src/legacy/components/CowSubsidyModal/SubsidyTable.tsx +++ b/apps/cowswap-frontend/src/legacy/components/CowSubsidyModal/SubsidyTable.tsx @@ -1,10 +1,9 @@ import { V_COW } from '@cowprotocol/common-const' import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { TokenAmount } from '@cowprotocol/ui' -import { UI } from '@cowprotocol/ui' +import { TokenAmount, UI } from '@cowprotocol/ui' import { CurrencyAmount } from '@uniswap/sdk-core' -import { transparentize, lighten } from 'color2k' +import { lighten, transparentize } from 'color2k' import styled from 'styled-components/macro' import { useIsDarkMode } from 'legacy/state/user/hooks' @@ -163,6 +162,8 @@ const vCowToken = V_COW[SupportedChainId.MAINNET] function SubsidyTable({ discount }: CowSubsidy) { const darkMode = useIsDarkMode() + if (!vCowToken) return null + return ( diff --git a/apps/cowswap-frontend/src/legacy/components/Header/NetworkSelector/index.tsx b/apps/cowswap-frontend/src/legacy/components/Header/NetworkSelector/index.tsx index 1a66519408..5e7b1ac9a3 100644 --- a/apps/cowswap-frontend/src/legacy/components/Header/NetworkSelector/index.tsx +++ b/apps/cowswap-frontend/src/legacy/components/Header/NetworkSelector/index.tsx @@ -1,6 +1,7 @@ import { useRef } from 'react' import { getChainInfo } from '@cowprotocol/common-const' +import { useAvailableChains } from '@cowprotocol/common-hooks' import { UI } from '@cowprotocol/ui' import { useIsSmartContractWallet, useWalletInfo } from '@cowprotocol/wallet' import { useWalletProvider } from '@cowprotocol/wallet-provider' @@ -13,6 +14,7 @@ import styled from 'styled-components/macro' import { upToMedium, useMediaQuery } from 'legacy/hooks/useMediaQuery' import { useCloseModal, useModalIsOpen, useOpenModal, useToggleModal } from 'legacy/state/application/hooks' import { ApplicationModal } from 'legacy/state/application/reducer' +import { useIsDarkMode } from 'legacy/state/user/hooks' import { MEDIA_WIDTHS } from 'legacy/theme' import { useIsProviderNetworkUnsupported } from 'common/hooks/useIsProviderNetworkUnsupported' @@ -163,6 +165,10 @@ export function NetworkSelector() { const onSelectChain = useOnSelectNetwork() const isUpToMedium = useMediaQuery(upToMedium) + const isDarkMode = useIsDarkMode() + const logoUrl = isDarkMode ? info.logo.dark : info.logo.light + + const availableChains = useAvailableChains() if (!provider || isSmartContractWallet) { return null @@ -178,7 +184,7 @@ export function NetworkSelector() { {!isChainIdUnsupported ? ( <> - + {info?.label} @@ -196,7 +202,12 @@ export function NetworkSelector() { Select a network - + )} diff --git a/apps/cowswap-frontend/src/legacy/components/Header/Polling.tsx b/apps/cowswap-frontend/src/legacy/components/Header/Polling.tsx index c09794a88b..9909f19430 100644 --- a/apps/cowswap-frontend/src/legacy/components/Header/Polling.tsx +++ b/apps/cowswap-frontend/src/legacy/components/Header/Polling.tsx @@ -1,10 +1,8 @@ import { useEffect, useState } from 'react' -import { useBlockNumber, useIsOnline } from '@cowprotocol/common-hooks' +import { useIsOnline } from '@cowprotocol/common-hooks' import { ExplorerDataType, getExplorerLink } from '@cowprotocol/common-utils' -import { HoverTooltip, UI } from '@cowprotocol/ui' -import { RowFixed } from '@cowprotocol/ui' -import { ExternalLink } from '@cowprotocol/ui' +import { ExternalLink, HoverTooltip, RowFixed, UI } from '@cowprotocol/ui' import { useWalletInfo } from '@cowprotocol/wallet' import { Trans } from '@lingui/macro' @@ -14,6 +12,8 @@ import styled, { keyframes } from 'styled-components/macro' import useGasPrice from 'legacy/hooks/useGasPrice' import { ThemedText } from 'legacy/theme' +import { useBlockNumber } from 'common/hooks/useBlockNumber' + import { ChainConnectivityWarning } from './ChainConnectivityWarning' export const StyledPolling = styled.div<{ warning: boolean }>` @@ -33,6 +33,7 @@ export const StyledPolling = styled.div<{ warning: boolean }>` export const StyledPollingNumber = styled(ThemedText.Small)<{ breathe: boolean; hovering: boolean }>` transition: opacity 0.25s ease; opacity: ${({ breathe, hovering }) => (hovering ? 0.7 : breathe ? 1 : 0.5)}; + :hover { opacity: 1; } @@ -40,6 +41,7 @@ export const StyledPollingNumber = styled(ThemedText.Small)<{ breathe: boolean; a { color: unset; } + a:hover { text-decoration: none; color: unset; @@ -193,7 +195,8 @@ export function Polling() { {priceGwei ? ( - The current fast gas amount for sending a transaction on L1. Gas fees are paid in @@ -214,7 +217,8 @@ export function Polling() { chainId && blockNumber ? getExplorerLink(chainId, blockNumber.toString(), ExplorerDataType.BLOCK) : '' } > - The most recent block number on this network. Prices update on every block.} > {blockNumber}  diff --git a/apps/cowswap-frontend/src/legacy/components/NetworkAlert/NetworkAlert.tsx b/apps/cowswap-frontend/src/legacy/components/NetworkAlert/NetworkAlert.tsx index ee93ad63ab..f3c306ced1 100644 --- a/apps/cowswap-frontend/src/legacy/components/NetworkAlert/NetworkAlert.tsx +++ b/apps/cowswap-frontend/src/legacy/components/NetworkAlert/NetworkAlert.tsx @@ -1,9 +1,7 @@ import { getChainInfo } from '@cowprotocol/common-const' import { useTheme } from '@cowprotocol/common-hooks' import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { AutoRow } from '@cowprotocol/ui' -import { ExternalLink } from '@cowprotocol/ui' -import { UI } from '@cowprotocol/ui' +import { AutoRow, ExternalLink, UI } from '@cowprotocol/ui' import { useWalletInfo } from '@cowprotocol/wallet' import { Trans } from '@lingui/macro' @@ -137,8 +135,9 @@ export function NetworkAlert() { return null } - const { label, logoUrl, bridge } = getChainInfo(chainId) + const { label, logo, bridge } = getChainInfo(chainId) const textColor = theme.text1 + const logoUrl = darkMode ? logo.dark : logo.light return bridge ? ( diff --git a/apps/cowswap-frontend/src/legacy/components/OrderProgressBar/utils.ts b/apps/cowswap-frontend/src/legacy/components/OrderProgressBar/utils.ts index c8542f4176..3715e6b8a2 100644 --- a/apps/cowswap-frontend/src/legacy/components/OrderProgressBar/utils.ts +++ b/apps/cowswap-frontend/src/legacy/components/OrderProgressBar/utils.ts @@ -5,6 +5,7 @@ const EXPECTED_EXECUTION_TIME_PERCENTAGE = 75 export const EXPECTED_EXECUTION_TIME: Record = { [SupportedChainId.MAINNET]: 120, [SupportedChainId.GNOSIS_CHAIN]: 50, + [SupportedChainId.ARBITRUM_ONE]: 50, [SupportedChainId.SEPOLIA]: 50, } diff --git a/apps/cowswap-frontend/src/legacy/hooks/useCombinedBalance.ts b/apps/cowswap-frontend/src/legacy/hooks/useCombinedBalance.ts index 889fd490d1..3130f5d6d7 100644 --- a/apps/cowswap-frontend/src/legacy/hooks/useCombinedBalance.ts +++ b/apps/cowswap-frontend/src/legacy/hooks/useCombinedBalance.ts @@ -24,7 +24,7 @@ function useCowBalance() { */ export function useCombinedBalance() { const { chainId, account } = useWalletInfo() - const { total: vCowBalance } = useVCowData() + const { isLoading: isVCowLoading, total: vCowBalance } = useVCowData() // const { allocated, claimed } = useCowFromLockedGnoBalances() const cowBalance = useCowBalance() @@ -39,7 +39,7 @@ export function useCombinedBalance() { return useMemo(() => { let tmpBalance = JSBI.BigInt(0) - const isLoading = !!(account && (!vCowBalance /* || !lockedGnoBalance */ || !cowBalance)) + const isLoading = !!(account && (isVCowLoading /* || !lockedGnoBalance */ || !cowBalance)) const cow = COW[chainId] diff --git a/apps/cowswap-frontend/src/modules/account/containers/AccountDetails/SurplusCard.tsx b/apps/cowswap-frontend/src/modules/account/containers/AccountDetails/SurplusCard.tsx index 35c2bd4026..e8f187d5f6 100644 --- a/apps/cowswap-frontend/src/modules/account/containers/AccountDetails/SurplusCard.tsx +++ b/apps/cowswap-frontend/src/modules/account/containers/AccountDetails/SurplusCard.tsx @@ -1,6 +1,5 @@ -import { FiatAmount, HelpTooltip, QuestionTooltipIconWrapper, TokenAmount } from '@cowprotocol/ui' -import { ExternalLink } from '@cowprotocol/ui' -import { UI } from '@cowprotocol/ui' +import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { ExternalLink, FiatAmount, HelpTooltip, QuestionTooltipIconWrapper, TokenAmount, UI } from '@cowprotocol/ui' import { transparentize } from 'color2k' import styled from 'styled-components/macro' @@ -12,12 +11,17 @@ import useNativeCurrency from 'lib/hooks/useNativeCurrency' import { InfoCard } from './styled' +const DEFAULT_START_DATE = 'March 2023' +const ARBITRUM_ONE_START_DATE = 'May 2024' + export function SurplusCard() { const { surplusAmount, isLoading } = useTotalSurplus() const showSurplusAmount = surplusAmount && surplusAmount.greaterThan(0) const surplusUsdAmount = useUsdAmount(showSurplusAmount ? surplusAmount : undefined).value - const nativeSymbol = useNativeCurrency()?.symbol || 'ETH' + const native = useNativeCurrency() + const nativeSymbol = native.symbol || 'ETH' + const isArbitrumOne = native.chainId === SupportedChainId.ARBITRUM_ONE const Wrapper = styled.div` margin: 12px auto 24px; @@ -146,7 +150,7 @@ export function SurplusCard() { Your total surplus{' '} diff --git a/apps/cowswap-frontend/src/modules/injectedWidget/utils/validatePartnerFee.test.ts b/apps/cowswap-frontend/src/modules/injectedWidget/utils/validatePartnerFee.test.ts index cbf2101393..4670d4b735 100644 --- a/apps/cowswap-frontend/src/modules/injectedWidget/utils/validatePartnerFee.test.ts +++ b/apps/cowswap-frontend/src/modules/injectedWidget/utils/validatePartnerFee.test.ts @@ -60,6 +60,7 @@ describe('validatePartnerFee()', () => { bps: 100, recipient: { [SupportedChainId.MAINNET]: '0x0000000000000000000000000000000000000000', + [SupportedChainId.ARBITRUM_ONE]: '0x0000000000000000000000000000000000000000', [SupportedChainId.GNOSIS_CHAIN]: 'rtrth', [SupportedChainId.SEPOLIA]: '0x0000000000000000000000000000000000000000', }, @@ -75,6 +76,7 @@ describe('validatePartnerFee()', () => { bps: 100, recipient: { [SupportedChainId.MAINNET]: '0x0000000000000000000000000000000000000000', + [SupportedChainId.ARBITRUM_ONE]: '0x0000000000000000000000000000000000000000', [SupportedChainId.GNOSIS_CHAIN]: '0x0000000000000000000000000000000000000000', [SupportedChainId.SEPOLIA]: '0x0000000000000000000000000000000000000000', }, diff --git a/apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/hooks/useCheckEthereumTransactions.ts b/apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/hooks/useCheckEthereumTransactions.ts index b7772d4d1b..cd44b53d18 100644 --- a/apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/hooks/useCheckEthereumTransactions.ts +++ b/apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/hooks/useCheckEthereumTransactions.ts @@ -1,7 +1,6 @@ import { useSetAtom } from 'jotai/index' import { useAddPriorityAllowance } from '@cowprotocol/balances-and-allowances' -import { useBlockNumber, useGetReceipt } from '@cowprotocol/common-hooks' import { useGnosisSafeInfo, useWalletInfo } from '@cowprotocol/wallet' import { useWalletProvider } from '@cowprotocol/wallet-provider' @@ -14,6 +13,8 @@ import { useCancelOrdersBatch } from 'legacy/state/orders/hooks' import { removeInFlightOrderIdAtom } from 'modules/swap/state/EthFlow/ethFlowInFlightOrderIdsAtom' import { useGetTwapOrderById } from 'modules/twap/hooks/useGetTwapOrderById' +import { useBlockNumber } from 'common/hooks/useBlockNumber' +import { useGetReceipt } from 'common/hooks/useGetReceipt' import useNativeCurrency from 'lib/hooks/useNativeCurrency' import { CheckEthereumTransactions } from '../types' diff --git a/apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/hooks/useShouldCheckPendingTx.ts b/apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/hooks/useShouldCheckPendingTx.ts index 7b7e0603bb..73106f43a9 100644 --- a/apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/hooks/useShouldCheckPendingTx.ts +++ b/apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/hooks/useShouldCheckPendingTx.ts @@ -1,10 +1,11 @@ import { useCallback } from 'react' -import { useBlockNumber } from '@cowprotocol/common-hooks' import { useWalletInfo } from '@cowprotocol/wallet' import { EnhancedTransactionDetails } from 'legacy/state/enhancedTransactions/reducer' +import { useBlockNumber } from 'common/hooks/useBlockNumber' + export function useShouldCheckPendingTx() { const { account } = useWalletInfo() diff --git a/apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/types.ts b/apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/types.ts index 9c4833c543..d45ee37f9d 100644 --- a/apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/types.ts +++ b/apps/cowswap-frontend/src/modules/onchainTransactions/updaters/FinalizeTxUpdater/types.ts @@ -1,5 +1,4 @@ import { useAddPriorityAllowance } from '@cowprotocol/balances-and-allowances' -import { GetReceipt } from '@cowprotocol/common-hooks' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { GnosisSafeInfo } from '@cowprotocol/wallet' @@ -9,6 +8,8 @@ import { CancelOrdersBatchCallback } from 'legacy/state/orders/hooks' import type { useGetTwapOrderById } from 'modules/twap/hooks/useGetTwapOrderById' +import type { GetReceipt } from 'common/hooks/useGetReceipt' + export interface CheckEthereumTransactions { chainId: SupportedChainId account: string | undefined diff --git a/apps/cowswap-frontend/src/modules/swap/pure/banners/TwapSuggestionBanner.tsx b/apps/cowswap-frontend/src/modules/swap/pure/banners/TwapSuggestionBanner.tsx index e37bf9c095..e11fefff2a 100644 --- a/apps/cowswap-frontend/src/modules/swap/pure/banners/TwapSuggestionBanner.tsx +++ b/apps/cowswap-frontend/src/modules/swap/pure/banners/TwapSuggestionBanner.tsx @@ -33,6 +33,7 @@ const PRICE_IMPACT_LIMIT = 1 // 1% const AMOUNT_LIMIT: Record = { [SupportedChainId.MAINNET]: 50_000, // $50,000 [SupportedChainId.GNOSIS_CHAIN]: 500, // $500 + [SupportedChainId.ARBITRUM_ONE]: 500, // $500 [SupportedChainId.SEPOLIA]: 100, // $100 } diff --git a/apps/cowswap-frontend/src/modules/twap/const.ts b/apps/cowswap-frontend/src/modules/twap/const.ts index 286aa79d88..8ed142ffae 100644 --- a/apps/cowswap-frontend/src/modules/twap/const.ts +++ b/apps/cowswap-frontend/src/modules/twap/const.ts @@ -39,8 +39,9 @@ export const TWAP_FINAL_STATUSES = [TwapOrderStatus.Fulfilled, TwapOrderStatus.E export const MINIMUM_PART_SELL_AMOUNT_FIAT: Record> = { [SupportedChainId.MAINNET]: CurrencyAmount.fromRawAmount(USDC[SupportedChainId.MAINNET], 1_000e6), // 1k - [SupportedChainId.SEPOLIA]: CurrencyAmount.fromRawAmount(USDC[SupportedChainId.SEPOLIA], 100e6), // 100 [SupportedChainId.GNOSIS_CHAIN]: CurrencyAmount.fromRawAmount(USDC[SupportedChainId.GNOSIS_CHAIN], 5e6), // 5 + [SupportedChainId.ARBITRUM_ONE]: CurrencyAmount.fromRawAmount(USDC[SupportedChainId.ARBITRUM_ONE], 5e6), // 5 + [SupportedChainId.SEPOLIA]: CurrencyAmount.fromRawAmount(USDC[SupportedChainId.SEPOLIA], 100e6), // 100 } export const MINIMUM_PART_TIME = ms`5min` / 1000 // in seconds diff --git a/apps/cowswap-frontend/src/modules/usdAmount/apis/getCoingeckoUsdPrice.ts b/apps/cowswap-frontend/src/modules/usdAmount/apis/getCoingeckoUsdPrice.ts index 30506c5e98..596b270213 100644 --- a/apps/cowswap-frontend/src/modules/usdAmount/apis/getCoingeckoUsdPrice.ts +++ b/apps/cowswap-frontend/src/modules/usdAmount/apis/getCoingeckoUsdPrice.ts @@ -18,6 +18,7 @@ type ErrorCoingeckoResponse = { status: { error_code: number; error_message: str export const COINGECKO_PLATFORMS: Record = { [SupportedChainId.MAINNET]: 'ethereum', [SupportedChainId.GNOSIS_CHAIN]: 'xdai', + [SupportedChainId.ARBITRUM_ONE]: 'arbitrum-one', [SupportedChainId.SEPOLIA]: null, } diff --git a/apps/cowswap-frontend/src/modules/usdAmount/apis/getDefillamaUsdPrice.ts b/apps/cowswap-frontend/src/modules/usdAmount/apis/getDefillamaUsdPrice.ts index 27d7e988c3..2063d89e8e 100644 --- a/apps/cowswap-frontend/src/modules/usdAmount/apis/getDefillamaUsdPrice.ts +++ b/apps/cowswap-frontend/src/modules/usdAmount/apis/getDefillamaUsdPrice.ts @@ -19,6 +19,7 @@ interface DefillamaUsdQuote { export const DEFILLAMA_PLATFORMS: Record = { [SupportedChainId.MAINNET]: 'ethereum', [SupportedChainId.GNOSIS_CHAIN]: 'xdai', + [SupportedChainId.ARBITRUM_ONE]: 'arbitrum-one', [SupportedChainId.SEPOLIA]: null, } diff --git a/apps/cowswap-frontend/src/pages/Account/Balances.tsx b/apps/cowswap-frontend/src/pages/Account/Balances.tsx index 3f5dbfd17e..28f67e04fe 100644 --- a/apps/cowswap-frontend/src/pages/Account/Balances.tsx +++ b/apps/cowswap-frontend/src/pages/Account/Balances.tsx @@ -4,12 +4,10 @@ import ArrowIcon from '@cowprotocol/assets/cow-swap/arrow.svg' import CowImage from '@cowprotocol/assets/cow-swap/cow_v2.svg' import vCOWImage from '@cowprotocol/assets/cow-swap/vCOW.png' import { useCurrencyAmountBalance } from '@cowprotocol/balances-and-allowances' -import { COW, COW_CONTRACT_ADDRESS, V_COW, V_COW_CONTRACT_ADDRESS } from '@cowprotocol/common-const' +import { COW, COW_CONTRACT_ADDRESS, V_COW } from '@cowprotocol/common-const' import { usePrevious } from '@cowprotocol/common-hooks' -import { useBlockNumber } from '@cowprotocol/common-hooks' import { getBlockExplorerUrl, getProviderErrorMessage } from '@cowprotocol/common-utils' -import { TokenAmount, ButtonPrimary } from '@cowprotocol/ui' -import { HoverTooltip } from '@cowprotocol/ui' +import { ButtonPrimary, HoverTooltip, TokenAmount } from '@cowprotocol/ui' import { useWalletInfo } from '@cowprotocol/wallet' import { useWalletProvider } from '@cowprotocol/wallet-provider' import { CurrencyAmount } from '@uniswap/sdk-core' @@ -21,8 +19,9 @@ import { Link } from 'react-router-dom' import CopyHelper from 'legacy/components/Copy' import { useErrorModal } from 'legacy/hooks/useErrorMessageAndModal' import { SwapVCowStatus } from 'legacy/state/cowToken/actions' -import { useVCowData, useSwapVCowCallback, useSetSwapVCowStatus, useSwapVCowStatus } from 'legacy/state/cowToken/hooks' +import { useSetSwapVCowStatus, useSwapVCowCallback, useSwapVCowStatus, useVCowData } from 'legacy/state/cowToken/hooks' +import { useBlockNumber } from 'common/hooks/useBlockNumber' import { useIsProviderNetworkUnsupported } from 'common/hooks/useIsProviderNetworkUnsupported' import { useModalState } from 'common/hooks/useModalState' import { ConfirmationPendingContent } from 'common/pure/ConfirmationPendingContent' @@ -30,15 +29,15 @@ import { HelpCircle } from 'common/pure/HelpCircle' import { CowModal } from 'common/pure/Modal' import { useCowFromLockedGnoBalances } from 'pages/Account/LockedGnoVesting/hooks' import { - ExtLink, + BalanceDisplay, Card, CardActions, - BalanceDisplay, - ConvertWrapper, - VestingBreakdown, CardsLoader, CardsSpinner, + ConvertWrapper, + ExtLink, StyledWatchAssetInWallet, + VestingBreakdown } from 'pages/Account/styled' import LockedGnoVesting from './LockedGnoVesting' @@ -198,8 +197,6 @@ export default function Profile() { } }, [account, isSwapInitial, previousAccount, setSwapVCowStatus]) - const currencyCOW = COW[chainId] - return ( <> @@ -222,7 +219,7 @@ export default function Profile() { ) : ( <> - {hasVCowBalance && ( + {hasVCowBalance && vCowToken && ( vCOW token @@ -256,10 +253,8 @@ export default function Profile() { - - View contract ↗ - - + View contract ↗ +

Copy contract
@@ -288,7 +283,7 @@ export default function Profile() {
Copy contract
diff --git a/apps/cowswap-frontend/src/pages/Account/LockedGnoVesting/claimData/index.ts b/apps/cowswap-frontend/src/pages/Account/LockedGnoVesting/claimData/index.ts index a35e7b73b8..e5cef58989 100644 --- a/apps/cowswap-frontend/src/pages/Account/LockedGnoVesting/claimData/index.ts +++ b/apps/cowswap-frontend/src/pages/Account/LockedGnoVesting/claimData/index.ts @@ -12,22 +12,25 @@ interface Claim { const indexFiles: Record = { [SupportedChainId.MAINNET]: mainnetIndex, [SupportedChainId.GNOSIS_CHAIN]: gnosisChainIndex, - [SupportedChainId.SEPOLIA]: [], // TODO SEPOLIA: check it + [SupportedChainId.ARBITRUM_ONE]: [], + [SupportedChainId.SEPOLIA]: [], } -const chainNames: Record = { +const chainNames: Record = { [SupportedChainId.MAINNET]: 'mainnet', [SupportedChainId.GNOSIS_CHAIN]: 'gnosisChain', - [SupportedChainId.SEPOLIA]: '', // TODO SEPOLIA: check it + [SupportedChainId.ARBITRUM_ONE]: null, + [SupportedChainId.SEPOLIA]: null, } const DISTRO_REPO_BRANCH_NAME = 'main' export const fetchClaim = async (address: string, chainId: SupportedChainId): Promise => { - const lowerCaseAddress = address.toLowerCase() + const chainName = chainNames[chainId] + if (!chainName) return null // no claim for this chain + const lowerCaseAddress = address.toLowerCase() const indexFile = indexFiles[chainId] - const chainName = chainNames[chainId] const chunkIndex = lookupChunkIndex(indexFile, lowerCaseAddress) if (chunkIndex === -1) return null // address is lower than the lowest address in the index, which means it's ineligible diff --git a/apps/explorer/src/api/operator/accountOrderUtils.ts b/apps/explorer/src/api/operator/accountOrderUtils.ts index 5177e3200e..dd2cd102f0 100644 --- a/apps/explorer/src/api/operator/accountOrderUtils.ts +++ b/apps/explorer/src/api/operator/accountOrderUtils.ts @@ -1,5 +1,4 @@ -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { EnrichedOrder } from '@cowprotocol/cow-sdk' +import { EnrichedOrder, SupportedChainId } from '@cowprotocol/cow-sdk' import { orderBookSDK } from 'cowSdk' @@ -29,14 +28,17 @@ export async function getAccountOrders(params: GetAccountOrdersParams): Promise< } const ordersPromise = state.prodHasNext - ? orderBookSDK.getOrders({ owner, offset, limit: limitPlusOne }, { chainId: networkId }) + ? orderBookSDK.getOrders({ owner, offset, limit: limitPlusOne }, { chainId: networkId }).catch((error) => { + console.error('[getAccountOrders] Error getting PROD orders for account', owner, networkId, error) + return [] + }) : [] const ordersPromiseBarn = state.barnHasNext ? orderBookSDK .getOrders({ owner, offset, limit: limitPlusOne }, { chainId: networkId, env: 'staging' }) .catch((error) => { - console.error('[getAccountOrders] Error getting orders for account ', owner, error) + console.error('[getAccountOrders] Error getting BARN orders for account', owner, networkId, error) return [] }) : [] diff --git a/apps/explorer/src/api/operator/operatorApi.ts b/apps/explorer/src/api/operator/operatorApi.ts index 49dfea9613..f6a4c90249 100644 --- a/apps/explorer/src/api/operator/operatorApi.ts +++ b/apps/explorer/src/api/operator/operatorApi.ts @@ -2,7 +2,7 @@ import { Address, UID } from '@cowprotocol/cow-sdk' import { orderBookSDK } from 'cowSdk' -import { GetOrderParams, RawOrder, RawTrade, GetTxOrdersParams, WithNetworkId } from './types' +import { GetOrderParams, GetTxOrdersParams, RawOrder, RawTrade, WithNetworkId } from './types' export { getAccountOrders } from './accountOrderUtils' @@ -25,7 +25,10 @@ export async function getTxOrders(params: GetTxOrdersParams): Promise { + console.error('[getTxOrders] Error getting PROD orders', networkId, txHash, error) + return [] + }) const orderPromisesBarn = orderBookSDK .getTxOrders(txHash, { chainId: networkId, @@ -33,7 +36,7 @@ export async function getTxOrders(params: GetTxOrdersParams): Promise { - console.error('[getTxOrders] Error getting the orders for Barn', error) + console.error('[getTxOrders] Error getting BARN orders', networkId, txHash, error) return [] }) @@ -61,11 +64,14 @@ export async function getTrades( const { networkId, owner, orderId: orderUid } = params console.log(`[getTrades] Fetching trades on network ${networkId} with filters`, { owner, orderUid }) - const tradesPromise = orderBookSDK.getTrades({ owner, orderUid }, { chainId: networkId }) + const tradesPromise = orderBookSDK.getTrades({ owner, orderUid }, { chainId: networkId }).catch((error) => { + console.error('[getTrades] Error getting PROD trades', params, error) + return [] + }) const tradesPromiseBarn = orderBookSDK .getTrades({ owner, orderUid }, { chainId: networkId, env: 'staging' }) .catch((error) => { - console.error('[getTrades] Error getting the trades for Barn', params, error) + console.error('[getTrades] Error getting BARN trades', params, error) return [] }) diff --git a/apps/explorer/src/api/web3/index.ts b/apps/explorer/src/api/web3/index.ts index f5746c61a5..605ad9ee2b 100644 --- a/apps/explorer/src/api/web3/index.ts +++ b/apps/explorer/src/api/web3/index.ts @@ -1,17 +1,14 @@ +import { RPC_URLS } from '@cowprotocol/common-const' import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { ETH_NODE_URL, NODE_PROVIDER_ID } from 'const' import Web3 from 'web3' import type { HttpProvider } from 'web3-core' -// TODO connect to mainnet if we need AUTOCONNECT at all -export const getDefaultProvider = (): string | null => (process.env.NODE_ENV === 'test' ? null : ETH_NODE_URL) - const web3cache: { [key: string]: Web3 } = {} export function createWeb3Api(provider?: string): Web3 { - const _provider = provider || getDefaultProvider() || '' + const _provider = provider || getProviderByNetwork(SupportedChainId.MAINNET) if (web3cache[_provider]) { return web3cache[_provider] @@ -43,14 +40,8 @@ export function createWeb3Api(provider?: string): Web3 { return web3 } -const PROVIDER_ENDPOINTS: Record = { - [SupportedChainId.MAINNET]: 'https://eth-mainnet.nodereal.io/v1/' + NODE_PROVIDER_ID, - [SupportedChainId.GNOSIS_CHAIN]: 'https://rpc.gnosis.gateway.fm/', - [SupportedChainId.SEPOLIA]: 'https://eth-sepolia.nodereal.io/v1/' + NODE_PROVIDER_ID, -} - -export function getProviderByNetwork(networkId: SupportedChainId): string | undefined { - return PROVIDER_ENDPOINTS[networkId] +export function getProviderByNetwork(networkId: SupportedChainId): string { + return RPC_URLS[networkId] } // Approach 2: update the provider in a single web3 instance diff --git a/apps/explorer/src/components/NetworkSelector/index.tsx b/apps/explorer/src/components/NetworkSelector/index.tsx index b96ddb5cf9..dc4209e058 100644 --- a/apps/explorer/src/components/NetworkSelector/index.tsx +++ b/apps/explorer/src/components/NetworkSelector/index.tsx @@ -1,11 +1,11 @@ import React, { useEffect, useRef, useState } from 'react' import { CHAIN_INFO, getChainInfo } from '@cowprotocol/common-const' -import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { useAvailableChains } from '@cowprotocol/common-hooks' import { faCheck } from '@fortawesome/free-solid-svg-icons' -import { SelectorContainer, OptionsContainer, Option, NetworkLabel, StyledFAIcon } from './NetworkSelector.styled' +import { NetworkLabel, Option, OptionsContainer, SelectorContainer, StyledFAIcon } from './NetworkSelector.styled' import { usePathSuffix } from '../../state/network' @@ -21,6 +21,7 @@ export const NetworkSelector: React.FC = ({ networkId }) = const currentNetworkName = currentNetwork.label.toLowerCase() const [open, setOpen] = useState(false) const pathSuffix = usePathSuffix() + const availableChains = useAvailableChains() useEffect(() => { const closeOpenSelector = (e: MouseEvent | KeyboardEvent): void => { @@ -51,8 +52,7 @@ export const NetworkSelector: React.FC = ({ networkId }) = {open && ( - {Object.keys(CHAIN_INFO).map((_itemNetworkId) => { - const itemNetworkId = +_itemNetworkId as unknown as SupportedChainId + {availableChains.map((itemNetworkId) => { const network = CHAIN_INFO[itemNetworkId] /** diff --git a/apps/explorer/src/components/common/BlockExplorerLink/BlockExplorerLink.tsx b/apps/explorer/src/components/common/BlockExplorerLink/BlockExplorerLink.tsx index d72b0bd3a9..57a4d5a21c 100644 --- a/apps/explorer/src/components/common/BlockExplorerLink/BlockExplorerLink.tsx +++ b/apps/explorer/src/components/common/BlockExplorerLink/BlockExplorerLink.tsx @@ -1,11 +1,13 @@ import React, { ReactElement } from 'react' +import { CHAIN_INFO } from '@cowprotocol/common-const' +import { BlockExplorerLinkType, getBlockExplorerUrl } from '@cowprotocol/common-utils' +import { SupportedChainId } from '@cowprotocol/cow-sdk' + import { ExternalLink } from 'components/analytics/ExternalLink' import LogoWrapper, { LOGO_MAP } from 'components/common/LogoWrapper' -import { Network } from 'types' import { abbreviateString } from 'utils' -import { BlockExplorerLinkType, getExplorerUrl } from 'utils/getExplorerUrl' export interface Props { /** @@ -19,7 +21,7 @@ export interface Props { /** * network number | chain id */ - networkId?: number + networkId?: SupportedChainId /** * label to replace textContent generated from identifier */ @@ -52,7 +54,7 @@ export const BlockExplorerLink: React.FC = (props: Props) => { return null } - const url = getExplorerUrl(networkId, type, identifier) + const url = getBlockExplorerUrl(networkId, type, identifier) const label = labelProp || (useUrlAsLabel && url) || abbreviateString(identifier, 6, 4) return ( @@ -60,7 +62,7 @@ export const BlockExplorerLink: React.FC = (props: Props) => { {label} {showLogo && ( )} diff --git a/apps/explorer/src/const.ts b/apps/explorer/src/const.ts index bc173f939a..e498c81f20 100644 --- a/apps/explorer/src/const.ts +++ b/apps/explorer/src/const.ts @@ -103,18 +103,17 @@ export const MEDIA = { }, } -export const NODE_PROVIDER_ID = process.env.NODE_PROVIDER_ID || 'ed3c6720eb3f470e9ceac8f8f12e8b14' -export const ETH_NODE_URL = process.env.ETH_NODE_URL || 'wss://eth-mainnet.nodereal.io/ws/v1/' + NODE_PROVIDER_ID - export const WETH_ADDRESS_MAINNET = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' -export const WETH_ADDRESS_SEPOLIA = '0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14' export const WXDAI_ADDRESS_XDAI = '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d' +export const WXDAI_ADDRESS_ARBITRUM_ONE = '0x82af49447d8a07e3bd95bd0d56f35241523fbab1' +export const WETH_ADDRESS_SEPOLIA = '0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14' export const NATIVE_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' export const NATIVE_TOKEN_ADDRESS_LOWERCASE = NATIVE_TOKEN_ADDRESS.toLowerCase() export const WRAPPED_NATIVE_ADDRESS: Record = { [SupportedChainId.MAINNET]: WETH_ADDRESS_MAINNET, [SupportedChainId.GNOSIS_CHAIN]: WXDAI_ADDRESS_XDAI, + [SupportedChainId.ARBITRUM_ONE]: WXDAI_ADDRESS_ARBITRUM_ONE, [SupportedChainId.SEPOLIA]: WETH_ADDRESS_SEPOLIA, } @@ -134,8 +133,9 @@ export const XDAI: TokenErc20 = { export const NATIVE_TOKEN_PER_NETWORK: Record = { [SupportedChainId.MAINNET]: ETH, - [SupportedChainId.SEPOLIA]: ETH, [SupportedChainId.GNOSIS_CHAIN]: XDAI, + [SupportedChainId.ARBITRUM_ONE]: ETH, + [SupportedChainId.SEPOLIA]: ETH, } export const TENDERLY_API_URL = 'https://api.tenderly.co/api/v1/public-contract' diff --git a/apps/explorer/src/consts/subgraphUrls.ts b/apps/explorer/src/consts/subgraphUrls.ts index b3806e6636..72f9ffba37 100644 --- a/apps/explorer/src/consts/subgraphUrls.ts +++ b/apps/explorer/src/consts/subgraphUrls.ts @@ -10,5 +10,6 @@ function getSubgraphUrl(chainId: SupportedChainId, suffix: string): string | nul export const SUBGRAPH_URLS: Record = { [SupportedChainId.MAINNET]: getSubgraphUrl(SupportedChainId.MAINNET, 'MAINNET'), [SupportedChainId.GNOSIS_CHAIN]: getSubgraphUrl(SupportedChainId.GNOSIS_CHAIN, 'GNOSIS_CHAIN'), + [SupportedChainId.ARBITRUM_ONE]: getSubgraphUrl(SupportedChainId.ARBITRUM_ONE, 'ARBITRUM_ONE'), [SupportedChainId.SEPOLIA]: getSubgraphUrl(SupportedChainId.SEPOLIA, 'SEPOLIA'), } diff --git a/apps/explorer/src/explorer/ExplorerApp.tsx b/apps/explorer/src/explorer/ExplorerApp.tsx index 3d6dec592e..73aa4416ae 100644 --- a/apps/explorer/src/explorer/ExplorerApp.tsx +++ b/apps/explorer/src/explorer/ExplorerApp.tsx @@ -116,9 +116,9 @@ const AppContent = (): JSX.Element => { useAnalyticsReporter(location, 'Explorer') return ( - }> - - + + }> + } /> } /> @@ -131,9 +131,9 @@ const AppContent = (): JSX.Element => { } /> } /> - - - + + + ) } diff --git a/apps/explorer/src/explorer/components/OrdersTableWidget/useSearchInAnotherNetwork.tsx b/apps/explorer/src/explorer/components/OrdersTableWidget/useSearchInAnotherNetwork.tsx index e552f64ba3..ccffe7bb9f 100644 --- a/apps/explorer/src/explorer/components/OrdersTableWidget/useSearchInAnotherNetwork.tsx +++ b/apps/explorer/src/explorer/components/OrdersTableWidget/useSearchInAnotherNetwork.tsx @@ -1,14 +1,15 @@ -import React, { useCallback, useState, useEffect } from 'react' +import { useCallback, useEffect, useState } from 'react' import { CHAIN_INFO, getChainInfo } from '@cowprotocol/common-const' -import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from '@cowprotocol/cow-sdk' +import { useAvailableChains } from '@cowprotocol/common-hooks' +import { SupportedChainId } from '@cowprotocol/cow-sdk' import { Link } from 'react-router-dom' import styled from 'styled-components/macro' import { BlockchainNetwork } from './context/OrdersTableContext' -import { Order, getAccountOrders } from '../../../api/operator' +import { getAccountOrders, Order } from '../../../api/operator' import { BlockExplorerLink } from '../../../components/common/BlockExplorerLink' import CowLoading from '../../../components/common/CowLoading' import { MEDIA } from '../../../const' @@ -28,6 +29,7 @@ const Wrapper = styled.div` align-items: center; justify-content: center; flex-direction: column; + p { margin-top: 0; } @@ -36,9 +38,11 @@ const Wrapper = styled.div` ul { padding: 0; margin: 0; + > li { list-style: none; padding-bottom: 1.5rem; + :last-child { padding-bottom: 0; } @@ -53,6 +57,7 @@ const StyledBlockExplorerLink = styled(BlockExplorerLink)` justify-content: center; } ` + interface OrdersInNetwork { network: number } @@ -146,24 +151,27 @@ export const useSearchInAnotherNetwork = ( const [isLoading, setIsLoading] = useState(true) const isOrdersLengthZero = !orders || orders.length === 0 const [error, setError] = useState(null) + const availableChains = useAvailableChains() const fetchAnotherNetworks = useCallback( async (_networkId: Network) => { setIsLoading(true) setError(null) - const promises = ALL_SUPPORTED_CHAIN_IDS.filter((net) => net !== _networkId).map((network) => - getAccountOrders({ networkId: network, owner: ownerAddress, offset: 0, limit: 1 }) - .then((response) => { - if (!response.orders.length) return - - return { network } - }) - .catch((e) => { - // Msg for when there are no orders on any network and a request has failed - setError('An error has occurred while requesting the data.') - console.error(`Failed to fetch order in ${Network[network]}`, e) - }) - ) + const promises = availableChains + .filter((net) => net !== _networkId) + .map((network) => + getAccountOrders({ networkId: network, owner: ownerAddress, offset: 0, limit: 1 }) + .then((response) => { + if (!response.orders.length) return + + return { network } + }) + .catch((e) => { + // Msg for when there are no orders on any network and a request has failed + setError('An error has occurred while requesting the data.') + console.error(`Failed to fetch order in ${Network[network]}`, e) + }) + ) const networksHaveOrders = (await Promise.allSettled(promises)).filter( (e) => e.status === 'fulfilled' && e.value?.network diff --git a/apps/explorer/src/explorer/components/TransactionsTableWidget/index.tsx b/apps/explorer/src/explorer/components/TransactionsTableWidget/index.tsx index f7cbfac53e..f95a560d27 100644 --- a/apps/explorer/src/explorer/components/TransactionsTableWidget/index.tsx +++ b/apps/explorer/src/explorer/components/TransactionsTableWidget/index.tsx @@ -105,7 +105,7 @@ export const TransactionsTableWidget: React.FC = ({ txHash }) => { Transaction details } + contentsToDisplay={} /> diff --git a/apps/explorer/src/explorer/components/TransanctionBatchGraph/nodesBuilder.ts b/apps/explorer/src/explorer/components/TransanctionBatchGraph/nodesBuilder.ts index 85d82ef118..33f82d3b11 100644 --- a/apps/explorer/src/explorer/components/TransanctionBatchGraph/nodesBuilder.ts +++ b/apps/explorer/src/explorer/components/TransanctionBatchGraph/nodesBuilder.ts @@ -1,5 +1,5 @@ import { getChainInfo } from '@cowprotocol/common-const' -import { isSellOrder } from '@cowprotocol/common-utils' +import { getBlockExplorerUrl, isSellOrder } from '@cowprotocol/common-utils' import { OrderKind, SupportedChainId } from '@cowprotocol/cow-sdk' import BigNumber from 'bignumber.js' @@ -23,10 +23,8 @@ import { APP_NAME, NATIVE_TOKEN_ADDRESS_LOWERCASE, WRAPPED_NATIVE_ADDRESS } from import { SingleErc20State } from '../../../state/erc20' import { Network } from '../../../types' import { abbreviateString, FormatAmountPrecision, formattingAmountPrecision } from '../../../utils' -import { getExplorerUrl } from '../../../utils/getExplorerUrl' import { SPECIAL_ADDRESSES, TOKEN_SYMBOL_UNKNOWN } from '../../const' - const PROTOCOL_NAME = APP_NAME const INTERNAL_NODE_NAME = `${APP_NAME} Buffer` @@ -97,7 +95,7 @@ export const buildContractViewNodes: BuildNodesFn = function getNodes( // Set flag to prevent creating more internalNodeCreated = true - const account = { alias: fromId, href: getExplorerUrl(networkId, 'address', transfer.from) } + const account = { alias: fromId, href: getBlockExplorerUrl(networkId, 'address', transfer.from) } builder.node( { type: TypeNodeOnTx.Special, @@ -392,7 +390,7 @@ export const buildTokenViewNodes: BuildNodesFn = function getNodesAlternative( const entity = accounts?.[node.address] || { alias: abbreviateString(node.address, 6, 4), address: node.address, - href: getExplorerUrl(networkId, 'contract', node.address), + href: getBlockExplorerUrl(networkId, 'contract', node.address), } const type = node.isHyperNode ? TypeNodeOnTx.Hyper : TypeNodeOnTx.Token const tooltip = getNodeTooltip(node, edges, tokens) diff --git a/apps/explorer/src/explorer/components/TransanctionBatchGraph/settlementBuilder.tsx b/apps/explorer/src/explorer/components/TransanctionBatchGraph/settlementBuilder.tsx index 0fd45f014a..203794ded4 100644 --- a/apps/explorer/src/explorer/components/TransanctionBatchGraph/settlementBuilder.tsx +++ b/apps/explorer/src/explorer/components/TransanctionBatchGraph/settlementBuilder.tsx @@ -1,3 +1,5 @@ +import { getBlockExplorerUrl } from '@cowprotocol/common-utils' + import BigNumber from 'bignumber.js' import { getContractTrades, getTokenAddress } from './nodesBuilder' @@ -9,7 +11,6 @@ import { TransactionData } from '../../../hooks/useTransactionData' import { SingleErc20State } from '../../../state/erc20' import { Network } from '../../../types' import { abbreviateString } from '../../../utils' -import { getExplorerUrl } from '../../../utils/getExplorerUrl' /** @@ -86,7 +87,7 @@ export function buildTransfersBasedSettlement(params: BuildSettlementParams): Se } }) Object.values(accountsWithReceiver).forEach((account) => { - if (account.address) account.href = getExplorerUrl(networkId, 'address', account.address) + if (account.address) account.href = getBlockExplorerUrl(networkId, 'address', account.address) }) const tokenAddresses = transfersWithKind.map((transfer: Transfer): string => transfer.token) @@ -139,7 +140,7 @@ export function buildTradesBasedSettlement(params: BuildSettlementParams): Settl acc[address] = { alias: symbol || abbreviateString(address, 6, 4), address, - href: getExplorerUrl(networkId, 'token', address), + href: getBlockExplorerUrl(networkId, 'token', address), } return acc diff --git a/apps/explorer/src/explorer/pages/Home/index.tsx b/apps/explorer/src/explorer/pages/Home/index.tsx index 15ca4f1963..7c32568f7d 100644 --- a/apps/explorer/src/explorer/pages/Home/index.tsx +++ b/apps/explorer/src/explorer/pages/Home/index.tsx @@ -55,6 +55,7 @@ const SummaryWrapper = styled.section` const SHOW_TOKENS_TABLE: Record = { [SupportedChainId.MAINNET]: true, [SupportedChainId.GNOSIS_CHAIN]: false, // Gchain data is not reliable + [SupportedChainId.ARBITRUM_ONE]: false, // No data for Arbitrum one [SupportedChainId.SEPOLIA]: false, // No data for Sepolia } diff --git a/apps/explorer/src/hooks/useTokenList.ts b/apps/explorer/src/hooks/useTokenList.ts index 589786b532..a16f1757e0 100644 --- a/apps/explorer/src/hooks/useTokenList.ts +++ b/apps/explorer/src/hooks/useTokenList.ts @@ -1,5 +1,5 @@ import { SWR_NO_REFRESH_OPTIONS } from '@cowprotocol/common-const' -import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from '@cowprotocol/cow-sdk' +import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId, mapSupportedNetworks } from '@cowprotocol/cow-sdk' import type { TokenInfo, TokenList } from '@uniswap/token-lists' import useSWR from 'swr' @@ -7,11 +7,7 @@ import useSWR from 'swr' type TokenListByAddress = Record type TokenListPerNetwork = Record -const INITIAL_TOKEN_LIST_PER_NETWORK: TokenListPerNetwork = { - [SupportedChainId.MAINNET]: {}, - [SupportedChainId.GNOSIS_CHAIN]: {}, - [SupportedChainId.SEPOLIA]: {}, -} +const INITIAL_TOKEN_LIST_PER_NETWORK: TokenListPerNetwork = mapSupportedNetworks({}) export function useTokenList(chainId: SupportedChainId | undefined): { data: TokenListByAddress; isLoading: boolean } { const { data: cowSwapList, isLoading: isCowListLoading } = useTokenListByUrl( @@ -25,9 +21,14 @@ export function useTokenList(chainId: SupportedChainId | undefined): { data: Tok const { data: honeyswapList, isLoading: isHoneyswapListLoading } = useTokenListByUrl( chainId === SupportedChainId.GNOSIS_CHAIN ? 'https://tokens.honeyswap.org' : '' ) + const { data: arbitrumOneList, isLoading: isArbitrumOneListLoading } = useTokenListByUrl( + chainId === SupportedChainId.ARBITRUM_ONE ? 'https://tokens.coingecko.com/arbitrum-one/all.json' : '' + ) - const data = chainId ? { ...coingeckoList, ...honeyswapList, ...cowSwapList }[chainId] : {} - const isLoading = chainId ? isCowListLoading || isHoneyswapListLoading || isCoingeckoListLoading : false + const data = chainId ? { ...coingeckoList, ...honeyswapList, ...cowSwapList, ...arbitrumOneList }[chainId] : {} + const isLoading = chainId + ? isCowListLoading || isHoneyswapListLoading || isCoingeckoListLoading || isArbitrumOneListLoading + : false return { data, isLoading } } diff --git a/apps/explorer/src/utils/getExplorerUrl.ts b/apps/explorer/src/utils/getExplorerUrl.ts deleted file mode 100644 index df85d88068..0000000000 --- a/apps/explorer/src/utils/getExplorerUrl.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Network } from 'types' - -export type BlockExplorerLinkType = 'tx' | 'address' | 'contract' | 'token' | 'event' - -function getEtherscanUrlPrefix(networkId: Network): string { - return !networkId || networkId === Network.MAINNET || networkId === Network.GNOSIS_CHAIN - ? '' - : (Network[networkId] || '').toLowerCase() + '.' -} - -function getEtherscanUrlSuffix(type: BlockExplorerLinkType, identifier: string): string { - switch (type) { - case 'tx': - return `tx/${identifier}` - case 'event': - return `tx/${identifier}#eventlog` - case 'address': - return `address/${identifier}` - case 'contract': - return `address/${identifier}#code` - case 'token': - return `token/${identifier}` - } -} - -function getEtherscanUrl(host: string, networkId: number, type: BlockExplorerLinkType, identifier: string): string { - return `https://${getEtherscanUrlPrefix(networkId)}${host}/${getEtherscanUrlSuffix(type, identifier)}` -} - -export function getExplorerUrl(networkId: number, type: BlockExplorerLinkType, identifier: string): string { - return networkId === Network.GNOSIS_CHAIN - ? getEtherscanUrl('gnosisscan.io', networkId, type, identifier) - : getEtherscanUrl('etherscan.io', networkId, type, identifier) -} diff --git a/apps/widget-configurator/src/WithLDProvider.ts b/apps/widget-configurator/src/WithLDProvider.ts new file mode 100644 index 0000000000..ff40191e52 --- /dev/null +++ b/apps/widget-configurator/src/WithLDProvider.ts @@ -0,0 +1,18 @@ +import { PropsWithChildren } from 'react' + +import { LAUNCH_DARKLY_CLIENT_KEY } from '@cowprotocol/common-const' + +import { withLDProvider } from 'launchdarkly-react-client-sdk' + +// TODO: remove duplicated component with app/cowswap-frontend/src/modules/application/containers/WithLDProvider + +function InnerWithLDProvider({ children }: PropsWithChildren) { + return children +} +// +export const WithLDProvider = withLDProvider({ + clientSideID: LAUNCH_DARKLY_CLIENT_KEY, + options: { + bootstrap: 'localStorage', + }, +})(InnerWithLDProvider) diff --git a/apps/widget-configurator/src/app/configurator/controls/NetworkControl.tsx b/apps/widget-configurator/src/app/configurator/controls/NetworkControl.tsx index 533d05dfd2..602b2475f7 100644 --- a/apps/widget-configurator/src/app/configurator/controls/NetworkControl.tsx +++ b/apps/widget-configurator/src/app/configurator/controls/NetworkControl.tsx @@ -1,5 +1,6 @@ import { Dispatch, SetStateAction } from 'react' +import { CHAIN_INFO } from '@cowprotocol/common-const' import { SupportedChainId } from '@cowprotocol/cow-sdk' import FormControl from '@mui/material/FormControl' @@ -12,11 +13,11 @@ export type NetworkOption = { label: string } -export const NetworkOptions: NetworkOption[] = [ - { chainId: SupportedChainId.MAINNET, label: 'Ethereum' }, - { chainId: SupportedChainId.GNOSIS_CHAIN, label: 'Gnosis Chain' }, - { chainId: SupportedChainId.SEPOLIA, label: 'Sepolia' }, -] +export const NetworkOptions: NetworkOption[] = Object.keys(CHAIN_INFO).map((key) => { + const chainId = +key as SupportedChainId + return ({ chainId, label: CHAIN_INFO[chainId].label }) +}) + const DEFAULT_CHAIN_ID = NetworkOptions[0].chainId @@ -24,13 +25,17 @@ const LABEL = 'Network' export const getNetworkOption = (chainId: SupportedChainId) => NetworkOptions.find((item) => item.chainId === chainId) +type NetworkControlProps = { + standaloneMode: boolean + state: [NetworkOption, Dispatch>] + availableChains: SupportedChainId[] +} + export function NetworkControl({ state, standaloneMode, -}: { - standaloneMode: boolean - state: [NetworkOption, Dispatch>] -}) { + availableChains +}: NetworkControlProps) { const [network, setNetwork] = state const switchNetwork = (chainId: number) => { @@ -42,6 +47,7 @@ export function NetworkControl({ } } + return ( {LABEL} @@ -54,11 +60,18 @@ export function NetworkControl({ disabled={standaloneMode} size="small" > - {NetworkOptions.map((option) => ( - - {option.label} - - ))} + {availableChains.map((chainId) => { + const option = NetworkOptions.find(o => o.chainId === chainId) + + if (!option) return null + + return ( + + {option.label} + + ) + + })} ) diff --git a/apps/widget-configurator/src/app/configurator/index.tsx b/apps/widget-configurator/src/app/configurator/index.tsx index bef3dd612f..a9bd9483f6 100644 --- a/apps/widget-configurator/src/app/configurator/index.tsx +++ b/apps/widget-configurator/src/app/configurator/index.tsx @@ -1,7 +1,8 @@ import { ChangeEvent, useContext, useEffect, useMemo, useState } from 'react' +import { useAvailableChains } from '@cowprotocol/common-hooks' import { CowEventListeners } from '@cowprotocol/events' -import { CowSwapWidgetParams, TradeType, TokenInfo } from '@cowprotocol/widget-lib' +import { CowSwapWidgetParams, TokenInfo, TradeType } from '@cowprotocol/widget-lib' import { CowSwapWidget } from '@cowprotocol/widget-react' import ChromeReaderModeIcon from '@mui/icons-material/ChromeReaderMode' @@ -10,7 +11,7 @@ import CodeIcon from '@mui/icons-material/Code' import EditIcon from '@mui/icons-material/Edit' import KeyboardDoubleArrowLeftIcon from '@mui/icons-material/KeyboardDoubleArrowLeft' import LanguageIcon from '@mui/icons-material/Language' -import { Radio, RadioGroup, FormControlLabel, FormControl, FormLabel, Snackbar, IconButton } from '@mui/material' +import { FormControl, FormControlLabel, FormLabel, IconButton, Radio, RadioGroup, Snackbar } from '@mui/material' import Box from '@mui/material/Box' import Divider from '@mui/material/Divider' import Drawer from '@mui/material/Drawer' @@ -22,7 +23,7 @@ import ListItemText from '@mui/material/ListItemText' import Typography from '@mui/material/Typography' import { useWeb3ModalAccount, useWeb3ModalTheme } from '@web3modal/ethers5/react' -import { COW_LISTENERS, DEFAULT_PARTNER_FEE_RECIPIENT, DEFAULT_TOKEN_LISTS, TRADE_MODES, IS_IFRAME } from './consts' +import { COW_LISTENERS, DEFAULT_PARTNER_FEE_RECIPIENT, DEFAULT_TOKEN_LISTS, IS_IFRAME, TRADE_MODES } from './consts' import { CurrencyInputControl } from './controls/CurrencyInputControl' import { CurrentTradeTypeControl } from './controls/CurrentTradeTypeControl' import { CustomImagesControl } from './controls/CustomImagesControl' @@ -31,7 +32,7 @@ import { NetworkControl, NetworkOption, NetworkOptions } from './controls/Networ import { PaletteControl } from './controls/PaletteControl' import { PartnerFeeControl } from './controls/PartnerFeeControl' import { ThemeControl } from './controls/ThemeControl' -import { TokenListControl } from './controls/TokenListControl' // Adjust the import path as needed +import { TokenListControl } from './controls/TokenListControl' import { TradeModesControl } from './controls/TradeModesControl' import { useColorPaletteManager } from './hooks/useColorPaletteManager' import { useEmbedDialogState } from './hooks/useEmbedDialogState' @@ -175,6 +176,8 @@ export function Configurator({ title }: { title: string }) { useSyncWidgetNetwork(chainId, setNetworkControlState, standaloneMode) + const availableChains = useAvailableChains() + return ( {!isDrawerOpen && ( @@ -224,7 +227,13 @@ export function Configurator({ title }: { title: string }) { - {!IS_IFRAME && } + {!IS_IFRAME && ( + + )} Tokens diff --git a/apps/widget-configurator/src/main.tsx b/apps/widget-configurator/src/main.tsx index 01de88cc46..7c889cf445 100644 --- a/apps/widget-configurator/src/main.tsx +++ b/apps/widget-configurator/src/main.tsx @@ -1,9 +1,9 @@ import { StrictMode, useMemo } from 'react' import { CssBaseline, GlobalStyles } from '@mui/material' -import 'inter-ui' import Box from '@mui/material/Box' import { createTheme, PaletteOptions, ThemeProvider } from '@mui/material/styles' +import 'inter-ui' import { createRoot } from 'react-dom/client' import { Configurator } from './app/configurator' @@ -12,6 +12,7 @@ import { commonTypography } from './theme/commonTypography' import { useColorMode } from './theme/hooks/useColorMode' import { darkPalette, lightPalette } from './theme/paletteOptions' import { initWeb3Modal } from './web3modalConfig' +import { WithLDProvider } from './WithLDProvider' const WrapperStyled = { display: 'flex', @@ -64,7 +65,9 @@ function Root() { - + + + diff --git a/libs/assets/src/cow-swap/network-arbitrum-one-logo-blue.svg b/libs/assets/src/cow-swap/network-arbitrum-one-logo-blue.svg new file mode 100644 index 0000000000..53bac51901 --- /dev/null +++ b/libs/assets/src/cow-swap/network-arbitrum-one-logo-blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/libs/assets/src/cow-swap/network-arbitrum-one-logo-white.svg b/libs/assets/src/cow-swap/network-arbitrum-one-logo-white.svg new file mode 100644 index 0000000000..a9f842947d --- /dev/null +++ b/libs/assets/src/cow-swap/network-arbitrum-one-logo-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/libs/assets/src/cow-swap/network-gnosis-chain-logo.svg b/libs/assets/src/cow-swap/network-gnosis-chain-logo.svg index bf49d1310a..231e85ff24 100644 --- a/libs/assets/src/cow-swap/network-gnosis-chain-logo.svg +++ b/libs/assets/src/cow-swap/network-gnosis-chain-logo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/libs/assets/src/cow-swap/network-rinkeby-logo.svg b/libs/assets/src/cow-swap/network-rinkeby-logo.svg deleted file mode 100644 index f2c9b10e8b..0000000000 --- a/libs/assets/src/cow-swap/network-rinkeby-logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/libs/common-const/src/chainInfo.ts b/libs/common-const/src/chainInfo.ts index 2b1f9645a9..0f9687d8ce 100644 --- a/libs/common-const/src/chainInfo.ts +++ b/libs/common-const/src/chainInfo.ts @@ -1,4 +1,5 @@ - +import ArbitrumOneLogoLight from '@cowprotocol/assets/cow-swap/network-arbitrum-one-logo-blue.svg' +import ArbitrumOneLogoDark from '@cowprotocol/assets/cow-swap/network-arbitrum-one-logo-white.svg' import GnosisChainLogo from '@cowprotocol/assets/cow-swap/network-gnosis-chain-logo.svg' import EthereumLogo from '@cowprotocol/assets/cow-swap/network-mainnet-logo.svg' import SepoliaLogo from '@cowprotocol/assets/cow-swap/network-sepolia-logo.svg' @@ -13,7 +14,7 @@ export interface BaseChainInfo { readonly bridge?: string readonly explorer: string readonly infoLink: string - readonly logoUrl: string + readonly logo: { light: string; dark: string } readonly name: string readonly addressPrefix: string readonly label: string @@ -36,22 +37,23 @@ export const CHAIN_INFO: ChainInfoMap = { addressPrefix: 'eth', explorerTitle: 'Etherscan', urlAlias: '', - logoUrl: EthereumLogo, + logo: { light: EthereumLogo, dark: EthereumLogo }, color: '#62688F', nativeCurrency: NATIVE_CURRENCIES[SupportedChainId.MAINNET], }, - [SupportedChainId.SEPOLIA]: { - docs: 'https://docs.cow.fi', - explorer: 'https://sepolia.etherscan.io', - infoLink: COW_PROTOCOL_LINK, - label: 'Sepolia', - name: 'sepolia', - addressPrefix: 'sep', - explorerTitle: 'Etherscan', - urlAlias: 'sepolia', - logoUrl: SepoliaLogo, - color: '#C12FF2', - nativeCurrency: NATIVE_CURRENCIES[SupportedChainId.SEPOLIA], + [SupportedChainId.ARBITRUM_ONE]: { + docs: 'https://docs.arbitrum.io', + bridge: 'https://bridge.arbitrum.io', + explorer: 'https://arbiscan.io', + infoLink: 'https://arbitrum.io', + label: 'Arbitrum One', + addressPrefix: 'arb1', + name: 'arbitrum_one', + explorerTitle: 'Arbiscan', + urlAlias: 'abr1', + logo: { light: ArbitrumOneLogoLight, dark: ArbitrumOneLogoDark }, + color: '#1B4ADD', + nativeCurrency: NATIVE_CURRENCIES[SupportedChainId.ARBITRUM_ONE], }, [SupportedChainId.GNOSIS_CHAIN]: { docs: 'https://docs.gnosischain.com', @@ -63,10 +65,23 @@ export const CHAIN_INFO: ChainInfoMap = { addressPrefix: 'gno', explorerTitle: 'Gnosisscan', urlAlias: 'gc', - logoUrl: GnosisChainLogo, + logo: { light: GnosisChainLogo, dark: GnosisChainLogo }, color: '#07795B', nativeCurrency: NATIVE_CURRENCIES[SupportedChainId.GNOSIS_CHAIN], }, + [SupportedChainId.SEPOLIA]: { + docs: 'https://docs.cow.fi', + explorer: 'https://sepolia.etherscan.io', + infoLink: COW_PROTOCOL_LINK, + label: 'Sepolia', + name: 'sepolia', + addressPrefix: 'sep', + explorerTitle: 'Etherscan', + urlAlias: 'sepolia', + logo: { light: SepoliaLogo, dark: SepoliaLogo }, + color: '#C12FF2', + nativeCurrency: NATIVE_CURRENCIES[SupportedChainId.SEPOLIA], + }, } export const CHAIN_INFO_ARRAY: BaseChainInfo[] = Object.values(CHAIN_INFO) diff --git a/libs/common-const/src/common.ts b/libs/common-const/src/common.ts index c2711061b2..ed5c03e7e6 100644 --- a/libs/common-const/src/common.ts +++ b/libs/common-const/src/common.ts @@ -4,6 +4,7 @@ import { COW_PROTOCOL_VAULT_RELAYER_ADDRESS, IpfsConfig, SupportedChainId, + mapSupportedNetworks, } from '@cowprotocol/cow-sdk' import { Fraction, Percent } from '@uniswap/sdk-core' @@ -52,32 +53,26 @@ export const APP_TITLE = 'CoW Swap | The smartest way to trade cryptocurrencies' type Env = 'barn' | 'prod' -export const COWSWAP_ETHFLOW_CONTRACT_ADDRESS: Record>> = { - prod: { - [SupportedChainId.MAINNET]: EthFlowProd[SupportedChainId.MAINNET].address, - [SupportedChainId.GNOSIS_CHAIN]: EthFlowProd[SupportedChainId.GNOSIS_CHAIN].address, - [SupportedChainId.SEPOLIA]: EthFlowProd[SupportedChainId.SEPOLIA].address, - }, - barn: { - [SupportedChainId.MAINNET]: EthFlowBarn[SupportedChainId.MAINNET].address, - [SupportedChainId.GNOSIS_CHAIN]: EthFlowBarn[SupportedChainId.GNOSIS_CHAIN].address, - [SupportedChainId.SEPOLIA]: EthFlowBarn[SupportedChainId.SEPOLIA].address, - }, +export const COWSWAP_ETHFLOW_CONTRACT_ADDRESS: Record> = { + prod: mapSupportedNetworks((chain) => EthFlowProd[chain].address), + barn: mapSupportedNetworks((chain) => EthFlowBarn[chain].address), } export const GP_SETTLEMENT_CONTRACT_ADDRESS = COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS export const GP_VAULT_RELAYER = COW_PROTOCOL_VAULT_RELAYER_ADDRESS -export const V_COW_CONTRACT_ADDRESS: Record = { +export const V_COW_CONTRACT_ADDRESS: Record = { [SupportedChainId.MAINNET]: '0xd057b63f5e69cf1b929b356b579cba08d7688048', [SupportedChainId.GNOSIS_CHAIN]: '0xc20C9C13E853fc64d054b73fF21d3636B2d97eaB', + [SupportedChainId.ARBITRUM_ONE]: null, // doesn't exist! [SupportedChainId.SEPOLIA]: '0x21d06a222bbb94ec1406a0a8ba86b4d761bc9864', } export const COW_CONTRACT_ADDRESS: Record = { [SupportedChainId.MAINNET]: '0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB', [SupportedChainId.GNOSIS_CHAIN]: '0x177127622c4A00F3d409B75571e12cB3c8973d3c', + [SupportedChainId.ARBITRUM_ONE]: '0xcb8b5cd20bdcaea9a010ac1f8d835824f5c87a04', [SupportedChainId.SEPOLIA]: '0x0625aFB445C3B6B7B929342a04A22599fd5dBB59', } @@ -118,16 +113,19 @@ export const GNOSIS_FORUM_ROADTODECENT_LINK = 'https://forum.gnosis.io/t/gpv2-ro export const MEV_TOTAL = '606 Million' export const FLASHBOTS_LINK = 'https://explore.flashbots.net/' +// TODO: test gas prices for all networks export const GAS_PRICE_UPDATE_THRESHOLD = ms`5s` export const GAS_FEE_ENDPOINTS: Record = { [SupportedChainId.MAINNET]: 'https://api.blocknative.com/gasprices/blockprices', [SupportedChainId.GNOSIS_CHAIN]: 'https://gnosis.blockscout.com/api/v1/gas-price-oracle', + [SupportedChainId.ARBITRUM_ONE]: 'https://arbitrum.blockscout.com/api/v1/gas-price-oracle', [SupportedChainId.SEPOLIA]: '', } -export const GAS_API_KEYS: Record = { - [SupportedChainId.MAINNET]: process.env.REACT_APP_BLOCKNATIVE_API_KEY || '', - [SupportedChainId.GNOSIS_CHAIN]: '', - [SupportedChainId.SEPOLIA]: '', +export const GAS_API_KEYS: Record = { + [SupportedChainId.MAINNET]: process.env.REACT_APP_BLOCKNATIVE_API_KEY || null, + [SupportedChainId.GNOSIS_CHAIN]: null, + [SupportedChainId.ARBITRUM_ONE]: null, + [SupportedChainId.SEPOLIA]: null, } export const UNSUPPORTED_TOKENS_FAQ_URL = '/faq/trading#what-token-pairs-does-cowswap-allow-to-trade' diff --git a/libs/common-const/src/nativeAndWrappedTokens.ts b/libs/common-const/src/nativeAndWrappedTokens.ts index 4a241f86d0..9455310c80 100644 --- a/libs/common-const/src/nativeAndWrappedTokens.ts +++ b/libs/common-const/src/nativeAndWrappedTokens.ts @@ -26,6 +26,14 @@ export const WRAPPED_NATIVE_CURRENCIES: Record 'WXDAI', 'Wrapped XDAI' ), + [SupportedChainId.ARBITRUM_ONE]: new TokenWithLogo( + ETH_LOGO_URL, + SupportedChainId.ARBITRUM_ONE, + '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', + DEFAULT_NATIVE_DECIMALS, + 'WETH', + 'Wrapped Ether' + ), [SupportedChainId.SEPOLIA]: new TokenWithLogo( ETH_LOGO_URL, SupportedChainId.SEPOLIA, @@ -45,21 +53,29 @@ export const NATIVE_CURRENCIES: Record = { 'ETH', 'Ether' ), - [SupportedChainId.SEPOLIA]: new TokenWithLogo( + [SupportedChainId.GNOSIS_CHAIN]: new TokenWithLogo( undefined, - SupportedChainId.SEPOLIA, + SupportedChainId.GNOSIS_CHAIN, + NATIVE_CURRENCY_ADDRESS, + DEFAULT_NATIVE_DECIMALS, + 'xDAI', + 'xDAI' + ), + [SupportedChainId.ARBITRUM_ONE]: new TokenWithLogo( + undefined, + SupportedChainId.ARBITRUM_ONE, NATIVE_CURRENCY_ADDRESS, DEFAULT_NATIVE_DECIMALS, 'ETH', 'Ether' ), - [SupportedChainId.GNOSIS_CHAIN]: new TokenWithLogo( + [SupportedChainId.SEPOLIA]: new TokenWithLogo( undefined, - SupportedChainId.GNOSIS_CHAIN, + SupportedChainId.SEPOLIA, NATIVE_CURRENCY_ADDRESS, DEFAULT_NATIVE_DECIMALS, - 'xDAI', - 'xDAI' + 'ETH', + 'Ether' ), } diff --git a/libs/common-const/src/networks.ts b/libs/common-const/src/networks.ts index 318a5c3a2b..b70980c548 100644 --- a/libs/common-const/src/networks.ts +++ b/libs/common-const/src/networks.ts @@ -6,12 +6,14 @@ const INFURA_KEY = process.env.REACT_APP_INFURA_KEY || '2af29cd5ac554ae3b8d991af const RPC_URL_ENVS: Record = { [SupportedChainId.MAINNET]: process.env.REACT_APP_NETWORK_URL_1 || undefined, [SupportedChainId.GNOSIS_CHAIN]: process.env.REACT_APP_NETWORK_URL_100 || undefined, + [SupportedChainId.ARBITRUM_ONE]: process.env.REACT_APP_NETWORK_URL_42161 || undefined, [SupportedChainId.SEPOLIA]: process.env.REACT_APP_NETWORK_URL_11155111 || undefined, } const DEFAULT_RPC_URL: Record = { [SupportedChainId.MAINNET]: { url: `https://mainnet.infura.io/v3/${INFURA_KEY}`, usesInfura: true }, [SupportedChainId.GNOSIS_CHAIN]: { url: `https://rpc.gnosis.gateway.fm`, usesInfura: false }, + [SupportedChainId.ARBITRUM_ONE]: { url: `https://arbitrum-mainnet.infura.io/v3/${INFURA_KEY}`, usesInfura: true }, [SupportedChainId.SEPOLIA]: { url: `https://sepolia.infura.io/v3/${INFURA_KEY}`, usesInfura: true }, } diff --git a/libs/common-const/src/tokens.ts b/libs/common-const/src/tokens.ts index f238b49ab5..c2b2db730c 100644 --- a/libs/common-const/src/tokens.ts +++ b/libs/common-const/src/tokens.ts @@ -94,6 +94,70 @@ export const GNO_GNOSIS_CHAIN = new TokenWithLogo( 'Gnosis Token' ) +export const EURE_GNOSIS_CHAIN = new TokenWithLogo( + cowprotocolTokenLogoUrl('0xcb444e90d8198415266c6a2724b7900fb12fc56e', SupportedChainId.GNOSIS_CHAIN), + SupportedChainId.GNOSIS_CHAIN, + '0xcb444e90d8198415266c6a2724b7900fb12fc56e', + 18, + 'EURe', + 'Monerium EUR emoney' +) + +// Arbitrum + +export const USDT_ARBITRUM_ONE = new TokenWithLogo( + USDT.logoURI, + SupportedChainId.ARBITRUM_ONE, + '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', + 6, + 'USDT', + 'Tether USD' +) +export const WBTC_ARBITRUM_ONE = new TokenWithLogo( + WBTC.logoURI, + SupportedChainId.ARBITRUM_ONE, + '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f', + 8, + 'WBTC', + 'Wrapped BTC' +) + +export const USDC_ARBITRUM_ONE = new TokenWithLogo( + USDC_MAINNET.logoURI, + SupportedChainId.ARBITRUM_ONE, + '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', + 6, + 'USDC', + 'USD Coin' +) + +export const DAI_ARBITRUM_ONE = new TokenWithLogo( + DAI.logoURI, + SupportedChainId.ARBITRUM_ONE, + '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', + 18, + 'DAI', + 'Dai Stablecoin' +) + +export const ARB_ARBITRUM_ONE = new TokenWithLogo( + cowprotocolTokenLogoUrl('0xb50721bcf8d664c30412cfbc6cf7a15145234ad1', SupportedChainId.ARBITRUM_ONE), + SupportedChainId.ARBITRUM_ONE, + '0x912ce59144191c1204e64559fe8253a0e49e6548', + 18, + 'ARB', + 'Arbitrum' +) + +export const GNO_ARBITRUM_ONE = new TokenWithLogo( + GNO_MAINNET.logoURI, + SupportedChainId.ARBITRUM_ONE, + '0xa0b862F60edEf4452F25B4160F177db44DeB6Cf1', + 18, + 'GNO', + 'Gnosis Token' +) + // Sepolia const GNO_SEPOLIA = new TokenWithLogo( @@ -118,14 +182,16 @@ export const USDC_SEPOLIA = new TokenWithLogo( export const USDC: Record = { [SupportedChainId.MAINNET]: USDC_MAINNET, [SupportedChainId.GNOSIS_CHAIN]: USDC_GNOSIS_CHAIN, + [SupportedChainId.ARBITRUM_ONE]: USDC_ARBITRUM_ONE, [SupportedChainId.SEPOLIA]: USDC_SEPOLIA, } export const TOKEN_SHORTHANDS: { [shorthand: string]: Record } = { USDC: { [SupportedChainId.MAINNET]: USDC_MAINNET.address, - [SupportedChainId.SEPOLIA]: USDC_SEPOLIA.address, [SupportedChainId.GNOSIS_CHAIN]: USDC_GNOSIS_CHAIN.address, + [SupportedChainId.ARBITRUM_ONE]: USDC_ARBITRUM_ONE.address, + [SupportedChainId.SEPOLIA]: USDC_SEPOLIA.address, }, } @@ -159,9 +225,11 @@ const V_COW_TOKEN_SEPOLIA = new TokenWithLogo( 'CoW Protocol Virtual Token' ) -export const V_COW: Record = { +// TODO: V_COW not present in all chains, make sure code using it can handle that +export const V_COW: Record = { [SupportedChainId.MAINNET]: V_COW_TOKEN_MAINNET, [SupportedChainId.GNOSIS_CHAIN]: V_COW_TOKEN_XDAI, + [SupportedChainId.ARBITRUM_ONE]: null, [SupportedChainId.SEPOLIA]: V_COW_TOKEN_SEPOLIA, } @@ -186,6 +254,15 @@ const COW_TOKEN_XDAI = new TokenWithLogo( 'CoW Protocol Token' ) +const COW_TOKEN_ARBITRUM = new TokenWithLogo( + COW_TOKEN_MAINNET.logoURI, + SupportedChainId.ARBITRUM_ONE, + COW_CONTRACT_ADDRESS[SupportedChainId.ARBITRUM_ONE] || '', + 18, + 'COW', + 'CoW Protocol Token' +) + const COW_TOKEN_SEPOLIA = new TokenWithLogo( COW_TOKEN_MAINNET.logoURI, SupportedChainId.SEPOLIA, @@ -198,24 +275,17 @@ const COW_TOKEN_SEPOLIA = new TokenWithLogo( export const COW: Record = { [SupportedChainId.MAINNET]: COW_TOKEN_MAINNET, [SupportedChainId.GNOSIS_CHAIN]: COW_TOKEN_XDAI, + [SupportedChainId.ARBITRUM_ONE]: COW_TOKEN_ARBITRUM, [SupportedChainId.SEPOLIA]: COW_TOKEN_SEPOLIA, } export const GNO: Record = { [SupportedChainId.MAINNET]: GNO_MAINNET, [SupportedChainId.GNOSIS_CHAIN]: GNO_GNOSIS_CHAIN, + [SupportedChainId.ARBITRUM_ONE]: GNO_ARBITRUM_ONE, [SupportedChainId.SEPOLIA]: GNO_SEPOLIA, } -export const EURE_GNOSIS_CHAIN = new TokenWithLogo( - cowprotocolTokenLogoUrl('0xcb444e90d8198415266c6a2724b7900fb12fc56e', SupportedChainId.GNOSIS_CHAIN), - SupportedChainId.GNOSIS_CHAIN, - '0xcb444e90d8198415266c6a2724b7900fb12fc56e', - 18, - 'EURe', - 'Monerium EUR emoney' -) - /** * Addresses related to COW vesting for Locked GNO * These are used in src/custom/pages/Account/LockedGnoVesting hooks and index files @@ -223,11 +293,13 @@ export const EURE_GNOSIS_CHAIN = new TokenWithLogo( export const MERKLE_DROP_CONTRACT_ADDRESSES: Record = { [SupportedChainId.MAINNET]: '0x64646f112FfD6F1B7533359CFaAF7998F23C8c40', [SupportedChainId.GNOSIS_CHAIN]: '0x48D8566887F8c7d99757CE29c2cD39962bfd9547', + [SupportedChainId.ARBITRUM_ONE]: '', // doesn't exist [SupportedChainId.SEPOLIA]: '', // TODO SEPOLIA: check it } export const TOKEN_DISTRO_CONTRACT_ADDRESSES: Record = { [SupportedChainId.MAINNET]: '0x68FFAaC7A431f276fe73604C127Bd78E49070c92', [SupportedChainId.GNOSIS_CHAIN]: '0x3d610e917130f9D036e85A030596807f57e11093', + [SupportedChainId.ARBITRUM_ONE]: '', // doesn't exist [SupportedChainId.SEPOLIA]: '', // TODO SEPOLIA: check it } diff --git a/libs/common-hooks/src/index.ts b/libs/common-hooks/src/index.ts index d21e339887..2993585e91 100644 --- a/libs/common-hooks/src/index.ts +++ b/libs/common-hooks/src/index.ts @@ -1,22 +1,17 @@ +export * from './useAvailableChains' export * from './useDebounce' export * from './usePrevious' export * from './useParsedQueryString' -export * from './useIsMounted' export * from './useLoadingWithTimeout' export * from './useInterval' export * from './useIsOnline' export * from './useTheme' -export * from './useToggle' export * from './useOnClickOutside' export * from './useFilterTokens' export * from './useTimeAgo' export * from './useIsWindowVisible' -export * from './useGetReceipt' -export * from './useBlockNumber' export * from './useMachineTime' export * from './useFetchFile' export * from './useWindowSize' export * from './useCopyClipboard' -export * from './useLast' -export * from './useDebouncedChangeHandler' export * from './useFeatureFlags' diff --git a/libs/common-hooks/src/useAvailableChains.ts b/libs/common-hooks/src/useAvailableChains.ts new file mode 100644 index 0000000000..2a6d79cbef --- /dev/null +++ b/libs/common-hooks/src/useAvailableChains.ts @@ -0,0 +1,15 @@ +import { useMemo } from 'react' + +import { getAvailableChains } from '@cowprotocol/common-utils' +import { SupportedChainId } from '@cowprotocol/cow-sdk' + +import { useFeatureFlags } from './index' + +export function useAvailableChains(): SupportedChainId[] { + const { isArbitrumOneEnabled } = useFeatureFlags() + + return useMemo( + () => getAvailableChains(isArbitrumOneEnabled ? undefined : [SupportedChainId.ARBITRUM_ONE]), + [isArbitrumOneEnabled] + ) +} diff --git a/libs/common-hooks/src/useDebouncedChangeHandler.tsx b/libs/common-hooks/src/useDebouncedChangeHandler.tsx deleted file mode 100644 index 5d78e63ca3..0000000000 --- a/libs/common-hooks/src/useDebouncedChangeHandler.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from 'react' - -/** - * Easy way to debounce the handling of a rapidly changing value, e.g. a changing slider input - * @param value value that is rapidly changing - * @param onChange change handler that should receive the debounced updates to the value - * @param debouncedMs how long we should wait for changes to be applied - */ -export function useDebouncedChangeHandler( - value: T, - onChange: (newValue: T) => void, - debouncedMs = 100 -): [T, (value: T) => void] { - const [inner, setInner] = useState(() => value) - const timer = useRef>() - - const onChangeInner = useCallback( - (newValue: T) => { - setInner(newValue) - if (timer.current) { - clearTimeout(timer.current) - } - timer.current = setTimeout(() => { - onChange(newValue) - timer.current = undefined - }, debouncedMs) - }, - [debouncedMs, onChange] - ) - - useEffect(() => { - if (timer.current) { - clearTimeout(timer.current) - timer.current = undefined - } - setInner(value) - }, [value]) - - return [inner, onChangeInner] -} diff --git a/libs/common-hooks/src/useIsMounted.ts b/libs/common-hooks/src/useIsMounted.ts deleted file mode 100644 index f91a60b959..0000000000 --- a/libs/common-hooks/src/useIsMounted.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { useRef, useEffect } from 'react' - -/** - * Creates a ref that can be used to solve the issue of - * "Can't perform a React state update on an unmounted component." - */ -export function useIsMounted() { - const isMounted = useRef(false) - - useEffect(() => { - isMounted.current = true - - return () => { - isMounted.current = false - } - }, []) - - return isMounted -} diff --git a/libs/common-hooks/src/useLast.ts b/libs/common-hooks/src/useLast.ts deleted file mode 100644 index 07c1fbc0ad..0000000000 --- a/libs/common-hooks/src/useLast.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useEffect, useState } from 'react' - -/** - * Returns the last value of type T that passes a filter function - * @param value changing value - * @param filterFn function that determines whether a given value should be considered for the last value - */ -export function useLast( - value: T | undefined | null, - filterFn?: (value: T | null | undefined) => boolean -): T | null | undefined { - const [last, setLast] = useState(filterFn && filterFn(value) ? value : undefined) - useEffect(() => { - setLast((last) => { - const shouldUse: boolean = filterFn ? filterFn(value) : true - if (shouldUse) return value - return last - }) - }, [filterFn, value]) - return last -} diff --git a/libs/common-hooks/src/useToggle.ts b/libs/common-hooks/src/useToggle.ts deleted file mode 100644 index b640243ed9..0000000000 --- a/libs/common-hooks/src/useToggle.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { useCallback, useState } from 'react' - -import { Command } from '@cowprotocol/types' - -export function useToggle(initialState = false): [boolean, Command] { - const [state, setState] = useState(initialState) - const toggle = useCallback(() => setState((state) => !state), []) - return [state, toggle] -} diff --git a/libs/common-utils/src/explorer.ts b/libs/common-utils/src/explorer.ts index 81024f955f..322b04f70e 100644 --- a/libs/common-utils/src/explorer.ts +++ b/libs/common-utils/src/explorer.ts @@ -2,7 +2,7 @@ import { SupportedChainId as ChainId, UID } from '@cowprotocol/cow-sdk' import { isBarn, isDev, isLocal, isPr, isStaging } from './environments' -function _getExplorerUrlByEnvironment() { +function _getExplorerUrlByEnvironment(): Record { let baseUrl: string | undefined if (isLocal || isDev || isPr) { baseUrl = process.env.REACT_APP_EXPLORER_URL_DEV || 'https://dev.explorer.cow.fi' @@ -18,6 +18,7 @@ function _getExplorerUrlByEnvironment() { return { [ChainId.MAINNET]: baseUrl, [ChainId.GNOSIS_CHAIN]: `${baseUrl}/gc`, + [ChainId.ARBITRUM_ONE]: `${baseUrl}/arb1`, [ChainId.SEPOLIA]: `${baseUrl}/sepolia`, } } @@ -50,6 +51,7 @@ enum Explorers { Explorer = 'Explorer', Blockscout = 'Blockscout', Etherscan = 'Etherscan', + Arbiscan = 'Arbiscan', } // Used for GA ExternalLink detection @@ -60,6 +62,8 @@ export function detectExplorer(href: string) { return Explorers.Blockscout } else if (href.includes('etherscan')) { return Explorers.Etherscan + } else if (href.includes('arbiscan')) { + return Explorers.Arbiscan } else { return undefined } diff --git a/libs/common-utils/src/getAvailableChains.ts b/libs/common-utils/src/getAvailableChains.ts new file mode 100644 index 0000000000..ffaaa1d41d --- /dev/null +++ b/libs/common-utils/src/getAvailableChains.ts @@ -0,0 +1,12 @@ +import { CHAIN_INFO } from '@cowprotocol/common-const' +import { SupportedChainId } from '@cowprotocol/cow-sdk' + +export function getAvailableChains(chainsToSkip: SupportedChainId[] = []): SupportedChainId[] { + if (chainsToSkip.length === 0) { + return Object.keys(CHAIN_INFO).map((chain) => +chain) + } + + return Object.keys(CHAIN_INFO) + .filter((chain) => !chainsToSkip.includes(+chain)) + .map((chain) => +chain) +} diff --git a/libs/common-utils/src/index.ts b/libs/common-utils/src/index.ts index 302867bbf9..440a761e75 100644 --- a/libs/common-utils/src/index.ts +++ b/libs/common-utils/src/index.ts @@ -19,6 +19,7 @@ export * from './format' export * from './fractionUtils' export * from './genericPropsChecker' export * from './getAddress' +export * from './getAvailableChains' export * from './getCurrentChainIdFromUrl' export * from './getExplorerLink' export * from './getIntOrFloat' diff --git a/libs/common-utils/src/legacyAddressUtils.ts b/libs/common-utils/src/legacyAddressUtils.ts index 2d99465196..b19bb623bd 100644 --- a/libs/common-utils/src/legacyAddressUtils.ts +++ b/libs/common-utils/src/legacyAddressUtils.ts @@ -64,27 +64,28 @@ export type BlockExplorerLinkType = | 'block' | 'token-transfer' | 'composable-order' + | 'event' + | 'contract' function getEtherscanUrl(chainId: SupportedChainId, data: string, type: BlockExplorerLinkType): string { const basePath = CHAIN_INFO[chainId].explorer switch (type) { - case 'transaction': { + case 'transaction': return `${basePath}/tx/${data}` - } - case 'token': { + case 'token': return `${basePath}/token/${data}` - } - case 'block': { + case 'block': return `${basePath}/block/${data}` - } - case 'token-transfer': { + case 'token-transfer': return `${basePath}/address/${data}#tokentxns` - } + case 'event': + return `${basePath}/tx/${data}#eventlog` + case 'contract': + return `${basePath}/address/${data}#code` case 'address': - default: { + default: return `${basePath}/address/${data}` - } } } @@ -112,11 +113,9 @@ export function getEtherscanLink(chainId: SupportedChainId, type: BlockExplorerL export function getExplorerLabel(chainId: SupportedChainId, type: BlockExplorerLinkType, data?: string): string { if (isCowOrder(type, data)) { return 'View on Explorer' - } else if (chainId === SupportedChainId.GNOSIS_CHAIN) { - return 'View on Gnosisscan' - } else { - return 'View on Etherscan' } + + return `View on ${CHAIN_INFO[chainId].explorerTitle}` } // Shortens OrderID (or any string really) removing initial 2 characters e.g 0x diff --git a/libs/core/src/gnosisSafe/index.ts b/libs/core/src/gnosisSafe/index.ts index 4e2f1c7392..b9819ddc32 100644 --- a/libs/core/src/gnosisSafe/index.ts +++ b/libs/core/src/gnosisSafe/index.ts @@ -1,3 +1,4 @@ +import { CHAIN_INFO } from '@cowprotocol/common-const' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { JsonRpcFetchFunc, Web3Provider } from '@ethersproject/providers' import SafeApiKit, { SafeInfoResponse } from '@safe-global/api-kit' @@ -7,18 +8,14 @@ import { SafeMultisigTransactionResponse } from '@safe-global/safe-core-sdk-type // eslint-disable-next-line no-restricted-imports import { ethers } from 'ethers' -const SAFE_TRANSACTION_SERVICE_URL: Partial> = { +const SAFE_TRANSACTION_SERVICE_URL: Record = { [SupportedChainId.MAINNET]: 'https://safe-transaction-mainnet.safe.global', [SupportedChainId.GNOSIS_CHAIN]: 'https://safe-transaction-gnosis-chain.safe.global', + [SupportedChainId.ARBITRUM_ONE]: 'https://safe-transaction-arbitrum.safe.global', [SupportedChainId.SEPOLIA]: 'https://safe-transaction-sepolia.safe.global', } const SAFE_BASE_URL = 'https://app.safe.global' -const CHAIN_SHORT_NAME: Record = { - [SupportedChainId.MAINNET]: 'eth', // https://github.com/ethereum-lists/chains/blob/master/_data/chains/eip155-1.json - [SupportedChainId.GNOSIS_CHAIN]: 'gno', // https://github.com/ethereum-lists/chains/blob/master/_data/chains/eip155-100.json - [SupportedChainId.SEPOLIA]: 'sep', // https://github.com/ethereum-lists/chains/blob/master/_data/chains/eip155-11155111.json -} const SAFE_TRANSACTION_SERVICE_CACHE: Partial> = {} @@ -71,8 +68,8 @@ function _getClientOrThrow(chainId: number, library: Web3Provider): SafeApiKit { return client } -export function getSafeWebUrl(chaindId: SupportedChainId, safeAddress: string, safeTxHash: string): string { - const chainShortName = CHAIN_SHORT_NAME[chaindId] +export function getSafeWebUrl(chainId: SupportedChainId, safeAddress: string, safeTxHash: string): string { + const chainShortName = CHAIN_INFO[chainId].addressPrefix return `${SAFE_BASE_URL}/${chainShortName}:${safeAddress}/transactions/tx?id=multisig_${safeAddress}_${safeTxHash}` } diff --git a/libs/ens/src/hooks/useENSRegistrarContract.ts b/libs/ens/src/hooks/useENSRegistrarContract.ts index a1b58863f3..55b8599f33 100644 --- a/libs/ens/src/hooks/useENSRegistrarContract.ts +++ b/libs/ens/src/hooks/useENSRegistrarContract.ts @@ -9,7 +9,8 @@ import useSWR from 'swr' const ENS_REGISTRAR_ADDRESSES: Record = { [SupportedChainId.MAINNET]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', [SupportedChainId.GNOSIS_CHAIN]: null, - [SupportedChainId.SEPOLIA]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', // from https://docs.ens.domains/ens-deployments + [SupportedChainId.SEPOLIA]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + [SupportedChainId.ARBITRUM_ONE]: null, } export function useENSRegistrarContract(): EnsRegistrar | undefined { diff --git a/libs/permit-utils/package.json b/libs/permit-utils/package.json index ee5be3b675..8d201c321b 100644 --- a/libs/permit-utils/package.json +++ b/libs/permit-utils/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/permit-utils", - "version": "0.2.1", + "version": "0.3.0-RC.0", "type": "module", "dependencies": { "ethers": "^5.7.2", diff --git a/libs/permit-utils/src/index.ts b/libs/permit-utils/src/index.ts index 49485e61d5..62b87d93b2 100644 --- a/libs/permit-utils/src/index.ts +++ b/libs/permit-utils/src/index.ts @@ -1,4 +1,4 @@ -export { PERMIT_SIGNER, DEFAULT_MIN_GAS_LIMIT } from './const' +export { DEFAULT_MIN_GAS_LIMIT, PERMIT_SIGNER } from './const' export { checkIsCallDataAValidPermit } from './lib/checkIsCallDataAValidPermit' export { generatePermitHook } from './lib/generatePermitHook' @@ -6,4 +6,4 @@ export { getPermitUtilsInstance } from './lib/getPermitUtilsInstance' export { getTokenPermitInfo } from './lib/getTokenPermitInfo' export { isSupportedPermitInfo } from './utils/isSupportedPermitInfo' -export type { PermitHookData, PermitHookParams, PermitInfo, PermitType, GetTokenPermitIntoResult } from './types' +export type { GetTokenPermitIntoResult, PermitHookData, PermitHookParams, PermitInfo, PermitType } from './types' diff --git a/libs/tokens/src/const/defaultFavouriteTokens.ts b/libs/tokens/src/const/defaultFavouriteTokens.ts index 4bf3b44509..f4e0d420e8 100644 --- a/libs/tokens/src/const/defaultFavouriteTokens.ts +++ b/libs/tokens/src/const/defaultFavouriteTokens.ts @@ -1,14 +1,20 @@ import { + ARB_ARBITRUM_ONE, COW, DAI, + DAI_ARBITRUM_ONE, EURE_GNOSIS_CHAIN, + GNO_ARBITRUM_ONE, GNO_GNOSIS_CHAIN, TokenWithLogo, + USDC_ARBITRUM_ONE, USDC_GNOSIS_CHAIN, USDC_MAINNET, USDC_SEPOLIA, USDT, + USDT_ARBITRUM_ONE, WBTC, + WBTC_ARBITRUM_ONE, WBTC_GNOSIS_CHAIN, WETH_GNOSIS_CHAIN, WRAPPED_NATIVE_CURRENCIES, @@ -48,6 +54,15 @@ export const DEFAULT_FAVOURITE_TOKENS: Record = { WETH_GNOSIS_CHAIN, WBTC_GNOSIS_CHAIN, ]), + [SupportedChainId.ARBITRUM_ONE]: tokensListToMap([ + USDC_ARBITRUM_ONE, + USDT_ARBITRUM_ONE, + WRAPPED_NATIVE_CURRENCIES[SupportedChainId.ARBITRUM_ONE], + WBTC_ARBITRUM_ONE, + DAI_ARBITRUM_ONE, + ARB_ARBITRUM_ONE, + GNO_ARBITRUM_ONE, + ]), [SupportedChainId.SEPOLIA]: tokensListToMap([ WRAPPED_NATIVE_CURRENCIES[SupportedChainId.SEPOLIA], COW[SupportedChainId.SEPOLIA], diff --git a/libs/tokens/src/const/tokensList.json b/libs/tokens/src/const/tokensList.json index 25c60ea252..4dd5bf94b5 100644 --- a/libs/tokens/src/const/tokensList.json +++ b/libs/tokens/src/const/tokensList.json @@ -73,6 +73,18 @@ "source": "https://raw.githubusercontent.com/cowprotocol/token-lists/main/src/public/GnosisCoingeckoTokensList.json" } ], + "42161": [ + { + "priority": 1, + "enabledByDefault": true, + "source": "https://files.cow.fi/tokens/CowSwap.json" + }, + { + "priority": 2, + "enabledByDefault": true, + "source": "https://tokens.coingecko.com/arbitrum-one/all.json" + } + ], "11155111": [ { "priority": 1, diff --git a/libs/tokens/src/const/tokensLists.ts b/libs/tokens/src/const/tokensLists.ts index 64258b967e..ee9941d626 100644 --- a/libs/tokens/src/const/tokensLists.ts +++ b/libs/tokens/src/const/tokensLists.ts @@ -7,4 +7,7 @@ export const DEFAULT_TOKENS_LISTS: ListsSourcesByNetwork = tokensList export const UNISWAP_TOKENS_LIST = 'https://gateway.ipfs.io/ipns/tokens.uniswap.org' export const GNOSIS_UNISWAP_TOKENS_LIST = - 'https://raw.githubusercontent.com/cowprotocol/token-lists/main/src/public/GnosisUniswapTokensList.json' \ No newline at end of file + 'https://raw.githubusercontent.com/cowprotocol/token-lists/main/src/public/GnosisUniswapTokensList.json' + +// TODO: add a uniswap mapped token list for arbitrum +export const ARBITRUM_ONE_TOKENS_LIST = '' diff --git a/libs/tokens/src/services/fetchTokenList.ts b/libs/tokens/src/services/fetchTokenList.ts index af9985ede4..4f9b159424 100644 --- a/libs/tokens/src/services/fetchTokenList.ts +++ b/libs/tokens/src/services/fetchTokenList.ts @@ -1,5 +1,12 @@ import { MAINNET_PROVIDER } from '@cowprotocol/common-const' -import { contenthashToUri, parseENSAddress, resolveENSContentHash, uriToHttp } from '@cowprotocol/common-utils' +import { + contenthashToUri, + isAddress, + parseENSAddress, + resolveENSContentHash, + uriToHttp, +} from '@cowprotocol/common-utils' +import { TokenList } from '@uniswap/token-lists' import { ListSourceConfig, ListState } from '../types' import { validateTokenList } from '../utils/validateTokenList' @@ -63,13 +70,32 @@ async function _fetchTokenList(source: string, urls: string[]): Promise { + // Remove tokens from the list that don't have valid addresses + const tokens = list.tokens.filter(({ address }) => isAddress(address)) + + const cleanedList = { ...list, tokens } + + // Validate the list + return validateTokenList(cleanedList) +} diff --git a/libs/tokens/src/utils/trustTokenLogoUrl.ts b/libs/tokens/src/utils/trustTokenLogoUrl.ts index ca7fd2a729..86eeb9eb8c 100644 --- a/libs/tokens/src/utils/trustTokenLogoUrl.ts +++ b/libs/tokens/src/utils/trustTokenLogoUrl.ts @@ -3,6 +3,7 @@ import { SupportedChainId } from '@cowprotocol/cow-sdk' const chainIdToName: Record = { [SupportedChainId.MAINNET]: 'ethereum', [SupportedChainId.GNOSIS_CHAIN]: 'xdai', + [SupportedChainId.ARBITRUM_ONE]: 'arbitrum', [SupportedChainId.SEPOLIA]: 'ethereum', } diff --git a/libs/wallet/src/web3-react/utils/switchChain.ts b/libs/wallet/src/web3-react/utils/switchChain.ts index ea4c8b7bbf..28e8320fe5 100644 --- a/libs/wallet/src/web3-react/utils/switchChain.ts +++ b/libs/wallet/src/web3-react/utils/switchChain.ts @@ -9,11 +9,16 @@ import { ConnectionType } from '../../api/types' import { getIsWalletConnect } from '../hooks/useIsWalletConnect' function getRpcUrls(chainId: SupportedChainId): [string] { - if (chainId === SupportedChainId.GNOSIS_CHAIN) { - return ['https://rpc.gnosischain.com/'] - } + const rpcUrl = WALLET_RPC_SUGGESTION[chainId] || RPC_URLS[chainId] + + return [rpcUrl] +} - return [RPC_URLS[chainId]] +const WALLET_RPC_SUGGESTION: Record = { + [SupportedChainId.MAINNET]: null, + [SupportedChainId.GNOSIS_CHAIN]: 'https://rpc.gnosischain.com/', + [SupportedChainId.ARBITRUM_ONE]: 'https://arb1.arbitrum.io/rpc', + [SupportedChainId.SEPOLIA]: null, } export const switchChain = async (connector: Connector, chainId: SupportedChainId) => { diff --git a/libs/widget-lib/src/types.ts b/libs/widget-lib/src/types.ts index 0e8a84109d..34f1fb3557 100644 --- a/libs/widget-lib/src/types.ts +++ b/libs/widget-lib/src/types.ts @@ -1,6 +1,6 @@ import type { SupportedChainId } from '@cowprotocol/cow-sdk' import { CowEventListeners, CowEventPayloadMap, CowEvents } from '@cowprotocol/events' -export type { SupportedChainId } from '@cowprotocol/cow-sdk' +export { SupportedChainId } from '@cowprotocol/cow-sdk' export enum WidgetMethodsEmit { ACTIVATE = 'ACTIVATE', diff --git a/package.json b/package.json index 6ad2339795..920ebd4365 100644 --- a/package.json +++ b/package.json @@ -64,8 +64,8 @@ "@cowprotocol/cms": "^0.3.0-RC.2", "@cowprotocol/contracts": "^1.3.1", "@cowprotocol/cow-runner-game": "^0.2.9", - "@cowprotocol/cow-sdk": "^5.2.1", - "@cowprotocol/ethflowcontract": "cowprotocol/ethflowcontract.git#v1.1.1-artifacts", + "@cowprotocol/cow-sdk": "^5.3.0", + "@cowprotocol/ethflowcontract": "cowprotocol/ethflowcontract.git#main-artifacts", "@davatar/react": "1.8.1", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", diff --git a/yarn.lock b/yarn.lock index 028319cb88..f22ed6a2ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2373,22 +2373,27 @@ dependencies: openapi-fetch "^0.9.3" -"@cowprotocol/contracts@^1.3.1", "@cowprotocol/contracts@^1.4.0": +"@cowprotocol/contracts@^1.3.1": version "1.4.0" resolved "https://registry.yarnpkg.com/@cowprotocol/contracts/-/contracts-1.4.0.tgz#e93e5f25aac76feeaa348fa57231903274676247" integrity sha512-XLs3SlPmXD4lbiWIO7mxxuCn1eE5isuO6EUlE1cj17HqN/wukDAN0xXYPx6umOH/XdjGS33miMiPHELEyY9siw== +"@cowprotocol/contracts@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@cowprotocol/contracts/-/contracts-1.6.0.tgz#d0fc83ed8c624b968d1a68bb5c74712c11ec81e0" + integrity sha512-+UKhYRzkvnqqviBru5D3btTLYc743n0O5YTG+wpYwGl4fb7VNKBkFHe28C5Mf1DF/kOfmqfu+0IAvX9Vuq5Dqw== + "@cowprotocol/cow-runner-game@^0.2.9": version "0.2.9" resolved "https://registry.yarnpkg.com/@cowprotocol/cow-runner-game/-/cow-runner-game-0.2.9.tgz#3f94b3f370bd114f77db8b1d238cba3ef4e9d644" integrity sha512-rX7HnoV+HYEEkBaqVUsAkGGo0oBrExi+d6Io+8nQZYwZk+IYLmS9jdcIObsLviM2h4YX8+iin6NuKl35AaiHmg== -"@cowprotocol/cow-sdk@^5.2.1": - version "5.2.1" - resolved "https://registry.yarnpkg.com/@cowprotocol/cow-sdk/-/cow-sdk-5.2.1.tgz#a53eb2b0e7439602bd3ba8ce7d085e12f24b1c3c" - integrity sha512-s3h/7YL3UdPDh5q7+Q1WCDR8cJuhJXk8rGeEzdqtSBhFMvb6ErzGvXCkYOg/K9rKpIa7sfQkKkVAS1PWc9h2pQ== +"@cowprotocol/cow-sdk@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@cowprotocol/cow-sdk/-/cow-sdk-5.3.0.tgz#3330a43134d6a15453afb22dd1f490738089e591" + integrity sha512-gH53emDVsHkO9sGb8rxqx5RLNBmH1ABrum4bSxRorT3eXWviJy5kYUlucCSUIk5uMCbcJnyvsnHBZptGiMCVAA== dependencies: - "@cowprotocol/contracts" "^1.4.0" + "@cowprotocol/contracts" "^1.6.0" "@ethersproject/abstract-signer" "^5.7.0" "@openzeppelin/merkle-tree" "^1.0.5" cross-fetch "^3.1.5" @@ -2397,9 +2402,9 @@ graphql-request "^4.3.0" limiter "^2.1.0" -"@cowprotocol/ethflowcontract@cowprotocol/ethflowcontract.git#v1.1.1-artifacts": - version "1.1.1" - resolved "https://codeload.github.com/cowprotocol/ethflowcontract/tar.gz/70e5ac842c10f82aff098f29abfcd2d14f083134" +"@cowprotocol/ethflowcontract@cowprotocol/ethflowcontract.git#main-artifacts": + version "1.2.0" + resolved "https://codeload.github.com/cowprotocol/ethflowcontract/tar.gz/0163ee489587fb7f2cbabcab9d566ebc4ec189f5" "@cspotcode/source-map-support@^0.8.0": version "0.8.1" @@ -27003,7 +27008,7 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -27020,6 +27025,15 @@ string-width@^2.1.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -27147,7 +27161,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -27161,6 +27175,13 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -30471,7 +30492,7 @@ workbox-window@7.0.0, workbox-window@^7.0.0: "@types/trusted-types" "^2.0.2" workbox-core "7.0.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -30498,6 +30519,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"