For the complete documentation index, see llms.txt. This page is also available as Markdown.

Ch 2: Contract Hooks

In this chapter, you'll build the React hooks that connect your UI to the ClownBeatdown contract. These hooks encapsulate all contract interaction logic so your components stay clean. Estimated time: ~15 minutes

Contract ABI setup

First, copy the compiled ABI from your contracts build output into the web package. After deploying (see Deploying), copy the ABI file:

mkdir -p packages/web/src/abis/contracts
cp packages/contracts/out/ClownBeatdown.sol/ClownBeatdown.json \
   packages/web/src/abis/contracts/ClownBeatdown.json

You'll also need to add the deployed contract address and chain ID to the JSON file. After copying, edit the file to include address and chainId at the top level. The final structure should look like:

{
  "address": "0xYourDeployedAddress",
  "chainId": 31337,
  "abi": [
    { "type": "constructor", "inputs": [...] },
    { "type": "function", "name": "hit", ... },
    ...
  ]
}

You can find the deployed address in packages/contracts/broadcast/ClownBeatdown.s.sol/31337/run-latest.json under transactions[0].contractAddress.

Contract type definition

Create src/types/contract.ts:

useContract hook

This hook creates a shielded contract instance using seismic-react. Create src/hooks/useContract.ts:

The useShieldedContract hook from seismic-react returns a contract instance that supports both shielded writes and signed reads — the same interface you used in the CLI with getShieldedContract, but integrated with React's lifecycle.

useContractClient hook

This hook wraps the contract methods into callable functions with proper error handling. Create src/hooks/useContractClient.ts:

What's happening here?

Notice the different contract namespaces used for each method:

  • appContract().twrite.hit() and appContract().twrite.reset() — these are shielded write transactions. The twrite namespace sends a Seismic transaction (type 0x70) that encrypts calldata.

  • appContract().read.rob() — this is a signed read. The read namespace performs a signed_call that proves the caller's identity to the contract, allowing onlyContributor to verify access. The result comes back as Hex and is decoded with hexToString().

  • appContract().tread.getClownStamina() — this is a transparent read. The tread namespace performs a standard eth_call since stamina is public state.

This distinction between twrite, read, and tread is the key difference from a standard Ethereum dApp.

Supporting components

Before building the game actions hook, create the helper components and hooks it depends on.

Explorer toast — Create src/components/chain/ExplorerToast.tsx:

Toast notifications — Create src/hooks/useToastNotifications.ts:

useGameActions hook

This hook orchestrates the game logic, managing state and coordinating contract calls with UI feedback. Create src/hooks/useGameActions.ts:

This hook manages the full game lifecycle:

  • fetchGameRounds — reads the current stamina from the contract via tread.getClownStamina()

  • handleHit — sends a shielded write via twrite.hit(), shows toast notifications with explorer links, waits for the receipt, increments punch count, and refetches stamina

  • handleReset — validates the clown is KO, sends a shielded write via twrite.reset(), clears punch count and rob result, and refetches stamina

  • handleRob — performs a signed read via read.rob() to decrypt and reveal a secret from the clown's pool

  • resetGameState — clears the rob result and punch count when the round changes

Last updated