Skip to content

Commit

Permalink
feat: fix too many token balances to load error
Browse files Browse the repository at this point in the history
  • Loading branch information
Space-Bean committed Jun 26, 2024
1 parent 993d7fb commit b473775
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 26 deletions.
67 changes: 42 additions & 25 deletions projects/dex-ui/src/tokens/useAllTokenBalance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { useAccount } from "wagmi";
import { useTokens } from "./TokenProvider";
import { Log } from "src/utils/logger";
import { config } from "src/utils/wagmi/config";
import { ContractFunctionParameters } from "viem";
import { queryKeys } from "src/utils/query/queryKeys";

const TokenBalanceABI = [
{
Expand All @@ -20,63 +22,78 @@ const TokenBalanceABI = [
}
] as const;

const MAX_PER_CALL = 20;

export const useAllTokensBalance = () => {
const tokens = useTokens();
const { address } = useAccount();
const queryClient = useQueryClient();

// Remove ETH from this list, manually get the balance below
const tokensToLoad = Object.values(tokens).filter((t) => t.symbol !== "ETH");
if (tokensToLoad.length > 20) throw new Error("Too many tokens to load balances. Fix me");

const calls = useMemo(() => {
const contractCalls: any[] = [];
Log.module("app").debug(`Fetching token balances for ${tokensToLoad.length} tokens, for address ${address}`);
for (const t of tokensToLoad) {
contractCalls.push({
address: t.address as `0x{string}`,
const contractCalls: ContractFunctionParameters[][] = [];
Log.module("app").debug(
`Fetching token balances for ${tokensToLoad.length} tokens, for address ${address}`
);

let callBucket: ContractFunctionParameters[] = [];

tokensToLoad.forEach((token, i) => {
callBucket.push({
address: token.address as `0x{string}`,
abi: TokenBalanceABI,
functionName: "balanceOf",
args: [address]
});
}

if (i % MAX_PER_CALL === MAX_PER_CALL - 1) {
contractCalls.push([...callBucket]);
callBucket = [];
}
});
return contractCalls;

// eslint-disable-next-line react-hooks/exhaustive-deps -- doing just tokensToLoad doesn't work and causes multiple calls
}, [address, tokensToLoad.map((t) => t.symbol).join()]);

const { data, isLoading, error, refetch, isFetching } = useQuery({
queryKey: ["token", "balance"],

queryKey: queryKeys.tokenBalancesAll,
queryFn: async () => {
if (!address) return {};
const res = (await multicall(config, {
contracts: calls,
allowFailure: false
})) as unknown as BigNumber[];

const ETH = tokens.ETH;

const [ethBalance, ...results] = await Promise.all([
ETH.getBalance(address),
...(calls.map((calls) =>
multicall(config, { contracts: calls, allowFailure: false })
) as unknown as BigNumber[])
]);

const res = results.flat();
const balances: Record<string, TokenValue> = {};

if (ethBalance) {
Log.module("app").debug(`ETH balance: `, ethBalance.toHuman());
queryClient.setQueryData(queryKeys.tokenBalance(ETH.symbol), { ETH: ethBalance });
balances.ETH = ethBalance;
}

for (let i = 0; i < res.length; i++) {
const value = res[i];
const token = tokensToLoad[i];
balances[token.symbol] = token.fromBlockchain(value);

// set the balance in the query cache too
queryClient.setQueryData(["token", "balance", token.symbol], { [token.symbol]: balances[token.symbol] });

}

const ETH = tokens.ETH;
if (ETH) {
const ethBalance = await ETH.getBalance(address);
Log.module("app").debug(`ETH balance: `, ethBalance.toHuman());
queryClient.setQueryData(["token", "balance", "ETH"], { ETH: ethBalance });
balances.ETH = ethBalance;
queryClient.setQueryData(queryKeys.tokenBalance(token.symbol), {
[token.symbol]: balances[token.symbol]
});
}

return balances;
},

enabled: !!address && !!tokensToLoad.length,
staleTime: 1000 * 30,
refetchInterval: 1000 * 30
});
Expand Down
6 changes: 5 additions & 1 deletion projects/dex-ui/src/utils/query/queryKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,9 @@ export const queryKeys = {
// prices
tokenPricesAll: ["prices", "token"],
tokenPrices: (address: string[]) => ["prices", "token", ...address],
lpTokenPrices: (addresses: string[]) => ["prices", "lp-token", ...addresses]
lpTokenPrices: (addresses: string[]) => ["prices", "lp-token", ...addresses],

// token balance
tokenBalancesAll: ["token", "balance"],
tokenBalance: (symbol: string) => ["token", "balance", symbol]
} as const;

0 comments on commit b473775

Please sign in to comment.