diff --git a/src/components/DonateWidget.tsx b/src/components/DonateWidget.tsx
index 247b8926..c3374521 100644
--- a/src/components/DonateWidget.tsx
+++ b/src/components/DonateWidget.tsx
@@ -2,6 +2,7 @@
import { useState, useCallback } from "react";
import { useAccount, useWriteContract } from "wagmi";
+import { useQuery } from "@tanstack/react-query";
import { parseUnits, formatUnits } from "viem";
import { publicClient } from "../../lib/rpc";
import { erc20Abi } from "../../lib/price";
@@ -29,6 +30,24 @@ export function DonateWidget({ storylineId }: DonateWidgetProps) {
? parseUnits(amount, 18)
: BigInt(0);
+ // Fetch reserve token balance
+ const { data: balance, refetch: refetchBalance } = useQuery({
+ queryKey: ["token-balance", PLOT_TOKEN, address],
+ queryFn: async () => {
+ return publicClient.readContract({
+ address: PLOT_TOKEN,
+ abi: erc20Abi,
+ functionName: "balanceOf",
+ args: [address!],
+ });
+ },
+ enabled: !!address,
+ refetchInterval: 15000,
+ });
+
+ const insufficientBalance =
+ balance !== undefined && parsedAmount > BigInt(0) && parsedAmount > balance;
+
const executeDonate = useCallback(async () => {
if (!address || parsedAmount === BigInt(0)) return;
@@ -82,11 +101,12 @@ export function DonateWidget({ storylineId }: DonateWidgetProps) {
setTxState("done");
setAmount("");
+ refetchBalance();
} catch (err) {
setError(err instanceof Error ? err.message : "Transaction failed");
setTxState("error");
}
- }, [address, parsedAmount, storylineId, writeContractAsync]);
+ }, [address, parsedAmount, storylineId, writeContractAsync, refetchBalance]);
const reset = useCallback(() => {
setTxState("idle");
@@ -107,18 +127,37 @@ export function DonateWidget({ storylineId }: DonateWidgetProps) {
- {
- setAmount(e.target.value);
- if (txState !== "idle") reset();
- }}
- disabled={txState !== "idle" && txState !== "error" && txState !== "done"}
- className="border-border bg-background text-foreground mt-1 w-full rounded border px-3 py-2 text-sm focus:border-accent focus:outline-none disabled:opacity-50"
- />
+
+ {
+ setAmount(e.target.value);
+ if (txState !== "idle") reset();
+ }}
+ disabled={txState !== "idle" && txState !== "error" && txState !== "done"}
+ className="border-border bg-background text-foreground w-full rounded border px-3 py-2 pr-14 text-sm focus:border-accent focus:outline-none disabled:opacity-50"
+ />
+ {balance !== undefined && (
+
+ )}
+
+ {balance !== undefined && (
+
+ Balance: {formatUnits(balance, 18)} {reserveLabel}
+
+ )}
+ {insufficientBalance && (
+ Insufficient balance
+ )}
{parsedAmount > BigInt(0) && (
@@ -134,7 +173,7 @@ export function DonateWidget({ storylineId }: DonateWidgetProps) {