diff --git a/app/build/dao/cast-vote.tsx b/app/build/dao/cast-vote.tsx index 72f16fc..e116156 100644 --- a/app/build/dao/cast-vote.tsx +++ b/app/build/dao/cast-vote.tsx @@ -26,7 +26,7 @@ const CHOICES: { label: string; support: Support; cls: string }[] = [ ]; export function CastVote({ chain, proposalId, onVoted }: { chain: DaoChain; proposalId: string; onVoted?: () => void }) { - const { address, isConnected, walletClient, open, onTargetChain, ensureChain } = useDaoWallet(chain); + const { address, isConnected, open, getSigner } = useDaoWallet(chain); const [voted, setVoted] = useState(null); const [powerWei, setPowerWei] = useState(null); const [vote, setVote] = useState({ phase: "idle" }); @@ -55,12 +55,12 @@ export function CastVote({ chain, proposalId, onVoted }: { chain: DaoChain; prop }, [isConnected, address, chain, proposalId, check]); const castVote = async (support: Support) => { - if (!walletClient || !address) return open(); - setVote({ phase: "working", support, msg: onTargetChain ? "Confirm your vote in your wallet..." : "Switch network in your wallet..." }); + if (!isConnected || !address) return open(); + setVote({ phase: "working", support, msg: "Confirm in your wallet (you may be asked to switch network first)..." }); try { - await ensureChain(); + const signer = await getSigner(); const fees = chain === "lightchain" ? await pinnedFees(daoPublicClient(chain)) : undefined; - const hash = await walletClient.writeContract({ + const hash = await signer.writeContract({ address: GOVERNOR[chain], abi: GOVERNOR_ABI, functionName: "castVote", @@ -78,7 +78,7 @@ export function CastVote({ chain, proposalId, onVoted }: { chain: DaoChain; prop }; return ( -
+

Cast your vote

The SDK behind it

- +
); diff --git a/app/build/dao/use-dao-wallet.ts b/app/build/dao/use-dao-wallet.ts index c0415f7..11da9b9 100644 --- a/app/build/dao/use-dao-wallet.ts +++ b/app/build/dao/use-dao-wallet.ts @@ -1,34 +1,46 @@ "use client"; -import { useAccount, useWalletClient, useSwitchChain, useChainId } from "wagmi"; +import { useAccount, useSwitchChain, useChainId } from "wagmi"; +import { getWalletClient } from "@wagmi/core"; import { useAppKit } from "@reown/appkit/react"; +import { wagmiConfig } from "@/lib/wagmi"; import { DAO_CHAIN_ID, type DaoChain } from "./dao-chain"; /** * Shared wallet wiring for the DAO write controls (delegate, cast vote). Uses * `open()` from AppKit for connect rather than the global ConnectButton, which * treats Ethereum as "unsupported" and would nag the user back to LightChain. + * + * `getSigner` is the load-bearing piece: the app's network toggle drives wagmi's + * tracked chain (e.g. LightChain 9200) independently of the wallet's real chain, + * so the reactive `useWalletClient()` can stay bound to the wrong chain and viem + * then rejects the write ("wallet chain ... does not match target chain ..."). We + * instead switch to the target chain and fetch a FRESH client bound to it from + * the connector's actual state. */ export function useDaoWallet(chain: DaoChain) { const { address, isConnected } = useAccount(); - const { data: walletClient } = useWalletClient(); const { switchChainAsync } = useSwitchChain(); const { open } = useAppKit(); const chainId = useChainId(); const targetChainId = DAO_CHAIN_ID[chain]; - const ensureChain = async () => { - if (chainId !== targetChainId) await switchChainAsync({ chainId: targetChainId }); + const getSigner = async () => { + // Switching to the chain you're already on is a no-op (no wallet prompt), + // and it realigns wagmi's tracked chain with the connector before we read it. + await switchChainAsync({ chainId: targetChainId }); + const signer = await getWalletClient(wagmiConfig, { chainId: targetChainId }); + if (!signer) throw new Error("Could not get a wallet client for this network. Reconnect and try again."); + return signer; }; return { address, isConnected, - walletClient, open, chainId, targetChainId, onTargetChain: chainId === targetChainId, - ensureChain, + getSigner, }; } diff --git a/app/build/dao/voting-power-card.tsx b/app/build/dao/voting-power-card.tsx index e7cf723..37467f4 100644 --- a/app/build/dao/voting-power-card.tsx +++ b/app/build/dao/voting-power-card.tsx @@ -23,7 +23,7 @@ type Tx = { phase: "idle" | "working" | "submitted" | "confirmed" | "error"; msg const SYMBOL: Record = { ethereum: "LCAIB", lightchain: "LCAI" }; export function VotingPowerCard({ chain }: { chain: DaoChain }) { - const { address, isConnected, walletClient, open, onTargetChain, ensureChain } = useDaoWallet(chain); + const { address, isConnected, open, getSigner } = useDaoWallet(chain); const [reads, setReads] = useState(null); const [loading, setLoading] = useState(false); const [tx, setTx] = useState({ phase: "idle" }); @@ -47,12 +47,12 @@ export function VotingPowerCard({ chain }: { chain: DaoChain }) { }, [isConnected, address, chain, refresh]); const delegateToSelf = async () => { - if (!walletClient || !address) return open(); - setTx({ phase: "working", msg: onTargetChain ? "Confirm the delegation in your wallet..." : "Switch network in your wallet..." }); + if (!isConnected || !address) return open(); + setTx({ phase: "working", msg: "Confirm in your wallet (you may be asked to switch network first)..." }); try { - await ensureChain(); + const signer = await getSigner(); const fees = chain === "lightchain" ? await pinnedFees(daoPublicClient(chain)) : undefined; - const hash = await walletClient.writeContract({ + const hash = await signer.writeContract({ address: VOTE_TOKEN[chain], abi: VOTES_ABI, functionName: "delegate", @@ -137,6 +137,15 @@ function DelegationRow({

); } + // Nothing to delegate: holding zero balance on this chain. Don't push a + // pointless "delegate to activate" prompt. + if (reads.balanceWei === 0n) { + return ( +

+ You hold no {SYMBOL[chain]} on this chain, so you have no voting power here. +

+ ); + } const msg = status.kind === "undelegated" ? `You hold ${formatLcaiWei(reads.balanceWei, 2)} ${SYMBOL[chain]} but 0 voting power. Delegate to yourself to activate it.`