Building a Simple DApp Frontend — Kudos build — DApp UI pass
Connect frontend to smart contracts.
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.
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
- 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.
- 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. - 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.
- 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 ( );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.