diff --git a/docs/gitbook/tutorials/clown-beatdown/building-the-cli/chapter-2-core-app-logic.md b/docs/gitbook/tutorials/clown-beatdown/building-the-cli/chapter-2-core-app-logic.md
index c912657..2cabf5f 100644
--- a/docs/gitbook/tutorials/clown-beatdown/building-the-cli/chapter-2-core-app-logic.md
+++ b/docs/gitbook/tutorials/clown-beatdown/building-the-cli/chapter-2-core-app-logic.md
@@ -24,7 +24,7 @@ import {
type ShieldedWalletClient,
createShieldedWalletClient,
} from "seismic-viem";
-import { Abi, Address, Chain, http } from "viem";
+import { Abi, Address, Chain, http, hexToString } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { getShieldedContractWithCheck } from "../lib/utils";
@@ -144,6 +144,7 @@ async rob(playerName: string) {
console.log(`- Player ${playerName} reading rob()`)
const contract = this.getPlayerContract(playerName)
const result = await contract.read.rob() // signed read
- console.log(`- Player ${playerName} robbed secret:`, result)
+ const decoded = hexToString(result as `0x${string}`)
+ console.log(`- Player ${playerName} robbed secret:`, decoded)
}
```
diff --git a/docs/gitbook/tutorials/clown-beatdown/building-the-cli/chapter-3-bringing-it-all-together.md b/docs/gitbook/tutorials/clown-beatdown/building-the-cli/chapter-3-bringing-it-all-together.md
index 56bac81..28476be 100644
--- a/docs/gitbook/tutorials/clown-beatdown/building-the-cli/chapter-3-bringing-it-all-together.md
+++ b/docs/gitbook/tutorials/clown-beatdown/building-the-cli/chapter-3-bringing-it-all-together.md
@@ -20,6 +20,7 @@ Open `.env` and paste the following:
```properties
CHAIN_ID=31337
+VITE_CHAIN_ID=31337
RPC_URL=http://127.0.0.1:8545
ALICE_PRIVKEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
BOB_PRIVKEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
@@ -27,7 +28,8 @@ BOB_PRIVKEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
**What's Happening Here?**
-- `CHAIN_ID=31337`: `31337` is the default chain ID for `sanvil` (your local Seismic node).
+- `CHAIN_ID=31337`: Used to locate the deployment broadcast file for the correct chain.
+- `VITE_CHAIN_ID=31337`: Used for chain selection (`sanvil` vs testnet). `31337` is the default chain ID for `sanvil` (your local Seismic node).
- `RPC_URL=http://127.0.0.1:8545`: This is the RPC URL for interacting with the local Seismic node.
- `ALICE_PRIVKEY` and `BOB_PRIVKEY`: These are Alice and Bob's private keys, allowing them to play the game. (These are standard test keys provided by `sanvil`)
diff --git a/docs/gitbook/tutorials/clown-beatdown/building-the-frontend/chapter-1-project-setup-and-providers.md b/docs/gitbook/tutorials/clown-beatdown/building-the-frontend/chapter-1-project-setup-and-providers.md
index 20ded8b..4af5c88 100644
--- a/docs/gitbook/tutorials/clown-beatdown/building-the-frontend/chapter-1-project-setup-and-providers.md
+++ b/docs/gitbook/tutorials/clown-beatdown/building-the-frontend/chapter-1-project-setup-and-providers.md
@@ -31,6 +31,12 @@ bun add seismic-react@1.1.1 seismic-viem@1.1.1 viem@^2.22.3 \
@tailwindcss/vite tailwindcss@^4
```
+The Vite config below uses the SWC plugin for faster builds. Install it as a dev dependency:
+
+```bash
+bun add -d @vitejs/plugin-react-swc
+```
+
### Copy public assets
Copy the `public/` folder from the [seismic-starter](https://github.com/SeismicSystems/seismic-starter) repo into `packages/web/public/`. This includes the clown sprites, button images, background, logo, and audio files used by the game UI.
@@ -88,7 +94,7 @@ import {
} from 'seismic-react'
import { sanvil, seismicTestnet } from 'seismic-react/rainbowkit'
import { http } from 'viem'
-import { Config, WagmiProvider } from 'wagmi'
+import { type Config, WagmiProvider } from 'wagmi'
import { AuthProvider } from '@/components/chain/WalletConnectButton'
import Home from '@/pages/Home'
@@ -188,6 +194,56 @@ The provider stack nests four layers, each adding functionality:
The `onAddressChange` handler auto-funds new wallets when running on `sanvil` (local dev), so you don't need to manually send ETH to test accounts.
+### Supporting files
+
+Before wiring up the entry point, create the supporting modules that `main.tsx` and `App.tsx` will import.
+
+**Redux store** — Create `src/store/store.ts`:
+
+```typescript
+import { configureStore } from '@reduxjs/toolkit'
+
+export const store = configureStore({
+ reducer: {},
+})
+```
+
+**MUI theme** — Create `src/theme.ts`:
+
+```typescript
+import { createTheme } from '@mui/material/styles'
+
+const theme = createTheme({
+ palette: {
+ mode: 'dark',
+ },
+})
+
+export default theme
+```
+
+**Page components** — Create `src/pages/Home.tsx`:
+
+```typescript
+import ClownPuncher from '@/components/game/ClownPuncher'
+
+const Home = () =>
+export default Home
+```
+
+Create `src/pages/NotFound.tsx`:
+
+```typescript
+const NotFound = () =>
404 - Page not found
+export default NotFound
+```
+
+**Stylesheets** — Create `src/App.css` (empty for now) and replace `src/index.css` with:
+
+```css
+@import "tailwindcss";
+```
+
### Entry point: main.tsx
Create `src/main.tsx` to bootstrap the app with theme and state management:
diff --git a/docs/gitbook/tutorials/clown-beatdown/building-the-frontend/chapter-2-contract-hooks.md b/docs/gitbook/tutorials/clown-beatdown/building-the-frontend/chapter-2-contract-hooks.md
index 369bc3e..494bd5a 100644
--- a/docs/gitbook/tutorials/clown-beatdown/building-the-frontend/chapter-2-contract-hooks.md
+++ b/docs/gitbook/tutorials/clown-beatdown/building-the-frontend/chapter-2-contract-hooks.md
@@ -16,7 +16,21 @@ 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. The ABI file should contain `abi`, `address`, and `chainId` fields.
+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:
+
+```json
+{
+ "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
@@ -58,8 +72,8 @@ This hook wraps the contract methods into callable functions with proper error h
import { useCallback, useEffect, useState } from "react";
import { useShieldedWallet } from "seismic-react";
import {
- ShieldedPublicClient,
- ShieldedWalletClient,
+ type ShieldedPublicClient,
+ type ShieldedWalletClient,
addressExplorerUrl,
txExplorerUrl,
} from "seismic-viem";
@@ -181,6 +195,40 @@ Notice the different contract namespaces used for each method:
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`:
+
+```typescript
+import React from 'react'
+
+type ExplorerToastProps = {
+ url: string
+ text: string
+ hash: string
+}
+
+export const ExplorerToast: React.FC = ({ url, text, hash }) => (
+
+ {text}{hash.slice(0, 10)}...
+
+)
+```
+
+**Toast notifications** — Create `src/hooks/useToastNotifications.ts`:
+
+```typescript
+import { toast } from 'react-toastify'
+
+export const useToastNotifications = () => ({
+ notifySuccess: (msg: string) => toast.success(msg),
+ notifyError: (msg: string) => toast.error(msg),
+ notifyInfo: (msg: string | React.ReactElement) => toast.info(msg),
+})
+```
+
### useGameActions hook
This hook orchestrates the game logic, managing state and coordinating contract calls with UI feedback. Create `src/hooks/useGameActions.ts`:
diff --git a/docs/gitbook/tutorials/clown-beatdown/setting-up-your-project/initialize-cli.md b/docs/gitbook/tutorials/clown-beatdown/setting-up-your-project/initialize-cli.md
index fc91b65..3d06152 100644
--- a/docs/gitbook/tutorials/clown-beatdown/setting-up-your-project/initialize-cli.md
+++ b/docs/gitbook/tutorials/clown-beatdown/setting-up-your-project/initialize-cli.md
@@ -18,7 +18,7 @@ bun init -y
3. Now, create an `src/` folder and move `index.ts` there.
```bash
-mkdir -p src && mv -t src index.ts
+mkdir -p src && mv index.ts src/
```
4. Now, edit `package.json` to be the following:
@@ -33,7 +33,7 @@ mkdir -p src && mv -t src index.ts
},
"dependencies": {
"dotenv": "^16.4.7",
- "seismic-viem": "1.0.9",
+ "seismic-viem": "1.1.1",
"viem": "^2.22.3"
},
"devDependencies": {
diff --git a/docs/gitbook/tutorials/clown-beatdown/setting-up-your-project/initialize-contracts.md b/docs/gitbook/tutorials/clown-beatdown/setting-up-your-project/initialize-contracts.md
index 6c73736..8bbfff6 100644
--- a/docs/gitbook/tutorials/clown-beatdown/setting-up-your-project/initialize-contracts.md
+++ b/docs/gitbook/tutorials/clown-beatdown/setting-up-your-project/initialize-contracts.md
@@ -9,7 +9,7 @@ cd packages/contracts
2. **Initialize a project with `sforge`:**
```bash
-sforge init --no-commit && rm -rf .github
+sforge init && rm -rf .github
```
This command will:
diff --git a/docs/gitbook/tutorials/clown-beatdown/writing-the-contract/chapter-1-the-secrets-pool.md b/docs/gitbook/tutorials/clown-beatdown/writing-the-contract/chapter-1-the-secrets-pool.md
index 1fda0c1..aa788d8 100644
--- a/docs/gitbook/tutorials/clown-beatdown/writing-the-contract/chapter-1-the-secrets-pool.md
+++ b/docs/gitbook/tutorials/clown-beatdown/writing-the-contract/chapter-1-the-secrets-pool.md
@@ -8,7 +8,7 @@ In this chapter, you'll learn to create and initialize the secrets pool — a co
### Defining the secrets pool
-The **secrets pool** is the collection of hidden strings that the clown carries. Using Seismic's **`sbytes`** type, each secret is shielded on-chain — encrypted and invisible to observers. A shielded **`suint256`** index determines which secret gets revealed when the clown is robbed. Open up `packages/contracts/ClownBeatdown.sol` and define the state variables:
+The **secrets pool** is the collection of hidden strings that the clown carries. Using Seismic's **`sbytes`** type, each secret is shielded on-chain — encrypted and invisible to observers. A shielded **`suint256`** index determines which secret gets revealed when the clown is robbed. Open up `packages/contracts/src/ClownBeatdown.sol` and define the state variables:
```solidity
// SPDX-License-Identifier: MIT License
@@ -26,6 +26,8 @@ contract ClownBeatdown {
}
```
+We accept `_clownStamina` in the constructor but won't use it until Chapter 2, when we add the stamina system.
+
### Add the addSecret function
Next, let's implement a function to add secrets to the pool. The `addSecret` function takes a plain `string` and converts it to `sbytes` for shielded storage:
diff --git a/docs/gitbook/tutorials/clown-beatdown/writing-the-contract/chapter-3-reset-mechanism-rounds-and-contributor-access.md b/docs/gitbook/tutorials/clown-beatdown/writing-the-contract/chapter-3-reset-mechanism-rounds-and-contributor-access.md
index 1f48e46..43b8f4c 100644
--- a/docs/gitbook/tutorials/clown-beatdown/writing-the-contract/chapter-3-reset-mechanism-rounds-and-contributor-access.md
+++ b/docs/gitbook/tutorials/clown-beatdown/writing-the-contract/chapter-3-reset-mechanism-rounds-and-contributor-access.md
@@ -118,7 +118,7 @@ contract ClownBeatdown {
// Tracks the number of hits per player per round.
mapping(uint256 => mapping(address => uint256)) hitsPerRound;
- // Events to log hits, shakes, and resets.
+ // Events to log hits and resets.
// Event to log hits.
event Hit(uint256 indexed round, address indexed hitter, uint256 remaining); // Logged when a hit lands.
diff --git a/docs/gitbook/tutorials/clown-beatdown/writing-the-contract/deploying.md b/docs/gitbook/tutorials/clown-beatdown/writing-the-contract/deploying.md
index 51910d0..b61438c 100644
--- a/docs/gitbook/tutorials/clown-beatdown/writing-the-contract/deploying.md
+++ b/docs/gitbook/tutorials/clown-beatdown/writing-the-contract/deploying.md
@@ -31,17 +31,14 @@ contract ClownBeatdownScript is Script {
vm.startBroadcast(deployerPrivateKey);
clownBeatdown = new ClownBeatdown(3);
- clownBeatdown.addSecret("The moon is made of cheese");
- clownBeatdown.addSecret("Clowns rule the underworld");
- clownBeatdown.addSecret("The cake is a lie");
- clownBeatdown.addSecret("42 is the answer");
- clownBeatdown.addSecret("Never trust a smiling clown");
vm.stopBroadcast();
+
+ console.log("Deployed at:", address(clownBeatdown));
}
}
```
-This script will deploy a new instance of the ClownBeatdown contract with an initial stamina of 3 and populate it with 5 secrets.
+This script will deploy a new instance of the ClownBeatdown contract with an initial stamina of 3. We'll add secrets separately in the next step, since `addSecret` performs shielded writes that need to be sent as on-chain transactions.
### Deploying the contract
@@ -62,7 +59,7 @@ PRIVKEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
The `RPC_URL` denotes the port on which `sanvil` is running and the `PRIVKEY` is one of the standard `sanvil` testing private keys.
-3. Now, from `packages/contracts`, run
+3. Now, from `packages/contracts`, deploy the contract:
```bash
source .env
@@ -71,4 +68,21 @@ sforge script script/ClownBeatdown.s.sol:ClownBeatdownScript \
--broadcast
```
-Your contract should be up and deployed to your local Seismic node!
+The output will show the deployed contract address (e.g. `0x5FbDB2315678afecb367f032d93F642f64180aa3`).
+
+4. Add secrets to the deployed contract using `scast send`. Replace `` with the address from the previous step:
+
+```bash
+scast send "addSecret(string)" "The moon is made of cheese" \
+ --rpc-url $RPC_URL --private-key $PRIVKEY
+scast send "addSecret(string)" "Clowns rule the underworld" \
+ --rpc-url $RPC_URL --private-key $PRIVKEY
+scast send "addSecret(string)" "The cake is a lie" \
+ --rpc-url $RPC_URL --private-key $PRIVKEY
+scast send "addSecret(string)" "42 is the answer" \
+ --rpc-url $RPC_URL --private-key $PRIVKEY
+scast send "addSecret(string)" "Never trust a smiling clown" \
+ --rpc-url $RPC_URL --private-key $PRIVKEY
+```
+
+Your contract should be up and deployed to your local Seismic node with 5 secrets!