@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.
85 lines • 4.25 kB
JavaScript
import { ChainType, getWalletBalances, } from '@lifi/sdk';
import { useQuery } from '@tanstack/react-query';
import { useMemo } from 'react';
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js';
import { getConfigItemSets, isFormItemAllowed } from '../utils/item.js';
import { isSupportedToken } from '../utils/tokenList.js';
export const useFilteredTokensByBalance = (accountsWithTokens, formType) => {
const { tokens: configTokens } = useWidgetConfig();
const evmAddress = useMemo(() => {
const evmAccount = Object.entries(accountsWithTokens ?? {}).find(([_, { chainType }]) => chainType === ChainType.EVM);
return evmAccount?.[0];
}, [accountsWithTokens]);
const { data: existingBalances, isLoading } = useQuery({
queryKey: ['existing-evm-balances', evmAddress],
queryFn: () => getWalletBalances(evmAddress ?? ''),
enabled: !!evmAddress,
refetchInterval: 30000, // 30 seconds
staleTime: 30000, // 30 seconds
retry: false,
});
const accountsWithFilteredTokens = useMemo(() => {
if (!accountsWithTokens) {
return undefined;
}
// Early return if no existing balances - return all tokens
const result = {};
if (!existingBalances) {
for (const [address, { tokens }] of Object.entries(accountsWithTokens)) {
result[address] = tokens;
}
return result;
}
for (const [address, { tokens }] of Object.entries(accountsWithTokens)) {
result[address] = {};
for (const [chainIdStr, chainTokens] of Object.entries(tokens)) {
const chainId = Number(chainIdStr);
// Get balances for this specific chain
const balances = existingBalances?.[chainId];
// If no balances, RPC all tokens of the chain
if (!balances?.length) {
if (chainTokens.length) {
result[address][chainId] = chainTokens;
}
continue;
}
// Optimize token matching with Set for O(1) lookup
const balanceSet = new Set(balances.map((balance) => balance.address.toLowerCase()));
// Get tokens that are in chainTokens and have balances
const filteredTokens = chainTokens.filter((token) => {
const tokenKey = token.address.toLowerCase();
return balanceSet.has(tokenKey);
});
// Get tokens that are in balances but not in chainTokens
const chainTokenSet = new Set(chainTokens.map((token) => token.address.toLowerCase()));
// Get allowed addresses from config tokens
const allowedAddressesConfig = getConfigItemSets(configTokens, (tokens) => new Set(tokens
.filter((t) => Number(t.chainId) === chainId)
.map((t) => t.address.toLowerCase())), formType);
const additionalTokens = balances
.filter((balance) => {
const balanceKey = balance.address.toLowerCase();
return (!chainTokenSet.has(balanceKey) &&
isSupportedToken(balance) &&
isFormItemAllowed(balance, allowedAddressesConfig, formType, (t) => t.address.toLowerCase()));
})
// Mark tokens from wallet balances as unverified
.map((token) => ({
...token,
verified: false,
}));
// Combine both sets of tokens - convert WalletTokenExtended to TokenAmount
const allTokens = [
...filteredTokens,
...additionalTokens,
];
if (allTokens.length) {
result[address][chainId] = allTokens;
}
}
}
return result;
}, [accountsWithTokens, existingBalances, configTokens, formType]);
return { data: accountsWithFilteredTokens, isLoading };
};
//# sourceMappingURL=useFilteredByTokenBalances.js.map