Intermediate

Building a Simple DApp Frontend — Kudos build — DApp UI pass

Connect frontend to smart contracts.

Developer Intermediate
5/6 — Building a Simple DApp Frontend — Kudos build — DApp UI pass

Objective Expose Kudos balances, badge traits, and minting in a user-friendly DApp.

Bitcoin seems to be a very promising idea. I like the idea of basing security on the assumption that the CPU power of honest participants outweighs that of the attacker.
— Hal Finney

Flow Use wagmi for reads/writes, add pending/error states, and render rarity/price data from the oracle step with explorer links.

What to write

  1. Connect + guard
    Track account and chain; block actions on wrong network.
    const { address, chain } = useAccount();
    const isSepolia = chain?.id === 11155111;

    All actions depend on a connected wallet and the right network; disable buttons otherwise.

  2. Read balances/traits
    Live watch Kudos balance and badge traits.
    const { data: kudosBal } = useContractRead({
      address: kudos,
      abi,
      functionName: 'balanceOf',
      args: [address],
      watch: true,
    });
    const { data: trait } = useContractRead({
      address: badge,
      abi: badgeAbi,
      functionName: 'badgeTrait',
      args: [tokenId],
      watch: true,
    });

    Reads stay fresh with watch; render rarity/pricing pulled from oracle data.

  3. Prepare mint
    Chain-guarded mint config with args.
    const { config } = usePrepareContractWrite({
      address: badge,
      abi: badgeAbi,
      functionName: 'safeMint',
      args: [address, uri],
      enabled: !!address && isSepolia,
    });

    The mint button only enables when connected and on the right chain; prep keeps gas estimation ready.

  4. Send + monitor
    Fire the tx, show pending, and surface explorer links/errors.
    const { data: tx, write, error } = useContractWrite(config);
    const { isLoading: pending, isSuccess } = useWaitForTransaction({ hash: tx?.hash });
    
    return (
      
    {tx?.hash && View tx ↗} {error &&
    {error.message}
    } {isSuccess &&
    Minted!
    }
    );

    Users see pending/success states and can click through to the explorer; errors stay readable.

Why Clear states build trust: users see balances/traits, know when a tx is pending, and can click through to the explorer.

import { useAccount, useContractRead, usePrepareContractWrite, useContractWrite, useWaitForTransaction } from 'wagmi';
import kudosAbi from './kudosAbi.json';
import badgeAbi from './badgeAbi.json';

export function KudosDapp({ kudos, badge, tokenId, uri }) {
  const { address, chain } = useAccount();
  const isSepolia = chain?.id === 11155111;
  const { data: kudosBal } = useContractRead({
    address: kudos,
    abi: kudosAbi,
    functionName: 'balanceOf',
    args: [address],
    watch: true,
  });
  const { data: trait } = useContractRead({
    address: badge,
    abi: badgeAbi,
    functionName: 'badgeTrait',
    args: [tokenId],
    watch: true,
  });
  const { config } = usePrepareContractWrite({
    address: badge,
    abi: badgeAbi,
    functionName: 'safeMint',
    args: [address, uri],
    enabled: !!address && isSepolia,
  });
  const { data: tx, write, error } = useContractWrite(config);
  const { isLoading: pending, isSuccess } = useWaitForTransaction({ hash: tx?.hash });

  return (
    
Balance: {kudosBal?.toString() || '—'}
Trait: {trait || '—'}
{tx?.hash && View tx ↗} {error &&
{error.message}
} {isSuccess &&
Minted!
}
); }

Next Remove extra approval clicks with permit flows.

← Previous section
Next section →