All checks were successful
CI / lint-and-test (push) Successful in 9m52s
Co-authored-by: Cursor <cursoragent@cursor.com>
133 lines
3.8 KiB
TypeScript
133 lines
3.8 KiB
TypeScript
"use client";
|
|
|
|
import { useTokenBalances } from "@/lib/hooks/useTokenBalances";
|
|
import {
|
|
formatUsd,
|
|
tokenUsdValue,
|
|
useUsdPrices,
|
|
} from "@/lib/prices";
|
|
import { formatTokenAmount } from "@/lib/tokens";
|
|
import { formatAddress } from "@/lib/utils";
|
|
import { CHAIN138_ID } from "@/lib/constants/chain138";
|
|
|
|
interface WalletBalancePanelProps {
|
|
address: `0x${string}`;
|
|
onDisconnect?: () => void;
|
|
}
|
|
|
|
function BalanceRow({
|
|
label,
|
|
amount,
|
|
usd,
|
|
loading,
|
|
}: {
|
|
label: string;
|
|
amount: string;
|
|
usd: number | null;
|
|
loading?: boolean;
|
|
}) {
|
|
return (
|
|
<div className="flex items-center justify-between gap-3 py-2 border-b border-gray-800 last:border-b-0">
|
|
<div>
|
|
<p className="text-sm font-medium text-gray-200">{label}</p>
|
|
<p className="text-xs text-gray-500 font-mono">
|
|
{loading ? "Loading…" : amount}
|
|
</p>
|
|
</div>
|
|
<p className="text-sm font-semibold text-emerald-400 tabular-nums">
|
|
{loading ? "…" : formatUsd(usd)}
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function WalletBalancePanel({ address, onDisconnect }: WalletBalancePanelProps) {
|
|
const { ethUsd, isLoading: pricesLoading } = useUsdPrices();
|
|
const {
|
|
nativeBalance,
|
|
cusdtRaw,
|
|
cusdcRaw,
|
|
erc20Tokens,
|
|
isLoading: balancesLoading,
|
|
refetch,
|
|
} = useTokenBalances(address);
|
|
|
|
const loading = balancesLoading || pricesLoading;
|
|
|
|
const ethRaw = nativeBalance?.value;
|
|
const ethUsdVal = tokenUsdValue(ethRaw, 18, "ETH", ethUsd);
|
|
const cusdtUsd = tokenUsdValue(cusdtRaw, erc20Tokens[0].decimals, "cUSDT", ethUsd);
|
|
const cusdcUsd = tokenUsdValue(cusdcRaw, erc20Tokens[1].decimals, "cUSDC", ethUsd);
|
|
|
|
const totalUsd =
|
|
[ethUsdVal, cusdtUsd, cusdcUsd].reduce<number | null>((sum, v) => {
|
|
if (v == null) return sum;
|
|
return (sum ?? 0) + v;
|
|
}, null);
|
|
|
|
return (
|
|
<div className="p-4 w-full sm:w-80">
|
|
<div className="mb-3">
|
|
<p className="text-xs uppercase tracking-wide text-gray-500">Connected wallet</p>
|
|
<p className="font-mono text-sm text-white mt-0.5">{formatAddress(address)}</p>
|
|
<p className="text-xs text-gray-500 mt-1">Chain {CHAIN138_ID} balances</p>
|
|
</div>
|
|
|
|
<div className="rounded-lg bg-gray-950/80 border border-gray-800 px-3">
|
|
<BalanceRow
|
|
label="ETH"
|
|
amount={ethRaw !== undefined ? `${nativeBalance?.formatted ?? "0"} ETH` : "0 ETH"}
|
|
usd={ethUsdVal}
|
|
loading={loading}
|
|
/>
|
|
<BalanceRow
|
|
label="cUSDT"
|
|
amount={
|
|
cusdtRaw !== undefined
|
|
? formatTokenAmount(cusdtRaw.toString(), erc20Tokens[0])
|
|
: "0 cUSDT"
|
|
}
|
|
usd={cusdtUsd}
|
|
loading={loading}
|
|
/>
|
|
<BalanceRow
|
|
label="cUSDC"
|
|
amount={
|
|
cusdcRaw !== undefined
|
|
? formatTokenAmount(cusdcRaw.toString(), erc20Tokens[1])
|
|
: "0 cUSDC"
|
|
}
|
|
usd={cusdcUsd}
|
|
loading={loading}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between mt-3 pt-3 border-t border-gray-800">
|
|
<span className="text-xs text-gray-500">Estimated total</span>
|
|
<span className="text-base font-bold text-white tabular-nums">
|
|
{loading ? "…" : formatUsd(totalUsd)}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="flex gap-2 mt-4">
|
|
<button
|
|
type="button"
|
|
onClick={() => refetch()}
|
|
className="flex-1 px-3 py-2 text-xs bg-gray-800 hover:bg-gray-700 rounded-lg transition-colors"
|
|
>
|
|
Refresh
|
|
</button>
|
|
{onDisconnect && (
|
|
<button
|
|
type="button"
|
|
onClick={onDisconnect}
|
|
className="flex-1 px-3 py-2 text-xs bg-red-600/90 hover:bg-red-600 rounded-lg transition-colors"
|
|
>
|
|
Disconnect
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|