diff --git a/lavamoat/build-webpack/policy.json b/lavamoat/build-webpack/policy.json index 7db10498a1..19440ec708 100644 --- a/lavamoat/build-webpack/policy.json +++ b/lavamoat/build-webpack/policy.json @@ -614,10 +614,10 @@ "eslint-config-rainbow>eslint-import-resolver-babel-module>@babel/core>@babel/helper-module-transforms>@babel/helper-module-imports": true, "eslint-config-rainbow>eslint-import-resolver-babel-module>@babel/core>@babel/helper-module-transforms>@babel/helper-simple-access": true, "eslint-config-rainbow>eslint-import-resolver-babel-module>@babel/core>@babel/helper-module-transforms>@babel/helper-split-export-declaration": true, - "eslint-config-rainbow>eslint-import-resolver-babel-module>@babel/core>@babel/helper-module-transforms>@babel/helper-validator-identifier": true, "eslint-config-rainbow>eslint-import-resolver-babel-module>@babel/core>@babel/template": true, "jest>@jest/core>jest-snapshot>@babel/traverse": true, - "jest>@jest/core>jest-snapshot>@babel/types": true + "jest>@jest/core>jest-snapshot>@babel/types": true, + "jest>@jest/core>jest-snapshot>@babel/types>@babel/helper-validator-identifier": true } }, "eslint-config-rainbow>eslint-import-resolver-babel-module>@babel/core>@babel/helper-module-transforms>@babel/helper-module-imports": { @@ -1141,16 +1141,9 @@ "console.warn": true, "process.emitWarning": true }, - "packages": { - "jest>@jest/core>jest-snapshot>@babel/traverse>@babel/code-frame>@babel/highlight": true, - "jest>@jest/core>jest-snapshot>@babel/traverse>@babel/code-frame>chalk": true - } - }, - "jest>@jest/core>jest-snapshot>@babel/traverse>@babel/code-frame>@babel/highlight": { "packages": { "jest>@jest/core>jest-snapshot>@babel/traverse>@babel/code-frame>chalk": true, - "jest>@jest/core>jest-snapshot>@babel/traverse>@babel/types>@babel/helper-validator-identifier": true, - "react>loose-envify>js-tokens": true + "lavamoat>@babel/highlight": true } }, "jest>@jest/core>jest-snapshot>@babel/traverse>@babel/code-frame>chalk": { @@ -1224,6 +1217,34 @@ "lavamoat>lavamoat-core>@babel/types>to-fast-properties": true } }, + "lavamoat>@babel/highlight": { + "packages": { + "lavamoat>@babel/highlight>@babel/helper-validator-identifier": true, + "lavamoat>@babel/highlight>chalk": true, + "react>loose-envify>js-tokens": true + } + }, + "lavamoat>@babel/highlight>chalk": { + "globals": { + "process.env.TERM": true, + "process.platform": true + }, + "packages": { + "lavamoat>@babel/highlight>chalk>ansi-styles": true, + "lavamoat>@babel/highlight>chalk>escape-string-regexp": true, + "supports-color": true + } + }, + "lavamoat>@babel/highlight>chalk>ansi-styles": { + "packages": { + "lavamoat>@babel/highlight>chalk>ansi-styles>color-convert": true + } + }, + "lavamoat>@babel/highlight>chalk>ansi-styles>color-convert": { + "packages": { + "lavamoat>@babel/highlight>chalk>ansi-styles>color-convert>color-name": true + } + }, "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-function-name": { "packages": { "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-function-name>@babel/template": true, @@ -1243,17 +1264,10 @@ "process.emitWarning": true }, "packages": { - "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-function-name>@babel/template>@babel/code-frame>@babel/highlight": true, + "lavamoat>@babel/highlight": true, "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-function-name>@babel/template>@babel/code-frame>chalk": true } }, - "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-function-name>@babel/template>@babel/code-frame>@babel/highlight": { - "packages": { - "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-function-name>@babel/template>@babel/code-frame>chalk": true, - "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-function-name>@babel/types>@babel/helper-validator-identifier": true, - "react>loose-envify>js-tokens": true - } - }, "lavamoat>lavamoat-tofu>@babel/traverse>@babel/helper-function-name>@babel/template>@babel/code-frame>chalk": { "globals": { "process.env.TERM": true, diff --git a/package.json b/package.json index 6495c32825..7a223dc65d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "browser-extension", "license": "GPL-3.0-only", - "version": "1.5.50", + "version": "1.5.53", "scripts": { "//enable dev mode": "", "devmode:on": "sed -i'' -e 's/IS_DEV.*/IS_DEV=true/g' .env", diff --git a/src/core/resources/search/parseTokenSearch.ts b/src/core/resources/search/parseTokenSearch.ts index 271ca9cd76..935a9171b0 100644 --- a/src/core/resources/search/parseTokenSearch.ts +++ b/src/core/resources/search/parseTokenSearch.ts @@ -8,6 +8,7 @@ export function parseTokenSearch( chainId: ChainId, ): SearchAsset { const networkInfo = asset.networks[chainId]; + const mainnetInfo = asset.networks[ChainId.mainnet]; return { ...asset, @@ -15,7 +16,7 @@ export function parseTokenSearch( chainId, decimals: networkInfo ? networkInfo.decimals : asset.decimals, isNativeAsset: isNativeAsset(asset.address, chainId), - mainnetAddress: asset.uniqueId as AddressOrEth, + mainnetAddress: mainnetInfo?.address as AddressOrEth, uniqueId: `${networkInfo?.address || asset.uniqueId}_${chainId}`, }; } diff --git a/src/core/resources/search/tokenDiscovery.ts b/src/core/resources/search/tokenDiscovery.ts index 42c1219091..e7a1c92ed3 100644 --- a/src/core/resources/search/tokenDiscovery.ts +++ b/src/core/resources/search/tokenDiscovery.ts @@ -12,11 +12,7 @@ const tokenSearchDiscoveryHttp = createHttpClient({ timeout: 30000, }); -type TokenDiscoveryArgs = { - chainId: ChainId; -}; - -const tokenDiscoveryQueryKey = ({ chainId }: TokenDiscoveryArgs) => +const tokenDiscoveryQueryKey = ({ chainId }: { chainId: ChainId }) => createQueryKey('TokenDiscovery', { chainId }, { persisterVersion: 1 }); async function tokenSearchQueryFunction({ @@ -36,12 +32,18 @@ async function tokenSearchQueryFunction({ } } -export function useTokenDiscovery({ chainId }: TokenDiscoveryArgs) { +export function useTokenDiscovery({ + chainId, + select, +}: { + chainId: ChainId; + select?: (data: SearchAsset[]) => T; +}) { return useQuery({ queryKey: tokenDiscoveryQueryKey({ chainId }), queryFn: tokenSearchQueryFunction, staleTime: 15 * 60 * 1000, // 15 min gcTime: 24 * 60 * 60 * 1000, // 1 day - select: (data) => data.slice(0, 3), + select, }); } diff --git a/src/core/resources/search/tokenSearch.ts b/src/core/resources/search/tokenSearch.ts index 42743502f6..91ca7f2243 100644 --- a/src/core/resources/search/tokenSearch.ts +++ b/src/core/resources/search/tokenSearch.ts @@ -1,4 +1,3 @@ -import { isAddress } from '@ethersproject/address'; import { useQueries, useQuery } from '@tanstack/react-query'; import qs from 'qs'; @@ -27,9 +26,7 @@ import { parseTokenSearch } from './parseTokenSearch'; export type TokenSearchArgs = { chainId: ChainId; fromChainId?: ChainId | ''; - keys: TokenSearchAssetKey[]; list: TokenSearchListId; - threshold: TokenSearchThreshold; query: string; }; @@ -46,14 +43,12 @@ export type TokenSearchAllNetworksArgs = { const tokenSearchQueryKey = ({ chainId, fromChainId, - keys, list, - threshold, query, }: TokenSearchArgs) => createQueryKey( 'TokenSearch', - { chainId, fromChainId, keys, list, threshold, query }, + { chainId, fromChainId, list, query }, { persisterVersion: 2 }, ); @@ -63,26 +58,19 @@ type TokenSearchQueryKey = ReturnType; // Query Function async function tokenSearchQueryFunction({ - queryKey: [{ chainId, fromChainId, keys, list, threshold, query }], + queryKey: [{ chainId, fromChainId, list, query }], }: QueryFunctionArgs) { const queryParams: { - keys: string; list: TokenSearchListId; - threshold: TokenSearchThreshold; query?: string; fromChainId?: number; } = { - keys: keys.join(','), list, - threshold, query, }; if (fromChainId) { queryParams.fromChainId = fromChainId; } - if (isAddress(query)) { - queryParams.keys = `networks.${chainId}.address`; - } const url = `/${chainId}/?${qs.stringify(queryParams)}`; try { const tokenSearch = await tokenSearchHttp.get<{ data: SearchAsset[] }>(url); @@ -100,7 +88,7 @@ type TokenSearchResult = QueryFunctionResult; // Query Fetcher export async function fetchTokenSearch( - { chainId, fromChainId, keys, list, threshold, query }: TokenSearchArgs, + { chainId, fromChainId, list, query }: TokenSearchArgs, config: QueryConfig< TokenSearchResult, Error, @@ -112,9 +100,7 @@ export async function fetchTokenSearch( queryKey: tokenSearchQueryKey({ chainId, fromChainId, - keys, list, - threshold, query, }), queryFn: tokenSearchQueryFunction, @@ -126,7 +112,7 @@ export async function fetchTokenSearch( // Query Hook export function useTokenSearch( - { chainId, fromChainId, keys, list, threshold, query }: TokenSearchArgs, + { chainId, fromChainId, list, query }: TokenSearchArgs, config: QueryConfig< TokenSearchResult, Error, @@ -138,9 +124,7 @@ export function useTokenSearch( queryKey: tokenSearchQueryKey({ chainId, fromChainId, - keys, list, - threshold, query, }), queryFn: tokenSearchQueryFunction, @@ -152,12 +136,7 @@ export function useTokenSearch( // Query Hook export function useTokenSearchAllNetworks( - { - keys, - list, - threshold, - query, - }: Omit, + { list, query }: Omit, config: QueryConfig< TokenSearchResult, Error, @@ -172,13 +151,7 @@ export function useTokenSearchAllNetworks( const queries = useQueries({ queries: rainbowSupportedChains.map(({ id: chainId }) => { return { - queryKey: tokenSearchQueryKey({ - chainId, - keys, - list, - threshold, - query, - }), + queryKey: tokenSearchQueryKey({ chainId, list, query }), queryFn: tokenSearchQueryFunction, refetchOnWindowFocus: false, ...config, diff --git a/src/core/state/rainbowChains/index.ts b/src/core/state/rainbowChains/index.ts index e71466d365..ee3aa1a0cb 100644 --- a/src/core/state/rainbowChains/index.ts +++ b/src/core/state/rainbowChains/index.ts @@ -122,7 +122,7 @@ export const rainbowChainsStore = createStore( { persist: persistOptions({ name: 'rainbowChains', - version: 9, + version: 10, migrations: [ // v1 didn't need a migration function v1(s: RainbowChainsState) { @@ -131,55 +131,73 @@ export const rainbowChainsStore = createStore( // version 2 added support for Avalanche and Avalanche Fuji function v2(state) { - return mergeNewOfficiallySupportedChainsState(state, [ + const rnbwChainState = state as RainbowChainsState; + return mergeNewOfficiallySupportedChainsState(rnbwChainState, [ ChainId.avalanche, ChainId.avalancheFuji, ]); }, // version 3 added support for Blast - function v3(state) { - return mergeNewOfficiallySupportedChainsState(state, [ChainId.blast]); + function v3(state: unknown) { + const rnbwChainState = state as RainbowChainsState; + return mergeNewOfficiallySupportedChainsState(rnbwChainState, [ + ChainId.blast, + ]); }, - function v4(state) { + function v4(state: unknown) { + const rnbwChainState = state as RainbowChainsState; return removeCustomRPC({ - state, + state: rnbwChainState, rpcUrl: 'https://rpc.zora.co', - rainbowChains: state.rainbowChains, + rainbowChains: rnbwChainState.rainbowChains, }); }, // version 5 added support for Degen - function v5(state) { - return mergeNewOfficiallySupportedChainsState(state, [ChainId.degen]); + function v5(state: unknown) { + const rnbwChainState = state as RainbowChainsState; + return mergeNewOfficiallySupportedChainsState(rnbwChainState, [ + ChainId.degen, + ]); }, - function v6(state) { + function v6(state: unknown) { + const rnbwChainState = state as RainbowChainsState; if ( - !state.rainbowChains[zora.id] || - state.rainbowChains[zora.id]?.chains.length === 0 + !rnbwChainState.rainbowChains[zora.id] || + rnbwChainState.rainbowChains[zora.id]?.chains.length === 0 ) { - return addCustomRPC({ chain: zora, state }); + return addCustomRPC({ chain: zora, state: rnbwChainState }); } return state; }, - function v7(state) { + function v7(state: unknown) { return state; }, - function v8(state) { + function v8(state: unknown) { + const rnbwChainState = state as RainbowChainsState; if ( - !state.rainbowChains[degen.id] || - state.rainbowChains[degen.id]?.chains.length === 0 + !rnbwChainState.rainbowChains[degen.id] || + rnbwChainState.rainbowChains[degen.id]?.chains.length === 0 ) { - return addCustomRPC({ chain: degen, state }); + return addCustomRPC({ + chain: degen, + state: state as RainbowChainsState, + }); } return state; }, - function v9(state) { - return replaceChainsWithInitial(state); + function v9(state: unknown) { + return replaceChainsWithInitial(state as RainbowChainsState); + }, + function v10(state: unknown) { + const rnbwState = state as RainbowChainsState; + rnbwState.rainbowChains = getInitialRainbowChains(); + return rnbwState; }, ], }), diff --git a/src/core/state/userChains/index.ts b/src/core/state/userChains/index.ts index 310b1bf6b9..3d16b13a3f 100644 --- a/src/core/state/userChains/index.ts +++ b/src/core/state/userChains/index.ts @@ -110,7 +110,7 @@ export const userChainsStore = createStore( { persist: { name: 'userChains', - version: 4, + version: 5, }, }, ); diff --git a/src/core/utils/persistOptions.ts b/src/core/utils/persistOptions.ts index fe308c90d0..314a94b426 100644 --- a/src/core/utils/persistOptions.ts +++ b/src/core/utils/persistOptions.ts @@ -88,6 +88,23 @@ interface PersistOptionsWithMigrations { ] >, ): R; + ( + opts: Opts< + Final, + [ + (s: any) => A, + (s: A) => B, + (s: B) => C, + (s: C) => D, + (s: D) => E, + (s: E) => F, + (s: F) => G, + (s: G) => H, + (s: H) => I, + (s: I) => Final, + ] + >, + ): R; // if you need more migrations, add more overloads here } diff --git a/src/core/utils/transactions.ts b/src/core/utils/transactions.ts index 820eebf77a..c509c604a8 100644 --- a/src/core/utils/transactions.ts +++ b/src/core/utils/transactions.ts @@ -175,20 +175,18 @@ const getAssetFromChanges = ( return changes[0]?.asset; }; -const getAddressTo = ( - meta: PaginatedTransactionsApiResponse['meta'], - changes: { - direction: TransactionDirection; - asset: ParsedUserAsset; - address_to: Address; - }[], -) => { - if (meta.type === 'approve') { - return meta?.approval_to; +const getAddressTo = (tx: PaginatedTransactionsApiResponse) => { + switch (tx.meta.type) { + case 'approve': + return tx.meta.approval_to; + case 'sale': + return tx.changes.find((c) => c?.direction === 'out')?.address_to; + case 'receive': + case 'airdrop': + return tx.changes[0]?.address_to; + default: + return tx.address_to; } - if (meta.type === 'sale') - return changes?.find((c) => c?.direction === 'out')?.address_to; - return changes[0]?.address_to; }; const getDescription = ( @@ -274,7 +272,7 @@ export function parseTransaction({ ? parseAsset({ asset: meta.asset, currency }) : getAssetFromChanges(changes, type); - const addressTo = asset ? getAddressTo(meta, changes) : tx.address_to; + const addressTo = getAddressTo(tx); const direction = tx.direction || getDirection(type); diff --git a/src/entries/popup/components/CommandK/useSearchableTokens.ts b/src/entries/popup/components/CommandK/useSearchableTokens.ts index 0ebe4197cc..2d614aec9f 100644 --- a/src/entries/popup/components/CommandK/useSearchableTokens.ts +++ b/src/entries/popup/components/CommandK/useSearchableTokens.ts @@ -18,7 +18,6 @@ import { useHideSmallBalancesStore } from '~/core/state/currentSettings/hideSmal import { useTestnetModeStore } from '~/core/state/currentSettings/testnetMode'; import { useHiddenAssetStore } from '~/core/state/hiddenAssets/hiddenAssets'; import { ParsedUserAsset } from '~/core/types/assets'; -import { TokenSearchAssetKey, TokenSearchThreshold } from '~/core/types/search'; import { isENSAddressFormat } from '~/core/utils/ethereum'; import { isLowerCaseMatch } from '~/core/utils/strings'; @@ -70,21 +69,6 @@ export const useSearchableTokens = ({ !isENSAddressFormat(debouncedSearchQuery) && !testnetMode; - const queryIsAddress = useMemo( - () => isAddress(debouncedSearchQuery), - [debouncedSearchQuery], - ); - - const keys: TokenSearchAssetKey[] = useMemo( - () => (queryIsAddress ? ['address'] : ['name', 'symbol']), - [queryIsAddress], - ); - - const threshold: TokenSearchThreshold = useMemo( - () => (queryIsAddress ? 'CASE_SENSITIVE_EQUAL' : 'CONTAINS'), - [queryIsAddress], - ); - const enableSearchChainAssets = isAddress(query) && !testnetMode; // All on chain searched assets from all user chains @@ -111,8 +95,6 @@ export const useSearchableTokens = ({ } = useTokenSearchAllNetworks( { list: 'verifiedAssets', - keys, - threshold, query: debouncedSearchQuery, }, { @@ -131,8 +113,6 @@ export const useSearchableTokens = ({ } = useTokenSearchAllNetworks( { list: 'highLiquidityAssets', - keys, - threshold, query: debouncedSearchQuery, }, { diff --git a/src/entries/popup/hooks/useFavoriteAssets.ts b/src/entries/popup/hooks/useFavoriteAssets.ts index e2414ecd5f..afd3ea5b0a 100644 --- a/src/entries/popup/hooks/useFavoriteAssets.ts +++ b/src/entries/popup/hooks/useFavoriteAssets.ts @@ -1,91 +1,42 @@ -import { isAddress } from 'ethers/lib/utils'; -import { useCallback, useEffect, useState } from 'react'; +import { keepPreviousData, useQuery } from '@tanstack/react-query'; +import { createQueryKey } from '~/core/react-query'; import { fetchTokenSearch } from '~/core/resources/search/tokenSearch'; import { useFavoritesStore } from '~/core/state/favorites'; -import { ParsedAsset } from '~/core/types/assets'; +import { AddressOrEth } from '~/core/types/assets'; import { ChainId } from '~/core/types/chains'; -import { SearchAsset } from '~/core/types/search'; -type FavoriteAssets = Record; -const FAVORITES_EMPTY_STATE = { - [ChainId.mainnet]: [], - [ChainId.optimism]: [], - [ChainId.bsc]: [], - [ChainId.polygon]: [], - [ChainId.arbitrum]: [], - [ChainId.base]: [], - [ChainId.zora]: [], - [ChainId.avalanche]: [], - [ChainId.hardhat]: [], - [ChainId.hardhatOptimism]: [], -}; +async function fetchFavoriteToken(address: AddressOrEth, chain: ChainId) { + const results = await fetchTokenSearch({ + chainId: chain, + list: 'verifiedAssets', + query: address.toLowerCase(), + }); + if (results?.[0]) return results[0]; -// expensive hook, only use in top level parent components -export function useFavoriteAssets() { - const { favorites } = useFavoritesStore(); - const [favoritesData, setFavoritesData] = useState( - FAVORITES_EMPTY_STATE, - ); - - const setFavoriteAssetsData = useCallback(async () => { - const chainIds = Object.keys(favorites) - .filter((k) => favorites?.[parseInt(k)]) - .map((c) => +c); - const searches: Promise[] = []; - const newSearchData = {} as Record; - for (const chain of chainIds) { - const addressesByChain = favorites[chain]; - addressesByChain?.forEach((address) => { - const searchAddress = async (add: string) => { - const query = add.toLocaleLowerCase(); - const queryIsAddress = isAddress(query); - const keys = ( - queryIsAddress ? ['address'] : ['name', 'symbol'] - ) as (keyof ParsedAsset)[]; - const threshold = queryIsAddress - ? 'CASE_SENSITIVE_EQUAL' - : 'CONTAINS'; - const results = await fetchTokenSearch({ - chainId: chain, - keys, - list: 'verifiedAssets', - threshold, - query, - }); + const unverifiedSearchResults = await fetchTokenSearch({ + chainId: chain, + list: 'highLiquidityAssets', + query: address.toLowerCase(), + }); + if (!unverifiedSearchResults?.[0]) return unverifiedSearchResults[0]; +} - const currentFavoritesData = newSearchData[chain] || []; - if (results?.[0]) { - newSearchData[chain] = [...currentFavoritesData, results[0]]; - } else { - const unverifiedSearchResults = await fetchTokenSearch({ - chainId: chain, - keys, - list: 'highLiquidityAssets', - threshold, - query, - }); - if (unverifiedSearchResults?.[0]) { - // eslint-disable-next-line require-atomic-updates - newSearchData[chain] = [ - ...currentFavoritesData, - unverifiedSearchResults[0], - ]; - } - } - }; - searches.push(searchAddress(address)); - }); - } - await Promise.all(searches); - setFavoritesData(newSearchData); - }, [favorites]); +export function useFavoriteAssets(chainId: ChainId) { + const favorites = useFavoritesStore((s) => s.favorites[chainId]); - useEffect(() => { - setFavoriteAssetsData(); - }, [setFavoriteAssetsData]); + const { data = [] } = useQuery({ + queryKey: createQueryKey('favorites assets', { chainId, favorites }), + queryFn: async () => { + if (!favorites) throw new Error('No chain favorites'); + return ( + await Promise.all( + favorites.map((address) => fetchFavoriteToken(address, chainId)), + ) + ).filter(Boolean); + }, + placeholderData: keepPreviousData, + }); - return { - favorites: favoritesData, - }; + return { favorites: data }; } diff --git a/src/entries/popup/hooks/useRainbowChains.ts b/src/entries/popup/hooks/useRainbowChains.ts index 46bc966849..6af2092c8c 100644 --- a/src/entries/popup/hooks/useRainbowChains.ts +++ b/src/entries/popup/hooks/useRainbowChains.ts @@ -4,20 +4,17 @@ import { useRainbowChainsStore } from '~/core/state'; export const useRainbowChains = () => { const rainbowChains = useRainbowChainsStore.use.rainbowChains(); - const chains = useMemo( - () => - Object.values(rainbowChains) - .map((rainbowChain) => - rainbowChain.chains.find( - (chain) => - chain.rpcUrls.default.http[0] === rainbowChain.activeRpcUrl, - ), - ) - .filter(Boolean), - [rainbowChains], - ); - return { - rainbowChains: chains, - }; + const chains = useMemo(() => { + return Object.values(rainbowChains) + .map((rainbowChain) => + rainbowChain.chains.find( + (chain) => + chain.rpcUrls.default.http[0] === rainbowChain.activeRpcUrl, + ), + ) + .filter(Boolean); + }, [rainbowChains]); + + return { rainbowChains: chains }; }; diff --git a/src/entries/popup/hooks/useSearchCurrencyLists.ts b/src/entries/popup/hooks/useSearchCurrencyLists.ts index 23765e61e6..6a79c986dd 100644 --- a/src/entries/popup/hooks/useSearchCurrencyLists.ts +++ b/src/entries/popup/hooks/useSearchCurrencyLists.ts @@ -1,6 +1,5 @@ import { isAddress } from '@ethersproject/address'; import { uniqBy } from 'lodash'; -import { rankings } from 'match-sorter'; import { useCallback, useMemo } from 'react'; import { Address } from 'viem'; @@ -12,30 +11,18 @@ import { useTokenSearchAllNetworks } from '~/core/resources/search/tokenSearch'; import { useTestnetModeStore } from '~/core/state/currentSettings/testnetMode'; import { ParsedSearchAsset } from '~/core/types/assets'; import { ChainId } from '~/core/types/chains'; -import { - SearchAsset, - TokenSearchAssetKey, - TokenSearchListId, - TokenSearchThreshold, -} from '~/core/types/search'; +import { SearchAsset, TokenSearchListId } from '~/core/types/search'; import { isSameAsset } from '~/core/utils/assets'; import { getChain, isNativeAsset } from '~/core/utils/chains'; -import { addHexPrefix } from '~/core/utils/hex'; import { isLowerCaseMatch } from '~/core/utils/strings'; -import { filterList } from '../utils/search'; - import { useFavoriteAssets } from './useFavoriteAssets'; const VERIFIED_ASSETS_PAYLOAD: { - keys: TokenSearchAssetKey[]; list: TokenSearchListId; - threshold: TokenSearchThreshold; query: string; } = { - keys: ['symbol', 'name'], list: 'verifiedAssets', - threshold: 'CONTAINS', query: '', }; @@ -83,6 +70,18 @@ function difference( }); } +function queryMatchesAsset( + query: string, + { symbol, name, address, mainnetAddress }: SearchAsset, +) { + return ( + symbol.toLowerCase().includes(query) || + name.toLowerCase().includes(query) || + isLowerCaseMatch(address, query) || + isLowerCaseMatch(mainnetAddress, query) + ); +} + export function useSearchCurrencyLists({ assetToSell, inputChainId, @@ -102,30 +101,15 @@ export function useSearchCurrencyLists({ const query = searchQuery?.toLowerCase() || ''; const enableUnverifiedSearch = query.trim().length > 2; - const isCrosschainSearch = useMemo(() => { - return inputChainId && inputChainId !== outputChainId; - }, [inputChainId, outputChainId]); + const isCrosschainSearch = inputChainId && inputChainId !== outputChainId; // provided during swap to filter token search by available routes - const fromChainId = useMemo(() => { - return isCrosschainSearch ? inputChainId : undefined; - }, [inputChainId, isCrosschainSearch]); - - const queryIsAddress = useMemo(() => isAddress(query), [query]); - - const keys: TokenSearchAssetKey[] = useMemo( - () => (queryIsAddress ? ['address'] : ['name', 'symbol']), - [queryIsAddress], - ); - - const threshold: TokenSearchThreshold = useMemo( - () => (queryIsAddress ? 'CASE_SENSITIVE_EQUAL' : 'CONTAINS'), - [queryIsAddress], - ); + const fromChainId = isCrosschainSearch ? inputChainId : undefined; const { testnetMode } = useTestnetModeStore(); - const enableAllNetworkTokenSearch = queryIsAddress && !testnetMode && !bridge; + const enableAllNetworkTokenSearch = + isAddress(query) && !testnetMode && !bridge; const networkSearchStatus = enableAllNetworkTokenSearch ? AssetToBuyNetworkSearchStatus.all @@ -226,9 +210,7 @@ export function useSearchCurrencyLists({ useTokenSearch( { chainId: outputChainId, - keys, list: 'verifiedAssets', - threshold, query, fromChainId, }, @@ -242,9 +224,7 @@ export function useSearchCurrencyLists({ } = useTokenSearch( { chainId: outputChainId, - keys, list: 'highLiquidityAssets', - threshold, query, fromChainId, }, @@ -255,12 +235,7 @@ export function useSearchCurrencyLists({ // All verified assets from all user chains const { data: targetAllNetworksUnverifiedAssets } = useTokenSearchAllNetworks( - { - keys, - list: 'highLiquidityAssets', - threshold, - query, - }, + { list: 'highLiquidityAssets', query }, { select: (data: SearchAsset[]) => { if (!enableAllNetworkTokenSearch) return []; @@ -273,12 +248,7 @@ export function useSearchCurrencyLists({ // All verified assets from all user chains const { data: targetAllNetworksVerifiedAssets } = useTokenSearchAllNetworks( - { - keys, - list: 'verifiedAssets', - threshold, - query, - }, + { list: 'verifiedAssets', query }, { select: (data: SearchAsset[]) => { if (!enableAllNetworkTokenSearch) return []; @@ -307,30 +277,22 @@ export function useSearchCurrencyLists({ const { data: popularAssets = [] } = useTokenDiscovery({ chainId: outputChainId, + select(popularAssets) { + if (!query) return popularAssets.slice(0, 3); + const a = popularAssets.filter((asset) => + queryMatchesAsset(query, asset), + ); + console.log('popularAssets', { a }); + return a; + }, }); - const { favorites } = useFavoriteAssets(); + const { favorites } = useFavoriteAssets(outputChainId); const favoritesList = useMemo(() => { - const favoritesByChain = favorites[outputChainId] || []; - if (query === '') { - return favoritesByChain; - } else { - const formattedQuery = queryIsAddress - ? addHexPrefix(query).toLowerCase() - : query; - return filterList( - favoritesByChain || [], - formattedQuery, - keys, - { - threshold: queryIsAddress - ? rankings.CASE_SENSITIVE_EQUAL - : rankings.CONTAINS, - }, - ); - } - }, [favorites, keys, outputChainId, query, queryIsAddress]); + if (!query) return favorites; + return favorites.filter((asset) => queryMatchesAsset(query, asset)); + }, [favorites, query]); // static verified asset lists prefetched to display curated lists // we only display crosschain exact matches if located here @@ -609,7 +571,7 @@ export function useSearchCurrencyLists({ if (!sections.length && crosschainExactMatches?.length) { sections.push({ - data: difference(crosschainExactMatches, otherSectionsAssets), + data: crosschainExactMatches, id: 'other_networks', }); } diff --git a/src/entries/popup/pages/swap/index.tsx b/src/entries/popup/pages/swap/index.tsx index 2d0360a9f5..be5aab55b3 100644 --- a/src/entries/popup/pages/swap/index.tsx +++ b/src/entries/popup/pages/swap/index.tsx @@ -462,6 +462,17 @@ export function Swap({ bridge = false }: { bridge?: boolean }) { [assetsToSell, isHidden], ); + const highestEth = useMemo(() => { + const eths = unhiddenAssetsToSell.filter((asset) => asset.symbol === 'ETH'); + return eths?.length + ? eths.reduce((highest, next) => { + return Number(highest.balance.amount) > Number(next.balance.amount) + ? highest + : next; + }) + : null; + }, [unhiddenAssetsToSell]); + const unhiddenAssetsToBuy = useMemo(() => { return assetsToBuy.map((assets) => { return { @@ -627,7 +638,11 @@ export function Swap({ bridge = false }: { bridge?: boolean }) { if (savedTokenToSell) { setAssetToSell(savedTokenToSell); } else { - setAssetToSell(unhiddenAssetsToSell[0]); + if (highestEth) { + setAssetToSell(highestEth); + } else { + setAssetToSell(unhiddenAssetsToSell[0]); + } setDefaultAssetWasSet(true); } setDidPopulateSavedTokens(true); diff --git a/static/manifest.json b/static/manifest.json index fa5edb8f61..3cf3819141 100644 --- a/static/manifest.json +++ b/static/manifest.json @@ -68,7 +68,7 @@ "notifications" ], "short_name": "Rainbow", - "version": "1.5.50", + "version": "1.5.53", "web_accessible_resources": [ { "matches": [