Files
dydx-v4-web/src/hooks/useAccountBalance.ts
T

111 lines
3.7 KiB
TypeScript

import { useCallback } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import { useBalance } from 'wagmi';
import { StargateClient } from '@cosmjs/stargate';
import { useQuery } from 'react-query';
import { formatUnits } from 'viem';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { EvmAddress } from '@/constants/wallets';
import { convertBech32Address } from '@/lib/addressUtils';
import { MustBigNumber } from '@/lib/numbers';
import { getBalances, getStakingBalances } from '@/state/accountSelectors';
import { getSelectedNetwork } from '@/state/appSelectors';
import { useAccounts } from './useAccounts';
import { useTokenConfigs } from './useTokenConfigs';
type UseAccountBalanceProps = {
// Token Items
addressOrDenom?: string;
decimals?: number;
// Chain Items
chainId?: string | number;
bech32AddrPrefix?: string;
rpc?: string;
isCosmosChain?: boolean;
};
/**
* 0xSquid uses this 0x address as the chain's default token.
* @todo We will need to add additional logic here if we 'useAccountBalance' on non-Squid related forms.
*/
export const CHAIN_DEFAULT_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
export const useAccountBalance = ({
addressOrDenom,
bech32AddrPrefix,
chainId,
decimals = 0,
rpc,
isCosmosChain,
}: UseAccountBalanceProps = {}) => {
const { evmAddress, dydxAddress } = useAccounts();
const selectedNetwork = useSelector(getSelectedNetwork);
const balances = useSelector(getBalances, shallowEqual);
const { chainTokenDenom, usdcDenom } = useTokenConfigs();
const evmChainId = Number(ENVIRONMENT_CONFIG_MAP[selectedNetwork].ethereumChainId);
const stakingBalances = useSelector(getStakingBalances, shallowEqual);
const evmQuery = useBalance({
enabled: Boolean(!isCosmosChain && addressOrDenom?.startsWith('0x')),
address: evmAddress,
chainId: typeof chainId === 'number' ? chainId : Number(evmChainId),
token:
addressOrDenom === CHAIN_DEFAULT_TOKEN_ADDRESS ? undefined : (addressOrDenom as EvmAddress),
watch: true,
});
const cosmosQueryFn = useCallback(async () => {
if (dydxAddress && bech32AddrPrefix && rpc && addressOrDenom) {
const address = convertBech32Address({
address: dydxAddress,
bech32Prefix: bech32AddrPrefix,
});
const client = await StargateClient.connect(rpc);
const balanceAsCoin = await client.getBalance(address, addressOrDenom);
await client.disconnect();
return formatUnits(BigInt(balanceAsCoin.amount), decimals);
}
}, [addressOrDenom, chainId, rpc]);
const cosmosQuery = useQuery({
enabled: Boolean(isCosmosChain && dydxAddress && bech32AddrPrefix && rpc && addressOrDenom),
queryKey: `accountBalances_${chainId}_${addressOrDenom}`,
queryFn: cosmosQueryFn,
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
refetchInterval: 10_000,
staleTime: 10_000,
});
const { formatted: evmBalance } = evmQuery.data || {};
const balance = isCosmosChain ? cosmosQuery.data : evmBalance;
const nativeTokenCoinBalance = balances?.[chainTokenDenom];
const nativeTokenBalance = MustBigNumber(nativeTokenCoinBalance?.amount);
const usdcCoinBalance = balances?.[usdcDenom];
const usdcBalance = MustBigNumber(usdcCoinBalance?.amount).toNumber();
const nativeStakingCoinBalanace = stakingBalances?.[chainTokenDenom];
const nativeStakingBalance = MustBigNumber(nativeStakingCoinBalanace?.amount).toNumber();
return {
balance,
nativeTokenBalance,
nativeStakingBalance,
usdcBalance,
queryStatus: isCosmosChain ? cosmosQuery.status : evmQuery.status,
isQueryFetching: isCosmosChain ? cosmosQuery.isFetching : evmQuery.fetchStatus === 'fetching',
};
};