UNPKG

@lifi/widget

Version:

LI.FI Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.

146 lines (131 loc) 4.24 kB
import { ChainType, getToken, getTokens, type TokensExtendedResponse, } from '@lifi/sdk' import { useQuery } from '@tanstack/react-query' import { useMemo } from 'react' import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js' import type { FormType } from '../stores/form/types.js' import type { TokensByChain } from '../types/token.js' import { defaultChainIdsByType, getChainTypeFromAddress, } from '../utils/chainType.js' import { isItemAllowed } from '../utils/item.js' import { getQueryKey } from '../utils/queries.js' import { filterAllowedTokens, mergeVerifiedWithSearchTokens, } from '../utils/token.js' const refetchInterval = 300_000 export const useTokens = ( formType?: FormType, search?: string, chainId?: number ) => { const { tokens: configTokens, chains: chainsConfig, keyPrefix, } = useWidgetConfig() // Main tokens cache - verified tokens from API const { data: verifiedTokens, isLoading } = useQuery({ queryKey: [getQueryKey('tokens', keyPrefix)], queryFn: async ({ signal }) => { const chainTypes = [ ChainType.EVM, ChainType.SVM, ChainType.UTXO, ChainType.MVM, ].filter((chainType) => isItemAllowed(chainType, chainsConfig?.types)) const tokensResponse: TokensExtendedResponse = await getTokens( { chainTypes, orderBy: 'volumeUSD24H', extended: true, limit: 1000, }, { signal } ) // Mark all tokens as verified const tokens: TokensByChain = Object.fromEntries( Object.entries(tokensResponse.tokens).map(([chainId, tokens]) => [ chainId, tokens.map((token) => ({ ...token, verified: true })), ]) ) return tokens }, refetchInterval, staleTime: refetchInterval, }) // Search tokens cache - unverified tokens from search const { data: searchTokens, isLoading: isSearchLoading } = useQuery({ queryKey: [getQueryKey('tokens-search', keyPrefix), search, chainId], queryFn: async ({ queryKey, signal }) => { const [, searchQuery, searchChainId] = queryKey as [ string, string, number, ] const chainTypes = [ ChainType.EVM, ChainType.SVM, ChainType.UTXO, ChainType.MVM, ].filter((chainType) => isItemAllowed(chainType, chainsConfig?.types)) const tokensResponse: TokensExtendedResponse = await getTokens( { chainTypes, orderBy: 'volumeUSD24H', extended: true, search: searchQuery, limit: 1000, }, { signal } ) // If the chainId is not provided, try to get it from the search query let _chainId = searchChainId if (!_chainId) { const chainType = getChainTypeFromAddress(searchQuery) if (chainType) { _chainId = defaultChainIdsByType[chainType] } } // Fallback: If the main search returned no tokens for the specific chainId, // fetch a single token using the /token endpoint if (_chainId && searchQuery) { const existingTokens = tokensResponse.tokens[_chainId] || [] if (!existingTokens.length) { const token = await getToken(_chainId, searchQuery, { signal }) if (token) { tokensResponse.tokens[_chainId] = [token] } } } // Mark all search tokens as unverified const tokens: TokensByChain = Object.fromEntries( Object.entries(tokensResponse.tokens).map(([chainId, tokens]) => [ chainId, tokens.map((token) => ({ ...token, verified: false })), ]) ) return tokens }, enabled: !!search, refetchInterval, staleTime: refetchInterval, }) // Merge tokens at read time - single place where caches are combined const allTokens = useMemo(() => { const merged = mergeVerifiedWithSearchTokens(verifiedTokens, searchTokens) return filterAllowedTokens(merged, configTokens, chainsConfig, formType) }, [verifiedTokens, searchTokens, configTokens, chainsConfig, formType]) return { allTokens, isLoading, isSearchLoading, } }