First confidential dApp
End-to-end tutorial building a token dashboard with React, wagmi, and the Zama React SDK.
We'll build a token dashboard that shows a confidential balance, lets users shield tokens, transfer privately, and unshield. The finished app uses React, wagmi, and the Zama React SDK.
What you'll build
A single-page dashboard where a connected wallet can manage confidential ERC-20 tokens -- shield, view balance, transfer, and unshield -- all from one screen.
Prerequisites
Node.js 18+
A wallet browser extension (MetaMask or similar)
Testnet ETH on Sepolia
An encrypted ERC-20 token address deployed on Sepolia
1. Create the project
Scaffold a new Vite project with React and TypeScript:
pnpm create vite@latest my-confidential-dapp -- --template react-ts
cd my-confidential-dapp2. Install dependencies
pnpm add @zama-fhe/sdk @zama-fhe/react-sdk @tanstack/react-query wagmi viem@zama-fhe/react-sdk provides React hooks. Core SDK symbols (classes, types, chain presets, relayer factories) are imported from @zama-fhe/sdk directly.
3. Configure wagmi and the SDK
Create src/config.ts. This file sets up wagmi, the signer, and the relayer -- the three pieces every Zama app needs.
Replace YOUR_KEY with your Infura (or Alchemy) project ID, and update the relayer URL to point at your backend proxy. See the Authentication guide for proxy setup details.
4. Create the App layout with providers
Replace the contents of src/App.tsx. We wrap the app in three providers: wagmi for wallet state, React Query for async caching, and ZamaProvider for FHE operations. The Zama config is built by createConfig from @zama-fhe/react-sdk/wagmi, which derives the signer from your wagmi config so it tracks connection state automatically.
5. Build the balance display
Create src/BalanceDisplay.tsx. The useConfidentialBalance hook decrypts the on-chain balance. It polls the encrypted value cheaply and only triggers full decryption when the balance changes.
The first call prompts the wallet for a signature to generate FHE decrypt permits. Subsequent calls reuse cached permits silently.
6. Add shielding
Create src/ShieldForm.tsx. Shielding converts public ERC-20 tokens into their encrypted form. The SDK handles the ERC-20 approval automatically.
After a successful shield, the balance display updates automatically -- mutation hooks invalidate the relevant caches.
7. Add confidential transfers
Create src/TransferForm.tsx. The transfer amount is encrypted before it reaches the chain. Nobody can see how much was sent.
8. Add unshielding
Create src/UnshieldForm.tsx. Unshielding withdraws confidential tokens back to public ERC-20. This is a two-step on-chain process (unwrap + finalize), but the hook orchestrates it in a single call. We use progress callbacks to update the UI.
See Hooks > useUnshield for the full callback reference.
9. Add error handling
Create src/ErrorMessage.tsx. The matchZamaError utility maps SDK error codes to user-friendly messages without long instanceof chains. See Error Handling for the full list of error codes.
Use this component alongside any mutation hook. Pass the hook's error property:
10. Wire it up
Create src/Dashboard.tsx to bring all components together.
Start the dev server:
Open the app in your browser, connect your wallet, and try the full flow: shield some tokens, check the balance, send a confidential transfer, then unshield.
Next steps
Configuration -- customize authentication, storage backends, and network presets
Error Handling -- handle every SDK error type
React Hooks -- explore all available hooks
Core SDK -- use the imperative API for non-React apps
Last updated