Skip to content

Commit 7a385e6

Browse files
authored
Merge pull request #441 from scaffold-eth/challenge-zk-voting-foundry
Foundry support - Challenge-zk-voting-foundry
2 parents 20bc98a + e9de266 commit 7a385e6

16 files changed

Lines changed: 803 additions & 396 deletions

File tree

README.md

Lines changed: 139 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Before you begin, you need to install the following tools:
4747
- [Node (>= v20.18.3)](https://nodejs.org/en/download/)
4848
- [Yarn (v2+)](https://yarnpkg.com/getting-started/install)
4949
- [Git](https://git-scm.com/downloads)
50+
- [Foundry](https://book.getfoundry.sh/getting-started/installation) (If you chose Foundry as solidity framework)
5051
- [Nargo](https://noir-lang.org/docs/getting_started/quick_start#installation) (v1.0.0-beta.3)
5152
- [bb](https://barretenberg.aztec.network/docs/getting_started/) (v0.82.2)
5253

@@ -88,10 +89,12 @@ If you are using vscode you may want to install the [Noir Language Support](http
8889
Then download the challenge to your computer and install dependencies by running:
8990

9091
```sh
91-
npx create-eth@2.0.13 -e scaffold-eth/se-2-challenges:challenge-zk-voting challenge-zk-voting
92+
npx create-eth@2.0.18 -e scaffold-eth/se-2-challenges:challenge-zk-voting challenge-zk-voting
9293
cd challenge-zk-voting
9394
```
9495

96+
> When prompted, choose your preferred solidity framework (Hardhat or Foundry)
97+
9598
In the same terminal, start your local network (a blockchain emulator in your computer):
9699

97100
```sh
@@ -163,8 +166,23 @@ Our contract will support three main functions:
163166

164167
🔍 Next, switch to the **`Debug Contracts`** page. For now, you should see just one contract there — **`Voting`**.
165168

169+
<Tabs>
170+
<Tab label="Hardhat">
171+
172+
**Hardhat**
173+
166174
📁 The contract lives in **`packages/hardhat/contracts/Voting.sol`**
167175

176+
</Tab>
177+
<Tab label="Foundry">
178+
179+
**Foundry**
180+
181+
📁 The contract lives in **`packages/foundry/contracts/Voting.sol`**
182+
183+
</Tab>
184+
</Tabs>
185+
168186
🔍 Open it up and check out the placeholder functions. Each of them represents a key piece of the voting logic.
169187
If you can already explain what they’re supposed to do, you’re ahead of the game! 😎
170188

@@ -364,23 +382,58 @@ Scroll down to the functions **`getVotingData()`** and **`getVoterData(address _
364382

365383
Then run:
366384

385+
<Tabs>
386+
<Tab label="Hardhat">
387+
388+
**Hardhat**
389+
367390
```sh
368391
yarn test --grep "Checkpoint2"
369392
```
370393

394+
</Tab>
395+
<Tab label="Foundry">
396+
397+
**Foundry**
398+
399+
```sh
400+
yarn test --match-test "Checkpoint2"
401+
```
402+
403+
</Tab>
404+
</Tabs>
405+
371406
### 🚀 Tests Passed? You’re Almost There!
372407

373408
Great job! If your tests are passing, you’re just one step away from deployment! 🚀
374409

375410
Before deploying, make one important change:
376411

412+
<Tabs>
413+
<Tab label="Hardhat">
414+
415+
**Hardhat**
416+
377417
1. Open **`00_deploy_your_voting_contract.ts`**
378418
2. Set your address as the `ownerAddress`
379419
3. Uncomment deployment of both `poseidon3` and `leanIMT`
380420
4. Set LeanIMT library address (`leanIMT.address`) at line 62
381421

382422
> 💡 **Poseidon3** is the hash function we use. More on that later.
383423
424+
</Tab>
425+
<Tab label="Foundry">
426+
427+
**Foundry**
428+
429+
1. Open **`DeployVoting.s.sol`**
430+
2. Set your address as the `ownerAddress`
431+
432+
> 💡 In Foundry, library linking is handled automatically at compile time — no need to deploy PoseidonT3 or LeanIMT separately.
433+
434+
</Tab>
435+
</Tabs>
436+
384437
Once that’s done, you’re ready to deploy! 🔗
385438

386439
Run `yarn deploy` and check out the front-end
@@ -952,12 +1005,31 @@ You’ve built the circuit, created the verifier contract — now it’s time to
9521005

9531006
### 🔹 Step 1: Bring in the Verifier Contract
9541007

1008+
<Tabs>
1009+
<Tab label="Hardhat">
1010+
1011+
**Hardhat**
1012+
9551013
1. Replace the placeholder verifier contract **`Verifier.sol`** in **`packages/hardhat/contracts`** with the newly generated contract located in **`packages/circuits/target`**.
9561014
2. Open **`00_deploy_your_voting_contract.ts`** and:
9571015
- Uncomment the verifier deployment
9581016
- Comment out the `verifierAddress`
9591017
- Update the `args` to match your setup
9601018

1019+
</Tab>
1020+
<Tab label="Foundry">
1021+
1022+
**Foundry**
1023+
1024+
1. Replace the placeholder verifier contract **`Verifier.sol`** in **`packages/foundry/contracts`** with the newly generated contract located in **`packages/circuits/target`**.
1025+
2. Open **`DeployVoting.s.sol`** and:
1026+
- Uncomment the verifier deployment
1027+
- Comment out the placeholder `verifierAddress`
1028+
- Update the constructor call to match your setup
1029+
1030+
</Tab>
1031+
</Tabs>
1032+
9611033
3. In **`Voting.sol`**:
9621034
- At the top, import the verifier contract (just uncomment the existing line)
9631035
- In the constructor, initialize the verifier and store it in a variable called `i_verifier`
@@ -1116,10 +1188,27 @@ function vote(bytes memory _proof, bytes32 _nullifierHash, bytes32 _root, bytes3
11161188

11171189
Once implemented, run your tests to make sure everything works:
11181190

1191+
<Tabs>
1192+
<Tab label="Hardhat">
1193+
1194+
**Hardhat**
1195+
11191196
```sh
11201197
yarn test --grep "Checkpoint6"
11211198
```
11221199

1200+
</Tab>
1201+
<Tab label="Foundry">
1202+
1203+
**Foundry**
1204+
1205+
```sh
1206+
yarn test --match-test "Checkpoint6"
1207+
```
1208+
1209+
</Tab>
1210+
</Tabs>
1211+
11231212
### **✅ Tests Passed? You're So Close!**
11241213

11251214
If your tests are green, congratulations — you’ve just completed the **core `Voting.sol` contract!** 🎉
@@ -1813,10 +1902,27 @@ For **production-grade apps**, you should generate your own API keys to avoid hi
18131902

18141903
Configure your keys here:
18151904

1905+
<Tabs>
1906+
<Tab label="Hardhat">
1907+
1908+
**Hardhat**
1909+
18161910
- 🔷 **`ALCHEMY_API_KEY`** in `packages/hardhat/.env` and `packages/nextjs/.env.local`[Get key from Alchemy](https://dashboard.alchemy.com/)
18171911
- 🔑 **`NEXT_PUBLIC_PIMLICO_API_KEY`** in `packages/nextjs/.env.local`[Get key from Pimlico](https://dashboard.pimlico.io/)
18181912
- 📃 **`ETHERSCAN_API_KEY`** in `packages/hardhat/.env`[Get key from Etherscan](https://etherscan.io/myapikey)
18191913

1914+
</Tab>
1915+
<Tab label="Foundry">
1916+
1917+
**Foundry**
1918+
1919+
- 🔷 **`ALCHEMY_API_KEY`** in `packages/foundry/.env` and `packages/nextjs/.env.local`[Get key from Alchemy](https://dashboard.alchemy.com/)
1920+
- 🔑 **`NEXT_PUBLIC_PIMLICO_API_KEY`** in `packages/nextjs/.env.local`[Get key from Pimlico](https://dashboard.pimlico.io/)
1921+
- 📃 **`ETHERSCAN_API_KEY`** in `packages/foundry/.env`[Get key from Etherscan](https://etherscan.io/myapikey)
1922+
1923+
</Tab>
1924+
</Tabs>
1925+
18201926
> 💬 Hint: Store environment variables for **Next.js** in Vercel/system env config for live apps, and use `.env.local` for local testing.
18211927
18221928
### Deploying Your Smart Contracts
@@ -1827,11 +1933,28 @@ Configure your keys here:
18271933

18281934
⛽️ You will need to send ETH to your deployer address with your wallet, or obtain it from a public faucet of your chosen network.
18291935

1830-
> 🚨 Don’t forget to set the owner address inside the 00_deploy_your_voting_contract.ts .
1936+
<Tabs>
1937+
<Tab label="Hardhat">
1938+
1939+
**Hardhat**
1940+
1941+
> 🚨 Don't forget to set the owner address inside the `00_deploy_your_voting_contract.ts`.
1942+
1943+
🚀 Run `yarn deploy --network sepolia` to deploy your smart contract to Sepolia.
1944+
1945+
> 💬 Hint: You can set the defaultNetwork in hardhat.config.ts to sepolia OR you can yarn deploy --network sepolia.
18311946
1832-
🚀 Run `yarn deploy --network sepolia` to deploy your smart contract to Sepolia.
1947+
</Tab>
1948+
<Tab label="Foundry">
18331949

1834-
> 💬 Hint: You can set the defaultNetwork in hardhat.config.ts to sepolia OR you can yarn deploy --network sepolia.
1950+
**Foundry**
1951+
1952+
> 🚨 Don't forget to set the owner address inside the `DeployVoting.s.sol`.
1953+
1954+
🚀 Run `yarn deploy --network sepolia` to deploy your smart contract to Sepolia.
1955+
1956+
</Tab>
1957+
</Tabs>
18351958

18361959
💻 Inside `scaffold.config.ts` change the `targetNetwork` to `chains.sepolia`. View your front-end at http://localhost:3000 and verify you see the correct network Sepolia.
18371960

@@ -1953,3 +2076,15 @@ But this is just the beginning. The same **commitment + nullifier** pattern that
19532076
This challenge is your **entry point into a new design space.** 💥
19542077

19552078
**What will you build with Noir and ZK circuits? 🧪✨**
2079+
2080+
## AI-Guided Learning Mode (Optional)
2081+
2082+
This challenge supports an interactive AI learning mode. Instead of reading instructions above, you can let an AI guide you step by step.
2083+
2084+
**How to use:**
2085+
1. Open a terminal in the project root
2086+
2. Run `/start` to begin the guided challenge
2087+
3. The AI will teach concepts and give you coding tasks
2088+
4. Say `check` to validate your code, `hint` for help, or `/skip` to skip a task
2089+
2090+
> Note: If you chose Foundry as your framework, the screenshots in this README show the Hardhat version, but the contract logic and challenge flow are identical.

extension/.ai/CHALLENGE.yaml

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ difficulty: "advanced"
55
estimated_time: "6 - 16 hours"
66

77
setup:
8-
file: "packages/hardhat/contracts/Voting.sol"
8+
file:
9+
hardhat: "packages/hardhat/contracts/Voting.sol"
10+
foundry: "packages/foundry/contracts/Voting.sol"
911

1012
welcome_message: |
1113
Welcome to the ZK Voting Challenge!
@@ -169,8 +171,12 @@ checkpoints:
169171
4. Check `getVotingData()` — you should see Merkle tree data (size, depth, root).
170172
171173
Say **"check"** when you're ready!
172-
file: "packages/hardhat/contracts/Voting.sol"
173-
test: "yarn test --grep 'Checkpoint2'"
174+
file:
175+
hardhat: "packages/hardhat/contracts/Voting.sol"
176+
foundry: "packages/foundry/contracts/Voting.sol"
177+
test:
178+
hardhat: "yarn test --grep 'Checkpoint2'"
179+
foundry: "yarn test --match-test 'Checkpoint2'"
174180
hints:
175181
- "The two guard checks use existing state: `s_voters` (allowlist) and the new `s_hasRegistered` mapping."
176182
- |
@@ -719,8 +725,12 @@ checkpoints:
719725
4. Head to the **Voting** page — the full voting flow is now functional on the contract side!
720726
721727
Say **"check"** when you're ready!
722-
file: "packages/hardhat/contracts/Voting.sol"
723-
test: "yarn test --grep 'Checkpoint6'"
728+
file:
729+
hardhat: "packages/hardhat/contracts/Voting.sol"
730+
foundry: "packages/foundry/contracts/Voting.sol"
731+
test:
732+
hardhat: "yarn test --grep 'Checkpoint6'"
733+
foundry: "yarn test --match-test 'Checkpoint6'"
724734
hints:
725735
- "Don't forget the setup steps: replace Verifier.sol, update the deploy script, uncomment the import, add the two state variables, and initialize `i_verifier` in the constructor."
726736
- |

extension/.ai/instructions/skip-content.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ Read `.ai/CHALLENGE.yaml` and find the current checkpoint by its ID.
1414

1515
- If the current checkpoint does NOT have a `task` field, tell the user:
1616
"This checkpoint doesn't have a coding task to skip. Say 'hint' if you need help with the current question."
17-
- If it has a `task` field, continue to Step 3
17+
- If it has a `task` field, continue to Step 2b
18+
19+
### Step 2b: Resolve Framework-Specific Fields
20+
CHALLENGE.yaml `file:` and `test:` fields contain framework-specific values (e.g. `hardhat:` and `foundry:`). Detect which framework this project uses by checking which directory exists (`packages/hardhat/` vs `packages/foundry/`). Use the matching key for all `file:` and `test:` lookups throughout the skip flow.
1821

1922
### Step 3: Show the Solution
2023
Display the solution from `task.solution` with an explanation:
@@ -38,7 +41,7 @@ Read the current contract file (`task.file`) and apply the solution code:
3841
- Be careful to place code in the correct sections of the contract
3942

4043
### Step 5: Run Tests
41-
Execute the `task.test` command (e.g., `yarn test --grep "Checkpoint1"`) to verify the solution works.
44+
Execute the `task.test` command (resolved for the detected framework, e.g., `yarn test --grep "Checkpoint1"` for Hardhat or `yarn test --match-test "Checkpoint1"` for Foundry) to verify the solution works.
4245
- If tests pass: continue to Step 6
4346
- If tests fail: debug and fix (this shouldn't happen with correct solutions)
4447

extension/.ai/instructions/start-content.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ Read the file `.ai/CHALLENGE.yaml` to understand:
3333
- All checkpoints with their context, questions, tasks, and code unlocks
3434
- Whether each checkpoint is a **concept checkpoint** (has `unlocks`) or a **code-writing checkpoint** (has `task`)
3535

36+
### Step 1b: Resolve Framework-Specific Fields
37+
CHALLENGE.yaml `file:` and `test:` fields contain framework-specific values (e.g. `hardhat:` and `foundry:`). Detect which framework this project uses by checking which directory exists (`packages/hardhat/` vs `packages/foundry/`). Use the matching key for all `file:` and `test:` lookups throughout the challenge.
38+
3639
### Step 2: Apply Setup (if applicable)
3740
Check if CHALLENGE.yaml has a `setup.template` field:
3841

@@ -274,7 +277,7 @@ The user will respond with one of:
274277

275278
### Phase 5: Validate with Tests
276279

277-
Run the test command from `task.test` (e.g., `yarn test --grep "Checkpoint1"`).
280+
Run the test command from `task.test` (resolved for the detected framework, e.g., `yarn test --grep "Checkpoint1"` for Hardhat or `yarn test --match-test "Checkpoint1"` for Foundry).
278281

279282
**If ALL tests pass:**
280283
```

0 commit comments

Comments
 (0)