Skip to main content
Agora DAO provides a collection of custom React hooks built on top of wagmi and viem for seamless smart contract interactions.

Scaffold-ETH Hooks

Powerful hooks that simplify blockchain interactions with automatic ABI loading and type safety.

useScaffoldReadContract

Read data from smart contracts with automatic ABI resolution. Location: hooks/scaffold-eth/useScaffoldReadContract.ts Usage:
import { useScaffoldReadContract } from "~~/hooks/scaffold-eth";

function DaoInfo() {
  const { data: daoName } = useScaffoldReadContract({
    contractName: "AgoraDAO",
    functionName: "name",
  });

  return <h1>{daoName}</h1>;
}
Parameters:
contractName
ContractName
required
Name of the deployed contract (from deployedContracts.ts)
functionName
string
required
Contract function to call (view or pure)
args
unknown[]
Function arguments as array
chainId
number
Target chain ID for multi-chain support
contractAddress
string
Override deployed contract address
watch
boolean
default:"true"
Automatically refetch on new blocks
Returns:
  • data - Contract function return value
  • isLoading - Initial loading state
  • isError - Error state
  • error - Error object
  • refetch() - Manual refetch function

useScaffoldWriteContract

Write to smart contracts with transaction handling. Location: hooks/scaffold-eth/useScaffoldWriteContract.ts Usage:
import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth";

function CreateProposal() {
  const { writeContractAsync, isMining } = useScaffoldWriteContract({
    contractName: "AgoraDAO",
  });

  const handleCreate = async () => {
    try {
      await writeContractAsync({
        functionName: "createProposal",
        args: ["Proposal Title", "Description"],
      });
    } catch (error) {
      console.error(error);
    }
  };

  return (
    <button onClick={handleCreate} disabled={isMining}>
      {isMining ? "Creating..." : "Create Proposal"}
    </button>
  );
}
Parameters:
contractName
ContractName
required
Name of the deployed contract
chainId
number
Target chain ID
disableSimulate
boolean
default:"false"
Skip transaction simulation (faster but less safe)
contractAddress
string
Override contract address
Returns:
  • writeContract() - Trigger transaction (fire and forget)
  • writeContractAsync() - Trigger transaction (returns promise)
  • isMining - True while transaction is pending
  • data - Transaction hash after submission
  • Standard wagmi write contract returns
Transaction Options:
await writeContractAsync(
  { functionName, args, value },
  {
    blockConfirmations: 2,
    onBlockConfirmation: (receipt) => { /* callback */ },
  }
);

useScaffoldWatchContractEvent

Listen to real-time contract events. Location: hooks/scaffold-eth/useScaffoldWatchContractEvent.ts Usage:
import { useScaffoldWatchContractEvent } from "~~/hooks/scaffold-eth";
import { useState } from "react";

function ProposalCreatedListener() {
  const [proposals, setProposals] = useState([]);

  useScaffoldWatchContractEvent({
    contractName: "AgoraDAO",
    eventName: "ProposalCreated",
    onLogs: (logs) => {
      logs.forEach((log) => {
        setProposals((prev) => [...prev, {
          id: log.args.proposalId,
          title: log.args.title,
          creator: log.args.creator,
        }]);
      });
    },
  });

  return (
    <ul>
      {proposals.map((p) => (
        <li key={p.id}>{p.title}</li>
      ))}
    </ul>
  );
}
Parameters:
contractName
ContractName
required
Contract to watch
eventName
string
required
Event name from contract ABI
onLogs
(logs: Log[]) => void
required
Callback when events are emitted
chainId
number
Target chain ID

useTransactor

Handles transaction execution with UI feedback. Location: hooks/scaffold-eth/useTransactor.tsx Usage:
import { useTransactor } from "~~/hooks/scaffold-eth";
import { parseEther } from "viem";

function SendETH() {
  const writeTx = useTransactor();
  const { data: walletClient } = useWalletClient();

  const handleSend = async () => {
    const tx = async () => {
      return await walletClient.sendTransaction({
        to: recipientAddress,
        value: parseEther("0.1"),
      });
    };

    await writeTx(tx, {
      blockConfirmations: 1,
      onBlockConfirmation: (receipt) => {
        console.log("Transaction confirmed!", receipt);
      },
    });
  };

  return <button onClick={handleSend}>Send 0.1 ETH</button>;
}
Features:
  • Automatic toast notifications
  • “Awaiting user confirmation” state
  • “Waiting for transaction” state
  • Success/error feedback
  • Block explorer links
  • Transaction receipt handling

useTargetNetwork

Get the currently selected network from scaffold config. Location: hooks/scaffold-eth/useTargetNetwork.ts Usage:
import { useTargetNetwork } from "~~/hooks/scaffold-eth";

function NetworkInfo() {
  const { targetNetwork } = useTargetNetwork();

  return (
    <div>
      <p>Network: {targetNetwork.name}</p>
      <p>Chain ID: {targetNetwork.id}</p>
      <p>Currency: {targetNetwork.nativeCurrency.symbol}</p>
    </div>
  );
}
Returns:
{
  targetNetwork: {
    id: number;
    name: string;
    nativeCurrency: { name: string; symbol: string; decimals: number };
    rpcUrls: { default: { http: string[] } };
    blockExplorers: { default: { url: string } };
  }
}

useWatchBalance

Watch wallet balance with real-time updates. Location: hooks/scaffold-eth/useWatchBalance.ts Usage:
import { useWatchBalance } from "~~/hooks/scaffold-eth";
import { formatEther } from "viem";

function WalletBalance({ address }: { address: Address }) {
  const { data: balance, isLoading } = useWatchBalance({ address });

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      Balance: {formatEther(balance.value)} {balance.symbol}
    </div>
  );
}

useDeployedContractInfo

Get deployed contract info (address, ABI) by name. Location: hooks/scaffold-eth/useDeployedContractInfo.ts Usage:
import { useDeployedContractInfo } from "~~/hooks/scaffold-eth";

function ContractInfo() {
  const { data: contract } = useDeployedContractInfo({
    contractName: "AgoraDAO",
  });

  return (
    <div>
      <p>Address: {contract?.address}</p>
      <p>Deployed Block: {contract?.deployedOnBlock}</p>
    </div>
  );
}

Wagmi Hooks

Agora DAO uses standard wagmi hooks throughout:

useAccount

import { useAccount } from "wagmi";

const { address, isConnected, isConnecting, chain } = useAccount();

useReadContract

import { useReadContract } from "wagmi";

const { data } = useReadContract({
  address: contractAddress,
  abi: contractABI,
  functionName: "balanceOf",
  args: [userAddress],
});

useWriteContract

import { useWriteContract } from "wagmi";

const { writeContract, isPending } = useWriteContract();

useBalance

import { useBalance } from "wagmi";

const { data: balance } = useBalance({ address: userAddress });

useEnsName / useEnsAddress

import { useEnsName, useEnsAddress } from "wagmi";

const { data: ensName } = useEnsName({ address: "0x..." });
const { data: address } = useEnsAddress({ name: "vitalik.eth" });

Utility Hooks

Additional hooks for common UI patterns.

useCopyToClipboard

Copy text to clipboard with feedback. Location: hooks/scaffold-eth/useCopyToClipboard.ts Usage:
import { useCopyToClipboard } from "~~/hooks/scaffold-eth";

function CopyAddress({ address }: { address: string }) {
  const { copyToClipboard, isCopiedToClipboard } = useCopyToClipboard();

  return (
    <button onClick={() => copyToClipboard(address)}>
      {isCopiedToClipboard ? "Copied!" : "Copy Address"}
    </button>
  );
}

useOutsideClick

Detect clicks outside an element. Location: hooks/scaffold-eth/useOutsideClick.ts Usage:
import { useOutsideClick } from "~~/hooks/scaffold-eth";
import { useRef, useState } from "react";

function Dropdown() {
  const [isOpen, setIsOpen] = useState(false);
  const dropdownRef = useRef(null);

  useOutsideClick(dropdownRef, () => setIsOpen(false));

  return (
    <div ref={dropdownRef}>
      <button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
      {isOpen && <div>Dropdown content</div>}
    </div>
  );
}

useNetworkColor

Get color for current network. Location: hooks/scaffold-eth/useNetworkColor.ts Usage:
import { useNetworkColor } from "~~/hooks/scaffold-eth";

function NetworkBadge() {
  const networkColor = useNetworkColor();

  return (
    <span style={{ color: networkColor }}>
      Connected Network
    </span>
  );
}

State Management Hooks

Zustand stores for global state.

useDaoStore

DAO selection and address storage. Location: services/store/dao.store.ts Usage:
import { useDaoStore } from "~~/services/store/dao.store";

function DaoSelector() {
  const { daoAddress, setDaoAddress, selectedDao, setSelectedDao } = useDaoStore();

  const handleSelect = (address: string) => {
    setDaoAddress(address);
    setSelectedDao({ category: "governance", name: "Main DAO" });
    localStorage.setItem(LOCAL_STORAGE_KEYS.DAO_ADDRESS, address);
  };

  return <div>Current DAO: {daoAddress}</div>;
}
Store Shape:
type DaoStore = {
  selectedDao: {
    category: string;
    name: string;
  };
  setSelectedDao: (dao: { category: string; name: string }) => void;
  daoAddress: string;
  setDaoAddress: (address: string) => void;
};

Hook Composition Example

Combining multiple hooks:
import { useScaffoldReadContract, useScaffoldWriteContract } from "~~/hooks/scaffold-eth";
import { useAccount } from "wagmi";
import { useDaoStore } from "~~/services/store/dao.store";

function ProposalVoting({ proposalId }: { proposalId: bigint }) {
  const { address } = useAccount();
  const { daoAddress } = useDaoStore();
  
  // Read proposal details
  const { data: proposal } = useScaffoldReadContract({
    contractName: "AgoraDAO",
    functionName: "proposals",
    args: [proposalId],
  });

  // Check if user has voted
  const { data: hasVoted } = useScaffoldReadContract({
    contractName: "AgoraDAO",
    functionName: "hasVoted",
    args: [proposalId, address],
  });

  // Write vote transaction
  const { writeContractAsync, isMining } = useScaffoldWriteContract({
    contractName: "AgoraDAO",
  });

  const vote = async (support: boolean) => {
    await writeContractAsync({
      functionName: "castVote",
      args: [proposalId, support],
    });
  };

  return (
    <div>
      <h2>{proposal?.title}</h2>
      {!hasVoted ? (
        <div>
          <button onClick={() => vote(true)} disabled={isMining}>Yes</button>
          <button onClick={() => vote(false)} disabled={isMining}>No</button>
        </div>
      ) : (
        <p>You have already voted</p>
      )}
    </div>
  );
}

Build docs developers (and LLMs) love