harb/packages/web3/src/composables/useTokenBalance.ts

59 lines
1.7 KiB
TypeScript

import { ref, watch, type Ref } from 'vue';
import { useAccount, useChainId } from '@wagmi/vue';
import { createPublicClient, erc20Abi, formatUnits, http } from 'viem';
import { getHarbConfig, KRAIKEN_LOCAL_CHAIN } from '../config';
/**
* Read-only KRK token balance for the connected wallet.
* Lightweight — no contract writes, no staking, just balanceOf.
*/
export function useTokenBalance(tokenAddress: Ref<`0x${string}` | undefined> | `0x${string}`) {
const { address, isConnected } = useAccount();
const chainId = useChainId();
const balance = ref(0n);
const formatted = ref('0');
const loading = ref(false);
async function refresh() {
const token = typeof tokenAddress === 'string' ? tokenAddress : tokenAddress.value;
if (!address.value || !token) {
balance.value = 0n;
formatted.value = '0';
return;
}
loading.value = true;
try {
const config = getHarbConfig();
const chain = config.chains.find(c => c.id === chainId.value) ?? KRAIKEN_LOCAL_CHAIN;
const rpcUrl = chain.rpcUrls.default.http[0];
const client = createPublicClient({ chain, transport: http(rpcUrl) });
const result = await client.readContract({
abi: erc20Abi,
address: token,
functionName: 'balanceOf',
args: [address.value],
});
balance.value = result;
formatted.value = formatUnits(result, 18);
} catch {
balance.value = 0n;
formatted.value = '0';
} finally {
loading.value = false;
}
}
watch([address, isConnected], () => {
if (isConnected.value) {
void refresh();
} else {
balance.value = 0n;
formatted.value = '0';
}
}, { immediate: true });
return { balance, formatted, loading, refresh };
}