# Building dApp Front Ends — Blockchain & Smart Contracts (Solidity)

Source: https://www.geekswithgeeks.com/en/solidity/g-dapps

> Connect wallets, read state and send transactions from a web app.

## The user-facing side

A **decentralised application (dApp)** front end is a normal web app (often React or Next.js) that talks to contracts through the user's **wallet** and an **RPC provider**. Libraries such as **wagmi** (React hooks built on viem) with connectors like RainbowKit, ConnectKit or Reown AppKit handle **wallet connection** (browser extensions, mobile wallets via WalletConnect, and smart wallets), **network switching**, reading contract state, **simulating and sending transactions**, and **waiting for confirmations**. Good dApp UX: show clear transaction states (awaiting signature, pending, confirmed, failed), decode and explain revert reasons (custom errors help here), display amounts with correct decimals, warn on wrong networks, request the **minimum approval** needed, and never ask for seed phrases. **Index events** for history and lists using services such as The Graph, Ponder or your own indexer, rather than scanning the chain from the browser. Treat all on-chain data as public. Sign-in with Ethereum (**SIWE**, EIP-4361) lets users authenticate to a backend with a signed message. Host front ends securely: compromised front ends and DNS hijacks have been used to steal funds.

## Reading and writing with wagmi hooks

Connect, read a balance and send a deposit with clear states.

```typescript
import { useAccount, useConnect, useReadContract, useWriteContract, useWaitForTransactionReceipt } from "wagmi";
import { injected } from "wagmi/connectors";
import { parseEther, formatEther } from "viem";
import { vaultAbi } from "./abi";

const VAULT = "0xYourVaultAddress" as const;

export function VaultPanel() {
  const { address, isConnected } = useAccount();
  const { connect } = useConnect();
  const { data: balance, refetch } = useReadContract({
    address: VAULT, abi: vaultAbi, functionName: "balances",
    args: address ? [address] : undefined,
    query: { enabled: Boolean(address) },
  });
  const { writeContract, data: hash, isPending, error } = useWriteContract();
  const { isLoading: confirming, isSuccess } = useWaitForTransactionReceipt({ hash });

  if (!isConnected) return <button onClick={() => connect({ connector: injected() })}>Connect wallet</button>;

  return (
    <div>
      <p>Your vault balance: {balance !== undefined ? formatEther(balance) : "..."} ETH</p>
      <button
        disabled={isPending || confirming}
        onClick={() => writeContract({ address: VAULT, abi: vaultAbi, functionName: "deposit", value: parseEther("0.01") })}
      >
        {isPending ? "Confirm in wallet..." : confirming ? "Waiting for confirmation..." : "Deposit 0.01 ETH"}
      </button>
      {isSuccess && <p>Deposit confirmed. <button onClick={() => refetch()}>Refresh</button></p>}
      {error && <p role="alert">Transaction failed: {error.message}</p>}
    </div>
  );
}
```

## A shop counter and a customer's own wallet

The dApp is the shop counter showing prices and preparing the bill; the user's wallet is their own purse. The shop never holds the purse: it hands over a bill (transaction) and the customer decides whether to sign and pay.

**Quiz:** Why should a dApp index events with a service rather than scanning the chain in the browser?

- [ ] Events cannot be read
- [x] Scanning many blocks from the browser is slow and rate-limited; indexers provide fast, queryable history
- [ ] Indexers make transactions free
- [ ] Browsers cannot connect to RPC nodes

*Answer:* Scanning many blocks from the browser is slow and rate-limited; indexers provide fast, queryable history. Indexers process events once and serve them efficiently.
