From 649cfb3c50daa26e67b0928074e0cd612eb60944 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Tue, 10 Oct 2023 13:21:18 +0600 Subject: [PATCH 1/3] refactor(tokens): remove legacy code --- apps/cowswap-frontend/src/api/proxy/api.ts | 13 - apps/cowswap-frontend/src/api/proxy/hooks.ts | 111 ---- apps/cowswap-frontend/src/api/proxy/index.ts | 1 - .../cowswap-frontend/src/api/proxy/queries.ts | 22 - apps/cowswap-frontend/src/api/proxy/types.ts | 37 -- .../containers/ImportTokenModal/index.tsx | 133 ---- .../hooks/useAreThereTokensWithSameSymbol.ts | 19 - .../common/hooks/useExternalTokenSearch.ts | 20 - .../common/hooks/useTokenBySymbolOrAddress.ts | 40 -- .../CurrencyLogo/hooks/useCurrencyLogoURIs.ts | 100 --- .../src/common/pure/CurrencyLogo/index.tsx | 51 -- .../src/common/updaters/ListsUpdater.ts | 95 --- .../legacy/components/DoubleLogo/index.tsx | 41 -- .../CommonBases/CommonBasesMod.tsx | 80 --- .../SearchModal/CommonBases/index.ts | 108 --- .../CurrencyList/CurrencyListMod.tsx | 389 ----------- .../SearchModal/CurrencyList/index.tsx | 101 --- .../SearchModal/CurrencyList/styled.ts | 92 --- .../CurrencySearch/CurrencySearchMod.tsx | 291 -------- .../SearchModal/CurrencySearch/index.tsx | 65 -- .../SearchModal/CurrencySearch/sorting.ts | 64 -- .../SearchModal/CurrencySearchModal.tsx | 133 ---- .../SearchModal/ImportList/ImportListMod.tsx | 151 ----- .../SearchModal/ImportList/index.tsx | 32 - .../SearchModal/ImportRow/ImportRowMod.tsx | 109 --- .../SearchModal/ImportRow/index.tsx | 61 -- .../ImportToken/ImportTokenMod.tsx | 99 --- .../SearchModal/ImportToken/index.tsx | 103 --- .../SearchModal/Manage/ManageMod.tsx | 58 -- .../components/SearchModal/Manage/index.tsx | 76 --- .../ManageLists/ManageListsMod.tsx | 424 ------------ .../SearchModal/ManageLists/index.tsx | 107 --- .../ManageTokens/ManageTokensMod.tsx | 161 ----- .../SearchModal/ManageTokens/index.tsx | 103 --- .../TokenImportCard/TokenImportCardMod.tsx | 80 --- .../SearchModal/TokenImportCard/index.tsx | 2 - .../legacy/components/SearchModal/styleds.tsx | 104 --- .../components/TokenWarningModal/index.tsx | 23 - .../src/legacy/hooks/Tokens.ts | 124 ---- .../hooks/useFavouriteOrCommonTokens.tsx | 17 - .../src/legacy/hooks/useFetchListCallback.ts | 72 -- .../src/legacy/hooks/useTotalSupply.ts | 19 - .../legacy/state/lists/actions/actionsMod.ts | 55 -- .../src/legacy/state/lists/actions/index.ts | 1 - .../src/legacy/state/lists/hooks.ts | 196 ------ .../src/legacy/state/lists/reducer.test.ts | 625 ------------------ .../src/legacy/state/lists/reducer.ts | 267 -------- .../legacy/state/lists/wrappedTokenInfo.ts | 82 --- .../src/lib/hooks/useCurrency.ts | 121 ---- .../hooks/useTokenList/fetchTokenList.test.ts | 49 -- .../lib/hooks/useTokenList/fetchTokenList.ts | 71 -- .../src/lib/hooks/useTokenList/filtering.ts | 33 - .../lib/hooks/useTokenList/mockTokenList.json | 32 - .../src/lib/hooks/useTokenList/sorting.ts | 67 -- .../src/lib/hooks/useTokenList/utils.ts | 37 -- .../useTokenList/validateTokenList.test.ts | 42 -- .../hooks/useTokenList/validateTokenList.ts | 55 -- .../containers/SelectTokenModal/mocks.ts | 77 --- .../SelectTokenModal/searchToken.ts | 55 -- .../tokensList/pure/AllTokensList/index.tsx | 34 - .../tokensList/pure/AllTokensList/styled.ts | 33 - .../pure/LoadedTokenListItem/index.tsx | 26 - .../pure/LoadedTokenListItem/styled.ts | 17 - .../tokensList/pure/TokenListInfo/index.tsx | 30 - .../tokensList/pure/TokenListInfo/styled.ts | 21 - .../tokensList/pure/TokenLogo/index.tsx | 38 -- .../tokensList/state/tokensListAtom.ts | 10 - .../tokensList/updaters/TokensListUpdater.ts | 57 -- .../tokensList/utils/getTokensByAddress.ts | 20 - .../state/composableOrdersPopupMiddleware.ts | 41 -- .../src/tokens/goerli-token-list.json | 69 -- .../src/gnosis_chain/constants.ts | 79 --- libs/common-const/src/goerli/constants.ts | 34 - libs/common-const/src/lists.ts | 80 --- libs/common-hooks/src/useColor/index.ts | 55 -- libs/common-utils/src/currencyId.ts | 7 - 76 files changed, 6447 deletions(-) delete mode 100644 apps/cowswap-frontend/src/api/proxy/api.ts delete mode 100644 apps/cowswap-frontend/src/api/proxy/hooks.ts delete mode 100644 apps/cowswap-frontend/src/api/proxy/index.ts delete mode 100644 apps/cowswap-frontend/src/api/proxy/queries.ts delete mode 100644 apps/cowswap-frontend/src/api/proxy/types.ts delete mode 100644 apps/cowswap-frontend/src/common/containers/ImportTokenModal/index.tsx delete mode 100644 apps/cowswap-frontend/src/common/hooks/useAreThereTokensWithSameSymbol.ts delete mode 100644 apps/cowswap-frontend/src/common/hooks/useExternalTokenSearch.ts delete mode 100644 apps/cowswap-frontend/src/common/hooks/useTokenBySymbolOrAddress.ts delete mode 100644 apps/cowswap-frontend/src/common/pure/CurrencyLogo/hooks/useCurrencyLogoURIs.ts delete mode 100644 apps/cowswap-frontend/src/common/pure/CurrencyLogo/index.tsx delete mode 100644 apps/cowswap-frontend/src/common/updaters/ListsUpdater.ts delete mode 100644 apps/cowswap-frontend/src/legacy/components/DoubleLogo/index.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/CommonBases/CommonBasesMod.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/CommonBases/index.ts delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencyList/CurrencyListMod.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencyList/index.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencyList/styled.ts delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearch/CurrencySearchMod.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearch/index.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearch/sorting.ts delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearchModal.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/ImportList/ImportListMod.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/ImportList/index.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/ImportRow/ImportRowMod.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/ImportRow/index.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/ImportToken/ImportTokenMod.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/ImportToken/index.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/Manage/ManageMod.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/Manage/index.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/ManageLists/ManageListsMod.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/ManageLists/index.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/ManageTokens/ManageTokensMod.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/ManageTokens/index.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/TokenImportCard/TokenImportCardMod.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/TokenImportCard/index.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/SearchModal/styleds.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/components/TokenWarningModal/index.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/hooks/Tokens.ts delete mode 100644 apps/cowswap-frontend/src/legacy/hooks/useFavouriteOrCommonTokens.tsx delete mode 100644 apps/cowswap-frontend/src/legacy/hooks/useFetchListCallback.ts delete mode 100644 apps/cowswap-frontend/src/legacy/hooks/useTotalSupply.ts delete mode 100644 apps/cowswap-frontend/src/legacy/state/lists/actions/actionsMod.ts delete mode 100644 apps/cowswap-frontend/src/legacy/state/lists/actions/index.ts delete mode 100644 apps/cowswap-frontend/src/legacy/state/lists/hooks.ts delete mode 100644 apps/cowswap-frontend/src/legacy/state/lists/reducer.test.ts delete mode 100644 apps/cowswap-frontend/src/legacy/state/lists/reducer.ts delete mode 100644 apps/cowswap-frontend/src/legacy/state/lists/wrappedTokenInfo.ts delete mode 100644 apps/cowswap-frontend/src/lib/hooks/useCurrency.ts delete mode 100644 apps/cowswap-frontend/src/lib/hooks/useTokenList/fetchTokenList.test.ts delete mode 100644 apps/cowswap-frontend/src/lib/hooks/useTokenList/fetchTokenList.ts delete mode 100644 apps/cowswap-frontend/src/lib/hooks/useTokenList/filtering.ts delete mode 100644 apps/cowswap-frontend/src/lib/hooks/useTokenList/mockTokenList.json delete mode 100644 apps/cowswap-frontend/src/lib/hooks/useTokenList/sorting.ts delete mode 100644 apps/cowswap-frontend/src/lib/hooks/useTokenList/utils.ts delete mode 100644 apps/cowswap-frontend/src/lib/hooks/useTokenList/validateTokenList.test.ts delete mode 100644 apps/cowswap-frontend/src/lib/hooks/useTokenList/validateTokenList.ts delete mode 100644 apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenModal/mocks.ts delete mode 100644 apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenModal/searchToken.ts delete mode 100644 apps/cowswap-frontend/src/modules/tokensList/pure/AllTokensList/index.tsx delete mode 100644 apps/cowswap-frontend/src/modules/tokensList/pure/AllTokensList/styled.ts delete mode 100644 apps/cowswap-frontend/src/modules/tokensList/pure/LoadedTokenListItem/index.tsx delete mode 100644 apps/cowswap-frontend/src/modules/tokensList/pure/LoadedTokenListItem/styled.ts delete mode 100644 apps/cowswap-frontend/src/modules/tokensList/pure/TokenListInfo/index.tsx delete mode 100644 apps/cowswap-frontend/src/modules/tokensList/pure/TokenListInfo/styled.ts delete mode 100644 apps/cowswap-frontend/src/modules/tokensList/pure/TokenLogo/index.tsx delete mode 100644 apps/cowswap-frontend/src/modules/tokensList/state/tokensListAtom.ts delete mode 100644 apps/cowswap-frontend/src/modules/tokensList/updaters/TokensListUpdater.ts delete mode 100644 apps/cowswap-frontend/src/modules/tokensList/utils/getTokensByAddress.ts delete mode 100644 apps/cowswap-frontend/src/modules/twap/state/composableOrdersPopupMiddleware.ts delete mode 100644 apps/cowswap-frontend/src/tokens/goerli-token-list.json delete mode 100644 libs/common-const/src/gnosis_chain/constants.ts delete mode 100644 libs/common-const/src/goerli/constants.ts delete mode 100644 libs/common-const/src/lists.ts delete mode 100644 libs/common-hooks/src/useColor/index.ts delete mode 100644 libs/common-utils/src/currencyId.ts diff --git a/apps/cowswap-frontend/src/api/proxy/api.ts b/apps/cowswap-frontend/src/api/proxy/api.ts deleted file mode 100644 index cd40b1ec22..0000000000 --- a/apps/cowswap-frontend/src/api/proxy/api.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { GraphQLClient } from 'graphql-request' - -import { SEARCH_TOKENS } from './queries' -import { FetchTokensApiResult } from './types' - -const BASE_URL = 'https://cow-web-services.vercel.app/api/serverless/proxy' -const GQL_CLIENT = new GraphQLClient(BASE_URL) - -export async function getTokens(searchQuery: string): Promise { - return await GQL_CLIENT.request(SEARCH_TOKENS, { - searchQuery, - }) -} diff --git a/apps/cowswap-frontend/src/api/proxy/hooks.ts b/apps/cowswap-frontend/src/api/proxy/hooks.ts deleted file mode 100644 index 4d1d43bc92..0000000000 --- a/apps/cowswap-frontend/src/api/proxy/hooks.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { atom, useAtomValue, useSetAtom } from 'jotai' - -import { isAddress } from '@cowprotocol/common-utils' -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { ALL_SUPPORTED_CHAIN_IDS } from '@cowprotocol/cow-sdk' - -import * as Sentry from '@sentry/react' -import useSWR from 'swr' -import { Nullish } from 'types' - -import { getTokens } from './api' - -import type { Chain, FetchTokensApiResult, FetchTokensResult, TokenLogoCache } from './types' - -function isValidQuery(query: string): boolean { - return typeof query === 'string' && query.length > 0 -} - -const SUPPORTED_CHAINS: Partial> = { - ETHEREUM: SupportedChainId.MAINNET, - ETHEREUM_GOERLI: SupportedChainId.GOERLI, -} as const - -const UNSUPPORTED_CHAIN_ID = null - -/** - * Validate any value to be of type FetchTokensResult, and tell TypeScript that it is. - * - * Intention is to use this function with API responses, so that we can rely on the type system. - * As we are using an external party, and their responses may change arbitrarily, such a guard is necessary. - * - * @param token value to be validated - * @returns result of validation - true for matching the type, false otherwise - */ -function isValidFetchTokensResult(token: any): token is FetchTokensResult { - // Verify if token is of correct type - if (typeof token !== 'object' || token === null) { - return false - } - - // Verify if token has the expected fields. - if (!token.chainId || !token.address) { - return false - } - - const hasValidChainId = token.chainId !== UNSUPPORTED_CHAIN_ID && typeof token.chainId === 'number' - - // API we are using supports other chains such as Arbitrum as well. Verify that the chainId is something we have on our systems. - const hasSupportedChainId = ALL_SUPPORTED_CHAIN_IDS.includes(token.chainId) - - const hasValidAddress = typeof token.address === 'string' && !!isAddress(token.address) - - return hasValidChainId && hasSupportedChainId && hasValidAddress -} - -function chainToChainId(chain: Chain) { - const chainId = SUPPORTED_CHAINS[chain] - - return chainId ?? UNSUPPORTED_CHAIN_ID -} - -const tokenLogoCacheAtom = atom(new Map()) -const tokenLogoCache = atom], unknown>( - (get) => get(tokenLogoCacheAtom), - (get, _, { chainId, address, project: { logoUrl } }) => { - const cache = get(tokenLogoCacheAtom) - let cacheByChainId = cache.get(chainId) - - if (!cacheByChainId) { - cacheByChainId = new Map() - cache.set(chainId, cacheByChainId) - } - - cacheByChainId.set(address.toLowerCase(), logoUrl) - } -) - -export function useProxyTokens(query: string): FetchTokensResult[] { - const updateTokenLogoCache = useSetAtom(tokenLogoCache) - const { data: apiResult } = useSWR(['uniswapTokens', query], () => - isValidQuery(query) ? getTokens(query) : null - ) - - try { - if (apiResult && Array.isArray(apiResult.searchTokens)) { - const result = apiResult.searchTokens - .map((token) => ({ ...token, chainId: chainToChainId(token.chain) })) - .filter(isValidFetchTokensResult) - - // Build a logo cache. - result.forEach(({ chainId, address, project }) => updateTokenLogoCache({ chainId, address, project })) - - return result - } - - return [] - } catch (error: unknown) { - Sentry.captureException(error) - return [] - } -} - -export function useProxyTokenLogo(chainId?: SupportedChainId, address?: Nullish): string | undefined { - const tokenLogos = useAtomValue(tokenLogoCache) - - if (!chainId || !address) { - return undefined - } - - return tokenLogos.get(chainId)?.get(address.toLowerCase()) -} diff --git a/apps/cowswap-frontend/src/api/proxy/index.ts b/apps/cowswap-frontend/src/api/proxy/index.ts deleted file mode 100644 index d667403e28..0000000000 --- a/apps/cowswap-frontend/src/api/proxy/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { useProxyTokens, useProxyTokenLogo } from './hooks' diff --git a/apps/cowswap-frontend/src/api/proxy/queries.ts b/apps/cowswap-frontend/src/api/proxy/queries.ts deleted file mode 100644 index 8b7932a03b..0000000000 --- a/apps/cowswap-frontend/src/api/proxy/queries.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { gql } from 'graphql-request' - -export const SEARCH_TOKENS = gql` - query SearchTokens($searchQuery: String!) { - searchTokens(searchQuery: $searchQuery) { - id - decimals - name - chain - standard - address - symbol - project { - id - logoUrl - safetyLevel - __typename - } - __typename - } - } -` diff --git a/apps/cowswap-frontend/src/api/proxy/types.ts b/apps/cowswap-frontend/src/api/proxy/types.ts deleted file mode 100644 index 4b51222c5f..0000000000 --- a/apps/cowswap-frontend/src/api/proxy/types.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { SupportedChainId } from '@cowprotocol/cow-sdk' - -export type Address = `0x${string}` -export type Chain = - | 'ARBITRUM' - | 'ETHEREUM' - | 'ETHEREUM_GOERLI' - | 'OPTIMISM' - | 'POLYGON' - | 'CELO' - | 'BNB' - | 'UNKNOWN_CHAIN' - -export interface FetchTokensResult { - chainId: SupportedChainId - id: string - decimals: number - name: string - chain: Chain - standard: string - address: Address - symbol: string - project: { - id: string - logoUrl: string - safetyLevel: string - } -} - -export interface FetchTokensApiResult { - searchTokens: Omit[] -} - -export type NDimensionalMap = Key extends [infer First, ...infer Rest] - ? Map> - : Value -export type TokenLogoCache = NDimensionalMap<[SupportedChainId, string], string> diff --git a/apps/cowswap-frontend/src/common/containers/ImportTokenModal/index.tsx b/apps/cowswap-frontend/src/common/containers/ImportTokenModal/index.tsx deleted file mode 100644 index b3d32f64fc..0000000000 --- a/apps/cowswap-frontend/src/common/containers/ImportTokenModal/index.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import { useAtomValue } from 'jotai' -import { useCallback, useEffect, useMemo, useState } from 'react' - -import { TOKEN_SHORTHANDS, WRAPPED_NATIVE_CURRENCY } from '@cowprotocol/common-const' -import { useDebounce } from '@cowprotocol/common-hooks' -import { isInjectedWidget } from '@cowprotocol/common-utils' -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { Token } from '@uniswap/sdk-core' - -import TokenWarningModal from 'legacy/components/TokenWarningModal' -import { useSearchInactiveTokenLists } from 'legacy/hooks/Tokens' -import { Field } from 'legacy/state/types' -import { useAddUserToken } from 'legacy/state/user/hooks' - -import { tokensByAddressAtom, tokensBySymbolAtom } from 'modules/tokensList/state/tokensListAtom' -import { useNavigateOnCurrencySelection } from 'modules/trade/hooks/useNavigateOnCurrencySelection' -import { useTradeState } from 'modules/trade/hooks/useTradeState' - -export interface ImportTokenModalProps { - chainId: SupportedChainId -} - -export function ImportTokenModal(props: ImportTokenModalProps) { - const { chainId } = props - - const { state } = useTradeState() - const addToken = useAddUserToken() - - const loadedInputCurrency = useSearchInactiveTokenLists( - state?.inputCurrencyId || undefined, - 1, - true // - )?.[0] - const loadedOutputCurrency = useSearchInactiveTokenLists(state?.outputCurrencyId || undefined, 1, true)?.[0] - const onCurrencySelection = useNavigateOnCurrencySelection() - - const onDismiss = useCallback( - (unknownFields: Field[]) => { - unknownFields.forEach((field) => onCurrencySelection(field, null)) - }, - [onCurrencySelection] - ) - - const urlLoadedTokens: Token[] = useMemo( - () => [loadedInputCurrency, loadedOutputCurrency]?.filter((c): c is Token => c?.isToken ?? false) ?? [], - // eslint-disable-next-line react-hooks/exhaustive-deps - [loadedInputCurrency?.address, loadedOutputCurrency?.address] - ) - - // dismiss warning if all imported tokens are in active lists - const tokensByAddress = useAtomValue(tokensByAddressAtom) - const tokensBySymbol = useAtomValue(tokensBySymbolAtom) - // example: https://cowswap.dev.gnosisdev.com/#/swap?chain=mainnet&inputCurrency=0xe0b7927c4af23765cb51314a0e0521a9645f0e2a&outputCurrency=0x539F3615C1dBAfa0D008d87504667458acBd16Fa - const _importTokensNotInDefault = useMemo(() => { - // We should return an empty array until the defaultTokens are loaded - // Otherwise WETH will be in urlLoadedTokens but defaultTokens will be empty - // Fix for https://github.com/cowprotocol/cowswap/issues/534 - if (!Object.keys(tokensByAddress).length) return [] - - return ( - urlLoadedTokens && - urlLoadedTokens - .filter((token: Token) => { - return ( - !(token.address.toLowerCase() in tokensByAddress) && - !(token.symbol && token.symbol.toLowerCase() in tokensBySymbol) - ) - }) - .filter((token: Token) => { - // Any token addresses that are loaded from the shorthands map do not need to show the import URL - const isTokenInShorthands = Object.keys(TOKEN_SHORTHANDS).some((shorthand) => { - const shorthandTokenAddress = TOKEN_SHORTHANDS[shorthand][chainId] - return shorthandTokenAddress && shorthandTokenAddress.toLowerCase() === token.address.toLowerCase() - }) - - const isTokenInWrapped = - WRAPPED_NATIVE_CURRENCY[chainId].address.toLowerCase() === token.address.toLowerCase() - - return !isTokenInShorthands && !isTokenInWrapped - }) - ) - }, [chainId, tokensByAddress, tokensBySymbol, urlLoadedTokens]) - const importTokensNotInDefault = useDebounce(_importTokensNotInDefault, 200) - - const [dismissTokenWarning, setDismissTokenWarning] = useState(false) - - const handleConfirmTokenWarning = useCallback(() => { - setDismissTokenWarning(true) - }, []) - - // reset if they close warning without tokens in params - const handleDismissTokenWarning = useCallback(() => { - const unknownFields: Field[] = [] - - if (loadedInputCurrency && importTokensNotInDefault.includes(loadedInputCurrency as Token)) { - unknownFields.push(Field.INPUT) - } - - if (loadedOutputCurrency && importTokensNotInDefault.includes(loadedOutputCurrency as Token)) { - unknownFields.push(Field.OUTPUT) - } - - setDismissTokenWarning(true) - onDismiss(unknownFields) - }, [onDismiss, importTokensNotInDefault, loadedInputCurrency, loadedOutputCurrency]) - - const importTokensLength = importTokensNotInDefault.length - - // Reset dismiss state after importing token - useEffect(() => { - if (importTokensLength === 0) { - setDismissTokenWarning(false) - } - }, [importTokensLength]) - - // Automatically import unknown tokens in Widget mode - useEffect(() => { - if (isInjectedWidget()) { - importTokensNotInDefault.forEach(addToken) - } - }, [addToken, importTokensNotInDefault]) - - if (isInjectedWidget()) return null - - return ( - 0 && !dismissTokenWarning} - tokens={importTokensNotInDefault} - onConfirm={handleConfirmTokenWarning} - onDismiss={handleDismissTokenWarning} - /> - ) -} diff --git a/apps/cowswap-frontend/src/common/hooks/useAreThereTokensWithSameSymbol.ts b/apps/cowswap-frontend/src/common/hooks/useAreThereTokensWithSameSymbol.ts deleted file mode 100644 index cbc61ffca8..0000000000 --- a/apps/cowswap-frontend/src/common/hooks/useAreThereTokensWithSameSymbol.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { useAtomValue } from 'jotai' -import { useCallback } from 'react' - -import { isAddress } from '@cowprotocol/common-utils' - -import { tokensBySymbolAtom } from 'modules/tokensList/state/tokensListAtom' - -export function useAreThereTokensWithSameSymbol(): (tokenAddressOrSymbol: string | null | undefined) => boolean { - const tokensBySymbol = useAtomValue(tokensBySymbolAtom) - - return useCallback( - (tokenAddressOrSymbol: string | null | undefined) => { - if (!tokenAddressOrSymbol || isAddress(tokenAddressOrSymbol)) return false - - return tokensBySymbol[tokenAddressOrSymbol.toLowerCase()]?.length > 1 - }, - [tokensBySymbol] - ) -} diff --git a/apps/cowswap-frontend/src/common/hooks/useExternalTokenSearch.ts b/apps/cowswap-frontend/src/common/hooks/useExternalTokenSearch.ts deleted file mode 100644 index 6a28ceb4e0..0000000000 --- a/apps/cowswap-frontend/src/common/hooks/useExternalTokenSearch.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useMemo } from 'react' - -import { useWalletInfo } from '@cowprotocol/wallet' -import { Token } from '@uniswap/sdk-core' - -import { useProxyTokens } from 'api/proxy' - -export function useExternalTokenSearch(query: string, existingTokens: Map): Token[] { - const { chainId: currentChainId } = useWalletInfo() - const result = useProxyTokens(query) - const tokens = useMemo( - () => - result - .filter(({ address, chainId }) => !existingTokens.get(address) && chainId === currentChainId) - .map(({ chainId, address, decimals, symbol, name }) => new Token(chainId, address, decimals, symbol, name)), - [result, existingTokens, currentChainId] - ) - - return tokens -} diff --git a/apps/cowswap-frontend/src/common/hooks/useTokenBySymbolOrAddress.ts b/apps/cowswap-frontend/src/common/hooks/useTokenBySymbolOrAddress.ts deleted file mode 100644 index 5bc0025f73..0000000000 --- a/apps/cowswap-frontend/src/common/hooks/useTokenBySymbolOrAddress.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { useAtomValue } from 'jotai' -import { useMemo } from 'react' - -import { doesTokenMatchSymbolOrAddress } from '@cowprotocol/common-utils' -import { NativeCurrency, Token } from '@uniswap/sdk-core' - -import { useFavouriteTokens } from 'legacy/state/user/hooks' - -import { tokensByAddressAtom, tokensBySymbolAtom } from 'modules/tokensList/state/tokensListAtom' - -import useNativeCurrency from 'lib/hooks/useNativeCurrency' - -export function useTokenBySymbolOrAddress(symbolOrAddress?: string | null): Token | NativeCurrency | null { - const tokensByAddress = useAtomValue(tokensByAddressAtom) - const tokensBySymbol = useAtomValue(tokensBySymbolAtom) - const nativeCurrency = useNativeCurrency() - const favouriteTokens = useFavouriteTokens() - - return useMemo(() => { - if (!symbolOrAddress) { - return null - } - - const symbolOrAddressLowerCase = symbolOrAddress.toLowerCase() - - if (nativeCurrency.symbol?.toLowerCase() === symbolOrAddressLowerCase) { - return nativeCurrency - } - - const foundByAddress = tokensByAddress[symbolOrAddressLowerCase] - - if (foundByAddress) return foundByAddress - - const foundBySymbol = tokensBySymbol[symbolOrAddressLowerCase] - - if (foundBySymbol) return foundBySymbol[0] - - return favouriteTokens.find((item) => doesTokenMatchSymbolOrAddress(item, symbolOrAddress)) || null - }, [symbolOrAddress, nativeCurrency, tokensByAddress, tokensBySymbol, favouriteTokens]) -} diff --git a/apps/cowswap-frontend/src/common/pure/CurrencyLogo/hooks/useCurrencyLogoURIs.ts b/apps/cowswap-frontend/src/common/pure/CurrencyLogo/hooks/useCurrencyLogoURIs.ts deleted file mode 100644 index 17040c8aae..0000000000 --- a/apps/cowswap-frontend/src/common/pure/CurrencyLogo/hooks/useCurrencyLogoURIs.ts +++ /dev/null @@ -1,100 +0,0 @@ -import XDaiLogo from '@cowprotocol/assets/cow-swap/xdai.png' -import EthereumLogo from '@cowprotocol/assets/images/ethereum-logo.png' -import { NATIVE_CURRENCY_BUY_ADDRESS, ADDRESS_IMAGE_OVERRIDE } from '@cowprotocol/common-const' -import { uriToHttp } from '@cowprotocol/common-utils' -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { Currency } from '@uniswap/sdk-core' - -import { useProxyTokenLogo } from 'api/proxy' - -type Network = 'ethereum' | 'xdai' - -function chainIdToNetworkName(networkId: SupportedChainId): Network { - switch (networkId) { - case SupportedChainId.MAINNET: - return 'ethereum' - // mod - case SupportedChainId.GNOSIS_CHAIN: - return 'xdai' - default: - return 'ethereum' - } -} -function getNativeLogoURI(chainId: SupportedChainId = SupportedChainId.MAINNET): string { - switch (chainId) { - case SupportedChainId.GNOSIS_CHAIN: - return XDaiLogo - default: - return EthereumLogo - } -} - -function getTokenLogoURI(address: string, chainId: SupportedChainId = SupportedChainId.MAINNET): string | void { - const networkName = chainIdToNetworkName(chainId) - const networksWithUrls = [SupportedChainId.MAINNET, SupportedChainId.GNOSIS_CHAIN] - - if (networksWithUrls.includes(chainId)) { - return `https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/${networkName}/assets/${address}/logo.png` - } -} - -const currencyLogoCache = new Map>() - -// TODO: must be refactored -export default function useCurrencyLogoURIs(currency?: Currency | null): string[] { - const currencyAddress = currency ? (currency.isNative ? NATIVE_CURRENCY_BUY_ADDRESS : currency.address) : null - // There is a modification of Token in useDetectNativeToken() - const externalLogo = useProxyTokenLogo(currency?.chainId, currencyAddress) - const cacheKey = `${currencyAddress}|${currency?.chainId}` - const cached = currencyLogoCache.get(cacheKey) - const logoURI = currency ? (currency as Currency & { logoURI: string }).logoURI : null - const imageOverride = currency?.isToken ? ADDRESS_IMAGE_OVERRIDE[currency.address] : null - - if (cached) { - /** - * There can be a wrong token icon deriving if tokens in COMMON_BASES - * Example: EURE_GNOSIS_CHAIN - * This token doesn't have a logo in the const above, but it has it in cowprotocol/token-lists - * However, since the token from COMMON_BASES is always used first (because it's hardcoded) - * It gets a logo url from getTokenLogoURI() and it's invalid - * After loading the tokens list we should update the cache if there is a logoURI and no imageOverride - */ - if (logoURI && !cached.includes(logoURI) && !imageOverride) { - cached.unshift(logoURI) - } - - return cached - } - - const logoURIs = logoURI ? uriToHttp(logoURI) : [] - - if (!currency) { - return [] - } - - // mod: CoW Swap Native buy orders have address set to EeeEE... rather than `isNative` flag - if (currency.isNative || currency.address === NATIVE_CURRENCY_BUY_ADDRESS) { - logoURIs.push(getNativeLogoURI(currency.chainId)) - } else if (currency.isToken) { - // mod - // Explicit overrides should take priority, otherwise append potential other logoURIs to existing candidates - if (imageOverride) { - // Add image override with higher priority - logoURIs.unshift(imageOverride) - } - - // Last resource, we get the logo from @Uniswap/assets - const logoURI = getTokenLogoURI(currency.address, currency.chainId) - if (logoURI) { - logoURIs.push(logoURI) - } - - if (externalLogo) { - logoURIs.push(externalLogo) - } - } - - currencyLogoCache.set(`${currencyAddress}|${currency?.chainId}`, logoURIs) - - return logoURIs -} diff --git a/apps/cowswap-frontend/src/common/pure/CurrencyLogo/index.tsx b/apps/cowswap-frontend/src/common/pure/CurrencyLogo/index.tsx deleted file mode 100644 index e4d58c4152..0000000000 --- a/apps/cowswap-frontend/src/common/pure/CurrencyLogo/index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react' - -import { Currency } from '@uniswap/sdk-core' - -import styled from 'styled-components/macro' - -import Logo from 'legacy/components/Loader/Logo' - -import useCurrencyLogoURIs from './hooks/useCurrencyLogoURIs' - -export const StyledLogo = styled(Logo)<{ size: string; $native: boolean }>` - width: ${({ size }) => size}; - height: ${({ size }) => size}; - min-width: ${({ size }) => size}; // MOD - min-height: ${({ size }) => size}; // MOD - /* background: radial-gradient(white 50%, #ffffff00 calc(75% + 1px), #ffffff00 100%); - border-radius: 50%; - -mox-box-shadow: 0 0 1px ${({ $native }) => ($native ? 'white' : 'black')}; - -webkit-box-shadow: 0 0 1px ${({ $native }) => ($native ? 'white' : 'black')}; - box-shadow: 0 0 1px ${({ $native }) => ($native ? 'white' : 'black')}; - border: 0px solid rgba(255, 255, 255, 0); */ - border-radius: ${({ size }) => size}; - /* box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075); */ - /* background-color: ${({ theme }) => theme.white}; */ - background-color: ${({ theme }) => theme.white}; // MOD - color: ${({ theme }) => theme.black}!important; // MOD -` - -export function CurrencyLogo({ - currency, - size = '24px', - style, - ...rest -}: { - currency?: Currency | null - size?: string - style?: React.CSSProperties -}) { - const logoURIs = useCurrencyLogoURIs(currency) - - return ( - - ) -} diff --git a/apps/cowswap-frontend/src/common/updaters/ListsUpdater.ts b/apps/cowswap-frontend/src/common/updaters/ListsUpdater.ts deleted file mode 100644 index 4998458b55..0000000000 --- a/apps/cowswap-frontend/src/common/updaters/ListsUpdater.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { useCallback, useEffect } from 'react' - -import { UNSUPPORTED_LIST_URLS } from '@cowprotocol/common-const' -import { useInterval, useIsWindowVisible } from '@cowprotocol/common-hooks' -import { useWalletInfo } from '@cowprotocol/wallet' -import { getVersionUpgrade, minVersionBump, VersionUpgrade } from '@uniswap/token-lists' -import { useWeb3React } from '@web3-react/core' - -import { useFetchListCallback } from 'legacy/hooks/useFetchListCallback' -import { updateVersion } from 'legacy/state/global/actions' -import { useAppDispatch } from 'legacy/state/hooks' -import { acceptListUpdate } from 'legacy/state/lists/actions' -import { useAllLists } from 'legacy/state/lists/hooks' -import { useActiveListUrls } from 'legacy/state/lists/hooks' - -export function ListsUpdater(): null { - const { provider } = useWeb3React() - const { chainId } = useWalletInfo() - const dispatch = useAppDispatch() - const isWindowVisible = useIsWindowVisible() - - // get all loaded lists, and the active urls - const lists = useAllLists() - const activeListUrls = useActiveListUrls() - - const fetchList = useFetchListCallback() - const fetchAllListsCallback = useCallback(() => { - if (!isWindowVisible) return - Object.keys(lists).forEach((url) => - fetchList(url).catch((error) => console.debug('interval list fetching error', error)) - ) - }, [fetchList, isWindowVisible, lists]) - - // fetch all lists every 10 minutes, but only after we initialize provider - useInterval(fetchAllListsCallback, provider ? 1000 * 60 * 10 : null) - - // whenever a list is not loaded and not loading, try again to load it - useEffect(() => { - Object.keys(lists).forEach((listUrl) => { - const list = lists[listUrl] - if (!list.current && !list.loadingRequestId && !list.error) { - fetchList(listUrl).catch((error) => console.debug('list added fetching error', error)) - } - }) - }, [dispatch, fetchList, lists]) - - // if any lists from unsupported lists are loaded, check them too (in case new updates since last visit) - useEffect(() => { - Object.keys(UNSUPPORTED_LIST_URLS[chainId]).forEach((listUrl) => { - const list = lists[listUrl] - if (!list || (!list.current && !list.loadingRequestId && !list.error)) { - fetchList(listUrl).catch((error) => console.debug('list added fetching error', error)) - } - }) - }, [chainId, dispatch, fetchList, lists]) - - // automatically update lists if versions are minor/patch - useEffect(() => { - Object.keys(lists).forEach((listUrl) => { - const list = lists[listUrl] - if (list.current && list.pendingUpdate) { - const bump = getVersionUpgrade(list.current.version, list.pendingUpdate.version) - switch (bump) { - case VersionUpgrade.NONE: - throw new Error('unexpected no version bump') - case VersionUpgrade.PATCH: - case VersionUpgrade.MINOR: - const min = minVersionBump(list.current.tokens, list.pendingUpdate.tokens) - // automatically update minor/patch as long as bump matches the min update - if (bump >= min) { - dispatch(acceptListUpdate({ chainId, url: listUrl })) - } else { - console.error( - `List at url ${listUrl} could not automatically update because the version bump was only PATCH/MINOR while the update had breaking changes and should have been MAJOR` - ) - } - break - - // update any active or inactive lists - case VersionUpgrade.MAJOR: - dispatch(acceptListUpdate({ chainId, url: listUrl })) - } - } - }) - }, [dispatch, lists, activeListUrls, chainId]) - - // automatically initialise lists if chainId changes - useEffect(() => { - if (chainId) { - dispatch(updateVersion({ chainId })) - } - }, [chainId, dispatch]) - - return null -} diff --git a/apps/cowswap-frontend/src/legacy/components/DoubleLogo/index.tsx b/apps/cowswap-frontend/src/legacy/components/DoubleLogo/index.tsx deleted file mode 100644 index 5eb6ffa0ab..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/DoubleLogo/index.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Currency } from '@uniswap/sdk-core' - -import styled from 'styled-components/macro' - -import { CurrencyLogo } from 'common/pure/CurrencyLogo' - -const Wrapper = styled.div<{ margin: boolean; sizeraw: number }>` - position: relative; - display: flex; - flex-direction: row; - margin-left: ${({ sizeraw, margin }) => margin && (sizeraw / 3 + 8).toString() + 'px'}; -` - -export interface DoubleCurrencyLogoProps { - margin?: boolean - size?: number - currency0?: Currency - currency1?: Currency -} - -const HigherLogo = styled(CurrencyLogo)` - z-index: 2; -` -const CoveredLogo = styled(CurrencyLogo)<{ sizeraw: number }>` - position: absolute; - left: ${({ sizeraw }) => '-' + (sizeraw / 2).toString() + 'px'} !important; -` - -export default function DoubleCurrencyLogo({ - currency0, - currency1, - size = 16, - margin = false, -}: DoubleCurrencyLogoProps) { - return ( - - {currency0 && } - {currency1 && } - - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/CommonBases/CommonBasesMod.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/CommonBases/CommonBasesMod.tsx deleted file mode 100644 index 5843ee98a6..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/CommonBases/CommonBasesMod.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { currencyId } from '@cowprotocol/common-utils' -import { TokenSymbol, AutoRow } from '@cowprotocol/ui' -import { Currency } from '@uniswap/sdk-core' - -import { Trans } from '@lingui/macro' -import { Text } from 'rebass' -import styled from 'styled-components/macro' - -import QuestionHelper from 'legacy/components/QuestionHelper' -import { useFavouriteOrCommonTokens } from 'legacy/hooks/useFavouriteOrCommonTokens' - -import { CurrencyLogo } from 'common/pure/CurrencyLogo' - -import { BaseWrapper, CommonBasesRow, MobileWrapper } from './index' - -export const StyledScrollarea = styled.div` - overflow-y: auto; // fallback for 'overlay' - overflow-y: overlay; - padding: 0 20px; - ${({ theme }) => theme.colorScrollbar}; - - ${({ theme }) => theme.mediaWidth.upToSmall` - overflow-y: hidden; - overflow-x: auto; - `} -` - -const MAX_LENGTH_OVERFLOW = 12 -export default function CommonBases({ - onSelect, - selectedCurrency, -}: { - chainId?: number - selectedCurrency?: Currency | null - onSelect: (currency: Currency) => void - searchQuery?: string - isAddressSearch?: string | false -}) { - const tokens = useFavouriteOrCommonTokens() - - return tokens.length > 0 ? ( - MAX_LENGTH_OVERFLOW}> - - - Favourite tokens - - Your favourite saved tokens. Edit this list in your account page.} /> - - - - {tokens.map((currency: Currency) => { - const isSelected = selectedCurrency?.equals(currency) - - return ( - <> - !isSelected && e.key === 'Enter' && onSelect(currency)} - onClick={() => !isSelected && onSelect(currency)} - disable={isSelected} - key={currencyId(currency)} - > - - - - - - - ) - })} - - - - ) : null -} - -/** helper component to retrieve a base currency from the active token lists */ -function CurrencyLogoFromList({ currency }: { currency: Currency }) { - return -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/CommonBases/index.ts b/apps/cowswap-frontend/src/legacy/components/SearchModal/CommonBases/index.ts deleted file mode 100644 index 269b7f262b..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/CommonBases/index.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { AutoRow } from '@cowprotocol/ui' -import { Currency } from '@uniswap/sdk-core' - -import { transparentize } from 'polished' -import styled from 'styled-components/macro' - -import { AutoColumn as AutoColumnUni } from 'legacy/components/Column' - -export { default } from './CommonBasesMod' - -export const AutoColumn = styled(AutoColumnUni)` - ${({ theme }) => theme.mediaWidth.upToSmall` - position: relative; - - &::after { - content: ""; - display: block; - background: linear-gradient(to left, ${({ theme }) => theme.bg1} 0%, ${({ theme }) => - transparentize(1, theme.bg1)} 100%); - pointer-events: none; - height: 100%; - width: 80px; - position: absolute; - right: 0; - top: 0; - bottom: 0; - margin: auto; - } - `} -` - -export const CommonBasesRow = styled(AutoRow)` - ${({ theme }) => theme.mediaWidth.upToSmall` - flex-flow: row nowrap; - overflow-x: scroll; - padding: 0 100px 0 0; - position: relative; - ${({ theme }) => theme.colorScrollbar}; - `} -` - -export const MobileWrapper = styled(AutoColumn)<{ showOverflow?: boolean }>` - ${({ showOverflow }) => - showOverflow && - ` - ${CommonBasesRow} { - padding: 0 0 20px 0; - } - - &::after { - content: ''; - display: block; - // background: linear-gradient(to top, #163861 0%, rgba(22, 56, 97, 0) 100%); - pointer-events: none; - height: 40px; - width: 100%; - position: absolute; - right: 0; - top: 0; - bottom: 85px; - margin: auto; - } -`} - ${({ theme }) => theme.mediaWidth.upToSmall` - /* display: none; */ - `}; - - overflow-y: auto; // fallback for 'overlay' - overflow-y: overlay; - max-height: 135px; - padding-bottom: 20px; -` - -export const BaseWrapperMod = styled.div<{ disable?: boolean }>` - border: 1px solid ${({ theme, disable }) => (disable ? 'transparent' : theme.bg3)}; - border-radius: 10px; - display: flex; - padding: 6px; - - align-items: center; - :hover { - cursor: ${({ disable }) => !disable && 'pointer'}; - background-color: ${({ theme, disable }) => !disable && theme.bg4}; - } - - color: ${({ theme, disable }) => disable && theme.text3}; - background-color: ${({ theme, disable }) => disable && theme.bg3}; - filter: ${({ disable }) => disable && 'grayscale(1)'}; - - flex: 0 0 calc(33% - 8px); - justify-content: center; - - ${({ theme }) => theme.mediaWidth.upToSmall` - flex: auto; - `} -` - -export const BaseWrapper = styled(BaseWrapperMod)<{ disable?: boolean }>` - color: ${({ theme, disable }) => disable && transparentize(0.7, theme.text1)}; - filter: ${({ disable }) => disable && 'contrast(0.85)'}; -` - -export interface CommonBasesProps { - chainId?: number - selectedCurrency?: Currency | null - onSelect: (currency: Currency) => void - className?: string -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencyList/CurrencyListMod.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencyList/CurrencyListMod.tsx deleted file mode 100644 index 354a7510c1..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencyList/CurrencyListMod.tsx +++ /dev/null @@ -1,389 +0,0 @@ -import { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react' - -import TokenListLogo from '@cowprotocol/assets/svg/tokenlist.svg' -import { useTheme } from '@cowprotocol/common-hooks' -import { TokenAmount, TokenSymbol, Loader, RowBetween, RowFixed } from '@cowprotocol/ui' -import { MouseoverTooltip } from '@cowprotocol/ui' -import { useWalletInfo } from '@cowprotocol/wallet' -import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core' - -import { Trans } from '@lingui/macro' -import { FixedSizeList } from 'react-window' -import { Text } from 'rebass' -import styled from 'styled-components/macro' - -import { LightGreyCard } from 'legacy/components/Card' -import Column from 'legacy/components/Column' -import QuestionHelper from 'legacy/components/QuestionHelper' -import ImportRow from 'legacy/components/SearchModal/ImportRow' -import { LoadingRows } from 'legacy/components/SearchModal/styleds' -import { useAllTokens, useIsUserAddedToken } from 'legacy/hooks/Tokens' -import { useIsUnsupportedTokenGp } from 'legacy/state/lists/hooks' -import { WrappedTokenInfo } from 'legacy/state/lists/wrappedTokenInfo' -import { ThemedText } from 'legacy/theme' - -import useCurrencyBalance from 'modules/tokens/hooks/useCurrencyBalance' - -import { CurrencyLogo } from 'common/pure/CurrencyLogo' - -import { Tag } from './styled' // mod - -import { MenuItem } from './index' - -function currencyKey(currency: Currency): string { - return currency.isToken ? currency.address : 'ETHER' -} - -export const StyledBalanceText = styled(Text)` - white-space: nowrap; - overflow: hidden; - max-width: 5rem; - text-overflow: ellipsis; -` - -export const FixedContentRow = styled.div` - padding: 4px 20px; - height: 56px; - display: grid; - grid-gap: 16px; - align-items: center; -` - -function Balance({ balance }: { balance: CurrencyAmount }) { - return ( - - - - ) -} - -export const TagContainer = styled.div` - display: flex; - justify-content: flex-end; - flex-flow: row wrap; -` - -export const TokenListLogoWrapper = styled.img` - height: 20px; -` - -export const StyledScrollarea = styled.div` - div:first-of-type { - overflow-y: auto; // fallback for 'overlay' - overflow-y: overlay; - ${({ theme }) => theme.colorScrollbar}; - } -` - -function TokenTags({ currency }: { currency: Currency }) { - if (!(currency instanceof WrappedTokenInfo)) { - return - } - - const tags = currency.tags - if (!tags || tags.length === 0) return - - const tag = tags[0] - - return ( - - - - {tag.icon && {tag.name}} - {tag.name} - - - - {/* If there are more than one tag, show the first one and a '...' tag */} - {tags.length > 1 ? ( - `${name}: ${description}`) - .join('; \n')} - > - ... - - ) : null} - - ) -} - -function CurrencyRow({ - currency, - onSelect, - isSelected, - otherSelected, - style, - showCurrencyAmount, - isUnsupported, // gp-swap added - isPermitCompatible, // gp-swap added - allTokens, - TokenTagsComponent = TokenTags, // gp-swap added - BalanceComponent = Balance, // gp-swap added -}: { - currency: Currency - onSelect: () => void - isSelected: boolean - otherSelected: boolean - style: CSSProperties - showCurrencyAmount?: boolean - isUnsupported: boolean // gp-added - isPermitCompatible: boolean // gp-added - allTokens: { [address: string]: Token } // gp-added - BalanceComponent?: (params: { balance: CurrencyAmount }) => JSX.Element // gp-swap added - TokenTagsComponent?: (params: { - currency: Currency - isUnsupported: boolean - isPermitCompatible: boolean - }) => JSX.Element // gp-swap added -}) { - const { account } = useWalletInfo() - const key = currencyKey(currency) - const isOnSelectedList = currency?.isToken && !!allTokens[currency.address.toLowerCase()] - const customAdded = useIsUserAddedToken(currency) - const balance = useCurrencyBalance(account ?? undefined, currency) - - // only show add or remove buttons if not on selected list - return ( - <> - (!isSelected && e.key === 'Enter' ? onSelect() : null)} - onClick={() => (isSelected ? null : onSelect())} - disabled={isSelected} - selected={otherSelected} - > - - - - {/* MOD */} - - - {!currency.isNative && !isOnSelectedList && customAdded ? ( - {currency.name} • Added by user - ) : ( - currency.name - )} - - - {/* */} - - {showCurrencyAmount && ( - - {balance ? : account ? : null} - - )} - - - ) -} - -const BREAK_LINE_INACTIVE_LISTS = 'BREAK_INACTIVE_LISTS' -const BREAK_LINE_ADDITIONAL_RESULTS = 'BREAK_ADDITIONAL_RESULTS' -type BreakLine = typeof BREAK_LINE_INACTIVE_LISTS | typeof BREAK_LINE_ADDITIONAL_RESULTS -function isBreakLine(x: unknown): x is BreakLine { - return x === BREAK_LINE_INACTIVE_LISTS || x === BREAK_LINE_ADDITIONAL_RESULTS -} - -function BreakLineBaseComponent({ - style, - title, - description, -}: { - style: CSSProperties - title: string - description: string -}) { - const theme = useTheme() - return ( - - - - - - - {title} - - - {description}} /> - - - - ) -} - -const InactiveListsBreakLineComponent = styled(BreakLineBaseComponent).attrs({ - title: 'Expanded results from inactive Token Lists', - description: 'Tokens from inactive lists. Import specific tokens below or click Manage to activate more lists.', -})`` - -const AdditionalResultsBreakLineComponent = styled(BreakLineBaseComponent).attrs({ - title: 'Additional Results from External Sources', - description: 'Tokens from external sources.', -})`` - -interface TokenRowProps { - data: Array - index: number - style: CSSProperties -} - -// TODO: refactor the component -export default function CurrencyList({ - height, - currencies, - otherListTokens, - selectedCurrency, - onCurrencySelect, - otherCurrency, - fixedListRef, - showImportView, - setImportToken, - showCurrencyAmount, - isLoading, - additionalTokens, - BalanceComponent = Balance, // gp-swap added - TokenTagsComponent = TokenTags, // gp-swap added -}: { - height: number - currencies: Currency[] - otherListTokens?: Currency[] - selectedCurrency?: Currency | null - onCurrencySelect: (currency: Currency) => void - otherCurrency?: Currency | null - fixedListRef?: MutableRefObject - showImportView: () => void - setImportToken: (token: Token) => void - showCurrencyAmount?: boolean - isLoading: boolean - searchQuery?: string - isAddressSearch?: string | false - additionalTokens?: Currency[] - BalanceComponent?: (params: { balance: CurrencyAmount }) => JSX.Element // gp-swap added - TokenTagsComponent?: (params: { currency: Currency; isUnsupported: boolean }) => JSX.Element // gp-swap added -}) { - const allTokens = useAllTokens() - const isUnsupportedToken = useIsUnsupportedTokenGp() - - const itemData: (Currency | BreakLine)[] = useMemo(() => { - const result: (Currency | BreakLine)[] = [...currencies] - - if (otherListTokens && otherListTokens?.length > 0) { - // otherListTokens - it's a list of tokens from inactive lists - // here we remove tokens that already exist in the active lists - const filteredOtherListTokens = otherListTokens.filter((token) => - token.isToken ? !allTokens[token.address.toLowerCase()] : true - ) - - result.push(BREAK_LINE_INACTIVE_LISTS) - result.push(...filteredOtherListTokens) - } - - if (additionalTokens && additionalTokens.length > 0) { - result.push(BREAK_LINE_ADDITIONAL_RESULTS) - result.push(...additionalTokens) - } - - return result - }, [currencies, otherListTokens, allTokens, additionalTokens]) - - const Row = useCallback( - function TokenRow({ data, index, style }: TokenRowProps) { - const row: Currency | BreakLine = data[index] - - if (isBreakLine(row)) { - if (row === BREAK_LINE_ADDITIONAL_RESULTS) { - return - } - - if (row === BREAK_LINE_INACTIVE_LISTS) { - return - } - } - - const currency = row - - const isSelected = Boolean(currency && selectedCurrency && selectedCurrency.equals(currency)) - const otherSelected = Boolean(currency && otherCurrency && otherCurrency.equals(currency)) - const handleSelect = () => currency && onCurrencySelect(currency) - - const token = currency?.wrapped - - const showImport = index > currencies.length - - const isUnsupported = !!isUnsupportedToken(token?.address) - const isPermitCompatible = false // TODO: Make dynamic - - if (isLoading) { - return ( - -
-
-
- - ) - } else if (showImport && token) { - return ( - - ) - } else if (currency && token) { - return ( - - ) - } else { - return null - } - }, - [ - currencies.length, - onCurrencySelect, - otherCurrency, - selectedCurrency, - setImportToken, - showImportView, - showCurrencyAmount, - isLoading, - isUnsupportedToken, - BalanceComponent, - TokenTagsComponent, - allTokens, - ] - ) - - const itemKey = useCallback((index: number, data: typeof itemData) => { - const currency = data[index] - if (isBreakLine(currency)) return currency - return currencyKey(currency) - }, []) - - return ( - - - {Row} - - - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencyList/index.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencyList/index.tsx deleted file mode 100644 index 43987756c4..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencyList/index.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { UNSUPPORTED_TOKENS_FAQ_URL } from '@cowprotocol/common-const' -import { TokenAmount } from '@cowprotocol/ui' -import { MouseoverTooltip } from '@cowprotocol/ui' -import { Currency, CurrencyAmount } from '@uniswap/sdk-core' - -import ICON_GAS_FREE from 'assets/icon/gas-free.svg' -import { HashLink } from 'react-router-hash-link' -import styled from 'styled-components/macro' - -import { MenuItem as MenuItemMod } from 'legacy/components/SearchModal/styleds' -import { TagInfo } from 'legacy/state/lists/wrappedTokenInfo' - -import CurrencyListMod, { StyledBalanceText, TagContainer } from './CurrencyListMod' -import { Wrapper, Tag, TagLink } from './styled' - -enum Tags { - UNSUPPORTED = '0', - GAS_FREE = '1', -} - -const TOKEN_TAGS: Record = { - [Tags.UNSUPPORTED]: { - name: 'Unsupported', - description: - 'This token is unsupported as it does not operate optimally with CoW Protocol. Please refer to the FAQ for more information.', - id: '0', - }, - [Tags.GAS_FREE]: { - name: 'Gas-free approval', - icon: ICON_GAS_FREE, - description: 'This token can be approved without spending gas, using the token Permit.', - id: '1', - }, -} - -export const MenuItem = styled(MenuItemMod)` - &:hover { - background-color: ${({ theme, disabled }) => !disabled && theme.grey1}; - } -` - -function TokenTags({ isUnsupported, isPermitCompatible }: { isUnsupported: boolean; isPermitCompatible?: boolean }) { - const tagsToShow: TagInfo[] = [] - - if (isUnsupported) { - tagsToShow.push(TOKEN_TAGS[Tags.UNSUPPORTED]) - } else if (isPermitCompatible) { - tagsToShow.push(TOKEN_TAGS[Tags.GAS_FREE]) - } - - if (tagsToShow.length === 0) { - return - } - - return ( - - {isUnsupported && ( - - e.stopPropagation()}> - FAQ - - - )} - - ) -} - -function TagDescriptor({ tags, children }: { children?: React.ReactNode; tags: TagInfo[] }) { - return ( - - {tags.map((tag) => ( - - - {tag.icon ? {tag.name} : null} - {tag.name} - - - ))} - {children} - - ) -} - -export function Balance({ balance }: { balance: CurrencyAmount }) { - return ( - - - - ) -} - -export default function CurrencyList( - ...paramsList: Parameters -): ReturnType { - const [params] = paramsList - return ( - - - - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencyList/styled.ts b/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencyList/styled.ts deleted file mode 100644 index 610ead735d..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencyList/styled.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { RowFixed } from '@cowprotocol/ui' - -import styled from 'styled-components/macro' - -import { LightGreyCard } from 'legacy/components/Card' -import Column from 'legacy/components/Column' -import { TagInfo } from 'legacy/state/lists/wrappedTokenInfo' - -import { UI } from 'common/constants/theme' -import { StyledLogo } from 'common/pure/CurrencyLogo' - -export const Tag = styled.div<{ tag?: TagInfo }>` - display: flex; - align-items: center; - background: ${({ tag }) => - tag?.id === '0' - ? `var(${UI.COLOR_DANGER_BG})` - : tag?.id === '1' - ? `var(${UI.COLOR_SUCCESS_BG})` - : `var(${UI.COLOR_GREY})`}; - color: ${({ tag }) => - tag?.id === '0' - ? `var(${UI.COLOR_DANGER_TEXT})` - : tag?.id === '1' - ? `var(${UI.COLOR_SUCCESS_TEXT})` - : `var(${UI.COLOR_TEXT1})`}; - font-size: 12px; - font-weight: var(${UI.FONT_WEIGHT_MEDIUM}); - border-radius: 4px; - padding: 4px 6px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - justify-self: flex-end; - margin: 0 4px 0 0; - - > img { - --size: 14px; - display: inline-block; - margin: 0 5px 0 0; - width: var(--size); - height: var(--size); - } -` - -export const TagLink = styled(Tag)` - a { - color: inherit; - font-weight: inherit; - } -` - -export const Wrapper = styled.div` - ${Column} { - > div { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - max-width: 220px; - width: 100%; - - // Token symbol name - &:first-of-type { - color: var(${UI.COLOR_TEXT1}); - } - - // Token full name - &:last-of-type { - color: var(${UI.COLOR_TEXT2}); - font-weight: 400; - } - - ${({ theme }) => theme.mediaWidth.upToSmall` - max-width: 140px; - `}; - } - } - ${StyledLogo} { - height: 36px; - width: 36px; - border-radius: 36px; - } - ${TagLink} { - color: var(${UI.COLOR_TEXT1}); - } - ${LightGreyCard} { - background: var(${UI.COLOR_CONTAINER_BG_01}); - } - ${LightGreyCard} ${RowFixed} > div { - color: var(${UI.COLOR_TEXT1}); - } -` diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearch/CurrencySearchMod.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearch/CurrencySearchMod.tsx deleted file mode 100644 index 693e2efdfa..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearch/CurrencySearchMod.tsx +++ /dev/null @@ -1,291 +0,0 @@ -import { ChangeEvent, KeyboardEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react' - -import { searchByAddressAnalytics } from '@cowprotocol/analytics' -import { useTheme, useDebounce, useNetworkName, useOnClickOutside, useToggle } from '@cowprotocol/common-hooks' -import { isAddress } from '@cowprotocol/common-utils' -import { Row } from '@cowprotocol/ui' -import { useWalletInfo } from '@cowprotocol/wallet' -import { Currency, Token } from '@uniswap/sdk-core' - -import { t, Trans } from '@lingui/macro' -import AutoSizer from 'react-virtualized-auto-sizer' -import { FixedSizeList } from 'react-window' -import { Text } from 'rebass' -import styled, { DefaultTheme } from 'styled-components/macro' - -import Column from 'legacy/components/Column' -import CommonBases from 'legacy/components/SearchModal/CommonBases' -import CurrencyList from 'legacy/components/SearchModal/CurrencyList' -import ImportRow from 'legacy/components/SearchModal/ImportRow' -import { PaddedColumn, SearchInput, Separator, PaddedRow } from 'legacy/components/SearchModal/styleds' -import { - useAllTokenBalances, - useAllTokens, - useIsUserAddedToken, - useSearchInactiveTokenLists, - useToken, -} from 'legacy/hooks/Tokens' -import { ButtonText, CloseIcon, ThemedText } from 'legacy/theme' - -import { UI } from 'common/constants/theme' -import { useExternalTokenSearch } from 'common/hooks/useExternalTokenSearch' -import useNativeCurrency from 'lib/hooks/useNativeCurrency' -import { getTokenFilter } from 'lib/hooks/useTokenList/filtering' -import { tokenComparator, useSortTokensByQuery } from 'lib/hooks/useTokenList/sorting' - -import { ContentWrapper } from './index' - -export const Footer = styled.div` - width: 100%; - border-radius: 20px; - padding: 20px; - border-top-left-radius: 0; - border-top-right-radius: 0; - background-color: var(${UI.COLOR_CONTAINER_BG_01}); -` - -export interface CurrencySearchProps { - isOpen: boolean - onDismiss: () => void - selectedCurrency?: Currency | null - onCurrencySelect: (currency: Currency) => void - otherSelectedCurrency?: Currency | null - showCommonBases?: boolean - showCurrencyAmount?: boolean - disableNonToken?: boolean - showManageView: () => void - showImportView: () => void - setImportToken: (token: Token) => void - FooterButtonTextComponent: (props: { theme: DefaultTheme }) => JSX.Element // MOD -} - -export function CurrencySearch({ - selectedCurrency, - onCurrencySelect, - otherSelectedCurrency, - showCommonBases, - showCurrencyAmount, - disableNonToken, - onDismiss, - isOpen, - showManageView, - showImportView, - setImportToken, - FooterButtonTextComponent, -}: CurrencySearchProps) { - const { chainId } = useWalletInfo() - const theme = useTheme() - - const [tokenLoaderTimerElapsed, setTokenLoaderTimerElapsed] = useState(false) - - // refs for fixed size lists - const fixedList = useRef() - - const [searchQuery, setSearchQuery] = useState('') - const debouncedQuery = useDebounce(searchQuery, 200) - - const allTokens = useAllTokens() - - // if they input an address, use it - const isAddressSearch = isAddress(debouncedQuery) - - const searchToken = useToken(debouncedQuery) - - const searchTokenIsAdded = useIsUserAddedToken(searchToken) - - const network = useNetworkName() - - useEffect(() => { - if (isAddressSearch) { - searchByAddressAnalytics(isAddressSearch) - } - }, [isAddressSearch]) - - const filteredTokens: Token[] = useMemo(() => { - return Object.values(allTokens).filter(getTokenFilter(debouncedQuery)) - }, [allTokens, debouncedQuery]) - - const [balances, balancesIsLoading] = useAllTokenBalances() - const sortedTokens: Token[] = useMemo(() => { - void balancesIsLoading // creates a new array once balances load to update hooks - return [...filteredTokens].sort(tokenComparator.bind(null, balances)) - }, [balances, filteredTokens, balancesIsLoading]) - - const filteredSortedTokens = useSortTokensByQuery(debouncedQuery, sortedTokens) - - const native = useNativeCurrency() - - const filteredSortedTokensWithETH: Currency[] = useMemo(() => { - // Use Celo ERC20 Implementation and exclude the native asset - if (!native) { - return filteredSortedTokens - } - - const s = debouncedQuery.toLowerCase().trim() - if (native.symbol?.toLowerCase()?.indexOf(s) !== -1) { - // Always bump the native token to the top of the list. - return native ? [native, ...filteredSortedTokens.filter((t) => !t.equals(native))] : filteredSortedTokens - } - return filteredSortedTokens - }, [debouncedQuery, native, filteredSortedTokens]) - - const handleCurrencySelect = useCallback( - (currency: Currency) => { - onCurrencySelect(currency) - onDismiss() - }, - [onDismiss, onCurrencySelect] - ) - - // clear the input on open - useEffect(() => { - if (isOpen) setSearchQuery('') - }, [isOpen]) - - // manage focus on modal show - const inputRef = useRef() - const handleInput = useCallback((event: ChangeEvent) => { - const input = event.target.value - // Do a case-insensitive search - const checksummedInput = isAddress(input.toLowerCase().trim()) - setSearchQuery(checksummedInput || input) - fixedList.current?.scrollTo(0) - }, []) - - const handleEnter = useCallback( - (e: KeyboardEvent) => { - if (e.key === 'Enter') { - const s = debouncedQuery.toLowerCase().trim() - if (s === native?.symbol?.toLowerCase()) { - handleCurrencySelect(native) - } else if (filteredSortedTokensWithETH.length > 0) { - if ( - filteredSortedTokensWithETH[0].symbol?.toLowerCase() === debouncedQuery.trim().toLowerCase() || - filteredSortedTokensWithETH.length === 1 - ) { - handleCurrencySelect(filteredSortedTokensWithETH[0]) - } - } - } - }, - [debouncedQuery, native, filteredSortedTokensWithETH, handleCurrencySelect] - ) - - // menu ui - const [open, toggle] = useToggle(false) - const node = useRef() - useOnClickOutside(node, open ? toggle : undefined) - - // if no results on main list, show option to expand into inactive - const filteredInactiveTokens = useSearchInactiveTokenLists( - filteredTokens.length === 0 || (debouncedQuery.length > 2 && !isAddressSearch) ? debouncedQuery : undefined - ) - - // Timeout token loader after 3 seconds to avoid hanging in a loading state. - useEffect(() => { - const tokenLoaderTimer = setTimeout(() => { - setTokenLoaderTimerElapsed(true) - }, 3000) - return () => clearTimeout(tokenLoaderTimer) - }, []) - - const existingTokens = useMemo( - () => - new Map( - [...filteredSortedTokens, ...(filteredInactiveTokens ?? [])] - .filter((currency: Currency): currency is Token => currency.isToken) - .map(({ address }) => [address, true]) - ), - [filteredSortedTokens, filteredInactiveTokens] - ) - const additionalTokens = useExternalTokenSearch(searchQuery, existingTokens) - - // Autofocus search input - useEffect(() => { - inputRef.current?.focus() - }, []) - - return ( - <> - - - - - Select a token - - - - - } - onChange={handleInput} - onKeyDown={handleEnter} - /> - - {showCommonBases && ( - - )} - - - {searchToken && !searchTokenIsAdded && filteredSortedTokens?.length === 0 ? ( - - - - ) : filteredSortedTokens?.length > 0 || filteredInactiveTokens?.length > 0 || additionalTokens.length > 0 ? ( -
- - {({ height }) => ( - - )} - -
- ) : isAddressSearch ? ( - - - No tokens found with this address in {network} network - - - ) : ( - - - No tokens found for this name in {network} network - - - )} -
- - - - - -
-
- - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearch/index.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearch/index.tsx deleted file mode 100644 index 568b8c912b..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearch/index.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { RowFixed } from '@cowprotocol/ui' - -import { Trans } from '@lingui/macro' -import { transparentize } from 'polished' -import { Edit } from 'react-feather' -import styled from 'styled-components/macro' -import { DefaultTheme } from 'styled-components/macro' - -import Column from 'legacy/components/Column' -import { Separator } from 'legacy/components/SearchModal/styleds' -import { IconWrapper, ThemedText } from 'legacy/theme' - -import { UI } from 'common/constants/theme' - -import { CurrencySearch as CurrencySearchMod, CurrencySearchProps } from './CurrencySearchMod' - -export const ContentWrapper = styled(Column)` - width: 100%; - flex: 1 1 fit-content; - position: relative; - - > input { - border: none; - transition: background 0.3s ease-in-out; - background: var(${UI.COLOR_CONTAINER_BG_01}); - color: var(${UI.COLOR_TEXT1}); - } - - > input::placeholder { - font-size: 16px; - color: ${({ theme }) => transparentize(0.4, theme.text1)}; - } - - > input:focus::placeholder { - color: ${({ theme }) => transparentize(0.9, theme.text1)}; - } - - ${Separator} { - background: var(${UI.COLOR_GREY}); - - // Target the token list container - + div { - } - } -` - -const FooterButtonTextComponent = ({ theme }: { theme: DefaultTheme }) => ( - - - - - - Manage Token Lists - - -) - -export function CurrencySearch(props: Omit) { - return ( - } - /> - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearch/sorting.ts b/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearch/sorting.ts deleted file mode 100644 index c3fb6ccc26..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearch/sorting.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { useMemo } from 'react' - -import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core' - -import { useAllTokenBalances } from 'legacy/hooks/Tokens' - -import { TokenAmounts } from 'modules/tokens' - -const PRIORITISED_TOKENS = ['COW', 'GNO'] - -// compare two token amounts with highest one coming first -export function balanceComparator(balanceA?: CurrencyAmount, balanceB?: CurrencyAmount) { - if (balanceA && balanceB) { - return balanceA.greaterThan(balanceB) ? -1 : balanceA.equalTo(balanceB) ? 0 : 1 - } else if (balanceA && balanceA.greaterThan('0')) { - return -1 - } else if (balanceB && balanceB.greaterThan('0')) { - return 1 - } - return 0 -} - -function getTokenComparator(balances: [TokenAmounts, boolean]): (tokenA: Token, tokenB: Token) => number { - return function sortTokens(tokenA: Token, tokenB: Token): number { - // -1 = a is first - // 1 = b is first - - // sort by balances - const balanceA = balances[0][tokenA.address]?.value - const balanceB = balances[0][tokenB.address]?.value - - const balanceComp = balanceComparator(balanceA, balanceB) - if (balanceComp !== 0) return balanceComp - - // Mod: modify tokens list by prioritised list - const indexA = PRIORITISED_TOKENS.indexOf(tokenA.symbol || '') - const indexB = PRIORITISED_TOKENS.indexOf(tokenB.symbol || '') - - if (indexA !== -1 && indexB !== -1) { - return indexB < indexA ? 1 : -1 - } else if (indexA !== -1 || indexB !== -1) { - return indexB !== -1 ? 1 : -1 - } - - if (tokenA.symbol && tokenB.symbol) { - // sort by symbol - return tokenA.symbol.toLowerCase() < tokenB.symbol.toLowerCase() ? -1 : 1 - } else { - return tokenA.symbol ? -1 : tokenB.symbol ? -1 : 0 - } - } -} - -export function useTokenComparator(inverted: boolean): (tokenA: Token, tokenB: Token) => number { - const balances = useAllTokenBalances() - const comparator = useMemo(() => getTokenComparator(balances ?? {}), [balances]) - return useMemo(() => { - if (inverted) { - return (tokenA: Token, tokenB: Token) => comparator(tokenA, tokenB) * -1 - } else { - return comparator - } - }, [inverted, comparator]) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearchModal.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearchModal.tsx deleted file mode 100644 index 3fc6e94102..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/CurrencySearchModal.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import { useCallback, useEffect, useState } from 'react' - -import { usePrevious, useLast } from '@cowprotocol/common-hooks' -import { Currency, Token } from '@uniswap/sdk-core' -import { TokenList } from '@uniswap/token-lists' - -import { CurrencySearch } from 'legacy/components/SearchModal/CurrencySearch' -import { ImportList } from 'legacy/components/SearchModal/ImportList' -import { ImportToken } from 'legacy/components/SearchModal/ImportToken' -import Manage from 'legacy/components/SearchModal/Manage' -import { WrappedTokenInfo } from 'legacy/state/lists/wrappedTokenInfo' - -import { CowModal as Modal } from 'common/pure/Modal' - -export interface CurrencySearchModalProps { - isOpen: boolean - onDismiss: () => void - selectedCurrency?: Currency | null - onCurrencySelect: (currency: Currency) => void - otherSelectedCurrency?: Currency | null - showCommonBases?: boolean - showCurrencyAmount?: boolean - disableNonToken?: boolean -} - -export enum CurrencyModalView { - search, - manage, - importToken, - importList, -} - -export default function CurrencySearchModal({ - isOpen, - onDismiss, - onCurrencySelect, - selectedCurrency, - otherSelectedCurrency, - showCommonBases = false, - showCurrencyAmount = true, - disableNonToken = false, -}: CurrencySearchModalProps) { - const [modalView, setModalView] = useState(CurrencyModalView.manage) - const lastOpen = useLast(isOpen) - - useEffect(() => { - if (isOpen && !lastOpen) { - setModalView(CurrencyModalView.search) - } - }, [isOpen, lastOpen]) - - const handleCurrencySelect = useCallback( - (currency: Currency) => { - onCurrencySelect(currency) - onDismiss() - }, - [onDismiss, onCurrencySelect] - ) - - // for token import view - const prevView = usePrevious(modalView) - - // used for import token flow - const [importToken, setImportToken] = useState() - - // used for import list - const [importList, setImportList] = useState() - const [listURL, setListUrl] = useState() - - const showImportView = useCallback(() => setModalView(CurrencyModalView.importToken), [setModalView]) - const showManageView = useCallback(() => setModalView(CurrencyModalView.manage), [setModalView]) - const handleBackImport = useCallback( - () => setModalView(prevView && prevView !== CurrencyModalView.importToken ? prevView : CurrencyModalView.search), - [setModalView, prevView] - ) - - // change min height if not searching - const minHeight = modalView === CurrencyModalView.importToken || modalView === CurrencyModalView.importList ? 40 : 80 - let content = null - switch (modalView) { - case CurrencyModalView.search: - content = ( - - ) - break - case CurrencyModalView.importToken: - if (importToken) { - content = ( - - ) - } - break - case CurrencyModalView.importList: - if (importList && listURL) { - content = - } - break - case CurrencyModalView.manage: - content = ( - - ) - break - } - return ( - - {content} - - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportList/ImportListMod.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportList/ImportListMod.tsx deleted file mode 100644 index d047a1f786..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportList/ImportListMod.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import { useCallback, useState } from 'react' - -import { addListAnalytics } from '@cowprotocol/analytics' -import { useTheme } from '@cowprotocol/common-hooks' -import { ButtonPrimary } from '@cowprotocol/ui' -import { AutoRow, RowBetween, RowFixed } from '@cowprotocol/ui' -import { ExternalLink } from '@cowprotocol/ui' - -import { Trans } from '@lingui/macro' -import { transparentize } from 'polished' -import { AlertTriangle, ArrowLeft } from 'react-feather' -import styled from 'styled-components/macro' - -import { AutoColumn } from 'legacy/components/Column' -import ListLogo from 'legacy/components/ListLogo' -import { CurrencyModalView } from 'legacy/components/SearchModal/CurrencySearchModal' -import { Card } from 'legacy/components/SearchModal/ManageLists' -import { Checkbox, PaddedColumn, TextDot } from 'legacy/components/SearchModal/styleds' -import { SectionBreak } from 'legacy/components/swap/styleds' -import { useFetchListCallback } from 'legacy/hooks/useFetchListCallback' -import { useAllLists } from 'legacy/state/lists/hooks' -import { CloseIcon, ThemedText } from 'legacy/theme' - -import { ImportProps } from './index' - -const Wrapper = styled.div` - position: relative; - width: 100%; - overflow: auto; -` - -export function ImportList({ listURL, list, setModalView, onDismiss, enableList, removeList }: Required) { - const theme = useTheme() - - // user must accept - const [confirmed, setConfirmed] = useState(false) - - const lists = useAllLists() - const fetchList = useFetchListCallback() - - // monitor is list is loading - const adding = Boolean(lists[listURL]?.loadingRequestId) - const [addError, setAddError] = useState(null) - - const handleAddList = useCallback(() => { - if (adding) return - setAddError(null) - fetchList(listURL) - .then(() => { - addListAnalytics('Success', listURL) - // turn list on - enableList(listURL) - // go back to lists - setModalView(CurrencyModalView.manage) - }) - .catch((error) => { - addListAnalytics('Failed', listURL) - setAddError(error.message) - removeList(listURL) - }) - }, [adding, enableList, fetchList, listURL, removeList, setModalView]) - - return ( - - - - setModalView(CurrencyModalView.manage)} /> - - Import List - - - - - - - - - - - {list.logoURI && } - - - - {list.name} - - - - {list.tokens.length} tokens - - - - - {listURL} - - - - - - - - - - - Import at your own risk - - - - - - - By adding this list you are implicitly trusting that the data is correct. Anyone can create a list, - including creating fake versions of existing lists and lists that claim to represent projects that do - not have one. - - - - If you purchase a token from this list, you may not be able to sell it back. - - - setConfirmed(!confirmed)}> - setConfirmed(!confirmed)} - /> - - I understand - - - - - - Import - - {addError ? ( - - {addError} - - ) : null} - - - - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportList/index.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportList/index.tsx deleted file mode 100644 index 031b52b50c..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportList/index.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useCallback } from 'react' - -import { DEFAULT_NETWORK_FOR_LISTS } from '@cowprotocol/common-const' -import { useWalletInfo } from '@cowprotocol/wallet' -import { TokenList } from '@uniswap/token-lists' - -import { useDispatch } from 'react-redux' - -import { CurrencyModalView } from 'legacy/components/SearchModal/CurrencySearchModal' -import { enableList as enableListMod, removeList as removeListMod } from 'legacy/state/lists/actions' - -import { ImportList as ImportListMod } from './ImportListMod' - -export interface ImportProps { - listURL: string - list: TokenList - onDismiss: () => void - setModalView: (view: CurrencyModalView) => void - enableList?: (url: string) => ReturnType - removeList?: (url: string) => ReturnType -} - -export function ImportList(props: ImportProps) { - const { chainId = DEFAULT_NETWORK_FOR_LISTS } = useWalletInfo() - const dispatch = useDispatch() - - // wrap actions in dispatch and pass new 'chainId' prop here to avoid changing in children - const enableList = useCallback((url: string) => dispatch(enableListMod({ url, chainId })), [chainId, dispatch]) - const removeList = useCallback((url: string) => dispatch(removeListMod({ url, chainId })), [chainId, dispatch]) - - return -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportRow/ImportRowMod.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportRow/ImportRowMod.tsx deleted file mode 100644 index 628055712d..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportRow/ImportRowMod.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { CSSProperties } from 'react' - -import { useTheme } from '@cowprotocol/common-hooks' -import { ButtonPrimary } from '@cowprotocol/ui' -import { AutoRow, RowFixed } from '@cowprotocol/ui' -import { Token } from '@uniswap/sdk-core' - -import { Trans } from '@lingui/macro' -import { CheckCircle } from 'react-feather' -import styled from 'styled-components/macro' - -import { AutoColumn } from 'legacy/components/Column' -import ListLogo from 'legacy/components/ListLogo' -import { useIsTokenActive, useIsUserAddedToken } from 'legacy/hooks/Tokens' -import { WrappedTokenInfo } from 'legacy/state/lists/wrappedTokenInfo' -import { ThemedText } from 'legacy/theme' - -import { CurrencyLogo } from 'common/pure/CurrencyLogo' - -export const TokenSection = styled.div<{ dim?: boolean }>` - padding: 4px 20px; - height: 56px; - display: grid; - grid-template-columns: auto minmax(auto, 1fr) auto; - grid-gap: 16px; - align-items: center; - - opacity: ${({ dim }) => (dim ? '0.4' : '1')}; -` - -const CheckIcon = styled(CheckCircle)` - height: 16px; - width: 16px; - margin-right: 6px; - stroke: ${({ theme }) => theme.green1}; -` - -const NameOverflow = styled.div` - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - max-width: 140px; - font-size: 12px; -` - -export default function ImportRow({ - token, - style, - dim, - showImportView, - setImportToken, -}: { - token: Token - style?: CSSProperties - dim?: boolean - showImportView: () => void - setImportToken: (token: Token) => void -}) { - const theme = useTheme() - - // check if already active on list or local storage tokens - const isAdded = useIsUserAddedToken(token) - const isActive = useIsTokenActive(token) - - const list = token instanceof WrappedTokenInfo ? token.list : undefined - - return ( - - - - - {token.symbol} - - {token.name} - - - {list && list.logoURI && ( - - - via {list.name} - - - - )} - - {!isActive && !isAdded ? ( - { - setImportToken && setImportToken(token) - showImportView() - }} - > - Import - - ) : ( - - - - Active - - - )} - - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportRow/index.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportRow/index.tsx deleted file mode 100644 index 24e27be063..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportRow/index.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { CSSProperties } from 'react' - -import { ButtonPrimary } from '@cowprotocol/ui' -import { AutoRow } from '@cowprotocol/ui' -import { Token } from '@uniswap/sdk-core' - -import styled from 'styled-components/macro' - -import { StyledListLogo } from 'legacy/components/ListLogo' - -import { UI } from 'common/constants/theme' - -import ImportRowMod, { TokenSection } from './ImportRowMod' - -interface ImportRowProps { - token: Token - style?: CSSProperties - dim?: boolean - showImportView: () => void - setImportToken: (token: Token) => void -} - -const Wrapper = styled.div` - width: 100%; - - ${TokenSection} > div > div:not(:last-child) { - flex-flow: column wrap; - align-items: flex-start; - } - - ${StyledListLogo} { - margin: 0 0 0 5px; - } - - ${TokenSection} > svg { - stroke: var(${UI.COLOR_TEXT2}); - } - - ${TokenSection} ${AutoRow} { - flex-flow: column wrap; - align-items: flex-start; - } - - ${AutoRow} > div { - color: var(${UI.COLOR_TEXT1}); - margin: 0; - } - - ${TokenSection} > ${ButtonPrimary} { - min-height: auto; - font-size: 16px; - } -` - -export default function ImportRow(props: ImportRowProps) { - return ( - - - - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportToken/ImportTokenMod.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportToken/ImportTokenMod.tsx deleted file mode 100644 index f752e237ea..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportToken/ImportTokenMod.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { useTheme } from '@cowprotocol/common-hooks' -import { ButtonPrimary } from '@cowprotocol/ui' -import { RowBetween } from '@cowprotocol/ui' -import { Currency, Token } from '@uniswap/sdk-core' -import { TokenList } from '@uniswap/token-lists' - -import { Plural, Trans } from '@lingui/macro' -import { transparentize } from 'polished' -import { AlertCircle, ArrowLeft } from 'react-feather' -import styled from 'styled-components/macro' - -import Card from 'legacy/components/Card' -import { AutoColumn } from 'legacy/components/Column' -import { PaddedColumn } from 'legacy/components/SearchModal/styleds' -import TokenImportCard from 'legacy/components/SearchModal/TokenImportCard' -import { SectionBreak } from 'legacy/components/swap/styleds' -import { useAddUserToken } from 'legacy/state/user/hooks' -import { CloseIcon, ThemedText } from 'legacy/theme' - -import { CardComponentProps } from './index' - -const Wrapper = styled.div` - position: relative; - width: 100%; - overflow: auto; -` - -export const WarningWrapper = styled(Card)<{ highWarning: boolean }>` - background-color: ${({ theme, highWarning }) => - highWarning ? transparentize(0.8, theme.red1) : transparentize(0.8, theme.yellow2)}; - width: fit-content; -` - -export const AddressText = styled(ThemedText.Blue)` - font-size: 12px; - word-break: break-all; - ${({ theme }) => theme.mediaWidth.upToSmall` - font-size: 10px; -`} -` - -export interface ImportProps { - tokens: Token[] - list?: TokenList - onBack?: () => void - onDismiss?: () => void - handleCurrencySelect?: (currency: Currency) => void - CardComponent: (props: CardComponentProps) => JSX.Element // mod -} - -export function ImportToken(props: ImportProps) { - const { tokens, list, onBack, onDismiss, handleCurrencySelect } = props - const theme = useTheme() - - const addToken = useAddUserToken() - - return ( - - - - {onBack ? :
} - - - - {onDismiss ? :
} - - - - - - - - - This token doesn't appear on the active token list(s). Make sure this is the token that you want to - trade. - - - - {tokens.map((token) => ( - - ))} - <> - { - tokens.map((token) => addToken(token)) - handleCurrencySelect && handleCurrencySelect(tokens[0]) - }} - className=".token-dismiss-button" - > - Import - - - - - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportToken/index.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportToken/index.tsx deleted file mode 100644 index c06016c36d..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/ImportToken/index.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { getEtherscanLink as getExplorerLink } from '@cowprotocol/common-utils' -import { RowFixed, ExternalLink } from '@cowprotocol/ui' -import { Token } from '@uniswap/sdk-core' - -import { Trans } from '@lingui/macro' -import { AlertCircle } from 'react-feather' -import { DefaultTheme } from 'styled-components/macro' -import styled from 'styled-components/macro' - -import Card from 'legacy/components/Card' -import { AutoColumn } from 'legacy/components/Column' -import ListLogo from 'legacy/components/ListLogo' -import { PaddedColumn } from 'legacy/components/SearchModal/styleds' -import { ThemedText } from 'legacy/theme' - -import { UI } from 'common/constants/theme' -import { CurrencyLogo } from 'common/pure/CurrencyLogo' - -import { AddressText, ImportProps, ImportToken as ImportTokenMod, WarningWrapper } from './ImportTokenMod' - -export interface CardComponentProps extends Pick { - chainId?: number - theme: DefaultTheme - token: Token - key: string -} - -const Wrapper = styled.div` - width: 100%; - - > div { - height: 100%; - } - - ${({ theme }) => theme.mediaWidth.upToSmall` - overflow-y: scroll; - `} - - ${AutoColumn} > ${AutoColumn} > svg { - stroke: ${({ theme }) => theme.red1}; - } - - ${RowFixed} > svg { - stroke: ${({ theme }) => theme.red1}; - } - - ${PaddedColumn} { - ${({ theme }) => theme.mediaWidth.upToSmall` - position: sticky; - top: 0; - background: var(${UI.COLOR_CONTAINER_BG_01}); - `} - } -` - -function CardComponent({ theme, key, token, chainId, list }: CardComponentProps) { - return ( - - - - - - - {token.symbol} - - - {token.name} - - - {chainId && ( - - {token.address} - - )} - {list !== undefined ? ( - - {list.logoURI && } - - via {list.name} token list - - - ) : ( - - - - - Unknown Source - - - - )} - - - ) -} - -export function ImportToken(props: Omit) { - return ( - - {' '} - - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/Manage/ManageMod.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/Manage/ManageMod.tsx deleted file mode 100644 index 1a186824d8..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/Manage/ManageMod.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useState } from 'react' - -import { RowBetween } from '@cowprotocol/ui' - -import { Trans } from '@lingui/macro' -import { ArrowLeft } from 'react-feather' -import { Text } from 'rebass' - -import { CurrencyModalView } from 'legacy/components/SearchModal/CurrencySearchModal' -import { ManageLists } from 'legacy/components/SearchModal/ManageLists' -import ManageTokens from 'legacy/components/SearchModal/ManageTokens' -import { PaddedColumn, Separator } from 'legacy/components/SearchModal/styleds' -import { CloseIcon } from 'legacy/theme' - -import { ManageProps, Wrapper } from './index' - -export default function Manage({ - onDismiss, - setModalView, - setImportList, - setImportToken, - setListUrl, - ToggleOption, - ToggleWrapper, -}: ManageProps) { - // toggle between tokens and lists - const [showLists, setShowLists] = useState(true) - - return ( - - - - setModalView(CurrencyModalView.search)} /> - - Manage - - - - - - - - setShowLists((state) => !state)} active={showLists}> - Lists - - setShowLists((state) => !state)} active={!showLists}> - Tokens - - - - {showLists ? ( - - ) : ( - - )} - - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/Manage/index.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/Manage/index.tsx deleted file mode 100644 index 1c757d948d..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/Manage/index.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { RowBetween } from '@cowprotocol/ui' -import { Token } from '@uniswap/sdk-core' -import { TokenList } from '@uniswap/token-lists' - -import { transparentize } from 'polished' -import styled from 'styled-components/macro' - -import { CurrencyModalView } from 'legacy/components/SearchModal/CurrencySearchModal' -import { SearchInput, Separator } from 'legacy/components/SearchModal/styleds' - -import { UI } from 'common/constants/theme' - -import ManageMod from './ManageMod' - -export const Wrapper = styled.div` - width: 100%; - position: relative; - padding-bottom: 80px; - overflow-y: hidden; - - ${SearchInput} { - border: none; - transition: background 0.3s ease-in-out; - background: var(${UI.COLOR_GREY}); - } - - ${SearchInput}::placeholder { - font-size: 16px; - color: ${({ theme }) => transparentize(0.2, theme.text1)}; - } - - ${SearchInput}:focus::placeholder { - color: ${({ theme }) => transparentize(0.9, theme.text1)}; - } - - ${Separator} { - background: none; - } -` - -const ToggleWrapper = styled(RowBetween)` - border-radius: 12px; - padding: 6px; -` - -const ToggleOption = styled.div<{ active?: boolean }>` - width: 48%; - padding: 10px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 12px; - font-weight: 600; - background-color: ${({ theme, active }) => (active ? theme.bg1 : theme.bg1)}; - color: ${({ theme, active }) => (active ? `var(${UI.COLOR_TEXT1})` : theme.disabled)}; - user-select: none; - - :hover { - cursor: pointer; - opacity: 0.9; - } -` - -export interface ManageProps { - onDismiss: () => void - setModalView: (view: CurrencyModalView) => void - setImportToken: (token: Token) => void - setImportList: (list: TokenList) => void - setListUrl: (url: string) => void - ToggleOption: typeof ToggleOption - ToggleWrapper: typeof ToggleWrapper -} - -export default function Manage(props: Omit) { - return -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/ManageLists/ManageListsMod.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/ManageLists/ManageListsMod.tsx deleted file mode 100644 index 7f4cf67124..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/ManageLists/ManageListsMod.tsx +++ /dev/null @@ -1,424 +0,0 @@ -import { ChangeEvent, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' - -import { updateListAnalytics, removeListAnalytics, toggleListAnalytics } from '@cowprotocol/analytics' -import { useListColor, useTheme, useToggle, useOnClickOutside } from '@cowprotocol/common-hooks' -import { parseENSAddress, uriToHttp } from '@cowprotocol/common-utils' -import { Row, RowBetween, RowFixed, ButtonEmpty, ButtonPrimary, ExternalLink } from '@cowprotocol/ui' -import { useWalletInfo } from '@cowprotocol/wallet' -import { TokenList, Version } from '@uniswap/token-lists' - -import { t, Trans } from '@lingui/macro' -import { CheckCircle, Settings } from 'react-feather' -import { usePopper } from 'react-popper' -import styled from 'styled-components/macro' - -import Column, { AutoColumn } from 'legacy/components/Column' -import ListLogo from 'legacy/components/ListLogo' -import { CurrencyModalView } from 'legacy/components/SearchModal/CurrencySearchModal' -import { PaddedColumn, SearchInput, Separator, SeparatorDark } from 'legacy/components/SearchModal/styleds' -import { Toggle } from 'legacy/components/Toggle' -import { useFetchListCallback } from 'legacy/hooks/useFetchListCallback' -import { useAppDispatch, useAppSelector } from 'legacy/state/hooks' -import { useActiveListUrls, useAllLists, useIsListActive } from 'legacy/state/lists/hooks' -import { IconWrapper, LinkStyledButton, ThemedText } from 'legacy/theme' - -import { UI } from 'common/constants/theme' -import { useConfirmationRequest } from 'common/hooks/useConfirmationRequest' - -import { ListRowProps, RowWrapper, Card } from './index' - -function listVersionLabel(version: Version): string { - return `v${version.major}.${version.minor}.${version.patch}` -} - -const Wrapper = styled(Column)` - width: 100%; - height: 100%; -` - -const UnpaddedLinkStyledButton = styled(LinkStyledButton)` - padding: 0; - font-size: 1rem; - opacity: ${({ disabled }) => (disabled ? '0.4' : '1')}; -` - -export const PopoverContainer = styled.div<{ show: boolean }>` - z-index: 100; - visibility: ${(props) => (props.show ? 'visible' : 'hidden')}; - opacity: ${(props) => (props.show ? 1 : 0)}; - transition: visibility 150ms linear, opacity 150ms linear; - background: ${({ theme }) => theme.bg2}; - border: 1px solid ${({ theme }) => theme.bg3}; - box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), - 0px 24px 32px rgba(0, 0, 0, 0.01); - color: var(${UI.COLOR_TEXT2}); - border-radius: 0.5rem; - padding: 1rem; - display: grid; - grid-template-rows: 1fr; - grid-gap: 8px; - font-size: 1rem; - text-align: left; -` - -const StyledMenu = styled.div` - display: flex; - justify-content: center; - align-items: center; - position: relative; - border: none; -` - -const StyledTitleText = styled.div<{ active: boolean }>` - font-size: 16px; - overflow: hidden; - text-overflow: ellipsis; - font-weight: 600; - color: ${({ theme, active }) => (active ? theme.white : theme.text2)}; -` - -const StyledListUrlText = styled(ThemedText.Main)<{ active: boolean }>` - font-size: 12px; - color: ${({ theme, active }) => (active ? theme.white : theme.text2)}; -` - -function listUrlRowHTMLId(listUrl: string) { - return `list-row-${listUrl.replace(/\./g, '-')}` -} - -const ListRow = memo(function ListRow({ - listUrl, - acceptListUpdate, - removeList, - enableList, - disableList, -}: ListRowProps & { listUrl: string }) { - // We default to a chainId if none is available - const { chainId } = useWalletInfo() - - const listsByUrl = useAppSelector((state) => state.lists[chainId].byUrl) - const dispatch = useAppDispatch() - const { current: list, pendingUpdate: pending } = listsByUrl[listUrl] - - const activeTokensOnThisChain = useMemo(() => { - if (!list || !chainId) { - return 0 - } - return list.tokens.reduce((acc, cur) => (cur.chainId === chainId ? acc + 1 : acc), 0) - }, [chainId, list]) - - const theme = useTheme() - const listColor = useListColor(list?.logoURI) - const isActive = useIsListActive(listUrl) - - const [open, toggle] = useToggle(false) - const node = useRef() - const [referenceElement, setReferenceElement] = useState() - const [popperElement, setPopperElement] = useState() - const removeListAnalyticsOnConfirm = useCallback(() => { - removeListAnalytics('Confirm', listUrl) - dispatch(removeList(listUrl)) - }, [listUrl, dispatch, removeList]) - const removeListAnalyticsWithConfirm = useConfirmationRequest({ - onEnable: removeListAnalyticsOnConfirm, - }) - - const { styles, attributes } = usePopper(referenceElement, popperElement, { - placement: 'auto', - strategy: 'fixed', - modifiers: [{ name: 'offset', options: { offset: [8, 8] } }], - }) - - useOnClickOutside(node, open ? toggle : undefined) - - const handleAcceptListUpdate = useCallback(() => { - if (!pending) return - updateListAnalytics('List Select', listUrl) - dispatch(acceptListUpdate(listUrl)) - }, [acceptListUpdate, dispatch, listUrl, pending]) - - const handleRemoveList = useCallback(() => { - removeListAnalytics('Start', listUrl) - removeListAnalyticsWithConfirm({ - title: t`Remove this list?`, - description: t`This action will remove from your lists: ${list?.name ?? 'unknown'}`, - confirmWord: t`remove`, - callToAction: t`Remove list`, - warning: t`Are you sure? You will need to add this list by its url, if you would like to restore it later on.`, - action: t`remove list`, - }) - // }, [dispatch, listUrl]) - }, [listUrl, list, removeListAnalyticsWithConfirm]) - const handleEnableList = useCallback(() => { - toggleListAnalytics(true, listUrl) - dispatch(enableList(listUrl)) - }, [dispatch, enableList, listUrl]) - - const handleDisableList = useCallback(() => { - toggleListAnalytics(false, listUrl) - dispatch(disableList(listUrl)) - }, [disableList, dispatch, listUrl]) - - if (!list) return null - - return ( - 0} - bgColor={listColor} - key={listUrl} - id={listUrlRowHTMLId(listUrl)} - > - {list.logoURI ? ( - - ) : ( -
- )} - - - {list.name} - - - - {activeTokensOnThisChain} tokens - - - - - - {open && ( - -
{list && listVersionLabel(list.version)}
- - - View list - - - Remove list - - {pending && ( - - Update list - - )} -
- )} -
-
-
- { - isActive ? handleDisableList() : handleEnableList() - }} - /> - - ) -}) - -export const ListContainer = styled.div` - height: 100%; - overflow-y: auto; // fallback for 'overlay' - overflow-y: overlay; - ${({ theme }) => theme.colorScrollbar}; -` - -export function ManageLists({ - setModalView, - setImportList, - setListUrl, - // MOD - unsupportedListUrls, - listRowProps, -}: { - setModalView: (view: CurrencyModalView) => void - setImportList: (list: TokenList) => void - setListUrl: (url: string) => void - unsupportedListUrls: string[] - listRowProps: ListRowProps -}) { - const { chainId } = useWalletInfo() - const theme = useTheme() - - const [listUrlInput, setListUrlInput] = useState('') - - const lists = useAllLists() - - const tokenCountByListName = useMemo>( - () => - Object.values(lists).reduce((acc, { current: list }) => { - if (!list) { - return acc - } - return { - ...acc, - [list.name]: list.tokens.reduce((count: number, token) => (token.chainId === chainId ? count + 1 : count), 0), - } - }, {}), - [chainId, lists] - ) - - // sort by active but only if not visible - const activeListUrls = useActiveListUrls() - - const handleInput = useCallback((e: ChangeEvent) => { - setListUrlInput(e.target.value) - }, []) - - const fetchList = useFetchListCallback() - - const validUrl: boolean = useMemo(() => { - return uriToHttp(listUrlInput).length > 0 || Boolean(parseENSAddress(listUrlInput)) - }, [listUrlInput]) - - // Mod: Sort only on initial component load to avoid jumping UI issues - // Next time the component is loaded, the lists will be sorted - const sortedLists = useMemo(() => { - const listsUrls = Object.keys(lists) - - return listsUrls.sort((listUrlA, listUrlB) => { - const { current: listA } = lists[listUrlA] - const { current: listB } = lists[listUrlB] - - // first filter on active lists - if (activeListUrls?.includes(listUrlA) && !activeListUrls?.includes(listUrlB)) { - return -1 - } - if (!activeListUrls?.includes(listUrlA) && activeListUrls?.includes(listUrlB)) { - return 1 - } - - if (listA && listB) { - if (tokenCountByListName[listA.name] > tokenCountByListName[listB.name]) { - return -1 - } - if (tokenCountByListName[listA.name] < tokenCountByListName[listB.name]) { - return 1 - } - return listA.name.toLowerCase() < listB.name.toLowerCase() - ? -1 - : listA.name.toLowerCase() === listB.name.toLowerCase() - ? 0 - : 1 - } - if (listA) return -1 - if (listB) return 1 - return 0 - }) - // This is fine to have empty dependencies here - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) - - const filteredLists = useMemo( - () => { - return sortedLists.filter((listUrl) => { - // only show loaded lists, hide unsupported lists - // return Boolean(lists[listUrl].current) && !Boolean(UNSUPPORTED_LIST_URLS.includes(listUrl)) - return Boolean(lists[listUrl]?.current) && !unsupportedListUrls.includes(listUrl) - }) - }, - // [lists, activeListUrls, tokenCountByListName] - [sortedLists, lists, unsupportedListUrls] - ) - - // temporary fetched list for import flow - const [tempList, setTempList] = useState() - const [addError, setAddError] = useState() - - useEffect(() => { - async function fetchTempList() { - fetchList(listUrlInput, false) - .then((list) => setTempList(list)) - .catch(() => setAddError(t`Error importing list`)) - } - // if valid url, fetch details for card - if (validUrl) { - fetchTempList() - } else { - setTempList(undefined) - listUrlInput !== '' && setAddError(t`Enter valid list location`) - } - - // reset error - if (listUrlInput === '') { - setAddError(undefined) - } - }, [fetchList, listUrlInput, validUrl]) - - // check if list is already imported - const isImported = Object.keys(lists).includes(listUrlInput) - - // set list values and have parent modal switch to import list view - const handleImport = useCallback(() => { - if (!tempList) return - setImportList(tempList) - setModalView(CurrencyModalView.importList) - setListUrl(listUrlInput) - }, [listUrlInput, setImportList, setListUrl, setModalView, tempList]) - - return ( - - - - - - {addError ? ( - - {addError} - - ) : null} - - {tempList && ( - - - - - {tempList.logoURI && } - - {tempList.name} - - {tempList.tokens.length} tokens - - - - {isImported ? ( - - - - - - Loaded - - - ) : ( - - Import - - )} - - - - )} - - - - {filteredLists.map((listUrl) => ( - - ))} - - - - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/ManageLists/index.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/ManageLists/index.tsx deleted file mode 100644 index b43c63e955..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/ManageLists/index.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { UNSUPPORTED_LIST_URLS } from '@cowprotocol/common-const' -import { ButtonPrimary } from '@cowprotocol/ui' -import { Row, RowFixed, RowBetween } from '@cowprotocol/ui' -import { useWalletInfo } from '@cowprotocol/wallet' -import { TokenList } from '@uniswap/token-lists' - -import { transparentize } from 'polished' -import styled from 'styled-components/macro' - -import CardUni from 'legacy/components/Card' -import { CurrencyModalView } from 'legacy/components/SearchModal/CurrencySearchModal' -import { TextDot } from 'legacy/components/SearchModal/styleds' -import { acceptListUpdate, removeList, disableList, enableList } from 'legacy/state/lists/actions' - -import { UI } from 'common/constants/theme' - -import { ManageLists as ManageListsMod, ListContainer, PopoverContainer } from './ManageListsMod' - -export interface ListRowProps { - acceptListUpdate: (url: string) => ReturnType - removeList: (url: string) => ReturnType - disableList: (url: string) => ReturnType - enableList: (url: string) => ReturnType -} - -const Wrapper = styled.div` - width: 100%; - height: 100%; - - ${ListContainer} { - border-top: 1px solid ${({ theme }) => theme.grey1}; - padding: 1rem; - padding-bottom: 80px; - - svg > path, - svg > circle { - stroke: var(${UI.COLOR_TEXT1}); - } - } - - ${PopoverContainer} { - background: var(${UI.COLOR_CONTAINER_BG_01}); - } -` - -export const RowWrapper = styled(Row)<{ bgColor: string; active: boolean; hasActiveTokens: boolean }>` - background-color: ${({ bgColor, active, theme }) => - active ? `${transparentize(0.75, bgColor)}` ?? 'transparent' : theme.bg1}; - opacity: ${({ hasActiveTokens }) => (hasActiveTokens ? 1 : 0.4)}; - transition: 0.2s; - align-items: center; - padding: 1rem; - border-radius: 20px; - border: 1px solid ${({ theme }) => theme.grey1}; - - ${Row}, ${RowFixed}, ${RowBetween} { - > div { - color: ${({ active, theme }) => (active ? `var(${UI.COLOR_TEXT1})` : theme.text2)}; - } - } -` - -export const Card = styled(CardUni)` - background: var(${UI.COLOR_CONTAINER_BG_01}); - - ${Row}, - ${Row} > div > div, - ${Row} > div > div > div { - color: var(${UI.COLOR_TEXT1}); - } - - ${Row} > img { - background: ${({ theme }) => theme.bg2}; - border-radius: 40px; - padding: 3px; - object-fit: contain; - } - - ${TextDot} { - background: var(${UI.COLOR_TEXT1}); - } - - ${ButtonPrimary} { - min-height: auto; - padding: 8px 12px; - } -` - -export const ManageLists = (props: { - setModalView: (view: CurrencyModalView) => void - setImportList: (list: TokenList) => void - setListUrl: (url: string) => void -}) => { - const { chainId } = useWalletInfo() - - const listRowProps = { - acceptListUpdate: (url: string) => acceptListUpdate({ url, chainId }), - removeList: (url: string) => removeList({ url, chainId }), - disableList: (url: string) => disableList({ url, chainId }), - enableList: (url: string) => enableList({ url, chainId }), - } - return ( - - - - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/ManageTokens/ManageTokensMod.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/ManageTokens/ManageTokensMod.tsx deleted file mode 100644 index 5c8af0a5de..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/ManageTokens/ManageTokensMod.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import { ChangeEventHandler, RefObject, useCallback, useMemo, useRef, useState } from 'react' - -import { useNetworkName, useTheme } from '@cowprotocol/common-hooks' -import { getEtherscanLink as getExplorerLink, isAddress } from '@cowprotocol/common-utils' -import { TokenSymbol, Row, RowBetween, RowFixed } from '@cowprotocol/ui' -import { ExternalLink, ExternalLinkIcon } from '@cowprotocol/ui' -import { useWalletInfo } from '@cowprotocol/wallet' -import { Token } from '@uniswap/sdk-core' - -import { Trans } from '@lingui/macro' -import styled from 'styled-components/macro' - -import Column from 'legacy/components/Column' -import { CurrencyModalView } from 'legacy/components/SearchModal/CurrencySearchModal' -import { PaddedColumn, SearchInput, Separator } from 'legacy/components/SearchModal/styleds' -import { useToken } from 'legacy/hooks/Tokens' -import { useRemoveUserAddedToken, useUserAddedTokens } from 'legacy/state/user/hooks' -import { ButtonText, ThemedText, TrashIcon } from 'legacy/theme' - -import { CurrencyLogo } from 'common/pure/CurrencyLogo' - -import { ImportTokensRowProps } from './index' - -const Wrapper = styled.div` - width: 100%; - height: calc(100% - 60px); - position: relative; - padding-bottom: 80px; -` - -export const Footer = styled.div` - position: absolute; - bottom: 0; - width: 100%; - border-radius: 20px; - border-top-right-radius: 0; - border-top-left-radius: 0; - border-top: 1px solid ${({ theme }) => theme.bg3}; - padding: 20px; - text-align: center; -` - -export interface ManageTokensProps { - setModalView: (view: CurrencyModalView) => void - setImportToken: (token: Token) => void - ImportTokensRow: ({ theme, searchToken, setModalView, setImportToken }: ImportTokensRowProps) => JSX.Element -} - -export default function ManageTokens({ setModalView, setImportToken, ImportTokensRow }: ManageTokensProps) { - const { chainId } = useWalletInfo() - - const [searchQuery, setSearchQuery] = useState('') - const theme = useTheme() - - const network = useNetworkName() - - // manage focus on modal show - const inputRef = useRef() - const handleInput: ChangeEventHandler = useCallback((event) => { - const input = event.target.value - const checksummedInput = isAddress(input) - setSearchQuery(checksummedInput || input) - }, []) - - // if they input an address, use it - const isAddressSearch = isAddress(searchQuery) - const searchToken = useToken(searchQuery) - - // all tokens for local lisr - const userAddedTokens: Token[] = useUserAddedTokens() - const removeToken = useRemoveUserAddedToken() - - const handleRemoveAll = useCallback(() => { - if (chainId && userAddedTokens) { - userAddedTokens.map((token) => { - return removeToken(chainId, token.address) - }) - } - }, [removeToken, userAddedTokens, chainId]) - - const tokenList = useMemo(() => { - return ( - chainId && - userAddedTokens.map((token) => ( - - - - - - {/* MOD */} - - - - - removeToken(chainId, token.address)} /> - - - - )) - ) - }, [userAddedTokens, chainId, removeToken]) - - return ( - - - - - } - onChange={handleInput} - /> - - {searchQuery !== '' && !isAddressSearch && ( - - Enter valid token address - - )} - {searchQuery !== '' && isAddressSearch && !searchToken && ( - - No tokens found with this address in {network} network - - )} - {searchToken && ( // MOD - - )} - - - - - - {userAddedTokens?.length} Custom Tokens - - {userAddedTokens.length > 0 && ( - - - Clear all - - - )} - - {tokenList} - - -
- - Tip: Custom tokens are stored locally in your browser - -
-
- ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/ManageTokens/index.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/ManageTokens/index.tsx deleted file mode 100644 index 25b2642d96..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/ManageTokens/index.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { Row, RowFixed, RowBetween } from '@cowprotocol/ui' -import { LinkIcon } from '@cowprotocol/ui' -import { Token } from '@uniswap/sdk-core' - -import styled, { DefaultTheme } from 'styled-components/macro' - -import Card from 'legacy/components/Card' -import Column from 'legacy/components/Column' -import { CurrencyModalView } from 'legacy/components/SearchModal/CurrencySearchModal' -import ImportRow from 'legacy/components/SearchModal/ImportRow' -import { Separator } from 'legacy/components/SearchModal/styleds' -import { ButtonText } from 'legacy/theme' - -import { UI } from 'common/constants/theme' - -import ManageTokensMod, { ManageTokensProps, Footer } from './ManageTokensMod' - -export type ImportTokensRowProps = Omit & { - theme: DefaultTheme - searchToken: Token -} - -export const Wrapper = styled.div` - width: 100%; - height: 100%; - position: relative; - - > div { - padding: 0 0 40px; - } - - ${Row} > div { - margin: 0; - } - - ${RowBetween} > div { - color: var(${UI.COLOR_TEXT1}); - } - - // Custom Tokens Title - ${RowBetween}:first-child > div { - opacity: 0.7; - } - - // Token name - ${RowBetween} > div > a > div { - color: var(${UI.COLOR_TEXT1}); - } - - ${RowFixed} > svg, - ${RowFixed} > a { - transition: stroke 0.2s ease-in-out; - } - - ${RowFixed} > svg:hover, - ${RowFixed} > a, - ${RowFixed} > a:hover > svg { - stroke: ${({ theme }) => theme.text3}; - } - - ${LinkIcon} { - stroke: ${({ theme }) => theme.text3}; - } - - // 'Clear all' button - ${ButtonText} > div { - color: ${({ theme }) => theme.text3}; - } - - ${Column} > ${Separator} + div { - height: 100%; - grid-auto-rows: min-content; - border-top: 1px solid ${({ theme }) => theme.grey1}; - } - - ${Footer} { - display: flex; - justify-content: center; - align-items: center; - font-size: 13px; - padding: 0; - height: 50px; - } -` - -const ImportTokensRow = ({ theme, searchToken, setModalView, setImportToken }: ImportTokensRowProps) => ( - - setModalView(CurrencyModalView.importToken)} - setImportToken={setImportToken} - style={{ height: 'fit-content' }} - /> - -) - -export default function ManageTokens(props: Omit) { - return ( - - - - ) -} diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/TokenImportCard/TokenImportCardMod.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/TokenImportCard/TokenImportCardMod.tsx deleted file mode 100644 index e382e4e1da..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/TokenImportCard/TokenImportCardMod.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { getEtherscanLink as getExplorerLink } from '@cowprotocol/common-utils' -import { RowFixed } from '@cowprotocol/ui' -import { ExternalLink } from '@cowprotocol/ui' -import { useWalletInfo } from '@cowprotocol/wallet' -import { Token } from '@uniswap/sdk-core' -import { TokenList } from '@uniswap/token-lists' - -import { Trans } from '@lingui/macro' -import { transparentize } from 'polished' -import { AlertCircle } from 'react-feather' -import styled, { useTheme } from 'styled-components/macro' - -import Card from 'legacy/components/Card' -import { AutoColumn } from 'legacy/components/Column' -import ListLogo from 'legacy/components/ListLogo' -import { ThemedText } from 'legacy/theme' - -import { CurrencyLogo } from 'common/pure/CurrencyLogo' - -const WarningWrapper = styled(Card)<{ highWarning: boolean }>` - background-color: ${({ theme, highWarning }) => - highWarning ? transparentize(0.8, theme.red1) : transparentize(0.8, theme.yellow2)}; - width: fit-content; -` - -const AddressText = styled(ThemedText.Blue)` - font-size: 12px; - word-break: break-all; - - ${({ theme }) => theme.mediaWidth.upToSmall` - font-size: 10px; - `} -` -interface TokenImportCardProps { - list?: TokenList - token: Token -} -const TokenImportCard = ({ list, token }: TokenImportCardProps) => { - const theme = useTheme() - const { chainId } = useWalletInfo() - return ( - - - - - - {token.symbol} - - - {token.name} - - - {chainId && ( - - {token.address} - - )} - {list !== undefined ? ( - - {list.logoURI && } - - via {list.name} token list - - - ) : ( - - - - - Unknown Source - - - - )} - - - ) -} - -export default TokenImportCard diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/TokenImportCard/index.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/TokenImportCard/index.tsx deleted file mode 100644 index 00e7de65fd..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/TokenImportCard/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from './TokenImportCardMod' -export * from './TokenImportCardMod' diff --git a/apps/cowswap-frontend/src/legacy/components/SearchModal/styleds.tsx b/apps/cowswap-frontend/src/legacy/components/SearchModal/styleds.tsx deleted file mode 100644 index 652b80ed9c..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/SearchModal/styleds.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { LoadingRows as BaseLoadingRows, RowBetween } from '@cowprotocol/ui' - -import styled from 'styled-components/macro' - -import { UI } from 'common/constants/theme' - -import { AutoColumn } from '../Column' - -export const TextDot = styled.div` - height: 3px; - width: 3px; - background-color: var(${UI.COLOR_TEXT2}); - border-radius: 50%; -` - -export const Checkbox = styled.input` - border: 1px solid ${({ theme }) => theme.red3}; - height: 20px; - margin: 0; -` - -export const PaddedColumn = styled(AutoColumn)` - padding: 20px; -` - -export const PaddedRow = styled(RowBetween)` - padding: 20px; - padding-bottom: 0; -` - -export const MenuItem = styled(RowBetween)` - padding: 4px 20px; - height: 56px; - display: grid; - grid-template-columns: auto minmax(auto, 1fr) auto minmax(0, 72px); - grid-gap: 16px; - cursor: ${({ disabled }) => !disabled && 'pointer'}; - pointer-events: ${({ disabled }) => disabled && 'none'}; - :hover { - background-color: ${({ theme, disabled }) => !disabled && theme.bg2}; - } - opacity: ${({ disabled, selected }) => (disabled || selected ? 0.5 : 1)}; -` - -export const SearchInput = styled.input` - position: relative; - display: flex; - padding: 16px; - align-items: center; - width: 100%; - white-space: nowrap; - background: none; - border: none; - outline: none; - border-radius: 20px; - color: var(${UI.COLOR_TEXT1}); - border-style: solid; - border: 1px solid ${({ theme }) => theme.bg3}; - -webkit-appearance: none; - - font-size: 18px; - - ::placeholder { - color: ${({ theme }) => theme.text3}; - } - transition: border 100ms; - :focus { - border: 1px solid ${({ theme }) => theme.primary1}; - outline: none; - } -` -export const Separator = styled.div` - width: 100%; - height: 1px; - background-color: ${({ theme }) => theme.bg2}; -` - -export const SeparatorDark = styled.div` - width: 100%; - height: 1px; - background-color: ${({ theme }) => theme.bg3}; -` - -export const LoadingRows = styled(BaseLoadingRows)` - grid-column-gap: 0.5em; - grid-template-columns: repeat(12, 1fr); - max-width: 960px; - padding: 12px 20px; - - & > div:nth-child(4n + 1) { - grid-column: 1 / 8; - height: 1em; - margin-bottom: 0.25em; - } - & > div:nth-child(4n + 2) { - grid-column: 12; - height: 1em; - margin-top: 0.25em; - } - & > div:nth-child(4n + 3) { - grid-column: 1 / 4; - height: 0.75em; - } -` diff --git a/apps/cowswap-frontend/src/legacy/components/TokenWarningModal/index.tsx b/apps/cowswap-frontend/src/legacy/components/TokenWarningModal/index.tsx deleted file mode 100644 index acbc069526..0000000000 --- a/apps/cowswap-frontend/src/legacy/components/TokenWarningModal/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Token } from '@uniswap/sdk-core' - -import { ImportToken } from 'legacy/components/SearchModal/ImportToken' - -import { Modal } from 'common/pure/Modal' - -export default function TokenWarningModal({ - isOpen, - tokens, - onConfirm, - onDismiss, -}: { - isOpen: boolean - tokens: Token[] - onConfirm: () => void - onDismiss: () => void -}) { - return ( - - - - ) -} diff --git a/apps/cowswap-frontend/src/legacy/hooks/Tokens.ts b/apps/cowswap-frontend/src/legacy/hooks/Tokens.ts deleted file mode 100644 index 4026518ad4..0000000000 --- a/apps/cowswap-frontend/src/legacy/hooks/Tokens.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { useAtomValue } from 'jotai' -import { useMemo } from 'react' - -import { doesTokenMatchSymbolOrAddress } from '@cowprotocol/common-utils' -import { useWalletInfo } from '@cowprotocol/wallet' -import { Currency, Token } from '@uniswap/sdk-core' - -import { useAllLists, useInactiveListUrls } from 'legacy/state/lists/hooks' -import { deserializeToken, useFavouriteTokens, useUserAddedTokens } from 'legacy/state/user/hooks' - -import { TokensByAddress, tokensByAddressAtom } from 'modules/tokensList/state/tokensListAtom' - -import { getTokenFilter } from 'lib/hooks/useTokenList/filtering' - -import { useCurrencyFromMap, useTokenFromMapOrNetwork } from '../../lib/hooks/useCurrency' -import { TokenAmounts, useOnchainBalances } from '../../modules/tokens' - -export function useAllTokens(): TokensByAddress { - return useAtomValue(tokensByAddressAtom) -} - -export function useSearchInactiveTokenLists( - search: string | undefined, - minResults = 10, - strictSearch = false -): Token[] { - const lists = useAllLists() - const inactiveUrls = useInactiveListUrls() - const { chainId } = useWalletInfo() - const activeTokens = useAllTokens() - - return useMemo(() => { - if (!search || search.trim().length === 0) return [] - const tokenFilter = getTokenFilter(search) - const result: Token[] = [] - const addressSet: { [address: string]: true } = {} - - for (const url of inactiveUrls) { - const list = lists[url].current - if (!list) continue - - for (const tokenInfo of list.tokens) { - const isTokenMatched = strictSearch ? doesTokenMatchSymbolOrAddress(tokenInfo, search) : tokenFilter(tokenInfo) - - if (tokenInfo.chainId === chainId && isTokenMatched) { - try { - const tokenAddress = tokenInfo.address.toLowerCase() - - if (!(tokenInfo.address in activeTokens) && !addressSet[tokenAddress]) { - addressSet[tokenAddress] = true - result.push(deserializeToken(tokenInfo)) - if (result.length >= minResults) return result - } - } catch { - continue - } - } - } - } - return result - }, [activeTokens, chainId, inactiveUrls, lists, minResults, search, strictSearch]) -} - -export function useIsTokenActive(token: Token | undefined | null): boolean { - const activeTokens = useAllTokens() - - if (!activeTokens || !token) { - return false - } - - return !!activeTokens[token.address] -} - -// Check if currency is included in custom list from user storage -export function useIsUserAddedToken(currency: Currency | undefined | null): boolean { - const userAddedTokens = useUserAddedTokens() - - if (!currency) { - return false - } - - return !!userAddedTokens.find((token) => currency.equals(token)) -} - -// undefined if invalid or does not exist -// null if loading or null was passed -// otherwise returns the token -export function useToken(tokenAddress?: string | null): Token | null | undefined { - const tokens = useAllTokens() - return useTokenFromMapOrNetwork(tokens, tokenAddress) -} - -export function useCurrency(currencyId?: string | null): Currency | null | undefined { - const tokens = useAllTokens() - return useCurrencyFromMap(tokens, currencyId) -} - -// mimics useAllBalances -export function useAllTokenBalances(): [TokenAmounts, boolean] { - const { account } = useWalletInfo() - const allTokens = useAllTokens() - // Mod, add favourite tokens to balances - const favTokens = useFavouriteTokens() - - const allTokensArray = useMemo(() => { - const favTokensObj = favTokens.reduce( - (acc, cur: Token) => { - acc[cur.address] = cur - return acc - }, - {} as { - [address: string]: Token - } - ) - - return Object.values({ ...favTokensObj, ...allTokens }) - }, [allTokens, favTokens]) - - const { isLoading, amounts } = useOnchainBalances({ - account: account ?? undefined, - tokens: allTokensArray, - }) - return [amounts ?? {}, isLoading] -} diff --git a/apps/cowswap-frontend/src/legacy/hooks/useFavouriteOrCommonTokens.tsx b/apps/cowswap-frontend/src/legacy/hooks/useFavouriteOrCommonTokens.tsx deleted file mode 100644 index ed8f8f8f4d..0000000000 --- a/apps/cowswap-frontend/src/legacy/hooks/useFavouriteOrCommonTokens.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { useMemo } from 'react' - -import { COMMON_BASES } from '@cowprotocol/common-const' -import { useWalletInfo } from '@cowprotocol/wallet' - -import { useFavouriteTokens } from 'legacy/state/user/hooks' - -export function useFavouriteOrCommonTokens() { - const { chainId } = useWalletInfo() - - const favouriteTokens = useFavouriteTokens() - - return useMemo(() => { - const bases = typeof chainId !== 'undefined' ? COMMON_BASES[chainId] ?? [] : [] - return favouriteTokens.length > 0 ? favouriteTokens : bases - }, [chainId, favouriteTokens]) -} diff --git a/apps/cowswap-frontend/src/legacy/hooks/useFetchListCallback.ts b/apps/cowswap-frontend/src/legacy/hooks/useFetchListCallback.ts deleted file mode 100644 index 4246458c47..0000000000 --- a/apps/cowswap-frontend/src/legacy/hooks/useFetchListCallback.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { useAtomValue, useSetAtom } from 'jotai' -import { atomWithStorage } from 'jotai/utils' -import { useCallback } from 'react' - -import { MAINNET_PROVIDER } from '@cowprotocol/common-const' -import { atomWithPartialUpdate, resolveENSContentHash } from '@cowprotocol/common-utils' -import { useWalletInfo } from '@cowprotocol/wallet' -import { TokenList } from '@uniswap/token-lists' - -import { nanoid } from '@reduxjs/toolkit' -import ms from 'ms.macro' - -import { useAppDispatch } from 'legacy/state/hooks' -import { fetchTokenList } from 'legacy/state/lists/actions' - -import getTokenList from 'lib/hooks/useTokenList/fetchTokenList' - -const TOKENS_LIST_ENS_CACHE_TIMEOUT = ms`6h` - -interface TokensListEnsCache { - timestamp: number - value: string -} - -const { atom: tokensListEnsCachesAtom, updateAtom: updateTokensListEnsCachesAtom } = atomWithPartialUpdate( - atomWithStorage<{ [ensName: string]: TokensListEnsCache }>('tokensListEnsCaches:v1', {}) -) - -const isCacheValid = ({ timestamp }: TokensListEnsCache) => Date.now() - timestamp < TOKENS_LIST_ENS_CACHE_TIMEOUT - -export function useFetchListCallback(): (listUrl: string, sendDispatch?: boolean) => Promise { - const dispatch = useAppDispatch() - const { chainId } = useWalletInfo() - - const tokensListEnsCaches = useAtomValue(tokensListEnsCachesAtom) - const setTokensListEnsCaches = useSetAtom(updateTokensListEnsCachesAtom) - - // note: prevent dispatch if using for list search or unsupported list - return useCallback( - async (listUrl: string, sendDispatch = true) => { - const requestId = nanoid() - sendDispatch && dispatch(fetchTokenList.pending({ requestId, url: listUrl, chainId })) - return getTokenList(listUrl, (ensName: string) => { - const cached = tokensListEnsCaches[ensName] - - // Return cached value if it's not stale - if (cached && isCacheValid(cached)) { - return Promise.resolve(cached.value) - } - - return resolveENSContentHash(ensName, MAINNET_PROVIDER).then((value) => { - // Cache the fetched value - setTokensListEnsCaches({ [ensName]: { timestamp: Date.now(), value } }) - - return value - }) - }) - .then((tokenList) => { - // Mod: add chainId - sendDispatch && dispatch(fetchTokenList.fulfilled({ url: listUrl, tokenList, requestId, chainId })) - return tokenList - }) - .catch((error) => { - console.debug(`Failed to get list at url ${listUrl}`, error) - sendDispatch && - dispatch(fetchTokenList.rejected({ url: listUrl, requestId, errorMessage: error.message, chainId })) - throw error - }) - }, - [chainId, dispatch, setTokensListEnsCaches, tokensListEnsCaches] - ) -} diff --git a/apps/cowswap-frontend/src/legacy/hooks/useTotalSupply.ts b/apps/cowswap-frontend/src/legacy/hooks/useTotalSupply.ts deleted file mode 100644 index 87b1b9db60..0000000000 --- a/apps/cowswap-frontend/src/legacy/hooks/useTotalSupply.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { useMemo } from 'react' - -import { useTokenContract } from '@cowprotocol/common-hooks' -import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core' - -import { useSingleCallResult } from 'lib/hooks/multicall' - -// returns undefined if input token is undefined, or fails to get token contract, -// or contract total supply cannot be fetched -export function useTotalSupply(token?: Currency): CurrencyAmount | undefined { - const contract = useTokenContract(token?.isToken ? token.address : undefined, false) - - const totalSupplyStr: string | undefined = useSingleCallResult(contract, 'totalSupply')?.result?.[0]?.toString() - - return useMemo( - () => (token?.isToken && totalSupplyStr ? CurrencyAmount.fromRawAmount(token, totalSupplyStr) : undefined), - [token, totalSupplyStr] - ) -} diff --git a/apps/cowswap-frontend/src/legacy/state/lists/actions/actionsMod.ts b/apps/cowswap-frontend/src/legacy/state/lists/actions/actionsMod.ts deleted file mode 100644 index 473807f398..0000000000 --- a/apps/cowswap-frontend/src/legacy/state/lists/actions/actionsMod.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' -import { TokenList, Version } from '@uniswap/token-lists' - -import { ActionCreatorWithPayload, createAction } from '@reduxjs/toolkit' - -// MOD imports - -export interface WithChainId { - chainId?: ChainId -} - -interface WithChainIdAndUrl extends WithChainId { - url: string -} - -interface PendingFetchTokenList extends WithChainIdAndUrl { - requestId: string -} - -interface FulfilledFetchTokenList extends PendingFetchTokenList { - tokenList: TokenList -} - -interface RejectedFetchTokenList extends PendingFetchTokenList { - errorMessage: string -} - -export type RemoveGpUnsupportedTokenParams = WithChainId & { address: string } -export type AddGpUnsupportedTokenParams = RemoveGpUnsupportedTokenParams & { dateAdded: number } - -//MOD: adds chainId to param -export const fetchTokenList: Readonly<{ - pending: ActionCreatorWithPayload - fulfilled: ActionCreatorWithPayload - rejected: ActionCreatorWithPayload -}> = { - pending: createAction('lists/fetchTokenList/pending'), - fulfilled: createAction('lists/fetchTokenList/fulfilled'), - rejected: createAction('lists/fetchTokenList/rejected'), -} -// add and remove from list options -export const addList = createAction('lists/addList') -export const removeList = createAction('lists/removeList') - -// select which lists to search across from loaded lists -export const enableList = createAction('lists/enableList') -export const disableList = createAction('lists/disableList') - -// versioning -export const acceptListUpdate = createAction('lists/acceptListUpdate') -export const rejectVersionUpdate = createAction('lists/rejectVersionUpdate') - -// add/remove unsupported token for gp -export const addGpUnsupportedToken = createAction('lists/addGpUnsupportedToken') -export const removeGpUnsupportedToken = createAction('lists/removeGpUnsupportedToken') diff --git a/apps/cowswap-frontend/src/legacy/state/lists/actions/index.ts b/apps/cowswap-frontend/src/legacy/state/lists/actions/index.ts deleted file mode 100644 index 9ae8b8f15a..0000000000 --- a/apps/cowswap-frontend/src/legacy/state/lists/actions/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './actionsMod' diff --git a/apps/cowswap-frontend/src/legacy/state/lists/hooks.ts b/apps/cowswap-frontend/src/legacy/state/lists/hooks.ts deleted file mode 100644 index c9f96e4f02..0000000000 --- a/apps/cowswap-frontend/src/legacy/state/lists/hooks.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { useCallback, useMemo } from 'react' - -import { UNSUPPORTED_LIST_URLS } from '@cowprotocol/common-const' -import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' -import { useWalletInfo } from '@cowprotocol/wallet' -import DEFAULT_TOKEN_LIST from '@uniswap/default-token-list' -import { Currency } from '@uniswap/sdk-core' -import { TokenInfo } from '@uniswap/token-lists' - -import { shallowEqual } from 'react-redux' -import { Nullish } from 'types' - -import { UnsupportedToken } from 'api/gnosisProtocol' -import { ChainTokenMap, tokensToChainTokenMap } from 'lib/hooks/useTokenList/utils' - -import { - addGpUnsupportedToken, - AddGpUnsupportedTokenParams, - removeGpUnsupportedToken, - RemoveGpUnsupportedTokenParams, -} from './actions' - -import { useAppDispatch, useAppSelector } from '../hooks' -import { AppState } from '../index' - -export type TokenAddressMap = ChainTokenMap - -type Mutable = { - -readonly [P in keyof T]: Mutable -} - -export function useActiveListUrls(): string[] | undefined { - const { chainId } = useWalletInfo() - - return useAppSelector((state) => state.lists[chainId]?.activeListUrls, shallowEqual) -} - -export function useAllLists(): AppState['lists'][ChainId]['byUrl'] { - const { chainId } = useWalletInfo() - - return useAppSelector((state) => state.lists[chainId]?.byUrl, shallowEqual) -} - -/** - * Combine the tokens in map2 with the tokens on map1, where tokens on map1 take precedence - * @param map1 the base token map - * @param map2 the map of additioanl tokens to add to the base map - */ -export function combineMaps(map1: TokenAddressMap, map2: TokenAddressMap): TokenAddressMap { - const chainIds = Object.keys( - Object.keys(map1) - .concat(Object.keys(map2)) - .reduce<{ [chainId: string]: true }>((memo, value) => { - memo[value] = true - return memo - }, {}) - ).map((id) => parseInt(id)) - - return chainIds.reduce>((memo, chainId) => { - memo[chainId] = { - ...map2[chainId], - // map1 takes precedence - ...map1[chainId], - } - return memo - }, {}) as TokenAddressMap -} - -// merge tokens contained within lists from urls -export function useCombinedTokenMapFromUrls(urls: string[] | undefined): TokenAddressMap { - const lists = useAllLists() - return useMemo(() => { - if (!urls) return {} - return urls.slice().reduce((allTokens, currentUrl) => { - const current = lists[currentUrl]?.current - if (!current) return allTokens - try { - return combineMaps(allTokens, tokensToChainTokenMap(current)) - } catch (error: any) { - console.error('Could not show token list due to error', error) - return allTokens - } - }, {}) - }, [lists, urls]) -} - -// list of tokens not supported on interface for various reasons, used to show warnings and prevent swaps and adds -export function useUnsupportedTokenList(): TokenAddressMap { - return useCombinedTokenMapFromUrls(UNSUPPORTED_LIST_URLS[1]) -} - -export function useTokensListFromUrls(urls: string[] | undefined): TokenInfo[] { - const lists = useAllLists() - - return useMemo(() => { - if (!urls) return [] - - return urls - .slice() - .map((url) => { - return lists?.[url]?.current?.tokens || [] - }) - .flat() - }, [lists, urls]) -} - -export function useTokensListWithDefaults(): TokenInfo[] { - const { chainId } = useWalletInfo() - const activeListUrls = useActiveListUrls() - const allTokens = useTokensListFromUrls(activeListUrls) - const allUserAddedTokens = useAppSelector(({ user: { tokens } }) => tokens) - - return useMemo(() => { - if (!chainId) return [] - - const userAddedTokens = Object.values(allUserAddedTokens[chainId] || {}) as TokenInfo[] - const defaultTokens = DEFAULT_TOKEN_LIST.tokens - return allTokens - .concat(defaultTokens) - .concat(userAddedTokens) - .filter((token) => token.chainId === chainId) - }, [allTokens, chainId, allUserAddedTokens]) -} - -export function useIsListActive(url: string): boolean { - const activeListUrls = useActiveListUrls() - return Boolean(activeListUrls?.includes(url)) -} - -export function useGpUnsupportedTokens(): UnsupportedToken | null { - const { chainId } = useWalletInfo() - - return useAppSelector((state) => (chainId ? state.lists[chainId]?.gpUnsupportedTokens : null)) -} - -export function useAddGpUnsupportedToken() { - const dispatch = useAppDispatch() - - return useCallback((params: AddGpUnsupportedTokenParams) => dispatch(addGpUnsupportedToken(params)), [dispatch]) -} - -export function useRemoveGpUnsupportedToken() { - const dispatch = useAppDispatch() - - return useCallback((params: RemoveGpUnsupportedTokenParams) => dispatch(removeGpUnsupportedToken(params)), [dispatch]) -} - -export function useIsUnsupportedTokenGp() { - const { chainId } = useWalletInfo() - const gpUnsupportedTokens = useGpUnsupportedTokens() - - return useCallback( - (address?: string) => { - if (!address || !chainId || !gpUnsupportedTokens) return false - - return gpUnsupportedTokens[address.toLowerCase()] - }, - [chainId, gpUnsupportedTokens] - ) -} - -export function useIsUnsupportedTokens() { - const gpUnsupportedTokens = useGpUnsupportedTokens() - - return useCallback( - ({ sellToken, buyToken }: { sellToken: Nullish; buyToken: Nullish }) => { - if (!gpUnsupportedTokens) return false - - return !!( - (sellToken && gpUnsupportedTokens[sellToken.toLowerCase()]) || - (buyToken && gpUnsupportedTokens[buyToken.toLowerCase()]) - ) - }, - [gpUnsupportedTokens] - ) -} - -export function useIsTradeUnsupported( - inputCurrency: Currency | null | undefined, - outputCurrency: Currency | null | undefined -): boolean { - const isUnsupportedToken = useIsUnsupportedTokenGp() - const isInputCurrencyUnsupported = inputCurrency?.isNative ? false : !!isUnsupportedToken(inputCurrency?.address) - const isOutputCurrencyUnsupported = outputCurrency?.isNative ? false : !!isUnsupportedToken(outputCurrency?.address) - - return isInputCurrencyUnsupported || isOutputCurrencyUnsupported -} - -export function useInactiveListUrls(): string[] { - const { chainId } = useWalletInfo() - const lists = useAllLists() - const allActiveListUrls = useActiveListUrls() - return Object.keys(lists).filter( - (url) => !allActiveListUrls?.includes(url) && !UNSUPPORTED_LIST_URLS[chainId].includes(url) - ) -} diff --git a/apps/cowswap-frontend/src/legacy/state/lists/reducer.test.ts b/apps/cowswap-frontend/src/legacy/state/lists/reducer.test.ts deleted file mode 100644 index ce5824454c..0000000000 --- a/apps/cowswap-frontend/src/legacy/state/lists/reducer.test.ts +++ /dev/null @@ -1,625 +0,0 @@ -import { DEFAULT_ACTIVE_LIST_URLS_BY_NETWORK, DEFAULT_LIST_OF_LISTS_BY_NETWORK } from '@cowprotocol/common-const' -import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' - -import { createStore, Store } from 'redux' - -import { fetchTokenList, acceptListUpdate, addList, removeList, enableList } from './actions' -import reducer, { ListsStateByNetwork } from './reducer' - -import { updateVersion } from '../global/actions' - -const DEFAULT_LIST_OF_LISTS = DEFAULT_LIST_OF_LISTS_BY_NETWORK[ChainId.MAINNET] - -const STUB_TOKEN_LIST = { - name: '', - timestamp: '', - version: { major: 1, minor: 1, patch: 1 }, - tokens: [], -} - -const PATCHED_STUB_LIST = { - ...STUB_TOKEN_LIST, - version: { ...STUB_TOKEN_LIST.version, patch: STUB_TOKEN_LIST.version.patch + 1 }, -} -const MINOR_UPDATED_STUB_LIST = { - ...STUB_TOKEN_LIST, - version: { ...STUB_TOKEN_LIST.version, minor: STUB_TOKEN_LIST.version.minor + 1 }, -} -const MAJOR_UPDATED_STUB_LIST = { - ...STUB_TOKEN_LIST, - version: { ...STUB_TOKEN_LIST.version, major: STUB_TOKEN_LIST.version.major + 1 }, -} - -const EMPTY_STORE = { - [ChainId.MAINNET]: { - byUrl: {}, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }, - [ChainId.GOERLI]: { - byUrl: {}, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }, - [ChainId.GNOSIS_CHAIN]: { - byUrl: {}, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }, -} - -describe('list reducer', () => { - let store: Store - const getStoreSlice = (chainId: ChainId) => store.getState()[chainId] - - beforeEach(() => { - store = createStore(reducer, EMPTY_STORE) - }) - - describe('fetchTokenList [MAINNET]', () => { - // set what chain we're testing - const CHAIN_ID = ChainId.MAINNET - - describe('pending', () => { - it('sets pending', () => { - store.dispatch(fetchTokenList.pending({ chainId: CHAIN_ID, requestId: 'request-id', url: 'fake-url' })) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - activeListUrls: undefined, - byUrl: { - 'fake-url': { - error: null, - loadingRequestId: 'request-id', - current: null, - pendingUpdate: null, - }, - }, - selectedListUrl: undefined, - gpUnsupportedTokens: {}, - }) - }) - - it('does not clear current list', () => { - store = createStore(reducer, { - ...EMPTY_STORE, - [CHAIN_ID]: { - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - pendingUpdate: null, - loadingRequestId: null, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }, - }) - - store.dispatch(fetchTokenList.pending({ requestId: 'request-id', url: 'fake-url' })) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: 'request-id', - pendingUpdate: null, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }) - }) - }) - - describe('fulfilled', () => { - it('saves the list', () => { - store.dispatch( - fetchTokenList.fulfilled({ - chainId: CHAIN_ID, - tokenList: STUB_TOKEN_LIST, - requestId: 'request-id', - url: 'fake-url', - }) - ) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: null, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }) - }) - - it('does not save the list in pending if current is same', () => { - store.dispatch( - fetchTokenList.fulfilled({ tokenList: STUB_TOKEN_LIST, requestId: 'request-id', url: 'fake-url' }) - ) - store.dispatch( - fetchTokenList.fulfilled({ tokenList: STUB_TOKEN_LIST, requestId: 'request-id', url: 'fake-url' }) - ) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: null, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }) - }) - - it('does not save to current if list is newer patch version', () => { - store.dispatch( - fetchTokenList.fulfilled({ tokenList: STUB_TOKEN_LIST, requestId: 'request-id', url: 'fake-url' }) - ) - - store.dispatch( - fetchTokenList.fulfilled({ tokenList: PATCHED_STUB_LIST, requestId: 'request-id', url: 'fake-url' }) - ) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: PATCHED_STUB_LIST, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }) - }) - it('does not save to current if list is newer minor version', () => { - store.dispatch( - fetchTokenList.fulfilled({ tokenList: STUB_TOKEN_LIST, requestId: 'request-id', url: 'fake-url' }) - ) - - store.dispatch( - fetchTokenList.fulfilled({ tokenList: MINOR_UPDATED_STUB_LIST, requestId: 'request-id', url: 'fake-url' }) - ) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: MINOR_UPDATED_STUB_LIST, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }) - }) - it('does not save to pending if list is newer major version', () => { - store.dispatch( - fetchTokenList.fulfilled({ tokenList: STUB_TOKEN_LIST, requestId: 'request-id', url: 'fake-url' }) - ) - - store.dispatch( - fetchTokenList.fulfilled({ tokenList: MAJOR_UPDATED_STUB_LIST, requestId: 'request-id', url: 'fake-url' }) - ) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: MAJOR_UPDATED_STUB_LIST, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }) - }) - }) - - describe('rejected', () => { - it('no-op if not loading', () => { - store.dispatch(fetchTokenList.rejected({ requestId: 'request-id', errorMessage: 'abcd', url: 'fake-url' })) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: {}, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }) - }) - - it('sets the error if loading', () => { - store = createStore(reducer, { - ...EMPTY_STORE, - [CHAIN_ID]: { - byUrl: { - 'fake-url': { - error: null, - current: null, - loadingRequestId: 'request-id', - pendingUpdate: null, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }, - }) - store.dispatch(fetchTokenList.rejected({ requestId: 'request-id', errorMessage: 'abcd', url: 'fake-url' })) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: { - 'fake-url': { - error: 'abcd', - current: null, - loadingRequestId: null, - pendingUpdate: null, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }) - }) - }) - }) - - describe('addList', () => { - const CHAIN_ID = ChainId.MAINNET - it('adds the list key to byUrl', () => { - store.dispatch(addList({ url: 'list-id', chainId: CHAIN_ID })) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: { - 'list-id': { - error: null, - current: null, - loadingRequestId: null, - pendingUpdate: null, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }) - }) - it('no op for existing list', () => { - store = createStore(reducer, { - ...EMPTY_STORE, - [CHAIN_ID]: { - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: null, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }, - }) - store.dispatch(addList({ url: 'fake-url', chainId: CHAIN_ID })) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: null, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }) - }) - }) - - describe('acceptListUpdate', () => { - const CHAIN_ID = ChainId.MAINNET - it('swaps pending update into current', () => { - store = createStore(reducer, { - ...EMPTY_STORE, - [CHAIN_ID]: { - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: PATCHED_STUB_LIST, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }, - }) - store.dispatch(acceptListUpdate({ url: 'fake-url', chainId: CHAIN_ID })) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: { - 'fake-url': { - error: null, - current: PATCHED_STUB_LIST, - loadingRequestId: null, - pendingUpdate: null, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }) - }) - }) - - describe('removeList', () => { - const CHAIN_ID = ChainId.MAINNET - it('deletes the list key', () => { - store = createStore(reducer, { - ...EMPTY_STORE, - [CHAIN_ID]: { - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: PATCHED_STUB_LIST, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }, - }) - store.dispatch(removeList({ url: 'fake-url', chainId: CHAIN_ID })) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: {}, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }) - }) - it('Removes from active lists if active list is removed', () => { - store = createStore(reducer, { - ...EMPTY_STORE, - [CHAIN_ID]: { - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: PATCHED_STUB_LIST, - }, - }, - activeListUrls: ['fake-url'], - gpUnsupportedTokens: {}, - }, - }) - store.dispatch(removeList({ url: 'fake-url', chainId: CHAIN_ID })) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: {}, - activeListUrls: [], - gpUnsupportedTokens: {}, - }) - }) - }) - - describe('enableList', () => { - const CHAIN_ID = ChainId.MAINNET - it('enables a list url', () => { - store = createStore(reducer, { - ...EMPTY_STORE, - [CHAIN_ID]: { - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: PATCHED_STUB_LIST, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }, - }) - store.dispatch(enableList({ url: 'fake-url', chainId: CHAIN_ID })) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: PATCHED_STUB_LIST, - }, - }, - activeListUrls: ['fake-url'], - gpUnsupportedTokens: {}, - }) - }) - it('adds to url keys if not present already on enable', () => { - store = createStore(reducer, { - ...EMPTY_STORE, - [CHAIN_ID]: { - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: PATCHED_STUB_LIST, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }, - }) - store.dispatch(enableList({ url: 'fake-url-invalid', chainId: CHAIN_ID })) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: { - 'fake-url': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: PATCHED_STUB_LIST, - }, - 'fake-url-invalid': { - error: null, - current: null, - loadingRequestId: null, - pendingUpdate: null, - }, - }, - activeListUrls: ['fake-url-invalid'], - gpUnsupportedTokens: {}, - }) - }) - it('enable works if list already added', () => { - store = createStore(reducer, { - ...EMPTY_STORE, - [CHAIN_ID]: { - byUrl: { - 'fake-url': { - error: null, - current: null, - loadingRequestId: null, - pendingUpdate: null, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }, - }) - store.dispatch(enableList({ url: 'fake-url', chainId: CHAIN_ID })) - expect(getStoreSlice(CHAIN_ID)).toEqual({ - byUrl: { - 'fake-url': { - error: null, - current: null, - loadingRequestId: null, - pendingUpdate: null, - }, - }, - activeListUrls: ['fake-url'], - gpUnsupportedTokens: {}, - }) - }) - }) - - describe('updateVersion', () => { - const CHAIN_ID = ChainId.MAINNET - describe('never initialized', () => { - beforeEach(() => { - store = createStore(reducer, { - ...EMPTY_STORE, - [CHAIN_ID]: { - byUrl: { - 'https://unpkg.com/@uniswap/default-token-list@latest/uniswap-default.tokenlist.json': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: null, - }, - 'https://unpkg.com/@uniswap/default-token-list@latest': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: null, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - }, - }) - store.dispatch(updateVersion({ chainId: CHAIN_ID })) - }) - - it('clears the current lists', () => { - expect( - getStoreSlice(CHAIN_ID).byUrl[ - 'https://unpkg.com/@uniswap/default-token-list@latest/uniswap-default.tokenlist.json' - ] - ).toBeUndefined() - expect(getStoreSlice(CHAIN_ID).byUrl['https://unpkg.com/@uniswap/default-token-list@latest']).toBeUndefined() - }) - - it('puts in all the new lists', () => { - expect(Object.keys(getStoreSlice(CHAIN_ID).byUrl)).toEqual(DEFAULT_LIST_OF_LISTS) - }) - it('all lists are empty', () => { - const s = getStoreSlice(CHAIN_ID) - Object.keys(s.byUrl).forEach((url) => { - expect(s.byUrl[url]).toEqual({ - error: null, - current: null, - loadingRequestId: null, - pendingUpdate: null, - }) - }) - }) - it('sets initialized lists', () => { - expect(getStoreSlice(CHAIN_ID).lastInitializedDefaultListOfLists).toEqual(DEFAULT_LIST_OF_LISTS) - }) - it('sets selected list', () => { - expect(getStoreSlice(CHAIN_ID).activeListUrls).toEqual(DEFAULT_ACTIVE_LIST_URLS_BY_NETWORK[CHAIN_ID]) - }) - }) - describe('initialized with a different set of lists', () => { - beforeEach(() => { - store = createStore(reducer, { - ...EMPTY_STORE, - [CHAIN_ID]: { - byUrl: { - 'https://unpkg.com/@uniswap/default-token-list@latest/uniswap-default.tokenlist.json': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: null, - }, - 'https://unpkg.com/@uniswap/default-token-list@latest': { - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: null, - }, - }, - activeListUrls: undefined, - gpUnsupportedTokens: {}, - lastInitializedDefaultListOfLists: ['https://unpkg.com/@uniswap/default-token-list@latest'], - }, - }) - store.dispatch(updateVersion({ chainId: CHAIN_ID })) - }) - - it('does not remove lists not in last initialized list of lists', () => { - expect( - getStoreSlice(CHAIN_ID).byUrl[ - 'https://unpkg.com/@uniswap/default-token-list@latest/uniswap-default.tokenlist.json' - ] - ).toEqual({ - error: null, - current: STUB_TOKEN_LIST, - loadingRequestId: null, - pendingUpdate: null, - }) - }) - it('removes lists in the last initialized list of lists', () => { - expect(getStoreSlice(CHAIN_ID).byUrl['https://unpkg.com/@uniswap/default-token-list@latest']).toBeUndefined() - }) - - it('each of those initialized lists is empty', () => { - const byUrl = getStoreSlice(CHAIN_ID).byUrl - // note we don't expect the uniswap default list to be prepopulated - // this is ok. - Object.keys(byUrl).forEach((url) => { - if (url !== 'https://unpkg.com/@uniswap/default-token-list@latest/uniswap-default.tokenlist.json') { - expect(byUrl[url]).toEqual({ - error: null, - current: null, - loadingRequestId: null, - pendingUpdate: null, - }) - } - }) - }) - - it('sets initialized lists', () => { - expect(getStoreSlice(CHAIN_ID).lastInitializedDefaultListOfLists).toEqual(DEFAULT_LIST_OF_LISTS) - }) - it('sets default list to selected list', () => { - expect(getStoreSlice(CHAIN_ID).activeListUrls).toEqual(DEFAULT_ACTIVE_LIST_URLS_BY_NETWORK[CHAIN_ID]) - }) - }) - }) -}) diff --git a/apps/cowswap-frontend/src/legacy/state/lists/reducer.ts b/apps/cowswap-frontend/src/legacy/state/lists/reducer.ts deleted file mode 100644 index e1a59f405c..0000000000 --- a/apps/cowswap-frontend/src/legacy/state/lists/reducer.ts +++ /dev/null @@ -1,267 +0,0 @@ -import { - DEFAULT_ACTIVE_LIST_URLS_BY_NETWORK, - DEFAULT_LIST_OF_LISTS_BY_NETWORK, - DEFAULT_NETWORK_FOR_LISTS, - UNSUPPORTED_LIST_URLS, -} from '@cowprotocol/common-const' -import { getChainIdValues } from '@cowprotocol/common-utils' -import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' -import { getVersionUpgrade, TokenList, VersionUpgrade } from '@uniswap/token-lists' - -import { createReducer } from '@reduxjs/toolkit' - -import { UnsupportedToken } from 'api/gnosisProtocol' - -import { - acceptListUpdate, - addGpUnsupportedToken, - addList, - disableList, - enableList, - fetchTokenList, - removeGpUnsupportedToken, - removeList, -} from './actions' - -import { updateVersion } from '../global/actions' - -export interface ListsState { - readonly byUrl: { - readonly [url: string]: { - readonly current: TokenList | null - readonly pendingUpdate: TokenList | null - readonly loadingRequestId: string | null - readonly error: string | null - } - } - // this contains the default list of lists from the last time the updateVersion was called, i.e. the app was reloaded - readonly lastInitializedDefaultListOfLists?: string[] - - // currently active lists - readonly activeListUrls: string[] | undefined - - // unsupported tokens - readonly gpUnsupportedTokens: UnsupportedToken -} - -export type ListsStateByNetwork = { - [chain in ChainId]: ListsState -} - -export type ListState = ListsState['byUrl'][string] - -export const NEW_LIST_STATE: ListState = { - error: null, - current: null, - loadingRequestId: null, - pendingUpdate: null, -} - -export type Mutable = { -readonly [P in keyof T]: T[P] extends ReadonlyArray ? U[] : T[P] } - -// MOD: setInitialState sets each chainId state individually -const setInitialListState = (chainId: ChainId): ListsState => ({ - lastInitializedDefaultListOfLists: DEFAULT_LIST_OF_LISTS_BY_NETWORK[chainId], - byUrl: { - ...DEFAULT_LIST_OF_LISTS_BY_NETWORK[chainId] - .concat(...UNSUPPORTED_LIST_URLS[chainId]) - .reduce>((memo, listUrl) => { - memo[listUrl] = NEW_LIST_STATE - return memo - }, {}), - }, - activeListUrls: DEFAULT_ACTIVE_LIST_URLS_BY_NETWORK[chainId], - gpUnsupportedTokens: {}, -}) - -// MOD: change the intiialState shape -// we make an object with each chainId pulled from ChainId enum -// into a list and reduced into a map -const initialState: ListsStateByNetwork = { - ...getChainIdValues().reduce((memo, chainId) => { - if (!memo[chainId]) { - memo[chainId] = setInitialListState(chainId) - } - return memo - }, {} as ListsStateByNetwork), -} - -export default createReducer(initialState, (builder) => - builder - .addCase( - fetchTokenList.pending, - (baseState, { payload: { chainId = DEFAULT_NETWORK_FOR_LISTS, requestId, url } }) => { - const state = baseState[chainId] - const current = state.byUrl[url]?.current ?? null - const pendingUpdate = state.byUrl[url]?.pendingUpdate ?? null - - state.byUrl[url] = { - current, - pendingUpdate, - loadingRequestId: requestId, - error: null, - } - } - ) - .addCase( - fetchTokenList.fulfilled, - (baseState, { payload: { chainId = DEFAULT_NETWORK_FOR_LISTS, requestId, tokenList, url } }) => { - const state = baseState[chainId] - const current = state.byUrl[url]?.current - const loadingRequestId = state.byUrl[url]?.loadingRequestId - - // no-op if update does nothing - if (current) { - const upgradeType = getVersionUpgrade(current.version, tokenList.version) - - if (upgradeType === VersionUpgrade.NONE) return - if (loadingRequestId === null || loadingRequestId === requestId) { - state.byUrl[url] = { - current, - pendingUpdate: tokenList, - loadingRequestId: null, - error: null, - } - } - } else { - // activate if on default active - if (DEFAULT_ACTIVE_LIST_URLS_BY_NETWORK[chainId].includes(url)) { - state.activeListUrls?.push(url) - } - - state.byUrl[url] = { - current: tokenList, - pendingUpdate: null, - loadingRequestId: null, - error: null, - } - } - } - ) - .addCase( - fetchTokenList.rejected, - (baseState, { payload: { chainId = DEFAULT_NETWORK_FOR_LISTS, url, requestId, errorMessage } }) => { - const state = baseState[chainId] - if (state.byUrl[url]?.loadingRequestId !== requestId) { - // no-op since it's not the latest request - return - } - - state.byUrl[url] = { - current: null, - pendingUpdate: null, - loadingRequestId: null, - error: errorMessage, - } - } - ) - .addCase(addList, (baseState, { payload: { chainId = DEFAULT_NETWORK_FOR_LISTS, url } }) => { - const state = baseState[chainId] - if (!state.byUrl[url]) { - state.byUrl[url] = NEW_LIST_STATE - } - }) - .addCase(removeList, (baseState, { payload: { chainId = DEFAULT_NETWORK_FOR_LISTS, url } }) => { - const state = baseState[chainId] - if (state.byUrl[url]) { - delete state.byUrl[url] - } - // remove list from active urls if needed - if (state.activeListUrls && state.activeListUrls.includes(url)) { - state.activeListUrls = state.activeListUrls.filter((u) => u !== url) - } - }) - .addCase(enableList, (baseState, { payload: { chainId = DEFAULT_NETWORK_FOR_LISTS, url } }) => { - const state = baseState[chainId] - if (!state.byUrl[url]) { - state.byUrl[url] = NEW_LIST_STATE - } - - if (state.activeListUrls && !state.activeListUrls.includes(url)) { - state.activeListUrls.push(url) - } - - if (!state.activeListUrls) { - state.activeListUrls = [url] - } - }) - .addCase(disableList, (baseState, { payload: { chainId = DEFAULT_NETWORK_FOR_LISTS, url } }) => { - const state = baseState[chainId] - if (state.activeListUrls && state.activeListUrls.includes(url)) { - state.activeListUrls = state.activeListUrls.filter((u) => u !== url) - } - }) - .addCase(acceptListUpdate, (baseState, { payload: { chainId = DEFAULT_NETWORK_FOR_LISTS, url } }) => { - const state = baseState[chainId] - if (!state.byUrl[url]?.pendingUpdate) { - throw new Error('accept list update called without pending update') - } - state.byUrl[url] = { - ...state.byUrl[url], - current: state.byUrl[url].pendingUpdate, - pendingUpdate: null, - } - }) - .addCase( - updateVersion, - (baseState, { payload: { chainId = DEFAULT_NETWORK_FOR_LISTS } }): ListsStateByNetwork | void => { - const state = baseState[chainId] - // MOD: we need to check the localstrorage list shape against our schema as it has changed - if (!state) return (baseState = initialState) - - // state loaded from localStorage, but new lists have never been initialized - if (!state.lastInitializedDefaultListOfLists) { - state.byUrl = initialState[chainId].byUrl - state.activeListUrls = initialState[chainId].activeListUrls - } else if (state.lastInitializedDefaultListOfLists) { - const lastInitializedSet = state.lastInitializedDefaultListOfLists.reduce>( - (s, l) => s.add(l), - new Set() - ) - const newListOfListsSet = DEFAULT_LIST_OF_LISTS_BY_NETWORK[chainId].reduce>( - (s, l) => s.add(l), - new Set() - ) - - DEFAULT_LIST_OF_LISTS_BY_NETWORK[chainId].forEach((listUrl) => { - if (!lastInitializedSet.has(listUrl)) { - state.byUrl[listUrl] = NEW_LIST_STATE - } - }) - - state.lastInitializedDefaultListOfLists.forEach((listUrl) => { - if (!newListOfListsSet.has(listUrl)) { - delete state.byUrl[listUrl] - } - }) - } - - state.lastInitializedDefaultListOfLists = DEFAULT_LIST_OF_LISTS_BY_NETWORK[chainId] - - // if no active lists, activate defaults - if (!state.activeListUrls) { - state.activeListUrls = DEFAULT_ACTIVE_LIST_URLS_BY_NETWORK[chainId] - - // for each list on default list, initialize if needed - DEFAULT_ACTIVE_LIST_URLS_BY_NETWORK[chainId].map((listUrl: string) => { - if (!state.byUrl[listUrl]) { - state.byUrl[listUrl] = NEW_LIST_STATE - } - return true - }) - } - - if (!state.gpUnsupportedTokens) { - state.gpUnsupportedTokens = {} - } - } - ) - .addCase(addGpUnsupportedToken, (baseState, { payload: { chainId = DEFAULT_NETWORK_FOR_LISTS, ...restToken } }) => { - const state = baseState[chainId] - state.gpUnsupportedTokens[restToken.address.toLowerCase()] = restToken - }) - .addCase(removeGpUnsupportedToken, (baseState, { payload: { chainId = DEFAULT_NETWORK_FOR_LISTS, address } }) => { - const state = baseState[chainId] - delete state.gpUnsupportedTokens[address.toLowerCase()] - }) -) diff --git a/apps/cowswap-frontend/src/legacy/state/lists/wrappedTokenInfo.ts b/apps/cowswap-frontend/src/legacy/state/lists/wrappedTokenInfo.ts deleted file mode 100644 index 3e446c5866..0000000000 --- a/apps/cowswap-frontend/src/legacy/state/lists/wrappedTokenInfo.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { isAddress } from '@cowprotocol/common-utils' -import { Currency, Token } from '@uniswap/sdk-core' -import { Tags, TokenInfo, TokenList } from '@uniswap/token-lists' - -type TagDetails = Tags[keyof Tags] -export interface TagInfo extends TagDetails { - id: string - icon?: string -} -/** - * Token instances created from token info on a token list. - */ -export class WrappedTokenInfo implements Token { - public readonly isNative = false - public readonly isToken = true - public readonly list?: TokenList - public readonly tokenInfo: TokenInfo - - private _checksummedAddress: string - - constructor(tokenInfo: TokenInfo, list?: TokenList) { - this.tokenInfo = tokenInfo - this.list = list - const checksummedAddress = isAddress(this.tokenInfo.address) - if (!checksummedAddress) { - throw new Error(`Invalid token address: ${this.tokenInfo.address}`) - } - this._checksummedAddress = checksummedAddress - } - - public get address(): string { - return this._checksummedAddress - } - - public get chainId(): number { - return this.tokenInfo.chainId - } - - public get decimals(): number { - return this.tokenInfo.decimals - } - - public get name(): string { - return this.tokenInfo.name - } - - public get symbol(): string { - return this.tokenInfo.symbol - } - - public get logoURI(): string | undefined { - return this.tokenInfo.logoURI - } - - private _tags: TagInfo[] | null = null - public get tags(): TagInfo[] { - if (this._tags !== null) return this._tags - if (!this.tokenInfo.tags) return (this._tags = []) - const listTags = this.list?.tags - if (!listTags) return (this._tags = []) - - return (this._tags = this.tokenInfo.tags.map((tagId) => { - return { - ...listTags[tagId], - id: tagId, - } - })) - } - - equals(other: Currency): boolean { - return other.chainId === this.chainId && other.isToken && other.address.toLowerCase() === this.address.toLowerCase() - } - - sortsBefore(other: Token): boolean { - if (this.equals(other)) throw new Error('Addresses should not be equal') - return this.address.toLowerCase() < other.address.toLowerCase() - } - - public get wrapped(): Token { - return this - } -} diff --git a/apps/cowswap-frontend/src/lib/hooks/useCurrency.ts b/apps/cowswap-frontend/src/lib/hooks/useCurrency.ts deleted file mode 100644 index 12e4687e15..0000000000 --- a/apps/cowswap-frontend/src/lib/hooks/useCurrency.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { useMemo } from 'react' - -import { TOKEN_SHORTHANDS } from '@cowprotocol/common-const' -import { useBytes32TokenContract, useTokenContract } from '@cowprotocol/common-hooks' -import { isAddress } from '@cowprotocol/common-utils' -import { isChainAllowed, useWalletInfo } from '@cowprotocol/wallet' -import { arrayify } from '@ethersproject/bytes' -import { parseBytes32String } from '@ethersproject/strings' -import { Currency, Token } from '@uniswap/sdk-core' -import { useWeb3React } from '@web3-react/core' - -import { NEVER_RELOAD, useSingleCallResult } from 'lib/hooks/multicall' -import useNativeCurrency from 'lib/hooks/useNativeCurrency' - -// parse a name or symbol from a token response -const BYTES32_REGEX = /^0x[a-fA-F0-9]{64}$/ - -export function parseStringOrBytes32( - str: string | undefined, - bytes32: string | undefined, - defaultValue: string -): string { - return str && str.length > 0 - ? str - : // need to check for proper bytes string and valid terminator - bytes32 && BYTES32_REGEX.test(bytes32) && arrayify(bytes32)[31] === 0 - ? parseBytes32String(bytes32) - : defaultValue -} - -/** - * Returns a Token from the tokenAddress. - * Returns null if token is loading or null was passed. - * Returns undefined if tokenAddress is invalid or token does not exist. - */ -export function useTokenFromNetwork(tokenAddress: string | null | undefined): Token | null | undefined { - const { connector } = useWeb3React() - const { chainId } = useWalletInfo() - const chainAllowed = chainId && isChainAllowed(connector, chainId) - - const formattedAddress = isAddress(tokenAddress) - - const tokenContract = useTokenContract(formattedAddress ? formattedAddress : undefined, false) - const tokenContractBytes32 = useBytes32TokenContract(formattedAddress ? formattedAddress : undefined, false) - - const tokenName = useSingleCallResult(tokenContract, 'name', undefined, NEVER_RELOAD) - const tokenNameBytes32 = useSingleCallResult(tokenContractBytes32, 'name', undefined, NEVER_RELOAD) - const symbol = useSingleCallResult(tokenContract, 'symbol', undefined, NEVER_RELOAD) - const symbolBytes32 = useSingleCallResult(tokenContractBytes32, 'symbol', undefined, NEVER_RELOAD) - const decimals = useSingleCallResult(tokenContract, 'decimals', undefined, NEVER_RELOAD) - - return useMemo(() => { - if (typeof tokenAddress !== 'string' || !chainAllowed || !formattedAddress) return undefined - if (decimals.loading || symbol.loading || tokenName.loading) return null - if (decimals.result) { - return new Token( - chainId, - formattedAddress, - decimals.result[0], - parseStringOrBytes32(symbol.result?.[0], symbolBytes32.result?.[0], 'UNKNOWN'), - parseStringOrBytes32(tokenName.result?.[0], tokenNameBytes32.result?.[0], 'Unknown Token') - ) - } - return undefined - }, [ - formattedAddress, - chainId, - chainAllowed, - decimals.loading, - decimals.result, - symbol.loading, - symbol.result, - symbolBytes32.result, - tokenAddress, - tokenName.loading, - tokenName.result, - tokenNameBytes32.result, - ]) -} - -export type TokenMap = { [address: string]: Token } - -/** - * Returns a Token from the tokenAddress. - * Returns null if token is loading or null was passed. - * Returns undefined if tokenAddress is invalid or token does not exist. - */ -export function useTokenFromMapOrNetwork(tokens: TokenMap, tokenAddress?: string | null): Token | null | undefined { - const address = isAddress(tokenAddress) - const token: Token | undefined = address ? tokens[address] : undefined - - const tokenFromNetwork = useTokenFromNetwork(token ? undefined : address ? address : undefined) - - return tokenFromNetwork ?? token -} - -/** - * Returns a Currency from the currencyId. - * Returns null if currency is loading or null was passed. - * Returns undefined if currencyId is invalid or token does not exist. - */ -export function useCurrencyFromMap(tokens: TokenMap, currencyId?: string | null): Currency | null | undefined { - const nativeCurrency = useNativeCurrency() - const { connector } = useWeb3React() - const { chainId } = useWalletInfo() - const isNative = Boolean(nativeCurrency && ['ETH', 'XDAI'].includes(currencyId?.toUpperCase() || '')) // MOD!! - const shorthandMatchAddress = useMemo(() => { - return currencyId ? TOKEN_SHORTHANDS[currencyId.toUpperCase()]?.[chainId] : undefined - }, [chainId, currencyId]) - - const token = useTokenFromMapOrNetwork(tokens, isNative ? undefined : shorthandMatchAddress ?? currencyId) - - const chainAllowed = chainId && isChainAllowed(connector, chainId) - if (currencyId === null || currencyId === undefined || !chainAllowed) return null - - // this case so we use our builtin wrapped token instead of wrapped tokens on token lists - const wrappedNative = nativeCurrency?.wrapped - if (wrappedNative?.address?.toUpperCase() === currencyId?.toUpperCase()) return wrappedNative - - return isNative ? nativeCurrency : token -} diff --git a/apps/cowswap-frontend/src/lib/hooks/useTokenList/fetchTokenList.test.ts b/apps/cowswap-frontend/src/lib/hooks/useTokenList/fetchTokenList.test.ts deleted file mode 100644 index 368c3ebf67..0000000000 --- a/apps/cowswap-frontend/src/lib/hooks/useTokenList/fetchTokenList.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import fetchMock from 'jest-fetch-mock' - -import tokenList from 'lib/hooks/useTokenList/mockTokenList.json' - -import fetchTokenList, { DEFAULT_TOKEN_LIST } from './fetchTokenList' - -fetchMock.enableMocks() - -describe('fetchTokenList', () => { - let resolver: jest.Mock - - beforeEach(() => { - fetchMock.resetMocks() - resolver = jest.fn() - }) - - it('throws on an invalid list url', async () => { - fetchMock.mockReject(() => Promise.reject("URL doesn't exist")) - const url = 'https://example.com' - await expect(fetchTokenList(url, resolver)).rejects.toThrowError(`failed to fetch list: ${url}`) - expect(resolver).not.toHaveBeenCalled() - expect(fetchMock).toHaveBeenCalledWith(url, { credentials: 'omit' }) - }) - - it('tries to fetch an ENS address using the passed resolver', async () => { - const url = 'example.eth' - const contenthash = '0xD3ADB33F' - resolver.mockResolvedValue(contenthash) - await expect(fetchTokenList(url, resolver)).rejects.toThrow( - `failed to translate contenthash to URI: ${contenthash}` - ) - expect(resolver).toHaveBeenCalledWith(url) - expect(fetchMock).not.toHaveBeenCalled() - }) - - it('fetches and validates the default token list', async () => { - const list = tokenList - fetchMock.mockImplementationOnce( - () => - Promise.resolve({ - status: 200, - ok: true, - json: () => Promise.resolve(list), - }) as any - ) - await expect(fetchTokenList(DEFAULT_TOKEN_LIST, resolver)).resolves.toStrictEqual(list) - expect(resolver).not.toHaveBeenCalled() - }) -}) diff --git a/apps/cowswap-frontend/src/lib/hooks/useTokenList/fetchTokenList.ts b/apps/cowswap-frontend/src/lib/hooks/useTokenList/fetchTokenList.ts deleted file mode 100644 index e072b22b9d..0000000000 --- a/apps/cowswap-frontend/src/lib/hooks/useTokenList/fetchTokenList.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { contenthashToUri, parseENSAddress, uriToHttp } from '@cowprotocol/common-utils' -import type { TokenList } from '@uniswap/token-lists' - -import validateTokenList from './validateTokenList' - -export const DEFAULT_TOKEN_LIST = 'https://gateway.ipfs.io/ipns/tokens.uniswap.org' - -const listCache = new Map() - -/** Fetches and validates a token list. */ -export default async function fetchTokenList( - listUrl: string, - resolveENSContentHash: (ensName: string) => Promise -): Promise { - const cached = listCache?.get(listUrl) // avoid spurious re-fetches - if (cached) { - return cached - } - - let urls: string[] - const parsedENS = parseENSAddress(listUrl) - if (parsedENS) { - let contentHashUri - try { - contentHashUri = await resolveENSContentHash(parsedENS.ensName) - } catch (error: any) { - const message = `failed to resolve ENS name: ${parsedENS.ensName}` - console.debug(message, error) - throw new Error(message) - } - let translatedUri - try { - translatedUri = contenthashToUri(contentHashUri) - } catch (error: any) { - const message = `failed to translate contenthash to URI: ${contentHashUri}` - console.debug(message, error) - throw new Error(message) - } - urls = uriToHttp(`${translatedUri}${parsedENS.ensPath ?? ''}`) - } else { - urls = uriToHttp(listUrl) - } - - for (let i = 0; i < urls.length; i++) { - const url = urls[i] - const isLast = i === urls.length - 1 - let response - try { - response = await fetch(url, { credentials: 'omit' }) - } catch (error: any) { - const message = `failed to fetch list: ${listUrl}` - console.debug(message, error) - if (isLast) throw new Error(message) - continue - } - - if (!response.ok) { - const message = `failed to fetch list: ${listUrl}` - console.debug(message, response.statusText) - if (isLast) throw new Error(message) - continue - } - - const json = await response.json() - const list = await validateTokenList(json) - listCache?.set(listUrl, list) - return list - } - - throw new Error('Unrecognized list URL protocol.') -} diff --git a/apps/cowswap-frontend/src/lib/hooks/useTokenList/filtering.ts b/apps/cowswap-frontend/src/lib/hooks/useTokenList/filtering.ts deleted file mode 100644 index 2770f83eca..0000000000 --- a/apps/cowswap-frontend/src/lib/hooks/useTokenList/filtering.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { isAddress } from '@cowprotocol/common-utils' -import { NativeCurrency, Token } from '@uniswap/sdk-core' -import { TokenInfo } from '@uniswap/token-lists' - -const alwaysTrue = () => true - -/** Creates a filter function that filters tokens that do not match the query. */ -export function getTokenFilter(query: string): (token: T | NativeCurrency) => boolean { - const searchingAddress = isAddress(query) - - if (searchingAddress) { - const address = searchingAddress.toLowerCase() - return (t: T | NativeCurrency) => 'address' in t && address === t.address.toLowerCase() - } - - const queryParts = query - .toLowerCase() - .split(/\s+/) - .filter((s) => s.length > 0) - - if (queryParts.length === 0) return alwaysTrue - - const match = (s: string): boolean => { - const parts = s - .toLowerCase() - .split(/\s+/) - .filter((s) => s.length > 0) - - return queryParts.every((p) => p.length === 0 || parts.some((sp) => sp.startsWith(p) || sp.endsWith(p))) - } - - return ({ name, symbol }: T | NativeCurrency): boolean => Boolean((symbol && match(symbol)) || (name && match(name))) -} diff --git a/apps/cowswap-frontend/src/lib/hooks/useTokenList/mockTokenList.json b/apps/cowswap-frontend/src/lib/hooks/useTokenList/mockTokenList.json deleted file mode 100644 index c278e4f6e3..0000000000 --- a/apps/cowswap-frontend/src/lib/hooks/useTokenList/mockTokenList.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "Uniswap Labs Default", - "timestamp": "2022-06-29T15:57:01.868Z", - "version": { - "major": 4, - "minor": 1, - "patch": 0 - }, - "tags": {}, - "logoURI": "ipfs://QmNa8mQkrNKp1WEEeGjFezDmDeodkWRevGFN8JCV7b4Xir", - "keywords": ["uniswap", "default"], - "tokens": [ - { - "chainId": 1, - "address": "0x111111111117dC0aa78b770fA6A738034120C302", - "name": "1inch", - "symbol": "1INCH", - "decimals": 18, - "logoURI": "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", - "extensions": { - "bridgeInfo": { - "137": { - "tokenAddress": "0x9c2C5fd7b07E95EE044DDeba0E97a665F142394f" - }, - "42161": { - "tokenAddress": "0x6314C31A7a1652cE482cffe247E9CB7c3f4BB9aF" - } - } - } - } - ] -} diff --git a/apps/cowswap-frontend/src/lib/hooks/useTokenList/sorting.ts b/apps/cowswap-frontend/src/lib/hooks/useTokenList/sorting.ts deleted file mode 100644 index 460727307f..0000000000 --- a/apps/cowswap-frontend/src/lib/hooks/useTokenList/sorting.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { useMemo } from 'react' - -import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core' -import { TokenInfo } from '@uniswap/token-lists' - -import { TokenAmounts } from 'modules/tokens' - -/** Sorts currency amounts (descending). */ -function balanceComparator(a?: CurrencyAmount, b?: CurrencyAmount) { - if (a && b) { - return a.greaterThan(b) ? -1 : a.equalTo(b) ? 0 : 1 - } else if (a?.greaterThan('0')) { - return -1 - } else if (b?.greaterThan('0')) { - return 1 - } - return 0 -} - -/** Sorts tokens by currency amount (descending), then symbol (ascending). */ -export function tokenComparator(balances: TokenAmounts, a: Token, b: Token) { - // Sorts by balances - const balanceComparison = balanceComparator(balances[a.address]?.value, balances[b.address]?.value) - if (balanceComparison !== 0) return balanceComparison - - // Sorts by symbol - if (a.symbol && b.symbol) { - return a.symbol.toLowerCase() < b.symbol.toLowerCase() ? -1 : 1 - } - - return -1 -} - -/** Sorts tokens by query, giving precedence to exact matches and partial matches. */ -export function useSortTokensByQuery(query: string, tokens?: T[]): T[] { - return useMemo(() => { - if (!tokens) { - return [] - } - - const matches = query - .toLowerCase() - .split(/\s+/) - .filter((s) => s.length > 0) - - if (matches.length > 1) { - return tokens - } - - const exactMatches: T[] = [] - const symbolSubtrings: T[] = [] - const rest: T[] = [] - - // sort tokens by exact match -> subtring on symbol match -> rest - tokens.map((token) => { - if (token.symbol?.toLowerCase() === matches[0]) { - return exactMatches.push(token) - } else if (token.symbol?.toLowerCase().startsWith(query.toLowerCase().trim())) { - return symbolSubtrings.push(token) - } else { - return rest.push(token) - } - }) - - return [...exactMatches, ...symbolSubtrings, ...rest] - }, [tokens, query]) -} diff --git a/apps/cowswap-frontend/src/lib/hooks/useTokenList/utils.ts b/apps/cowswap-frontend/src/lib/hooks/useTokenList/utils.ts deleted file mode 100644 index 8d7e99085b..0000000000 --- a/apps/cowswap-frontend/src/lib/hooks/useTokenList/utils.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { TokenInfo, TokenList } from '@uniswap/token-lists' - -import { WrappedTokenInfo } from 'legacy/state/lists/wrappedTokenInfo' - -type TokenMap = Readonly<{ [tokenAddress: string]: { token: WrappedTokenInfo; list?: TokenList } }> -export type ChainTokenMap = Readonly<{ [chainId: number]: TokenMap }> - -type Mutable = { - -readonly [P in keyof T]: Mutable -} - -const mapCache = typeof WeakMap !== 'undefined' ? new WeakMap() : null - -export function tokensToChainTokenMap(tokens: TokenList | TokenInfo[]): ChainTokenMap { - const cached = mapCache?.get(tokens) - if (cached) return cached - - const [list, infos] = Array.isArray(tokens) ? [undefined, tokens] : [tokens, tokens.tokens] - const map = infos.reduce>((map, info) => { - try { - const token = new WrappedTokenInfo(info, list) - if (map[token.chainId]?.[token.address] !== undefined) { - console.warn(`Duplicate token skipped: ${token.address}`) - return map - } - if (!map[token.chainId]) { - map[token.chainId] = {} - } - map[token.chainId][token.address] = { token, list } - return map - } catch { - return map - } - }, {}) as ChainTokenMap - mapCache?.set(tokens, map) - return map -} diff --git a/apps/cowswap-frontend/src/lib/hooks/useTokenList/validateTokenList.test.ts b/apps/cowswap-frontend/src/lib/hooks/useTokenList/validateTokenList.test.ts deleted file mode 100644 index 92735cbe46..0000000000 --- a/apps/cowswap-frontend/src/lib/hooks/useTokenList/validateTokenList.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { TokenInfo } from '@uniswap/token-lists' - -import { validateTokens } from './validateTokenList' - -const INVALID_TOKEN: TokenInfo = { - name: 'Dai Stablecoin', - address: '0xD3ADB33F', - symbol: 'DAI', - decimals: 18, - chainId: 1, -} - -const INLINE_TOKEN_LIST = [ - { - name: 'Dai Stablecoin', - address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', - symbol: 'DAI', - decimals: 18, - chainId: 1, - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png', - }, - { - name: 'USDCoin', - address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - symbol: 'USDC', - decimals: 6, - chainId: 1, - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png', - }, -] - -describe('validateTokens', () => { - it('throws on invalid tokens', async () => { - await expect(validateTokens([INVALID_TOKEN])).rejects.toThrowError(/^Token list failed validation:.*address/) - }) - - it('validates the passed token info', async () => { - await expect(validateTokens(INLINE_TOKEN_LIST)).resolves.toBe(INLINE_TOKEN_LIST) - }) -}) diff --git a/apps/cowswap-frontend/src/lib/hooks/useTokenList/validateTokenList.ts b/apps/cowswap-frontend/src/lib/hooks/useTokenList/validateTokenList.ts deleted file mode 100644 index 5e1477a593..0000000000 --- a/apps/cowswap-frontend/src/lib/hooks/useTokenList/validateTokenList.ts +++ /dev/null @@ -1,55 +0,0 @@ -import type { TokenInfo, TokenList } from '@uniswap/token-lists' - -import type { Ajv, ValidateFunction } from 'ajv' - -enum ValidationSchema { - LIST = 'list', - TOKENS = 'tokens', -} - -const validator = new Promise(async (resolve) => { - const [ajv, schema] = await Promise.all([import('ajv'), import('@uniswap/token-lists/src/tokenlist.schema.json')]) - const validator = new ajv.default({ allErrors: true }) - .addSchema(schema, ValidationSchema.LIST) - // Adds a meta scheme of Pick - .addSchema( - { - ...schema, - $id: schema.$id + '#tokens', - required: ['tokens'], - }, - ValidationSchema.TOKENS - ) - resolve(validator) -}) - -function getValidationErrors(validate: ValidateFunction | undefined): string { - return ( - validate?.errors?.map((error) => [error.dataPath, error.message].filter(Boolean).join(' ')).join('; ') ?? - 'unknown error' - ) -} - -/** - * Validates an array of tokens. - * @param json the TokenInfo[] to validate - */ -export async function validateTokens(json: TokenInfo[]): Promise { - const validate = (await validator).getSchema(ValidationSchema.TOKENS) - if (validate?.({ tokens: json })) { - return json - } - throw new Error(`Token list failed validation: ${getValidationErrors(validate)}`) -} - -/** - * Validates a token list. - * @param json the TokenList to validate - */ -export default async function validateTokenList(json: TokenList): Promise { - const validate = (await validator).getSchema(ValidationSchema.LIST) - if (validate?.(json)) { - return json - } - throw new Error(`Token list failed validation: ${getValidationErrors(validate)}`) -} diff --git a/apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenModal/mocks.ts b/apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenModal/mocks.ts deleted file mode 100644 index 77ed5e8e8e..0000000000 --- a/apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenModal/mocks.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { TokenWithLogo } from '../../types' - -export const allTokensMock: TokenWithLogo[] = [ - { - name: 'Gnosis', - chainId: 5, - symbol: 'GNO', - decimals: 18, - address: '0x02abbdbaaa7b1bb64b5c878f7ac17f8dda169532', - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6810e776880C02933D47DB1b9fc05908e5386b96/logo.png', - }, - { - name: 'Basic Attention Token', - chainId: 5, - symbol: 'BAT', - decimals: 18, - address: '0x70cBa46d2e933030E2f274AE58c951C800548AeF', - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0D8775F648430679A709E98d2b0Cb6250d2887EF/logo.png', - }, - { - name: 'CoW Protocol Token', - chainId: 5, - symbol: 'COW', - decimals: 18, - address: '0x91056D4A53E1faa1A84306D4deAEc71085394bC8', - logoURI: 'https://gnosis.mypinata.cloud/ipfs/Qme9B6jRpGtZsRFcPjHvA5T4ugFuL4c3SzWfxyMPa59AMo', - }, - { - name: 'USD Coin', - chainId: 5, - symbol: 'USDC', - decimals: 6, - address: '0xD87Ba7A50B2E7E660f678A895E4B72E7CB4CCd9C', - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png', - }, - { - name: 'DAI', - chainId: 5, - symbol: 'DAI', - decimals: 18, - address: '0xdc31Ee1784292379Fbb2964b3B9C4124D8F89C60', - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png', - }, - { - name: '0x', - chainId: 5, - symbol: 'ZRX', - decimals: 18, - address: '0xe4E81Fa6B16327D4B78CFEB83AAdE04bA7075165', - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE41d2489571d322189246DaFA5ebDe1F4699F498/logo.png', - }, -].map((item) => new TokenWithLogo(item.logoURI, item.chainId, item.address, item.decimals, item.symbol, item.name)) - -export const favouriteTokensMock: TokenWithLogo[] = [ - { - name: 'Basic Attention Token', - chainId: 5, - symbol: 'BAT', - decimals: 18, - address: '0x70cBa46d2e933030E2f274AE58c951C800548AeF', - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0D8775F648430679A709E98d2b0Cb6250d2887EF/logo.png', - }, - { - name: 'Polymath Network', - chainId: 5, - symbol: 'POLY', - decimals: 18, - address: '0x9e32c0EfF886B6Ccae99350Fd5e7002dCED55F15', - logoURI: 'https://assets.coingecko.com/coins/images/2784/thumb/inKkF01.png?1605007034', - }, -].map((item) => new TokenWithLogo(item.logoURI, item.chainId, item.address, item.decimals, item.symbol, item.name)) diff --git a/apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenModal/searchToken.ts b/apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenModal/searchToken.ts deleted file mode 100644 index 04623a4139..0000000000 --- a/apps/cowswap-frontend/src/modules/tokensList/containers/SelectTokenModal/searchToken.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { TokenWithLogo } from '../../types' - -export enum TokenSearchSource { - Blockchain = 'Blockchain', - External = 'External', - InactiveList = 'InactiveList', -} - -type TokenSearchResult = { - source: TokenSearchSource - tokens: TokenWithLogo[] -} - -// TODO: implement search -export async function searchToken(input: string): Promise { - if (input === '0x252d98fab648203aa33310721bbbddfa8f1b6587') { - return { - source: TokenSearchSource.Blockchain, - tokens: [new TokenWithLogo(undefined, 5, '0x252d98fab648203aa33310721bbbddfa8f1b6587', 18, 'GSUc', 'GSU Coin')], - } - } - if (input === 'cDAI') { - return { - source: TokenSearchSource.InactiveList, - tokens: [ - new TokenWithLogo(undefined, 5, '0x822397d9a55d0fefd20F5c4bCaB33C5F65bd28Eb', 6, 'cDAI', 'Compound DAI'), - ], - } - } - if (input === 'Coo') { - return { - source: TokenSearchSource.External, - tokens: [ - new TokenWithLogo( - 'https://etherscan.io/token/images/indexcoop_new_32.png', - 5, - '0x0954906da0Bf32d5479e25f46056d22f08464cab', - 18, - 'INDEX', - 'Index DAI' - ), - new TokenWithLogo( - 'https://etherscan.io/token/images/ETH2x-FLI_32.png', - 5, - '0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD', - 18, - 'ETH2x-FLI', - 'ETH 2x Flexible Leverage Index' - ), - ], - } - } - - return undefined -} diff --git a/apps/cowswap-frontend/src/modules/tokensList/pure/AllTokensList/index.tsx b/apps/cowswap-frontend/src/modules/tokensList/pure/AllTokensList/index.tsx deleted file mode 100644 index a687a0b832..0000000000 --- a/apps/cowswap-frontend/src/modules/tokensList/pure/AllTokensList/index.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { TokenAmount } from '@cowprotocol/ui' -import { Currency, CurrencyAmount } from '@uniswap/sdk-core' - -import * as styledEl from './styled' - -import { TokenWithLogo } from '../../types' -import { TokenInfo } from '../TokenInfo' - -export interface AllTokensListProps { - tokens: TokenWithLogo[] - selectedToken?: TokenWithLogo - balances: { [key: string]: CurrencyAmount } -} - -export function AllTokensList(props: AllTokensListProps) { - const { tokens, selectedToken, balances } = props - - return ( - - {tokens.map((token) => { - const isTokenSelected = token.address.toLowerCase() === selectedToken?.address.toLowerCase() - - return ( - - - - - - - ) - })} - - ) -} diff --git a/apps/cowswap-frontend/src/modules/tokensList/pure/AllTokensList/styled.ts b/apps/cowswap-frontend/src/modules/tokensList/pure/AllTokensList/styled.ts deleted file mode 100644 index 27b207ebf1..0000000000 --- a/apps/cowswap-frontend/src/modules/tokensList/pure/AllTokensList/styled.ts +++ /dev/null @@ -1,33 +0,0 @@ -import styled from 'styled-components/macro' - -import { UI } from 'common/constants/theme' - -export const Wrapper = styled.div` - border-top: 1px solid var(${UI.COLOR_BORDER}); - border-bottom: 1px solid var(${UI.COLOR_BORDER}); -` - -export const TokenItem = styled.button` - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - width: 100%; - background: none; - border: 0; - outline: none; - color: var(${UI.COLOR_TEXT1}); - cursor: pointer; - font-size: 16px; - padding: 10px 20px; - margin-bottom: 10px; - opacity: ${({ disabled }) => (disabled ? 0.5 : 1)}; - - &:last-child { - margin-bottom: 0; - } - - &:hover { - background-color: ${({ disabled }) => !disabled && `var(${UI.COLOR_GREY})`}; - } -` diff --git a/apps/cowswap-frontend/src/modules/tokensList/pure/LoadedTokenListItem/index.tsx b/apps/cowswap-frontend/src/modules/tokensList/pure/LoadedTokenListItem/index.tsx deleted file mode 100644 index 714a00b983..0000000000 --- a/apps/cowswap-frontend/src/modules/tokensList/pure/LoadedTokenListItem/index.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { CheckCircle } from 'react-feather' - -import * as styledEl from './styled' - -import { TokenList } from '../../types' -import { TokenListInfo } from '../TokenListInfo' - -export interface LoadedTokenListItemProps { - list: TokenList -} - -export function LoadedTokenListItem(props: LoadedTokenListItemProps) { - const { list } = props - - return ( - - -
- - - Loaded - -
-
- ) -} diff --git a/apps/cowswap-frontend/src/modules/tokensList/pure/LoadedTokenListItem/styled.ts b/apps/cowswap-frontend/src/modules/tokensList/pure/LoadedTokenListItem/styled.ts deleted file mode 100644 index 5125a5e65b..0000000000 --- a/apps/cowswap-frontend/src/modules/tokensList/pure/LoadedTokenListItem/styled.ts +++ /dev/null @@ -1,17 +0,0 @@ -import styled from 'styled-components/macro' - -export const Wrapper = styled.div` - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - margin: 20px; - padding: 0 10px; -` - -export const LoadedInfo = styled.div` - display: flex; - flex-direction: row; - gap: 10px; - align-items: center; -` diff --git a/apps/cowswap-frontend/src/modules/tokensList/pure/TokenListInfo/index.tsx b/apps/cowswap-frontend/src/modules/tokensList/pure/TokenListInfo/index.tsx deleted file mode 100644 index 53ade782b0..0000000000 --- a/apps/cowswap-frontend/src/modules/tokensList/pure/TokenListInfo/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { ReactNode } from 'react' - -import * as styledEl from './styled' - -import { TokenList } from '../../types' -import { TokenLogo } from '../TokenLogo' - -export interface TokenListItemProps { - list: TokenList - className?: string - children?: ReactNode -} - -export function TokenListInfo(props: TokenListItemProps) { - const { list, children, className } = props - - return ( - -
- -
-
- {list.name} - - {list.tokensCount} tokens {children} - -
-
- ) -} diff --git a/apps/cowswap-frontend/src/modules/tokensList/pure/TokenListInfo/styled.ts b/apps/cowswap-frontend/src/modules/tokensList/pure/TokenListInfo/styled.ts deleted file mode 100644 index c97f74be3e..0000000000 --- a/apps/cowswap-frontend/src/modules/tokensList/pure/TokenListInfo/styled.ts +++ /dev/null @@ -1,21 +0,0 @@ -import styled from 'styled-components/macro' - -export const ListInfo = styled.div` - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - gap: 10px; - font-size: 12px; -` - -export const ListName = styled.div` - font-size: 16px; - font-weight: 600; - margin-bottom: 5px; -` - -export const TokensInfo = styled.div` - display: flex; - gap: 5px; -` diff --git a/apps/cowswap-frontend/src/modules/tokensList/pure/TokenLogo/index.tsx b/apps/cowswap-frontend/src/modules/tokensList/pure/TokenLogo/index.tsx deleted file mode 100644 index 4ffc0aed23..0000000000 --- a/apps/cowswap-frontend/src/modules/tokensList/pure/TokenLogo/index.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { atom, useAtom } from 'jotai' - -import { Slash } from 'react-feather' -import styled from 'styled-components/macro' - -import { UI } from 'common/constants/theme' - -const invalidUrlsAtom = atom<{ [url: string]: boolean }>({}) - -const TokenLogoWrapper = styled.div` - display: inline-block; - background: var(${UI.COLOR_CONTAINER_BG_01}); - border-radius: 50%; -` - -export interface TokenLogoProps { - logoURI: string | undefined - className?: string - size?: number -} - -export function TokenLogo({ logoURI, className, size = 36 }: TokenLogoProps) { - const [invalidUrls, setInvalidUrls] = useAtom(invalidUrlsAtom) - - const hasError = invalidUrls[logoURI!] - - const onError = () => setInvalidUrls({ ...invalidUrls, [logoURI!]: true }) - - return ( - - {hasError || !logoURI ? ( - - ) : ( - - )} - - ) -} diff --git a/apps/cowswap-frontend/src/modules/tokensList/state/tokensListAtom.ts b/apps/cowswap-frontend/src/modules/tokensList/state/tokensListAtom.ts deleted file mode 100644 index 39d2add888..0000000000 --- a/apps/cowswap-frontend/src/modules/tokensList/state/tokensListAtom.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { atom } from 'jotai' - -import { TokenWithLogo } from '../types' - -export type TokensByAddress = { [address: string]: TokenWithLogo } - -export type TokensBySymbol = { [address: string]: TokenWithLogo[] } - -export const tokensByAddressAtom = atom({}) -export const tokensBySymbolAtom = atom({}) diff --git a/apps/cowswap-frontend/src/modules/tokensList/updaters/TokensListUpdater.ts b/apps/cowswap-frontend/src/modules/tokensList/updaters/TokensListUpdater.ts deleted file mode 100644 index 2f6aa18f98..0000000000 --- a/apps/cowswap-frontend/src/modules/tokensList/updaters/TokensListUpdater.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { useSetAtom } from 'jotai' -import { useEffect, useMemo } from 'react' - -import { useTokensListWithDefaults } from 'legacy/state/lists/hooks' - -import { tokensByAddressAtom, tokensBySymbolAtom } from 'modules/tokensList/state/tokensListAtom' - -import { TokenWithLogo } from '../types' - -/** - * This updater protects from redundant recalculations - * Using it we call useTokensListWithDefaults() and map it's result only once - */ -export function TokensListUpdater() { - const allTokens = useTokensListWithDefaults() - const updateTokensByAddress = useSetAtom(tokensByAddressAtom) - const updateTokensBySymbol = useSetAtom(tokensBySymbolAtom) - - // eslint-disable-next-line react-hooks/exhaustive-deps - const allTokensMemo = useMemo(() => allTokens, [JSON.stringify(allTokens)]) - - useEffect(() => { - const tokensByAddressMap: { [address: string]: TokenWithLogo } = {} - const tokensBySymbolMap: { [address: string]: TokenWithLogo[] } = {} - - allTokensMemo.forEach((token) => { - const wrappedToken: TokenWithLogo = new TokenWithLogo( - token.logoURI, - token.chainId, - token.address, - token.decimals, - token.symbol, - token.name - ) - const addressLowerCase = token.address.toLowerCase() - const symbolLowerCase = token.symbol.toLowerCase() - - if (!tokensByAddressMap[addressLowerCase]) { - tokensByAddressMap[addressLowerCase] = wrappedToken - } - - tokensBySymbolMap[symbolLowerCase] = tokensBySymbolMap[symbolLowerCase] || [] - - const isTokenInTheSymbolMap = !!tokensBySymbolMap[symbolLowerCase].find( - (token) => token.address.toLowerCase() === addressLowerCase - ) - if (!isTokenInTheSymbolMap) { - tokensBySymbolMap[symbolLowerCase].push(wrappedToken) - } - }) - - updateTokensByAddress(tokensByAddressMap) - updateTokensBySymbol(tokensBySymbolMap) - }, [allTokensMemo, updateTokensByAddress, updateTokensBySymbol]) - - return null -} diff --git a/apps/cowswap-frontend/src/modules/tokensList/utils/getTokensByAddress.ts b/apps/cowswap-frontend/src/modules/tokensList/utils/getTokensByAddress.ts deleted file mode 100644 index 3895274499..0000000000 --- a/apps/cowswap-frontend/src/modules/tokensList/utils/getTokensByAddress.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NATIVE_CURRENCY_BUY_TOKEN } from '@cowprotocol/common-const' -import { doesTokenMatchSymbolOrAddress } from '@cowprotocol/common-utils' -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { Token } from '@uniswap/sdk-core' - -import { TokensByAddress } from '../state/tokensListAtom' - -export function getTokensByAddress( - chainId: SupportedChainId, - tokenId: string, - tokensByAddress: TokensByAddress -): Token { - const nativeToken = NATIVE_CURRENCY_BUY_TOKEN[chainId] - - if (doesTokenMatchSymbolOrAddress(nativeToken, tokenId)) { - return nativeToken - } - - return tokensByAddress[tokenId.toLowerCase()] -} diff --git a/apps/cowswap-frontend/src/modules/twap/state/composableOrdersPopupMiddleware.ts b/apps/cowswap-frontend/src/modules/twap/state/composableOrdersPopupMiddleware.ts deleted file mode 100644 index eb9c3673ea..0000000000 --- a/apps/cowswap-frontend/src/modules/twap/state/composableOrdersPopupMiddleware.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { isTruthy } from '@cowprotocol/common-utils' -import { jotaiStore } from '@cowprotocol/core' - -import { isAnyOf } from '@reduxjs/toolkit' -import { Middleware } from 'redux' - -import { AppState } from 'legacy/state' -import { cancelOrdersBatch } from 'legacy/state/orders/actions' -import { batchCancelOrdersPopup } from 'legacy/state/orders/middleware/batchCancelOrdersPopup' - -import { setTwapOrderStatusAtom, twapOrdersAtom } from './twapOrdersListAtom' - -import { tokensByAddressAtom } from '../../tokensList/state/tokensListAtom' -import { TwapOrderStatus } from '../types' -import { mapTwapOrderToStoreOrder } from '../utils/mapTwapOrderToStoreOrder' - -const isCancelOrdersBatch = isAnyOf(cancelOrdersBatch) - -export const composableOrdersPopupMiddleware: Middleware, AppState> = - (store) => (next) => (action) => { - const result = next(action) - - if (isCancelOrdersBatch(action)) { - // TODO: generalize this to all composable orders, not only twap - const composableOrders = jotaiStore.get(twapOrdersAtom) - const tokensByAddress = jotaiStore.get(tokensByAddressAtom) - - // Select TWAP orders by ids - const cancelledOrdersItems = action.payload.ids.map((id) => composableOrders[id]).filter(isTruthy) - // Map TWAP orders to store orders - const cancelledOrders = cancelledOrdersItems.map((order) => mapTwapOrderToStoreOrder(order, tokensByAddress)) - - batchCancelOrdersPopup(store, cancelledOrders) - - cancelledOrders.forEach((order) => { - jotaiStore.set(setTwapOrderStatusAtom, order.id, TwapOrderStatus.Cancelled) - }) - } - - return result - } diff --git a/apps/cowswap-frontend/src/tokens/goerli-token-list.json b/apps/cowswap-frontend/src/tokens/goerli-token-list.json deleted file mode 100644 index c304206a8a..0000000000 --- a/apps/cowswap-frontend/src/tokens/goerli-token-list.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "name": "CoW Swap Goerli", - "version": { - "major": 0, - "minor": 0, - "patch": 0 - }, - "keywords": ["CowSwap", "Goerli", "tokens", "trusted"], - "logoURI": "https://gnosis.mypinata.cloud/ipfs/Qme9B6jRpGtZsRFcPjHvA5T4ugFuL4c3SzWfxyMPa59AMo", - "timestamp": "2022-05-26T10:45:01.151Z", - "tokens": [ - { - "name": "Gnosis", - "chainId": 5, - "symbol": "GNO", - "decimals": 18, - "address": "0x02abbdbaaa7b1bb64b5c878f7ac17f8dda169532", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6810e776880C02933D47DB1b9fc05908e5386b96/logo.png" - }, - { - "name": "CoW Protocol Token", - "chainId": 5, - "symbol": "COW", - "decimals": 18, - "address": "0x91056D4A53E1faa1A84306D4deAEc71085394bC8", - "logoURI": "https://gnosis.mypinata.cloud/ipfs/Qme9B6jRpGtZsRFcPjHvA5T4ugFuL4c3SzWfxyMPa59AMo" - }, - { - "name": "USD Coin", - "chainId": 5, - "symbol": "USDC", - "decimals": 6, - "address": "0xD87Ba7A50B2E7E660f678A895E4B72E7CB4CCd9C", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png" - }, - { - "name": "DAI", - "chainId": 5, - "symbol": "DAI", - "decimals": 18, - "address": "0xdc31Ee1784292379Fbb2964b3B9C4124D8F89C60", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png" - }, - { - "name": "0x", - "chainId": 5, - "symbol": "ZRX", - "decimals": 18, - "address": "0xe4E81Fa6B16327D4B78CFEB83AAdE04bA7075165", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE41d2489571d322189246DaFA5ebDe1F4699F498/logo.png" - }, - { - "name": "Basic Attention Token", - "chainId": 5, - "symbol": "BAT", - "decimals": 18, - "address": "0x70cBa46d2e933030E2f274AE58c951C800548AeF", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0D8775F648430679A709E98d2b0Cb6250d2887EF/logo.png" - }, - { - "name": "Polymath Network", - "chainId": 5, - "symbol": "POLY", - "decimals": 18, - "address": "0x9e32c0EfF886B6Ccae99350Fd5e7002dCED55F15", - "logoURI": "https://assets.coingecko.com/coins/images/2784/thumb/inKkF01.png?1605007034" - } - ] -} diff --git a/libs/common-const/src/gnosis_chain/constants.ts b/libs/common-const/src/gnosis_chain/constants.ts deleted file mode 100644 index 839301f138..0000000000 --- a/libs/common-const/src/gnosis_chain/constants.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' -import { Token } from '@uniswap/sdk-core' - -// native constants -export const XDAI_SYMBOL = 'XDAI' -export const XDAI_NAME = 'xDai' - -// xDAI tokens -export const WXDAI = new Token( - ChainId.GNOSIS_CHAIN, - '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d', - 18, - 'WXDAI', - 'Wrapped XDAI' -) -export const HONEY_GNOSIS_CHAIN = new Token( - ChainId.GNOSIS_CHAIN, - '0x71850b7e9ee3f13ab46d67167341e4bdc905eef9', - 18, - 'HNY', - 'Honey' -) -export const USDT_GNOSIS_CHAIN = new Token( - ChainId.GNOSIS_CHAIN, - '0x4ECaBa5870353805a9F068101A40E0f32ed605C6', - 6, - 'USDT', - 'Tether USD' -) -export const USDC_GNOSIS_CHAIN = new Token( - ChainId.GNOSIS_CHAIN, - '0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83', - 6, - 'USDC', - 'USD Coin' -) -export const SUSD_GNOSIS_CHAIN = new Token( - ChainId.GNOSIS_CHAIN, - '0xB1950Fb2C9C0CbC8553578c67dB52Aa110A93393', - 18, - 'sUSD', - 'Synth sUSD' -) -export const WBTC_GNOSIS_CHAIN = new Token( - ChainId.GNOSIS_CHAIN, - '0x8e5bbbb09ed1ebde8674cda39a0c169401db4252', - 8, - 'WBTC', - 'Wrapped BTC' -) -export const WETH_GNOSIS_CHAIN = new Token( - ChainId.GNOSIS_CHAIN, - '0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1', - 18, - 'WETH', - 'Wrapped Ether on Gnosis Chain' -) - -export const GNO_GNOSIS_CHAIN = new Token( - ChainId.GNOSIS_CHAIN, - '0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb', - 18, - 'GNO', - 'Gnosis Token' -) -export const STAKE_GNOSIS_CHAIN = new Token( - ChainId.GNOSIS_CHAIN, - '0xb7D311E2Eb55F2f68a9440da38e7989210b9A05e', - 18, - 'STAKE', - 'STAKE' -) -export const OWL_GNOSIS_CHAIN = new Token( - ChainId.GNOSIS_CHAIN, - '0x0905Ab807F8FD040255F0cF8fa14756c1D824931', - 18, - 'OWL', - 'OWL' -) diff --git a/libs/common-const/src/goerli/constants.ts b/libs/common-const/src/goerli/constants.ts deleted file mode 100644 index 49b8e12f05..0000000000 --- a/libs/common-const/src/goerli/constants.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' -import { Token } from '@uniswap/sdk-core' - -// Todo: address should be updated -export const DAI_GOERLI = new Token(ChainId.GOERLI, '0xdc31Ee1784292379Fbb2964b3B9C4124D8F89C60', 18, 'DAI', 'DAI') - -export const USDT_GOERLI = new Token( - ChainId.GOERLI, - '0xe583769738b6dd4e7caf8451050d1948be717679', - 6, - 'USDT', - 'Tether USD' -) -export const USDC_GOERLI = new Token( - ChainId.GOERLI, - '0xD87Ba7A50B2E7E660f678A895E4B72E7CB4CCd9C', - 6, - 'USDC', - 'USD Coin' -) -export const WBTC_GOERLI = new Token( - ChainId.GOERLI, - '0xca063a2ab07491ee991dcecb456d1265f842b568', - 8, - 'WBTC', - 'Wrapped BTC' -) -export const WETH_GOERLI = new Token( - ChainId.GOERLI, - '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', - 18, - 'WETH', - 'Wrapped Görli Ether' -) diff --git a/libs/common-const/src/lists.ts b/libs/common-const/src/lists.ts deleted file mode 100644 index 7c57d026eb..0000000000 --- a/libs/common-const/src/lists.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' - -import { RAW_CODE_LINK } from './common' - -export type NetworkLists = { - [chain in ChainId]: string[] -} - -const COW_DAO_LIST = 'https://files.cow.fi/tokens/CowSwap.json' -const COW_COINGECKO_LIST = 'https://files.cow.fi/tokens/CoinGecko.json' - -const COMPOUND_LIST = 'https://raw.githubusercontent.com/compound-finance/token-list/master/compound.tokenlist.json' -const AAVE_LIST = 'tokenlist.aave.eth' -const SYNTHETIX_LIST = 'synths.snx.eth' -const WRAPPED_LIST = 'wrapped.tokensoft.eth' -const SET_LIST = 'https://raw.githubusercontent.com/SetProtocol/uniswap-tokenlist/main/set.tokenlist.json' -const OPYN_LIST = 'https://raw.githubusercontent.com/opynfinance/opyn-tokenlist/master/opyn-squeeth-tokenlist.json' -const ROLL_LIST = 'https://app.tryroll.com/tokens.json' -// const COINGECKO_LIST = 'https://tokens.coingecko.com/uniswap/all.json' -const CMC_ALL_LIST = 'defi.cmc.eth' -const CMC_STABLECOIN = 'stablecoin.cmc.eth' -const KLEROS_LIST = 't2crtokens.eth' -const BA_LIST = 'https://raw.githubusercontent.com/The-Blockchain-Association/sec-notice-list/master/ba-sec-list.json' - -// Goerli Default -const GOERLI_LIST = RAW_CODE_LINK + '/develop/apps/cowswap-frontend/src/tokens/goerli-token-list.json' - -// XDAI Default -const HONEY_SWAP_XDAI = 'https://tokens.honeyswap.org' - -export const UNSUPPORTED_LIST_URLS: NetworkLists = { - [ChainId.MAINNET]: [BA_LIST], - [ChainId.GOERLI]: [BA_LIST], - [ChainId.GNOSIS_CHAIN]: [BA_LIST], -} - -function buildNetworkDefaultLists({ networkLists, chainId }: { chainId: ChainId; networkLists: string[] }) { - // need to add unsupported lists as well - return [...UNSUPPORTED_LIST_URLS[chainId], ...networkLists] -} - -// lower index == higher priority for token import -export const DEFAULT_LIST_OF_LISTS_BY_NETWORK: NetworkLists = { - [ChainId.MAINNET]: buildNetworkDefaultLists({ - chainId: ChainId.MAINNET, - networkLists: [ - COW_DAO_LIST, - COW_COINGECKO_LIST, - COMPOUND_LIST, - AAVE_LIST, - SYNTHETIX_LIST, - WRAPPED_LIST, - SET_LIST, - OPYN_LIST, - ROLL_LIST, - // COINGECKO_LIST, - CMC_ALL_LIST, - CMC_STABLECOIN, - KLEROS_LIST, - ], - }), - [ChainId.GOERLI]: buildNetworkDefaultLists({ - chainId: ChainId.GOERLI, - networkLists: [GOERLI_LIST, COMPOUND_LIST], - }), - [ChainId.GNOSIS_CHAIN]: buildNetworkDefaultLists({ - chainId: ChainId.GNOSIS_CHAIN, - networkLists: [COW_DAO_LIST, HONEY_SWAP_XDAI], - }), -} - -// default lists to be 'active' aka searched across -export const DEFAULT_ACTIVE_LIST_URLS_BY_NETWORK: NetworkLists = { - [ChainId.MAINNET]: [COW_DAO_LIST, COW_COINGECKO_LIST], - [ChainId.GNOSIS_CHAIN]: [COW_DAO_LIST, HONEY_SWAP_XDAI], - [ChainId.GOERLI]: [COW_DAO_LIST, GOERLI_LIST], -} - -// Set what we want as the default list when no chain id available: default = MAINNET -export const DEFAULT_NETWORK_FOR_LISTS = ChainId.MAINNET diff --git a/libs/common-hooks/src/useColor/index.ts b/libs/common-hooks/src/useColor/index.ts deleted file mode 100644 index f1c2c8b8e1..0000000000 --- a/libs/common-hooks/src/useColor/index.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { useLayoutEffect, useState } from 'react' - -import Vibrant from 'node-vibrant/lib/bundle' -import { shade } from 'polished' -import { hex } from 'wcag-contrast' -import { uriToHttp } from '@cowprotocol/common-utils' - -export async function getColorFromUriPath(uri: string): Promise { - const formattedPath = uriToHttp(uri)[0] - - let palette - - try { - palette = await Vibrant.from(formattedPath).getPalette() - } catch (err: any) { - return null - } - if (!palette?.Vibrant) { - return null - } - - let detectedHex = palette.Vibrant.hex - let AAscore = hex(detectedHex, '#FFF') - while (AAscore < 3) { - detectedHex = shade(0.005, detectedHex) - AAscore = hex(detectedHex, '#FFF') - } - - return detectedHex -} - -export function useListColor(listImageUri?: string) { - const [color, setColor] = useState('#2172E5') - - useLayoutEffect(() => { - let stale = false - - if (listImageUri) { - getColorFromUriPath(listImageUri) - .then((color) => { - if (!stale && color !== null) { - setColor(color) - } - }) - .catch(console.warn) // mod: error handling - } - - return () => { - stale = true - setColor('#2172E5') - } - }, [listImageUri]) - - return color -} diff --git a/libs/common-utils/src/currencyId.ts b/libs/common-utils/src/currencyId.ts deleted file mode 100644 index 1fe6b0a846..0000000000 --- a/libs/common-utils/src/currencyId.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Currency } from '@uniswap/sdk-core' - -export function currencyId(currency: Currency): string { - if (currency.isNative) return 'ETH' - if (currency.isToken) return currency.address - throw new Error('invalid currency') -} From 4c73ccae6ff4b9402513a6a0ed3b5947e2457c25 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Mon, 23 Oct 2023 13:06:49 +0600 Subject: [PATCH 2/3] chore: remove unused files --- src/tokens/README.md | 2 - src/tokens/goerli-token-list.json | 69 ------------------------------- 2 files changed, 71 deletions(-) delete mode 100644 src/tokens/README.md delete mode 100644 src/tokens/goerli-token-list.json diff --git a/src/tokens/README.md b/src/tokens/README.md deleted file mode 100644 index 203cd64185..0000000000 --- a/src/tokens/README.md +++ /dev/null @@ -1,2 +0,0 @@ -This is temporary Goerli tokens file for backward compatibility. -It should be removed after the first release with the GOERLI_LIST update. diff --git a/src/tokens/goerli-token-list.json b/src/tokens/goerli-token-list.json deleted file mode 100644 index c304206a8a..0000000000 --- a/src/tokens/goerli-token-list.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "name": "CoW Swap Goerli", - "version": { - "major": 0, - "minor": 0, - "patch": 0 - }, - "keywords": ["CowSwap", "Goerli", "tokens", "trusted"], - "logoURI": "https://gnosis.mypinata.cloud/ipfs/Qme9B6jRpGtZsRFcPjHvA5T4ugFuL4c3SzWfxyMPa59AMo", - "timestamp": "2022-05-26T10:45:01.151Z", - "tokens": [ - { - "name": "Gnosis", - "chainId": 5, - "symbol": "GNO", - "decimals": 18, - "address": "0x02abbdbaaa7b1bb64b5c878f7ac17f8dda169532", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6810e776880C02933D47DB1b9fc05908e5386b96/logo.png" - }, - { - "name": "CoW Protocol Token", - "chainId": 5, - "symbol": "COW", - "decimals": 18, - "address": "0x91056D4A53E1faa1A84306D4deAEc71085394bC8", - "logoURI": "https://gnosis.mypinata.cloud/ipfs/Qme9B6jRpGtZsRFcPjHvA5T4ugFuL4c3SzWfxyMPa59AMo" - }, - { - "name": "USD Coin", - "chainId": 5, - "symbol": "USDC", - "decimals": 6, - "address": "0xD87Ba7A50B2E7E660f678A895E4B72E7CB4CCd9C", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png" - }, - { - "name": "DAI", - "chainId": 5, - "symbol": "DAI", - "decimals": 18, - "address": "0xdc31Ee1784292379Fbb2964b3B9C4124D8F89C60", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png" - }, - { - "name": "0x", - "chainId": 5, - "symbol": "ZRX", - "decimals": 18, - "address": "0xe4E81Fa6B16327D4B78CFEB83AAdE04bA7075165", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xE41d2489571d322189246DaFA5ebDe1F4699F498/logo.png" - }, - { - "name": "Basic Attention Token", - "chainId": 5, - "symbol": "BAT", - "decimals": 18, - "address": "0x70cBa46d2e933030E2f274AE58c951C800548AeF", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x0D8775F648430679A709E98d2b0Cb6250d2887EF/logo.png" - }, - { - "name": "Polymath Network", - "chainId": 5, - "symbol": "POLY", - "decimals": 18, - "address": "0x9e32c0EfF886B6Ccae99350Fd5e7002dCED55F15", - "logoURI": "https://assets.coingecko.com/coins/images/2784/thumb/inKkF01.png?1605007034" - } - ] -} From 9c4bfdf7efc78935aac36cb39c6ab1618bdb7e44 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Thu, 26 Oct 2023 18:36:42 +0600 Subject: [PATCH 3/3] fix(tokens): fix tokens list loading state (#3201) --- .../src/common/hooks/useNativeBalance.ts | 2 +- .../src/common/updaters/FeesUpdater.ts | 20 +- .../src/common/updaters/orders/utils.ts | 4 +- .../src/legacy/components/Header/index.tsx | 1 - .../LegacyConfirmationPendingContent.tsx | 9 +- .../index.cosmos.tsx | 5 +- .../legacy/hooks/useRefetchPriceCallback.tsx | 8 +- .../src/legacy/hooks/useTokenLazy.ts | 189 -------- .../src/legacy/state/orders/hooks.ts | 8 +- .../src/legacy/state/orders/utils.ts | 2 +- .../src/legacy/state/user/hooks.tsx | 64 +-- .../src/legacy/state/user/reducer.ts | 58 --- .../src/legacy/state/user/types.ts | 10 +- .../application/containers/App/Updaters.tsx | 3 +- .../orders/hooks/useTokensForOrdersList.ts | 15 +- .../tradeReceiveAmount.test.ts.snap | 20 +- .../containers/ManageLists/index.tsx | 9 +- .../containers/ManageLists/styled.ts | 5 + .../tokensList/pure/ModalHeader/index.tsx | 14 +- .../pure/SelectTokenModal/styled.ts | 25 +- .../tokensList/utils/tokensListSorter.ts | 6 +- .../tradeQuote/hooks/useTradeQuotePolling.ts | 4 +- apps/widget-configurator/src/app/app.spec.tsx | 15 - .../src/app/configurator/index.tsx | 7 +- .../src/nativeAndWrappedTokens.ts | 4 +- libs/common-const/src/tokens.ts | 7 +- libs/common-const/src/types.ts | 2 +- libs/tokens/src/pure/TokenLogo/index.tsx | 1 + libs/tokens/src/state/tokens/allTokensAtom.ts | 8 +- libs/tokens/src/utils/getTokenLogoUrls.ts | 1 - yarn.lock | 441 +----------------- 31 files changed, 142 insertions(+), 825 deletions(-) delete mode 100644 apps/cowswap-frontend/src/legacy/hooks/useTokenLazy.ts delete mode 100644 apps/widget-configurator/src/app/app.spec.tsx diff --git a/apps/cowswap-frontend/src/common/hooks/useNativeBalance.ts b/apps/cowswap-frontend/src/common/hooks/useNativeBalance.ts index c15af959b1..15e3353abf 100644 --- a/apps/cowswap-frontend/src/common/hooks/useNativeBalance.ts +++ b/apps/cowswap-frontend/src/common/hooks/useNativeBalance.ts @@ -7,7 +7,7 @@ import ms from 'ms.macro' import useSWR, { SWRConfiguration, SWRResponse } from 'swr' const swrOptions: SWRConfiguration = { - refreshInterval: ms`1m`, + refreshInterval: ms`30s`, revalidateOnFocus: false, } diff --git a/apps/cowswap-frontend/src/common/updaters/FeesUpdater.ts b/apps/cowswap-frontend/src/common/updaters/FeesUpdater.ts index 550d088745..3dafd58730 100644 --- a/apps/cowswap-frontend/src/common/updaters/FeesUpdater.ts +++ b/apps/cowswap-frontend/src/common/updaters/FeesUpdater.ts @@ -8,8 +8,6 @@ import { useENSAddress } from '@cowprotocol/ens' import { useIsUnsupportedToken } from '@cowprotocol/tokens' import { useWalletInfo } from '@cowprotocol/wallet' -import ms from 'ms.macro' - import { useRefetchQuoteCallback } from 'legacy/hooks/useRefetchPriceCallback' import { useAllQuotes, useIsBestQuoteLoading, useSetQuoteError } from 'legacy/state/price/hooks' import { QuoteInformationObject } from 'legacy/state/price/reducer' @@ -31,14 +29,6 @@ export const TYPED_VALUE_DEBOUNCE_TIME = 350 const REFETCH_CHECK_INTERVAL = 10000 // Every 10s const RENEW_FEE_QUOTES_BEFORE_EXPIRATION_TIME = 30000 // Will renew the quote if there's less than 30 seconds left for the quote to expire const WAITING_TIME_BETWEEN_EQUAL_REQUESTS = 5000 // Prevents from sending the same request to often (max, every 5s) -const UNSUPPORTED_TOKEN_TTL = ms`1h` - -/** - * Since a token might become supported, we should periodically (once in 1h) refresh its state - */ -const isUnsupportedTokenExpired = ({ dateAdded }: { dateAdded: number }) => { - return dateAdded + UNSUPPORTED_TOKEN_TTL < Date.now() -} type FeeQuoteParams = Omit @@ -158,7 +148,7 @@ export function FeesUpdater(): null { const isLoading = useIsBestQuoteLoading() const isEthFlow = useIsEoaEthFlow() - const isUnsupportedTokenGp = useIsUnsupportedToken() + const isUnsupportedToken = useIsUnsupportedToken() const appData = useAppData() @@ -238,14 +228,12 @@ export function FeesUpdater(): null { } } - const unsupportedToken = isUnsupportedTokenGp(sellCurrencyId) || isUnsupportedTokenGp(buyCurrencyId) + const unsupportedToken = isUnsupportedToken(sellCurrencyId) || isUnsupportedToken(buyCurrencyId) // Callback to re-fetch both the fee and the price const refetchQuoteIfRequired = () => { // if no token is unsupported and needs refetching - const hasToRefetch = - (!unsupportedToken || isUnsupportedTokenExpired(unsupportedToken)) && - isRefetchQuoteRequired(isLoading, quoteParams, quoteInfo) + const hasToRefetch = !unsupportedToken && isRefetchQuoteRequired(isLoading, quoteParams, quoteInfo) if (hasToRefetch) { // Decide if this is a new quote, or just a refresh @@ -286,7 +274,7 @@ export function FeesUpdater(): null { buyCurrency, quoteInfo, refetchQuote, - isUnsupportedTokenGp, + isUnsupportedToken, isLoading, setQuoteError, account, diff --git a/apps/cowswap-frontend/src/common/updaters/orders/utils.ts b/apps/cowswap-frontend/src/common/updaters/orders/utils.ts index 0ca27a6af0..7145865ba2 100644 --- a/apps/cowswap-frontend/src/common/updaters/orders/utils.ts +++ b/apps/cowswap-frontend/src/common/updaters/orders/utils.ts @@ -9,7 +9,9 @@ import { stringToCurrency } from 'legacy/state/swap/extension' import { getOrder } from 'api/gnosisProtocol' import { getIsComposableCowChildOrder } from 'utils/orderUtils/getIsComposableCowChildOrder' -export type OrderLogPopupMixData = OrderFulfillmentData | string +type OrderID = string + +export type OrderLogPopupMixData = OrderFulfillmentData | OrderID export function computeOrderSummary({ orderFromStore, diff --git a/apps/cowswap-frontend/src/legacy/components/Header/index.tsx b/apps/cowswap-frontend/src/legacy/components/Header/index.tsx index c6be2e8125..96443e678b 100644 --- a/apps/cowswap-frontend/src/legacy/components/Header/index.tsx +++ b/apps/cowswap-frontend/src/legacy/components/Header/index.tsx @@ -23,7 +23,6 @@ import { getDefaultTradeRawState } from 'modules/trade/types/TradeRawState' import { Routes } from 'common/constants/routes' import { useCategorizeRecentActivity } from 'common/hooks/useCategorizeRecentActivity' import { useIsProviderNetworkUnsupported } from 'common/hooks/useIsProviderNetworkUnsupported' -import { useNativeBalance } from 'common/hooks/useNativeBalance' import { AccountElement } from './AccountElement' import MobileMenuIcon from './MobileMenuIcon' diff --git a/apps/cowswap-frontend/src/legacy/components/TransactionConfirmationModal/LegacyConfirmationPendingContent.tsx b/apps/cowswap-frontend/src/legacy/components/TransactionConfirmationModal/LegacyConfirmationPendingContent.tsx index f7f117013c..61a3f2bfa9 100644 --- a/apps/cowswap-frontend/src/legacy/components/TransactionConfirmationModal/LegacyConfirmationPendingContent.tsx +++ b/apps/cowswap-frontend/src/legacy/components/TransactionConfirmationModal/LegacyConfirmationPendingContent.tsx @@ -2,8 +2,9 @@ import React, { ReactNode, useMemo } from 'react' import alertImage from '@cowprotocol/assets/cow-swap/alert-circle.svg' import checkImage from '@cowprotocol/assets/cow-swap/check.svg' -import { getChainCurrencySymbols } from '@cowprotocol/common-const' +import { NATIVE_CURRENCY_BUY_TOKEN, WRAPPED_NATIVE_CURRENCY } from '@cowprotocol/common-const' import { shortenAddress } from '@cowprotocol/common-utils' +import { SupportedChainId } from '@cowprotocol/cow-sdk' import { ExternalLink } from '@cowprotocol/ui' import { getWeb3ReactConnection, @@ -47,7 +48,11 @@ enum WalletType { } export function getOperationMessage(operationType: ConfirmOperationType, chainId: number): string { - const { native, wrapped } = getChainCurrencySymbols(chainId) + const nativeToken = NATIVE_CURRENCY_BUY_TOKEN[chainId as SupportedChainId] + const wrappedToken = WRAPPED_NATIVE_CURRENCY[chainId as SupportedChainId] + + const native = nativeToken.symbol + const wrapped = wrappedToken.symbol switch (operationType) { case ConfirmOperationType.WRAP_ETHER: diff --git a/apps/cowswap-frontend/src/legacy/components/TransactionConfirmationModal/index.cosmos.tsx b/apps/cowswap-frontend/src/legacy/components/TransactionConfirmationModal/index.cosmos.tsx index 0a1630b537..a2eb0ddd1f 100644 --- a/apps/cowswap-frontend/src/legacy/components/TransactionConfirmationModal/index.cosmos.tsx +++ b/apps/cowswap-frontend/src/legacy/components/TransactionConfirmationModal/index.cosmos.tsx @@ -9,6 +9,7 @@ import { ConfirmOperationType } from 'legacy/state/types' import { LegacyConfirmationPendingContent } from './LegacyConfirmationPendingContent' import { cowSwapStore } from '../../state' +import { SerializedToken } from '../../state/user/types' const txHash = '0xe87e1d02b052daa9605abe018e8172feffd1bc38ed2284e6hhhhhh' + Date.now() @@ -46,8 +47,8 @@ cowSwapStore.dispatch( id: txHash, chainId: 5, order: { - inputToken: COW[SupportedChainId.MAINNET], - outputToken: GNO[SupportedChainId.MAINNET], + inputToken: COW[SupportedChainId.MAINNET] as SerializedToken, + outputToken: GNO[SupportedChainId.MAINNET] as SerializedToken, sellToken: COW[SupportedChainId.MAINNET].address, buyToken: GNO[SupportedChainId.MAINNET].address, sellAmount: '1000', diff --git a/apps/cowswap-frontend/src/legacy/hooks/useRefetchPriceCallback.tsx b/apps/cowswap-frontend/src/legacy/hooks/useRefetchPriceCallback.tsx index bb00de1276..a338aea1b1 100644 --- a/apps/cowswap-frontend/src/legacy/hooks/useRefetchPriceCallback.tsx +++ b/apps/cowswap-frontend/src/legacy/hooks/useRefetchPriceCallback.tsx @@ -171,13 +171,17 @@ export function useRefetchQuoteCallback() { description: GpQuoteErrorDetails.ZeroPrice, }) - const previouslyUnsupportedToken = getIsUnsupportedToken(sellToken) || getIsUnsupportedToken(buyToken) + const previouslyUnsupportedToken = getIsUnsupportedToken(sellToken) + ? sellToken + : getIsUnsupportedToken(buyToken) + ? buyToken + : null // can be a previously unsupported token which is now valid // so we check against map and remove it if (previouslyUnsupportedToken) { console.debug('[useRefetchPriceCallback]::Previously unsupported token now supported - re-enabling.') - removeGpUnsupportedToken(previouslyUnsupportedToken.address) + removeGpUnsupportedToken(previouslyUnsupportedToken) } // Update quote diff --git a/apps/cowswap-frontend/src/legacy/hooks/useTokenLazy.ts b/apps/cowswap-frontend/src/legacy/hooks/useTokenLazy.ts deleted file mode 100644 index 017d38390d..0000000000 --- a/apps/cowswap-frontend/src/legacy/hooks/useTokenLazy.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { useCallback } from 'react' - -import { Erc20 } from '@cowprotocol/abis' -import { getBytes32TokenContract, getTokenContract } from '@cowprotocol/common-hooks' -import { retry } from '@cowprotocol/common-utils' -import { useWalletInfo } from '@cowprotocol/wallet' -import { arrayify } from '@ethersproject/bytes' -import { Contract } from '@ethersproject/contracts' -import { JsonRpcProvider } from '@ethersproject/providers' -import { parseBytes32String } from '@ethersproject/strings' -import { Token } from '@uniswap/sdk-core' -import { useWeb3React } from '@web3-react/core' - -import { useAddUserToken } from 'legacy/state/user/hooks' - -const contractsCache: Record = {} -const bytes32ContractsCache: Record = {} - -const RETRY_OPTIONS = { n: 3, minWait: 100, maxWait: 3_000 } - -export interface TokenInfo { - decimals: number - symbol?: string - name?: string -} - -export interface GetTokenInfoParams { - chainId: number - address: string - account: string - - provider: JsonRpcProvider -} - -// parse a name or symbol from a token response -const BYTES32_REGEX = /^0x[a-fA-F0-9]{64}$/ - -function parseStringOrBytes32(str: string | undefined, bytes32: string | undefined, defaultValue: string): string { - return str && str.length > 0 - ? str - : // need to check for proper bytes string and valid terminator - bytes32 && BYTES32_REGEX.test(bytes32) && arrayify(bytes32)[31] === 0 - ? parseBytes32String(bytes32) - : defaultValue -} - -async function _getTokenContract(params: GetTokenInfoParams): Promise { - const { address, account, chainId, provider } = params - - return contractsCache[address] || getTokenContract(address, undefined, provider, account, chainId) -} - -async function _getTokenBytes32Contract(params: GetTokenInfoParams): Promise { - const { address, account, chainId, provider } = params - - return bytes32ContractsCache[address] || getBytes32TokenContract(address, undefined, provider, account, chainId) -} - -async function _getBytes32NameAndSymbol( - address: string, - account: string, - chainId: number, - nameSettled: PromiseFulfilledResult | PromiseRejectedResult, - symbolSettled: PromiseFulfilledResult | PromiseRejectedResult, - params: GetTokenInfoParams -) { - console.debug( - `[useTokenLazy::callback] name or symbol failed, trying bytes32`, - address, - account, - chainId, - nameSettled, - symbolSettled - ) - - const bytes32Contract = await _getTokenBytes32Contract(params) - const [nameBytes32Settled, symbolBytes32Settled] = await Promise.allSettled([ - retry(bytes32Contract.name, RETRY_OPTIONS).promise, - retry(bytes32Contract.symbol, RETRY_OPTIONS).promise, - ]) - - // Merge regular and bytes32 versions in case one them was good - const name = parseStringOrBytes32( - nameSettled.status === 'fulfilled' ? nameSettled.value : '', - nameBytes32Settled.status === 'fulfilled' ? nameBytes32Settled.value : '', - 'Unknown Token' - ) - const symbol = parseStringOrBytes32( - symbolSettled.status === 'fulfilled' ? symbolSettled.value : '', - symbolBytes32Settled.status === 'fulfilled' ? (symbolBytes32Settled.value as any) : '', - 'UNKNOWN' - ) - return { name, symbol } -} - -async function _getNameAndSymbol( - namePromise: Promise, - symbolPromise: Promise, - address: string, - account: string, - chainId: number, - params: GetTokenInfoParams -) { - const [nameSettled, symbolSettled] = await Promise.allSettled([namePromise, symbolPromise]) - - let name, symbol - // If no name or symbol, try the bytes32 contract - if ( - nameSettled.status === 'rejected' || - !nameSettled.value || - symbolSettled.status === 'rejected' || - !symbolSettled.value - ) { - const bytes32Values = await _getBytes32NameAndSymbol(address, account, chainId, nameSettled, symbolSettled, params) - name = bytes32Values.name - symbol = bytes32Values.symbol - } else { - name = nameSettled.value - symbol = symbolSettled.value - } - return { name, symbol } -} - -async function _getTokenInfo(params: GetTokenInfoParams): Promise { - const { address, account, chainId } = params - console.debug(`[useTokenLazy::callback] input is valid`, address, account, chainId) - - const contract = await _getTokenContract(params) - - // Fetch each property individually - const { promise: decimalsPromise } = retry(contract.decimals, RETRY_OPTIONS) - const { promise: namePromise, cancel: cancelNamePromise } = retry(contract.name, RETRY_OPTIONS) - const { promise: symbolPromise, cancel: cancelSymbolPromise } = retry(contract.symbol, RETRY_OPTIONS) - - let decimals: number - - try { - // If no decimals, stop here - decimals = await decimalsPromise - } catch (e: any) { - console.debug(`[useTokenLazy::callback] no decimals, stopping`, address, account, chainId, e) - - cancelNamePromise() - cancelSymbolPromise() - return null - } - const { name, symbol } = await _getNameAndSymbol(namePromise, symbolPromise, address, account, chainId, params) - - return { decimals, symbol, name } -} - -/** - * @deprecated TODO: use libs/tokens - * Hook that returns a callback which fetches data for a single token from the chain - * - * Alternative to hooks/Token/useToken where token address does not need to be known - * at hook phase nor uses multicall - */ -export function useTokenLazy() { - const { provider } = useWeb3React() - const { account, chainId } = useWalletInfo() - const addUserToken = useAddUserToken() - - return useCallback( - async (address: string): Promise => { - console.debug(`[useTokenLazy::callback] callback called`, address, account, chainId) - - if (!account || !chainId || !address || !provider) { - return null - } - - const tokenInfo = await _getTokenInfo({ chainId, account, address, provider }) - - if (!tokenInfo) { - return null - } - - const { decimals, symbol, name } = tokenInfo - const token = new Token(chainId, address, decimals, symbol, name) - - console.debug(`[useTokenLazy::callback] loaded token`, address, account, chainId, token) - - addUserToken(token) - - return token - }, - [account, addUserToken, chainId, provider] - ) -} diff --git a/apps/cowswap-frontend/src/legacy/state/orders/hooks.ts b/apps/cowswap-frontend/src/legacy/state/orders/hooks.ts index 66510c0b1e..f8306f4195 100644 --- a/apps/cowswap-frontend/src/legacy/state/orders/hooks.ts +++ b/apps/cowswap-frontend/src/legacy/state/orders/hooks.ts @@ -1,5 +1,6 @@ import { useCallback, useMemo } from 'react' +import { TokenWithLogo } from '@cowprotocol/common-const' import { isTruthy } from '@cowprotocol/common-utils' import { OrderClass, SupportedChainId } from '@cowprotocol/cow-sdk' @@ -42,7 +43,8 @@ import { import { isOrderExpired, partialOrderUpdate } from './utils' import { AppDispatch, AppState } from '../index' -import { deserializeToken, serializeToken } from '../user/hooks' +import { serializeToken } from '../user/hooks' +import { SerializedToken } from '../user/types' type OrderID = string @@ -110,6 +112,10 @@ function _isV3Order(orderObject: any): orderObject is OrderObject { return orderObject?.order?.inputToken !== undefined || orderObject?.order?.outputToken !== undefined } +function deserializeToken(serializedToken: SerializedToken): TokenWithLogo { + return TokenWithLogo.fromToken(serializedToken, serializedToken.logoURI) +} + function _deserializeOrder(orderObject: OrderObject | V2OrderObject | undefined) { let order: Order | undefined // we need to make sure the incoming order is a valid diff --git a/apps/cowswap-frontend/src/legacy/state/orders/utils.ts b/apps/cowswap-frontend/src/legacy/state/orders/utils.ts index 3461931b06..98ea423d4c 100644 --- a/apps/cowswap-frontend/src/legacy/state/orders/utils.ts +++ b/apps/cowswap-frontend/src/legacy/state/orders/utils.ts @@ -382,7 +382,7 @@ export function partialOrderUpdate({ chainId, order }: UpdateOrderParams, dispat ...order, ...(order.inputToken && { inputToken: serializeToken(order.inputToken) }), ...(order.outputToken && { outputToken: serializeToken(order.outputToken) }), - }, + } as UpdateOrderParamsAction['order'], } dispatch(updateOrder(params)) } diff --git a/apps/cowswap-frontend/src/legacy/state/user/hooks.tsx b/apps/cowswap-frontend/src/legacy/state/user/hooks.tsx index 48d61edd78..5d60adb989 100644 --- a/apps/cowswap-frontend/src/legacy/state/user/hooks.tsx +++ b/apps/cowswap-frontend/src/legacy/state/user/hooks.tsx @@ -1,17 +1,19 @@ import { useCallback, useMemo } from 'react' -import { L2_DEADLINE_FROM_NOW, NATIVE_CURRENCY_BUY_TOKEN, SupportedLocale } from '@cowprotocol/common-const' +import { + L2_DEADLINE_FROM_NOW, + NATIVE_CURRENCY_BUY_TOKEN, + SupportedLocale, + TokenWithLogo, +} from '@cowprotocol/common-const' import { calculateValidTo, getIsNativeToken } from '@cowprotocol/common-utils' import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { useWalletInfo } from '@cowprotocol/wallet' -import { Currency, Percent, Token } from '@uniswap/sdk-core' +import { Currency, Percent } from '@uniswap/sdk-core' import JSBI from 'jsbi' import { shallowEqual } from 'react-redux' import { - addSerializedToken, - removeSerializedToken, updateRecipientToggleVisible, updateUserDarkMode, updateUserDeadline, @@ -25,16 +27,6 @@ import { useAppDispatch, useAppSelector } from '../hooks' import { AppState } from '../index' import { setRecipient } from '../swap/actions' -export function deserializeToken(serializedToken: SerializedToken): Token { - return new Token( - serializedToken.chainId, - serializedToken.address, - serializedToken.decimals, - serializedToken.symbol, - serializedToken.name - ) -} - export function useIsDarkMode(): boolean { const { userDarkMode, matchesDarkMode } = useAppSelector( ({ user: { matchesDarkMode, userDarkMode } }) => ({ @@ -185,39 +177,6 @@ export function useUserTransactionTTL(): [number, (slippage: number) => void] { return [deadline, setUserDeadline] } -export function useAddUserToken(): (token: Token) => void { - const dispatch = useAppDispatch() - return useCallback( - (token: Token) => { - dispatch(addSerializedToken({ serializedToken: serializeToken(token) })) - }, - [dispatch] - ) -} - -export function useRemoveUserAddedToken(): (chainId: number, address: string) => void { - const dispatch = useAppDispatch() - return useCallback( - (chainId: number, address: string) => { - dispatch(removeSerializedToken({ chainId, address })) - }, - [dispatch] - ) -} - -export function useUserAddedTokens(): Token[] { - const { chainId } = useWalletInfo() - const serializedTokensMap = useAppSelector(({ user: { tokens } }) => tokens) - - return useMemo(() => { - if (!chainId) return [] - const tokenMap: Token[] = serializedTokensMap?.[chainId] - ? Object.values(serializedTokensMap[chainId]).map(deserializeToken) - : [] - return tokenMap - }, [serializedTokensMap, chainId]) -} - export function useURLWarningVisible(): boolean { return useAppSelector((state: AppState) => state.user.URLWarningVisible) } @@ -231,16 +190,17 @@ export function useSelectedWallet(): string | undefined { return useAppSelector(({ user: { selectedWallet } }) => selectedWallet) } -export function serializeToken(token: Currency): SerializedToken { +export function serializeToken(token: Currency | TokenWithLogo): SerializedToken { const address = getIsNativeToken(token) ? NATIVE_CURRENCY_BUY_TOKEN[token.chainId as SupportedChainId].address : token.address return { - chainId: token.chainId, address, + logoURI: token instanceof TokenWithLogo ? token.logoURI : undefined, + chainId: token.chainId, decimals: token.decimals, - symbol: token.symbol, - name: token.name, + symbol: token.symbol || '', + name: token.name || '', } } diff --git a/apps/cowswap-frontend/src/legacy/state/user/reducer.ts b/apps/cowswap-frontend/src/legacy/state/user/reducer.ts index 611d223883..1cd9553fbc 100644 --- a/apps/cowswap-frontend/src/legacy/state/user/reducer.ts +++ b/apps/cowswap-frontend/src/legacy/state/user/reducer.ts @@ -3,8 +3,6 @@ import { ConnectionType } from '@cowprotocol/wallet' import { createSlice } from '@reduxjs/toolkit' -import { SerializedPair, SerializedToken } from './types' - import { updateVersion } from '../global/actions' const currentTimestamp = () => new Date().getTime() @@ -42,19 +40,6 @@ export interface UserState { // deadline set by user in minutes, used in all txns userDeadline: number - tokens: { - [chainId: number]: { - [address: string]: SerializedToken - } - } - - pairs: { - [chainId: number]: { - // keyed by token0Address:token1Address - [key: string]: SerializedPair - } - } - timestamp: number URLWarningVisible: boolean @@ -64,10 +49,6 @@ export interface UserState { showDonationLink: boolean } -function pairKey(token0Address: string, token1Address: string) { - return `${token0Address};${token1Address}` -} - export const initialState: UserState = { selectedWallet: undefined, selectedWalletBackfilled: false, @@ -82,8 +63,6 @@ export const initialState: UserState = { userSlippageTolerance: 'auto', userSlippageToleranceHasBeenMigratedToAuto: true, userDeadline: DEFAULT_DEADLINE_FROM_NOW, - tokens: {}, - pairs: {}, timestamp: currentTimestamp(), URLWarningVisible: true, showSurveyPopup: undefined, @@ -134,41 +113,6 @@ const userSlice = createSlice({ updateShowDonationLink(state, action) { state.showDonationLink = action.payload.showDonationLink }, - addSerializedToken(state, { payload: { serializedToken } }) { - if (!state.tokens) { - state.tokens = {} - } - state.tokens[serializedToken.chainId] = state.tokens[serializedToken.chainId] || {} - state.tokens[serializedToken.chainId][serializedToken.address] = serializedToken - state.timestamp = currentTimestamp() - }, - removeSerializedToken(state, { payload: { address, chainId } }) { - if (!state.tokens) { - state.tokens = {} - } - state.tokens[chainId] = state.tokens[chainId] || {} - delete state.tokens[chainId][address] - state.timestamp = currentTimestamp() - }, - addSerializedPair(state, { payload: { serializedPair } }) { - if ( - serializedPair.token0.chainId === serializedPair.token1.chainId && - serializedPair.token0.address !== serializedPair.token1.address - ) { - const chainId = serializedPair.token0.chainId - state.pairs[chainId] = state.pairs[chainId] || {} - state.pairs[chainId][pairKey(serializedPair.token0.address, serializedPair.token1.address)] = serializedPair - } - state.timestamp = currentTimestamp() - }, - removeSerializedPair(state, { payload: { chainId, tokenAAddress, tokenBAddress } }) { - if (state.pairs[chainId]) { - // just delete both keys if either exists - delete state.pairs[chainId][pairKey(tokenAAddress, tokenBAddress)] - delete state.pairs[chainId][pairKey(tokenBAddress, tokenAAddress)] - } - state.timestamp = currentTimestamp() - }, // MOD - legacy Uni code we want to keep toggleURLWarning(state) { state.URLWarningVisible = !state.URLWarningVisible @@ -217,8 +161,6 @@ const userSlice = createSlice({ export const { updateSelectedWallet, - addSerializedToken, - removeSerializedToken, updateMatchesDarkMode, updateUserDarkMode, updateUserDeadline, diff --git a/apps/cowswap-frontend/src/legacy/state/user/types.ts b/apps/cowswap-frontend/src/legacy/state/user/types.ts index 49d15241a3..c0a7dc8a41 100644 --- a/apps/cowswap-frontend/src/legacy/state/user/types.ts +++ b/apps/cowswap-frontend/src/legacy/state/user/types.ts @@ -1,12 +1,8 @@ export interface SerializedToken { + logoURI?: string chainId: number address: string decimals: number - symbol?: string - name?: string -} - -export interface SerializedPair { - token0: SerializedToken - token1: SerializedToken + symbol: string + name: string } diff --git a/apps/cowswap-frontend/src/modules/application/containers/App/Updaters.tsx b/apps/cowswap-frontend/src/modules/application/containers/App/Updaters.tsx index acb3a12eae..4d12a73956 100644 --- a/apps/cowswap-frontend/src/modules/application/containers/App/Updaters.tsx +++ b/apps/cowswap-frontend/src/modules/application/containers/App/Updaters.tsx @@ -1,4 +1,4 @@ -import { TokensListsUpdater } from '@cowprotocol/tokens' +import { TokensListsUpdater, UnsupportedTokensUpdater } from '@cowprotocol/tokens' import { useWalletInfo, WalletUpdater } from '@cowprotocol/wallet' import { GasPriceStrategyUpdater } from 'legacy/state/gas/gas-price-strategy-updater' @@ -60,6 +60,7 @@ export function Updaters() { + ) } diff --git a/apps/cowswap-frontend/src/modules/orders/hooks/useTokensForOrdersList.ts b/apps/cowswap-frontend/src/modules/orders/hooks/useTokensForOrdersList.ts index 0609c8ad91..b97fed5015 100644 --- a/apps/cowswap-frontend/src/modules/orders/hooks/useTokensForOrdersList.ts +++ b/apps/cowswap-frontend/src/modules/orders/hooks/useTokensForOrdersList.ts @@ -1,18 +1,25 @@ import { useCallback, useRef } from 'react' import { TokenWithLogo } from '@cowprotocol/common-const' -import { TokensByAddress, useTokensByAddressMap } from '@cowprotocol/tokens' +import { fetchTokenFromBlockchain, TokensByAddress, useTokensByAddressMap } from '@cowprotocol/tokens' import { useWalletInfo } from '@cowprotocol/wallet' import { Token } from '@uniswap/sdk-core' - -import { useTokenLazy } from 'legacy/hooks/useTokenLazy' +import { useWeb3React } from '@web3-react/core' import { getTokenFromMapping } from 'utils/orderUtils/getTokenFromMapping' export function useTokensForOrdersList(): (tokensToFetch: string[]) => Promise { const { chainId } = useWalletInfo() + const { provider } = useWeb3React() const allTokens = useTokensByAddressMap() - const getToken = useTokenLazy() + + const getToken = useCallback( + async (address: string) => { + if (!provider) return null + return fetchTokenFromBlockchain(address, chainId, provider).then(TokenWithLogo.fromToken) + }, + [chainId, provider] + ) // Using a ref to store allTokens to avoid re-fetching when new tokens are added // but still use the latest whenever the callback is invoked diff --git a/apps/cowswap-frontend/src/modules/swap/helpers/__snapshots__/tradeReceiveAmount.test.ts.snap b/apps/cowswap-frontend/src/modules/swap/helpers/__snapshots__/tradeReceiveAmount.test.ts.snap index 663406db74..5f0d07e058 100644 --- a/apps/cowswap-frontend/src/modules/swap/helpers/__snapshots__/tradeReceiveAmount.test.ts.snap +++ b/apps/cowswap-frontend/src/modules/swap/helpers/__snapshots__/tradeReceiveAmount.test.ts.snap @@ -9,9 +9,7 @@ exports[`Helpers to build ReceiveAmountInfo getInputReceiveAmountInfo() When sel "decimals": 18, "isNative": false, "isToken": true, - "logoURI": { - "ReactComponent": "IconMock", - }, + "logoURI": undefined, "name": "CoW Protocol Token", "symbol": "COW", }, @@ -44,9 +42,7 @@ exports[`Helpers to build ReceiveAmountInfo getInputReceiveAmountInfo() When sel "decimals": 18, "isNative": false, "isToken": true, - "logoURI": { - "ReactComponent": "IconMock", - }, + "logoURI": undefined, "name": "CoW Protocol Token", "symbol": "COW", }, @@ -76,9 +72,7 @@ exports[`Helpers to build ReceiveAmountInfo getOutputReceiveAmountInfo() Should "decimals": 18, "isNative": false, "isToken": true, - "logoURI": { - "ReactComponent": "IconMock", - }, + "logoURI": undefined, "name": "CoW Protocol Token", "symbol": "COW", }, @@ -108,9 +102,7 @@ exports[`Helpers to build ReceiveAmountInfo getOutputReceiveAmountInfo() Should "decimals": 18, "isNative": false, "isToken": true, - "logoURI": { - "ReactComponent": "IconMock", - }, + "logoURI": undefined, "name": "CoW Protocol Token", "symbol": "COW", }, @@ -140,9 +132,7 @@ exports[`Helpers to build ReceiveAmountInfo getOutputReceiveAmountInfo() Should "decimals": 18, "isNative": false, "isToken": true, - "logoURI": { - "ReactComponent": "IconMock", - }, + "logoURI": undefined, "name": "CoW Protocol Token", "symbol": "COW", }, diff --git a/apps/cowswap-frontend/src/modules/tokensList/containers/ManageLists/index.tsx b/apps/cowswap-frontend/src/modules/tokensList/containers/ManageLists/index.tsx index 33753033f3..fa98c56931 100644 --- a/apps/cowswap-frontend/src/modules/tokensList/containers/ManageLists/index.tsx +++ b/apps/cowswap-frontend/src/modules/tokensList/containers/ManageLists/index.tsx @@ -1,6 +1,7 @@ import { useMemo } from 'react' import { ListSearchResponse, ListState, useListsEnabledState, useRemoveList, useToggleList } from '@cowprotocol/tokens' +import { Loader } from '@cowprotocol/ui' import * as styledEl from './styled' @@ -27,11 +28,15 @@ export function ManageLists(props: ManageListsProps) { const removeList = useRemoveList() const toggleList = useToggleList() - const { source, listToImport } = useListSearchResponse(listSearchResponse) + const { source, listToImport, loading } = useListSearchResponse(listSearchResponse) - // TODO: add loading state return ( + {loading && ( + + + + )} {listToImport && ( )}
{children}
-
- - - -
+ {onClose && ( +
+ + + +
+ )} ) } diff --git a/apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/styled.ts b/apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/styled.ts index 980c6c1c47..25249996f1 100644 --- a/apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/styled.ts +++ b/apps/cowswap-frontend/src/modules/tokensList/pure/SelectTokenModal/styled.ts @@ -36,24 +36,29 @@ export const Header = styled.div` ` export const SearchInput = styled.input` + position: relative; + display: flex; + padding: 16px; + align-items: center; width: 100%; + white-space: nowrap; outline: none; - border-radius: 20px; + background: var(${UI.COLOR_GREY}); color: var(${UI.COLOR_TEXT1}); - padding: 16px; - border: 1px solid var(${UI.COLOR_GREY}); - -webkit-appearance: none; - transition: border 100ms; - background: transparent; - - font-size: 18px; + border: 1px solid var(${UI.COLOR_BORDER}); + appearance: none; + font-size: 16px; + border-radius: 12px; ::placeholder { - color: var(${UI.COLOR_LINK}); + color: var(${UI.COLOR_TEXT1}); + opacity: 0.7; } + transition: border 100ms; + :focus { - border: 1px solid var(${UI.COLOR_CONTAINER_BG_02}); + border: 1px solid var(${UI.COLOR_LINK}); outline: none; } ` diff --git a/apps/cowswap-frontend/src/modules/tokensList/utils/tokensListSorter.ts b/apps/cowswap-frontend/src/modules/tokensList/utils/tokensListSorter.ts index 765d8d9d78..fe689c9e11 100644 --- a/apps/cowswap-frontend/src/modules/tokensList/utils/tokensListSorter.ts +++ b/apps/cowswap-frontend/src/modules/tokensList/utils/tokensListSorter.ts @@ -7,9 +7,13 @@ export function tokensListSorter(balances: TokenAmounts): (a: TokenWithLogo, b: return (a: TokenWithLogo, b: TokenWithLogo) => { const aBalance = balances[a.address] const bBalance = balances[b.address] + const aIsNative = getIsNativeToken(a) + const bIsNative = getIsNativeToken(b) // Native always first - if (getIsNativeToken(a) || getIsNativeToken(b)) return 1 + if (aIsNative || bIsNative) { + return aIsNative ? -1 : 1 + } if (aBalance?.value && bBalance?.value) { return +bBalance.value.toExact() - +aBalance.value.toExact() diff --git a/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useTradeQuotePolling.ts b/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useTradeQuotePolling.ts index cf884e138f..183c71c57c 100644 --- a/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useTradeQuotePolling.ts +++ b/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useTradeQuotePolling.ts @@ -4,7 +4,7 @@ import { useLayoutEffect, useMemo } from 'react' import { useDebounce } from '@cowprotocol/common-hooks' import { onlyResolvesLast } from '@cowprotocol/common-utils' import { OrderQuoteResponse } from '@cowprotocol/cow-sdk' -import { useIsUnsupportedTokens } from '@cowprotocol/tokens' +import { useAreUnsupportedTokens } from '@cowprotocol/tokens' import ms from 'ms.macro' @@ -36,7 +36,7 @@ export function useTradeQuotePolling() { const updateQuoteState = useSetAtom(updateTradeQuoteAtom) const updateCurrencyAmount = useUpdateCurrencyAmount() - const getIsUnsupportedTokens = useIsUnsupportedTokens() + const getIsUnsupportedTokens = useAreUnsupportedTokens() const processUnsupportedTokenError = useProcessUnsupportedTokenError() useLayoutEffect(() => { diff --git a/apps/widget-configurator/src/app/app.spec.tsx b/apps/widget-configurator/src/app/app.spec.tsx deleted file mode 100644 index 1ea8da5bb1..0000000000 --- a/apps/widget-configurator/src/app/app.spec.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { render } from '@testing-library/react' - -import App from './app' - -describe('App', () => { - it('should render successfully', () => { - const { baseElement } = render() - expect(baseElement).toBeTruthy() - }) - - it('should have a greeting as the title', () => { - const { getByText } = render() - expect(getByText(/Welcome widget-configurator/gi)).toBeTruthy() - }) -}) diff --git a/apps/widget-configurator/src/app/configurator/index.tsx b/apps/widget-configurator/src/app/configurator/index.tsx index 8506ec13bc..f393dc237a 100644 --- a/apps/widget-configurator/src/app/configurator/index.tsx +++ b/apps/widget-configurator/src/app/configurator/index.tsx @@ -21,6 +21,7 @@ import ListItemText from '@mui/material/ListItemText' // import SaveIcon from '@mui/icons-material/Save' import WalletIcon from '@mui/icons-material/Wallet' import LoadingButton from '@mui/lab/LoadingButton' +import { SyntheticEvent } from 'react' enum TradeMode { Swap = 1, @@ -165,7 +166,7 @@ export function Configurator({ title }: { title: string }) { { + onChange={(event: SyntheticEvent, newValue: { chainID: number; label: string } | null) => { setNetwork(newValue || NetworkOptions[0]) }} getOptionLabel={(option) => option.label} @@ -179,7 +180,7 @@ export function Configurator({ title }: { title: string }) { { + onChange={(event: SyntheticEvent, newValue: string | null) => { setSellToken(newValue) }} inputValue={sellToken || ''} @@ -202,7 +203,7 @@ export function Configurator({ title }: { title: string }) { { + onChange={(event: SyntheticEvent, newValue: string | null) => { setBuyToken(newValue) }} inputValue={buyToken || ''} diff --git a/libs/common-const/src/nativeAndWrappedTokens.ts b/libs/common-const/src/nativeAndWrappedTokens.ts index 77d62ff38e..2f85e2611d 100644 --- a/libs/common-const/src/nativeAndWrappedTokens.ts +++ b/libs/common-const/src/nativeAndWrappedTokens.ts @@ -3,8 +3,6 @@ import { TokenWithLogo } from './types' import { WETH9 } from '@uniswap/sdk-core' import { cowprotocolTokenLogoUrl } from './cowprotocolTokenLogoUrl' -import wxDaiLogo from '@cowprotocol/assets/cow-swap/wxdai.png' - // See https://github.com/cowprotocol/contracts/commit/821b5a8da213297b0f7f1d8b17c893c5627020af#diff-12bbbe13cd5cf42d639e34a39d8795021ba40d3ee1e1a8282df652eb161a11d6R13 export const NATIVE_CURRENCY_BUY_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' @@ -19,7 +17,7 @@ export const WETH_MAINNET = new TokenWithLogo( ) // xDAI tokens export const WXDAI = new TokenWithLogo( - wxDaiLogo, + undefined, ChainId.GNOSIS_CHAIN, '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d', 18, diff --git a/libs/common-const/src/tokens.ts b/libs/common-const/src/tokens.ts index 94f0615d2e..d209f5dd4c 100644 --- a/libs/common-const/src/tokens.ts +++ b/libs/common-const/src/tokens.ts @@ -1,8 +1,5 @@ import { SupportedChainId as ChainId, SupportedChainId } from '@cowprotocol/cow-sdk' -import cowLogo from '@cowprotocol/assets/cow-swap/cow.svg' -import vCowLogo from '@cowprotocol/assets/cow-swap/vCOW.png' - import { COW_CONTRACT_ADDRESS, V_COW_CONTRACT_ADDRESS } from './common' import { TokenWithLogo } from './types' import { cowprotocolTokenLogoUrl } from './cowprotocolTokenLogoUrl' @@ -158,7 +155,7 @@ export const TOKEN_SHORTHANDS: { [shorthand: string]: { [chainId in SupportedCha * vCow token */ const V_COW_TOKEN_MAINNET = new TokenWithLogo( - vCowLogo, + undefined, SupportedChainId.MAINNET, V_COW_CONTRACT_ADDRESS[SupportedChainId.MAINNET] || '', 18, @@ -194,7 +191,7 @@ export const V_COW: Record = { * Cow token */ const COW_TOKEN_MAINNET = new TokenWithLogo( - cowLogo, + undefined, SupportedChainId.MAINNET, COW_CONTRACT_ADDRESS[SupportedChainId.MAINNET] || '', 18, diff --git a/libs/common-const/src/types.ts b/libs/common-const/src/types.ts index 5713630b2b..5ad0f96549 100644 --- a/libs/common-const/src/types.ts +++ b/libs/common-const/src/types.ts @@ -1,5 +1,5 @@ import { Token } from '@uniswap/sdk-core' -import { TokenInfo } from '@uniswap/token-lists' +import type { TokenInfo } from '@uniswap/token-lists' export class TokenWithLogo extends Token { static fromToken(token: Token | TokenInfo, logoURI?: string): TokenWithLogo { diff --git a/libs/tokens/src/pure/TokenLogo/index.tsx b/libs/tokens/src/pure/TokenLogo/index.tsx index a752625521..8be417c017 100644 --- a/libs/tokens/src/pure/TokenLogo/index.tsx +++ b/libs/tokens/src/pure/TokenLogo/index.tsx @@ -17,6 +17,7 @@ const TokenLogoWrapper = styled.div` display: inline-block; background: var(--cow-container-bg-01); border-radius: 50%; + overflow: hidden; img { width: 100%; diff --git a/libs/tokens/src/state/tokens/allTokensAtom.ts b/libs/tokens/src/state/tokens/allTokensAtom.ts index 05b344d146..a64d567ffc 100644 --- a/libs/tokens/src/state/tokens/allTokensAtom.ts +++ b/libs/tokens/src/state/tokens/allTokensAtom.ts @@ -35,9 +35,13 @@ export const tokensStateAtom = atom((get) => { const tokenAddress = token.address.toLowerCase() if (isListEnabled) { - acc.activeTokens[tokenAddress] = token + if (!acc.activeTokens[tokenAddress]) { + acc.activeTokens[tokenAddress] = token + } } else { - acc.inactiveTokens[tokenAddress] = token + if (!acc.inactiveTokens[tokenAddress]) { + acc.inactiveTokens[tokenAddress] = token + } } }) diff --git a/libs/tokens/src/utils/getTokenLogoUrls.ts b/libs/tokens/src/utils/getTokenLogoUrls.ts index 3d93fa3df8..7bb04b7315 100644 --- a/libs/tokens/src/utils/getTokenLogoUrls.ts +++ b/libs/tokens/src/utils/getTokenLogoUrls.ts @@ -21,7 +21,6 @@ export function getTokenLogoUrls(token: TokenWithLogo | undefined): string[] { function getTokenLogoFallbacks(address: string, chainId: SupportedChainId): string[] { return [ - cowprotocolTokenLogoUrl(address, chainId), cowprotocolTokenLogoUrl(address.toLowerCase(), chainId), cowprotocolTokenLogoUrl(address.toLowerCase(), SupportedChainId.MAINNET), trustTokenLogoUrl(address, chainId), diff --git a/yarn.lock b/yarn.lock index 1dcdb33270..2607e53132 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3064,105 +3064,6 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jimp/bmp@^0.16.13": - version "0.16.13" - resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.16.13.tgz#57ffa5b17417b5a181f6f184bdabc8218e8448ef" - integrity sha512-9edAxu7N2FX7vzkdl5Jo1BbACfycUtBQX+XBMcHA2bk62P8R0otgkHg798frgAk/WxQIzwxqOH6wMiCwrlAzdQ== - dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.16.13" - bmp-js "^0.1.0" - -"@jimp/core@^0.16.13": - version "0.16.13" - resolved "https://registry.yarnpkg.com/@jimp/core/-/core-0.16.13.tgz#7171745a912b5b847f8bf53e70b0672c5ca92744" - integrity sha512-qXpA1tzTnlkTku9yqtuRtS/wVntvE6f3m3GNxdTdtmc+O+Wcg9Xo2ABPMh7Nc0AHbMKzwvwgB2JnjZmlmJEObg== - dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.16.13" - any-base "^1.1.0" - buffer "^5.2.0" - exif-parser "^0.1.12" - file-type "^16.5.4" - load-bmfont "^1.3.1" - mkdirp "^0.5.1" - phin "^2.9.1" - pixelmatch "^4.0.2" - tinycolor2 "^1.4.1" - -"@jimp/custom@^0.16.1": - version "0.16.13" - resolved "https://registry.yarnpkg.com/@jimp/custom/-/custom-0.16.13.tgz#2e4ed447b7410b81fe9103682b4166af904daf84" - integrity sha512-LTATglVUPGkPf15zX1wTMlZ0+AU7cGEGF6ekVF1crA8eHUWsGjrYTB+Ht4E3HTrCok8weQG+K01rJndCp/l4XA== - dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/core" "^0.16.13" - -"@jimp/gif@^0.16.13": - version "0.16.13" - resolved "https://registry.yarnpkg.com/@jimp/gif/-/gif-0.16.13.tgz#fa72f35d8ad67d6ce3a3d7ef6c8d04a462afaaf9" - integrity sha512-yFAMZGv3o+YcjXilMWWwS/bv1iSqykFahFMSO169uVMtfQVfa90kt4/kDwrXNR6Q9i6VHpFiGZMlF2UnHClBvg== - dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.16.13" - gifwrap "^0.9.2" - omggif "^1.0.9" - -"@jimp/jpeg@^0.16.13": - version "0.16.13" - resolved "https://registry.yarnpkg.com/@jimp/jpeg/-/jpeg-0.16.13.tgz#e1c128a591bd7f8a26c8731fd0bc65d32d4ba32a" - integrity sha512-BJHlDxzTlCqP2ThqP8J0eDrbBfod7npWCbJAcfkKqdQuFk0zBPaZ6KKaQKyKxmWJ87Z6ohANZoMKEbtvrwz1AA== - dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.16.13" - jpeg-js "^0.4.2" - -"@jimp/plugin-resize@^0.16.1": - version "0.16.13" - resolved "https://registry.yarnpkg.com/@jimp/plugin-resize/-/plugin-resize-0.16.13.tgz#6267087f724d47e7bb8824c5b842d9315f50b8e7" - integrity sha512-qoqtN8LDknm3fJm9nuPygJv30O3vGhSBD2TxrsCnhtOsxKAqVPJtFVdGd/qVuZ8nqQANQmTlfqTiK9mVWQ7MiQ== - dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.16.13" - -"@jimp/png@^0.16.13": - version "0.16.13" - resolved "https://registry.yarnpkg.com/@jimp/png/-/png-0.16.13.tgz#8b130cc5e1e754c074c42fa3fe2609897cefdf7c" - integrity sha512-8cGqINvbWJf1G0Her9zbq9I80roEX0A+U45xFby3tDWfzn+Zz8XKDF1Nv9VUwVx0N3zpcG1RPs9hfheG4Cq2kg== - dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/utils" "^0.16.13" - pngjs "^3.3.3" - -"@jimp/tiff@^0.16.13": - version "0.16.13" - resolved "https://registry.yarnpkg.com/@jimp/tiff/-/tiff-0.16.13.tgz#9cf8d19f2b0b0c46758e81acfc7d656835ee6da1" - integrity sha512-oJY8d9u95SwW00VPHuCNxPap6Q1+E/xM5QThb9Hu+P6EGuu6lIeLaNBMmFZyblwFbwrH+WBOZlvIzDhi4Dm/6Q== - dependencies: - "@babel/runtime" "^7.7.2" - utif "^2.0.1" - -"@jimp/types@^0.16.1": - version "0.16.13" - resolved "https://registry.yarnpkg.com/@jimp/types/-/types-0.16.13.tgz#39be1886cbfa4fb5e77e17441a046a1f961d3046" - integrity sha512-mC0yVNUobFDjoYLg4hoUwzMKgNlxynzwt3cDXzumGvRJ7Kb8qQGOWJQjQFo5OxmGExqzPphkirdbBF88RVLBCg== - dependencies: - "@babel/runtime" "^7.7.2" - "@jimp/bmp" "^0.16.13" - "@jimp/gif" "^0.16.13" - "@jimp/jpeg" "^0.16.13" - "@jimp/png" "^0.16.13" - "@jimp/tiff" "^0.16.13" - timm "^1.6.1" - -"@jimp/utils@^0.16.13": - version "0.16.13" - resolved "https://registry.yarnpkg.com/@jimp/utils/-/utils-0.16.13.tgz#afde41b9c6cdadfb45d83cb5e16deb65f369bf99" - integrity sha512-VyCpkZzFTHXtKgVO35iKN0sYR10psGpV6SkcSeV4oF7eSYlR8Bl6aQLCzVeFjvESF7mxTmIiI3/XrMobVrtxDA== - dependencies: - "@babel/runtime" "^7.7.2" - regenerator-runtime "^0.13.3" - "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.3" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" @@ -5594,6 +5495,18 @@ dependencies: defer-to-connect "^2.0.1" +"@tanstack/react-virtual@^3.0.0-beta.65": + version "3.0.0-beta.68" + resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.0.0-beta.68.tgz#225532329d74d7c0f82564d36ad8bf461e320ab5" + integrity sha512-YEFNCf+N3ZlNou2r4qnh+GscMe24foYEjTL05RS0ZHvah2RoUDPGuhnuedTv0z66dO2vIq6+Bl4TXatht5T7GQ== + dependencies: + "@tanstack/virtual-core" "3.0.0-beta.68" + +"@tanstack/virtual-core@3.0.0-beta.68": + version "3.0.0-beta.68" + resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.0.0-beta.68.tgz#e6c478b393df57861e0a98fd0fea9982b79b6e7d" + integrity sha512-CnvsEJWK7cugigckt13AeY80FMzH+OMdEP0j0bS3/zjs44NiRe49x8FZC6R9suRXGMVMXtUHet0zbTp/Ec9Wfg== + "@testing-library/dom@^9.0.0": version "9.3.1" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.1.tgz#8094f560e9389fb973fe957af41bf766937a9ee9" @@ -6354,21 +6267,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.5.tgz#9dc0a5cb1ccce4f7a731660935ab70b9c00a5d69" integrity sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg== -"@types/node@16.9.1": - version "16.9.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708" - integrity sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g== - "@types/node@18.14.2": version "18.14.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.2.tgz#c076ed1d7b6095078ad3cf21dfeea951842778b1" integrity sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA== -"@types/node@^10.12.18": - version "10.17.60" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" - integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== - "@types/node@^12.12.54", "@types/node@^12.12.6": version "12.20.55" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" @@ -6501,13 +6404,6 @@ dependencies: "@types/react" "*" -"@types/react-window@^1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@types/react-window/-/react-window-1.8.5.tgz#285fcc5cea703eef78d90f499e1457e9b5c02fc1" - integrity sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw== - dependencies: - "@types/react" "*" - "@types/react@*": version "18.2.17" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.17.tgz#baa565b17ddb649c2dac85b5eaf9e9a1fe0f3b4e" @@ -6893,11 +6789,6 @@ "@typescript-eslint/types" "6.2.0" eslint-visitor-keys "^3.4.1" -"@uniswap/default-token-list@^11.5.0": - version "11.5.0" - resolved "https://registry.yarnpkg.com/@uniswap/default-token-list/-/default-token-list-11.5.0.tgz#a9c26837aa8ac179f23e7f5a13b5549c25238e61" - integrity sha512-ZlZZf9nFQze8aprGRZRmE+4wc0SzHpfGrlNwbp7ChP8Ajlrr72Ed+axO41shb65yEx2CxXn7twITVfw3/Ks61w== - "@uniswap/redux-multicall@^1.1.5": version "1.1.8" resolved "https://registry.yarnpkg.com/@uniswap/redux-multicall/-/redux-multicall-1.1.8.tgz#9cc5090305b10df68fb6162eb1ba7c2c762f5e7f" @@ -6932,94 +6823,6 @@ dependencies: "@use-gesture/core" "10.2.27" -"@vibrant/color@^3.2.1-alpha.1": - version "3.2.1-alpha.1" - resolved "https://registry.yarnpkg.com/@vibrant/color/-/color-3.2.1-alpha.1.tgz#1bcee4545d2276d36f9a1acb42ab3485a9b489ec" - integrity sha512-cvm+jAPwao2NerTr3d1JttYyLhp3eD/AQBeevxF7KT6HctToWZCwr2AeTr003/wKgbjzdOV1qySnbyOeu+R+Jw== - -"@vibrant/core@^3.2.1-alpha.1": - version "3.2.1-alpha.1" - resolved "https://registry.yarnpkg.com/@vibrant/core/-/core-3.2.1-alpha.1.tgz#9adff0835b5c750be3386ec01669d2a8d6389fdb" - integrity sha512-X9Oa9WfPEQnZ6L+5dLRlh+IlsxJkYTw9b/g3stFKoNXbVRKCeXHmH48l7jIBBOg3VcXOGUdsYBqsTwPNkIveaA== - dependencies: - "@vibrant/color" "^3.2.1-alpha.1" - "@vibrant/generator" "^3.2.1-alpha.1" - "@vibrant/image" "^3.2.1-alpha.1" - "@vibrant/quantizer" "^3.2.1-alpha.1" - "@vibrant/types" "^3.2.1-alpha.1" - "@vibrant/worker" "^3.2.1-alpha.1" - -"@vibrant/generator-default@^3.2.1-alpha.1": - version "3.2.1-alpha.1" - resolved "https://registry.yarnpkg.com/@vibrant/generator-default/-/generator-default-3.2.1-alpha.1.tgz#70ae71ea1f72d3e71aa6b244830d01ecae1d755a" - integrity sha512-BWnQhDaz92UhyHnpdAzKXHQecY+jvyMXtzjKYbveFxThm6+HVoLjwONlbck7oyOpFzV2OM7V11XuR85BxaHvjw== - dependencies: - "@vibrant/color" "^3.2.1-alpha.1" - "@vibrant/generator" "^3.2.1-alpha.1" - -"@vibrant/generator@^3.2.1-alpha.1": - version "3.2.1-alpha.1" - resolved "https://registry.yarnpkg.com/@vibrant/generator/-/generator-3.2.1-alpha.1.tgz#887b36f7ed978ff94c93cc8a3ac742ce769b6112" - integrity sha512-luS5YvMhwMqG01YTj1dJ+cmkuIw1VCByOR6zIaCOwQqI/mcOs88JBWcA1r2TywJTOPlVpjfnDvAlyaKBKh4dMA== - dependencies: - "@vibrant/color" "^3.2.1-alpha.1" - "@vibrant/types" "^3.2.1-alpha.1" - -"@vibrant/image-browser@^3.2.1-alpha.1": - version "3.2.1-alpha.1" - resolved "https://registry.yarnpkg.com/@vibrant/image-browser/-/image-browser-3.2.1-alpha.1.tgz#fe595bfe0c0ddc412300b5419e1e42d8b88d4380" - integrity sha512-6xWvQfB20sE6YtCWylgEAHuee3iD8h3aFIDbCS2yj7jIelKcYTrrp5jg2d2BhOOB6pC5JzF+QfpCrm0DmAIlgQ== - dependencies: - "@vibrant/image" "^3.2.1-alpha.1" - -"@vibrant/image-node@^3.2.1-alpha.1": - version "3.2.1-alpha.1" - resolved "https://registry.yarnpkg.com/@vibrant/image-node/-/image-node-3.2.1-alpha.1.tgz#2901e09aee05d64ac9e792a951ee0727299ab80f" - integrity sha512-/Io/Rpo4EkO6AhaXdcxUXkbOFhSFtjm0LSAM4c0AyGA5EbC8PyZqjk8b11bQAEMCaYaweFQfTdGD7oVbXe21CQ== - dependencies: - "@jimp/custom" "^0.16.1" - "@jimp/plugin-resize" "^0.16.1" - "@jimp/types" "^0.16.1" - "@vibrant/image" "^3.2.1-alpha.1" - -"@vibrant/image@^3.2.1-alpha.1": - version "3.2.1-alpha.1" - resolved "https://registry.yarnpkg.com/@vibrant/image/-/image-3.2.1-alpha.1.tgz#8bcde820f5ad873e2e96b00479def80f86e925a5" - integrity sha512-4aF5k79QfyhZOqRovJpbnIjWfe3uuWhY8voqVdd4/qgu4o70/AwVlM+pYmCaJVzI45VWNWWHYA5QlYuKsXnBqQ== - dependencies: - "@vibrant/color" "^3.2.1-alpha.1" - "@vibrant/types" "^3.2.1-alpha.1" - -"@vibrant/quantizer-mmcq@^3.2.1-alpha.1": - version "3.2.1-alpha.1" - resolved "https://registry.yarnpkg.com/@vibrant/quantizer-mmcq/-/quantizer-mmcq-3.2.1-alpha.1.tgz#b36ecb48f4bff9ea35ed23389d8af79c079c079a" - integrity sha512-Wuk9PTZtxr8qsWTcgP6lcrrmrq36syVwxf+BUxdgQYntBcQ053SaN34lVGOJ0WPdK5vABoxbYljhceCgiILtZw== - dependencies: - "@vibrant/color" "^3.2.1-alpha.1" - "@vibrant/image" "^3.2.1-alpha.1" - "@vibrant/quantizer" "^3.2.1-alpha.1" - -"@vibrant/quantizer@^3.2.1-alpha.1": - version "3.2.1-alpha.1" - resolved "https://registry.yarnpkg.com/@vibrant/quantizer/-/quantizer-3.2.1-alpha.1.tgz#8d29e288ea7acbcd0c9ab8c6b86f80adce606210" - integrity sha512-iHnPx/+n4iLtYLm1GClSfyg2fFbMatFG0ipCyp9M6tXNIPAg+pSvUJSGBnVnH7Nl/bR8Gkkj1h0pJ4RsKcdIrQ== - dependencies: - "@vibrant/color" "^3.2.1-alpha.1" - "@vibrant/image" "^3.2.1-alpha.1" - "@vibrant/types" "^3.2.1-alpha.1" - -"@vibrant/types@^3.2.1-alpha.1": - version "3.2.1-alpha.1" - resolved "https://registry.yarnpkg.com/@vibrant/types/-/types-3.2.1-alpha.1.tgz#54ecf8b4d1045af699bfaf592e455079801bc951" - integrity sha512-ts9u7nsrENoYI5s0MmPOeY5kCLFKvQndKVDOPFCbTA0z493uhDp8mpiQhjFYTf3kPbS04z9zbHLE2luFC7x4KQ== - -"@vibrant/worker@^3.2.1-alpha.1": - version "3.2.1-alpha.1" - resolved "https://registry.yarnpkg.com/@vibrant/worker/-/worker-3.2.1-alpha.1.tgz#d09e4ec72902d36b9632c2c0aab855747acf1015" - integrity sha512-mtSlBdHkFNr4FOnMtqtHJxy9z5AsUcZzGlpiHzvWOoaoN9lNTDPwxOBd0q4VTYWuGPrIm6Fuq5m7aRbLv7KqiQ== - dependencies: - "@vibrant/types" "^3.2.1-alpha.1" - "@vitejs/plugin-react-swc@^3.3.2": version "3.3.2" resolved "https://registry.yarnpkg.com/@vitejs/plugin-react-swc/-/plugin-react-swc-3.3.2.tgz#34a82c1728066f48a86dfecb2f15df60f89207fb" @@ -7848,11 +7651,6 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== -any-base@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/any-base/-/any-base-1.1.0.tgz#ae101a62bc08a597b4c9ab5b7089d456630549fe" - integrity sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg== - any-promise@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -8636,11 +8434,6 @@ bluebird@^3.5.0, bluebird@^3.5.5, bluebird@^3.7.2: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -bmp-js@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.1.0.tgz#e05a63f796a6c1ff25f4771ec7adadc148c07233" - integrity sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw== - bn.js@4.11.6: version "4.11.6" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" @@ -8923,11 +8716,6 @@ buffer-crc32@~0.2.3: resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== -buffer-equal@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" - integrity sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA== - buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -8951,7 +8739,7 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== -buffer@^5.0.5, buffer@^5.2.0, buffer@^5.4.3, buffer@^5.5.0, buffer@^5.6.0, buffer@^5.7.1: +buffer@^5.0.5, buffer@^5.4.3, buffer@^5.5.0, buffer@^5.6.0, buffer@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -11896,11 +11684,6 @@ eslint@~8.15.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -esm@^3.0.84: - version "3.2.25" - resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" - integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== - espree@^9.0.0, espree@^9.3.2, espree@^9.4.0, espree@^9.6.0, espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" @@ -12269,11 +12052,6 @@ exenv@^1.2.2: resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw== -exif-parser@^0.1.12: - version "0.1.12" - resolved "https://registry.yarnpkg.com/exif-parser/-/exif-parser-0.1.12.tgz#58a9d2d72c02c1f6f02a0ef4a9166272b7760922" - integrity sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw== - exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -12532,15 +12310,6 @@ file-loader@^6.2.0: loader-utils "^2.0.0" schema-utils "^3.0.0" -file-type@^16.5.4: - version "16.5.4" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.5.4.tgz#474fb4f704bee427681f98dd390058a172a6c2fd" - integrity sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw== - dependencies: - readable-web-to-node-stream "^3.0.0" - strtok3 "^6.2.4" - token-types "^4.1.1" - file-type@^17.1.6: version "17.1.6" resolved "https://registry.yarnpkg.com/file-type/-/file-type-17.1.6.tgz#18669e0577a4849ef6e73a41f8bdf1ab5ae21023" @@ -13025,14 +12794,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -gifwrap@^0.9.2: - version "0.9.4" - resolved "https://registry.yarnpkg.com/gifwrap/-/gifwrap-0.9.4.tgz#f4eb6169ba027d61df64aafbdcb1f8ae58ccc0c5" - integrity sha512-MDMwbhASQuVeD4JKd1fKgNgCRL3fGqMM4WaqpNhWO0JiMOAjbQdumbs4BbBZEy9/M00EHEjKN3HieVhCUlwjeQ== - dependencies: - image-q "^4.0.0" - omggif "^1.0.10" - git-raw-commits@^2.0.11: version "2.0.11" resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723" @@ -13756,13 +13517,6 @@ ignore@^5.0.4, ignore@^5.2.0, ignore@^5.2.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== -image-q@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/image-q/-/image-q-4.0.0.tgz#31e075be7bae3c1f42a85c469b4732c358981776" - integrity sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw== - dependencies: - "@types/node" "16.9.1" - immer@^10.0.2: version "10.0.2" resolved "https://registry.yarnpkg.com/immer/-/immer-10.0.2.tgz#11636c5b77acf529e059582d76faf338beb56141" @@ -15858,11 +15612,6 @@ jotai@2.2.0: resolved "https://registry.yarnpkg.com/jotai/-/jotai-2.2.0.tgz#80c176efeb59d32d7eef7f14510ba8e01ef7b0db" integrity sha512-2gsMVpoaBa6aPHLArNhphck4+n8unZ8xF4Ciqhb+Z+tjsvabn5QFisNFHBMWooMAAmmTNU5TcvBIP8/XAsZy8Q== -jpeg-js@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa" - integrity sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg== - js-base64@^3.7.5: version "3.7.5" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.5.tgz#21e24cf6b886f76d6f5f165bfcd69cc55b9e3fca" @@ -16388,20 +16137,6 @@ lit@2.8.0: lit-element "^3.3.0" lit-html "^2.8.0" -load-bmfont@^1.3.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.4.1.tgz#c0f5f4711a1e2ccff725a7b6078087ccfcddd3e9" - integrity sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA== - dependencies: - buffer-equal "0.0.1" - mime "^1.3.4" - parse-bmfont-ascii "^1.0.3" - parse-bmfont-binary "^1.0.5" - parse-bmfont-xml "^1.1.4" - phin "^2.9.1" - xhr "^2.0.1" - xtend "^4.0.0" - load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -16831,11 +16566,6 @@ memfs@^3.1.2, memfs@^3.4.3: dependencies: fs-monkey "^1.0.4" -"memoize-one@>=3.1.1 <6": - version "5.2.1" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" - integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== - meow@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4" @@ -17003,7 +16733,7 @@ mime-types@^2.1.12, mime-types@^2.1.16, mime-types@^2.1.27, mime-types@^2.1.31, dependencies: mime-db "1.52.0" -mime@1.6.0, mime@^1.3.4, mime@^1.6.0: +mime@1.6.0, mime@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== @@ -17161,7 +16891,7 @@ mkdirp@*: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== -mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@^0.5.6, mkdirp@~0.5.1: +mkdirp@^0.5.5, mkdirp@^0.5.6, mkdirp@~0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -17595,19 +17325,6 @@ node-stdlib-browser@^1.2.0: util "^0.12.4" vm-browserify "^1.0.1" -node-vibrant@^3.2.1-alpha.1: - version "3.2.1-alpha.1" - resolved "https://registry.yarnpkg.com/node-vibrant/-/node-vibrant-3.2.1-alpha.1.tgz#d80a3dd22741150b804ae0d3eb99ceaf9f79980a" - integrity sha512-EQergCp7fvbvUCE0VMCBnvaAV0lGWSP8SXLmuWQIBzQK5M5pIwcd9fIOXuzFkJx/8hUiiiLvAzzGDS/bIy2ikA== - dependencies: - "@types/node" "^10.12.18" - "@vibrant/core" "^3.2.1-alpha.1" - "@vibrant/generator-default" "^3.2.1-alpha.1" - "@vibrant/image-browser" "^3.2.1-alpha.1" - "@vibrant/image-node" "^3.2.1-alpha.1" - "@vibrant/quantizer-mmcq" "^3.2.1-alpha.1" - url "^0.11.0" - normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -17918,11 +17635,6 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -omggif@^1.0.10, omggif@^1.0.9: - version "1.0.10" - resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.10.tgz#ddaaf90d4a42f532e9e7cb3a95ecdd47f17c7b19" - integrity sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw== - on-exit-leak-free@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz#b39c9e3bf7690d890f4861558b0d7b90a442d209" @@ -18170,7 +17882,7 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" -pako@^1.0.5, pako@~1.0.5: +pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== @@ -18201,24 +17913,6 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" -parse-bmfont-ascii@^1.0.3: - version "1.0.6" - resolved "https://registry.yarnpkg.com/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz#11ac3c3ff58f7c2020ab22769079108d4dfa0285" - integrity sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA== - -parse-bmfont-binary@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz#d038b476d3e9dd9db1e11a0b0e53a22792b69006" - integrity sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA== - -parse-bmfont-xml@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz#015319797e3e12f9e739c4d513872cd2fa35f389" - integrity sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ== - dependencies: - xml-parse-from-string "^1.0.0" - xml2js "^0.4.5" - parse-duration@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/parse-duration/-/parse-duration-0.4.4.tgz#11c0f51a689e97d06c57bd772f7fda7dc013243c" @@ -18396,11 +18090,6 @@ pbkdf2@^3.0.17, pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" -peek-readable@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.1.0.tgz#4ece1111bf5c2ad8867c314c81356847e8a62e72" - integrity sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg== - peek-readable@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-5.0.0.tgz#7ead2aff25dc40458c60347ea76cfdfd63efdfec" @@ -18426,11 +18115,6 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -phin@^2.9.1: - version "2.9.3" - resolved "https://registry.yarnpkg.com/phin/-/phin-2.9.3.tgz#f9b6ac10a035636fb65dfc576aaaa17b8743125c" - integrity sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA== - picocolors@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" @@ -18496,13 +18180,6 @@ pirates@^4.0.1, pirates@^4.0.4: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== -pixelmatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-4.0.2.tgz#8f47dcec5011b477b67db03c243bc1f3085e8854" - integrity sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA== - dependencies: - pngjs "^3.0.0" - pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -18540,11 +18217,6 @@ pkg-up@^4.0.0: dependencies: find-up "^6.2.0" -pngjs@^3.0.0, pngjs@^3.3.3: - version "3.4.0" - resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" - integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== - pngjs@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb" @@ -19975,14 +19647,6 @@ react-virtualized-auto-sizer@^1.0.2: resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.20.tgz#d9a907253a7c221c52fa57dc775a6ef40c182645" integrity sha512-OdIyHwj4S4wyhbKHOKM1wLSj/UDXm839Z3Cvfg2a9j+He6yDa6i5p0qQvEiCnyQlGO/HyfSnigQwuxvYalaAXA== -react-window@^1.8.5: - version "1.8.9" - resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.9.tgz#24bc346be73d0468cdf91998aac94e32bc7fa6a8" - integrity sha512-+Eqx/fj1Aa5WnhRfj9dJg4VYATGwIUP2ItwItiJ6zboKWA6EX3lYDAXfGF2hyNqplEprhbtjbipiADEcwQ823Q== - dependencies: - "@babel/runtime" "^7.0.0" - memoize-one ">=3.1.1 <6" - react@18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -20055,7 +19719,7 @@ readable-stream@^2.0.1, readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-web-to-node-stream@^3.0.0, readable-web-to-node-stream@^3.0.2: +readable-web-to-node-stream@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== @@ -20165,7 +19829,7 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.9: +regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.9: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== @@ -20239,13 +19903,6 @@ relateurl@^0.2.7: resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== -relative-luminance@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/relative-luminance/-/relative-luminance-2.0.1.tgz#2babddf3a5a59673227d6f02e0f68e13989e3d13" - integrity sha512-wFuITNthJilFPwkK7gNJcULxXBcfFZvZORsvdvxeOdO44wCeZnuQkf3nFFzOR/dpJNxYsdRZJLsepWbyKhnMww== - dependencies: - esm "^3.0.84" - remark-parse@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-9.0.0.tgz#4d20a299665880e4f4af5d90b7c7b8a935853640" @@ -20700,7 +20357,7 @@ sass-loader@^12.3.0: klona "^2.0.4" neo-async "^2.6.2" -sax@>=0.6.0, sax@~1.2.4: +sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -21608,14 +21265,6 @@ strong-log-transformer@^2.1.0: minimist "^1.2.0" through "^2.3.4" -strtok3@^6.2.4: - version "6.3.0" - resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-6.3.0.tgz#358b80ffe6d5d5620e19a073aa78ce947a90f9a0" - integrity sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw== - dependencies: - "@tokenizer/token" "^0.3.0" - peek-readable "^4.1.0" - strtok3@^7.0.0-alpha.9: version "7.0.0" resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-7.0.0.tgz#868c428b4ade64a8fd8fee7364256001c1a4cbe5" @@ -22071,11 +21720,6 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" -timm@^1.6.1: - version "1.7.1" - resolved "https://registry.yarnpkg.com/timm/-/timm-1.7.1.tgz#96bab60c7d45b5a10a8a4d0f0117c6b7e5aff76f" - integrity sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw== - tiny-invariant@^1.1.0, tiny-invariant@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" @@ -22097,11 +21741,6 @@ tinybench@^2.5.0: resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.5.0.tgz#4711c99bbf6f3e986f67eb722fed9cddb3a68ba5" integrity sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA== -tinycolor2@^1.4.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e" - integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw== - tinypool@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.5.0.tgz#3861c3069bf71e4f1f5aa2d2e6b3aaacc278961e" @@ -22163,14 +21802,6 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -token-types@^4.1.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/token-types/-/token-types-4.2.1.tgz#0f897f03665846982806e138977dbe72d44df753" - integrity sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ== - dependencies: - "@tokenizer/token" "^0.3.0" - ieee754 "^1.2.1" - token-types@^5.0.0-alpha.2: version "5.0.1" resolved "https://registry.yarnpkg.com/token-types/-/token-types-5.0.1.tgz#aa9d9e6b23c420a675e55413b180635b86a093b4" @@ -22848,13 +22479,6 @@ utf8@3.0.0: resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== -utif@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/utif/-/utif-2.0.1.tgz#9e1582d9bbd20011a6588548ed3266298e711759" - integrity sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg== - dependencies: - pako "^1.0.5" - util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -23204,13 +22828,6 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -wcag-contrast@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/wcag-contrast/-/wcag-contrast-3.0.0.tgz#51c2df43f15162993006454b3362844205889efb" - integrity sha512-RWbpg/S7FOXDCwqC2oFhN/vh8dHzj0OS6dpyOSDHyQFSmqmR+lAUStV/ziTT1GzDqL9wol+nZQB4vCi5yEak+w== - dependencies: - relative-luminance "^2.0.0" - wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" @@ -24198,7 +23815,7 @@ xhr-request@^1.0.1, xhr-request@^1.1.0: url-set-query "^1.0.0" xhr "^2.0.4" -xhr@^2.0.1, xhr@^2.0.4, xhr@^2.3.3: +xhr@^2.0.4, xhr@^2.3.3: version "2.6.0" resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" integrity sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA== @@ -24218,24 +23835,6 @@ xml-name-validator@^4.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== -xml-parse-from-string@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28" - integrity sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g== - -xml2js@^0.4.5: - version "0.4.23" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" - integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - -xmlbuilder@~11.0.0: - version "11.0.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" - integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== - xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"