You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
in the same terminal, start your local network (a blockchain emulator in your computer):
87
+
When prompted, choose your preferred framework (Hardhat or Foundry).
88
+
89
+
> Start your local network (a blockchain emulator in your computer):
87
90
88
91
```sh
89
92
yarn chain
@@ -134,14 +137,29 @@ At its core, our prediction market has three essential parts:
134
137
135
138
In our version, when you deploy the market, it spins up two ERC20 tokens — one for "Yes", one for "No".
136
139
140
+
<Tabs>
141
+
<Tablabel="Hardhat">
142
+
137
143
🧱 You’ll find the token logic in `packages/hardhat/contracts/PredictionMarketToken.sol`
138
144
145
+
You’ll be working directly in `packages/hardhat/contracts/PredictionMarket.sol`.
146
+
147
+
</Tab>
148
+
<Tablabel="Foundry">
149
+
150
+
🧱 You’ll find the token logic in `packages/foundry/contracts/PredictionMarketToken.sol`
151
+
152
+
You’ll be working directly in `packages/foundry/contracts/PredictionMarket.sol`.
153
+
154
+
> **Note:** Screenshots in this guide show the Hardhat version, but the contract code and functionality are identical for Foundry.
155
+
156
+
</Tab>
157
+
</Tabs>
158
+
139
159
This contract extends the standard ERC20 spec with custom minting and burning logic. There’s also a transfer restriction in place to prevent the market owner from moving tokens — more on why later 👀
140
160
141
161
### 🛠️ The Main Contract: PredictionMarket.sol
142
162
143
-
You’ll be working directly in `packages/hardhat/contracts/PredictionMarket.sol`.
144
-
145
163
Our protocol revolves around three roles:
146
164
147
165
1.**👷♂️ Market Owner & Liquidity Provider** – sets up the market and seeds it with ETH
@@ -241,8 +259,19 @@ Your contract will also need to track the following data throughout the life of
241
259
-**🏆 `s_ethCollateral`**: the total ETH backing the tokens — think of this as your prize pool
242
260
-**💸 `s_lpTradingRevenue`** - tracks the fees earned from users buying/selling tokens — the LP’s reward
243
261
262
+
<Tabs>
263
+
<Tablabel="Hardhat">
264
+
244
265
> ❗️For easier testing, we set the oracle address to be the same as the liquidity provider during deployment (see `00_deploy_your_contract.ts`). Also, **double-check the parameter values** we're passing into the constructor, like `_question`, etc. (Hint: Add the first Hardhat account to your wallet or add your own account to interact as the oracle or contract owner. You can manually set your address in the deployment script or run `yarn account:import`.)
245
266
267
+
</Tab>
268
+
<Tablabel="Foundry">
269
+
270
+
> ❗️For easier testing, we set the oracle address to be the same as the liquidity provider during deployment (see `DeployPredictionMarket.s.sol`). Also, **double-check the parameter values** we're passing into the constructor, like `_question`, etc. (Hint: Add the first Anvil account to your wallet or add your own account to interact as the oracle or contract owner. You can manually set your address in the deployment script or run `yarn account:import`.)
271
+
272
+
</Tab>
273
+
</Tabs>
274
+
246
275
> ⏰ 🚨 In a prediction market in production, you would typically include a time-based restriction, a **fixed end date** to ensure that outcomes can only be reported after the predicted event occurs. For simplicity and ease of testing, we omit this time component in this implementation.
247
276
248
277
> 💡 `i_<variableName>` indicates an immutable variable, whereas `s_<variableName>` is a normal state variable that can be modified.
@@ -342,10 +371,23 @@ constructor(
342
371
343
372
Run the following command to check if you have implemented all variables and checks correctly.
344
373
374
+
<Tabs>
375
+
<Tablabel="Hardhat">
376
+
345
377
```sh
346
378
yarn test --grep "Checkpoint2"
347
379
```
348
380
381
+
</Tab>
382
+
<Tablabel="Foundry">
383
+
384
+
```sh
385
+
yarn test --match-test "Checkpoint2"
386
+
```
387
+
388
+
</Tab>
389
+
</Tabs>
390
+
349
391
> 🚨 Before we deploy the contract we need to finish implementing the constructor in the next Checkpoint 3.
350
392
351
393
## Checkpoint 3: 🔨🪙 Mint the tokens
@@ -525,10 +567,23 @@ constructor(
525
567
526
568
Run the following command to check if you've implemented all variable and checks correctly.
527
569
570
+
<Tabs>
571
+
<Tablabel="Hardhat">
572
+
528
573
```sh
529
574
yarn test --grep "Checkpoint3"
530
575
```
531
576
577
+
</Tab>
578
+
<Tablabel="Foundry">
579
+
580
+
```sh
581
+
yarn test --match-test "Checkpoint3"
582
+
```
583
+
584
+
</Tab>
585
+
</Tabs>
586
+
532
587
### ✅ Tests Passed? You're So Close!
533
588
534
589
Nice work — if your tests are green, you're just one step away from deployment! 🚀
@@ -650,10 +705,23 @@ function removeLiquidity(uint256 _ethToWithdraw) external onlyOwner {
650
705
651
706
Run the following command to check if you have implemented the functions correctly.
652
707
708
+
<Tabs>
709
+
<Tablabel="Hardhat">
710
+
653
711
```sh
654
712
yarn test --grep "Checkpoint4"
655
713
```
656
714
715
+
</Tab>
716
+
<Tablabel="Foundry">
717
+
718
+
```sh
719
+
yarn test --match-test "Checkpoint4"
720
+
```
721
+
722
+
</Tab>
723
+
</Tabs>
724
+
657
725
## Checkpoint 5: 🔮 Let the oracle report
658
726
659
727
Ethereum is a closed world — it doesn’t know what’s happening off-chain. That’s by design, to keep data secure and tamper-proof.
@@ -781,10 +849,23 @@ function removeLiquidity(uint256 _ethToWithdraw) external onlyOwner predictionNo
781
849
782
850
Run the following command to check if the report function for the oracle is implemented correctly.
783
851
852
+
<Tabs>
853
+
<Tablabel="Hardhat">
854
+
784
855
```sh
785
856
yarn test --grep "Checkpoint5"
786
857
```
787
858
859
+
</Tab>
860
+
<Tablabel="Foundry">
861
+
862
+
```sh
863
+
yarn test --match-test "Checkpoint5"
864
+
```
865
+
866
+
</Tab>
867
+
</Tabs>
868
+
788
869
✅ Tests Passed? You’re Almost There!
789
870
790
871
Awesome job — your tests are passing, and deployment is just around the corner! 🚀
@@ -797,8 +878,19 @@ But before you hit that deploy button, there’s one last tweak to make:
797
878
798
879
Now head over to the oracle tab in the UI and report the outcome (after you watched the race of course:)) 🏎️ 🏁
799
880
881
+
<Tabs>
882
+
<Tablabel="Hardhat">
883
+
800
884
> 💡 Make sure you're connected with the correct oracle address — check `00_deploy_your_contract.ts` to find out which one is being used. (Hint: Add the first Hardhat account to your wallet or add your own account to interact as the oracle or contract owner. You can manually set your address in the deployment script or run `yarn account:import`.)
801
885
886
+
</Tab>
887
+
<Tablabel="Foundry">
888
+
889
+
> 💡 Make sure you're connected with the correct oracle address — check `DeployPredictionMarket.s.sol` to find out which one is being used. (Hint: Add the first Anvil account to your wallet or add your own account to interact as the oracle or contract owner. You can manually set your address in the deployment script or run `yarn account:import`.)
## Checkpoint 6: 📉💧 Resolve market and withdraw liquidity and trading revenue
@@ -906,10 +998,23 @@ function resolveMarketAndWithdraw() external onlyOwner predictionReported return
906
998
907
999
Run the following command to check if the `resolveMarketAndWithdraw` function is implemented correctly.
908
1000
1001
+
<Tabs>
1002
+
<Tablabel="Hardhat">
1003
+
909
1004
```sh
910
1005
yarn test --grep "Checkpoint6"
911
1006
```
912
1007
1008
+
</Tab>
1009
+
<Tablabel="Foundry">
1010
+
1011
+
```sh
1012
+
yarn test --match-test "Checkpoint6"
1013
+
```
1014
+
1015
+
</Tab>
1016
+
</Tabs>
1017
+
913
1018
Make sure to redeploy the contract and report the outcome again using the Oracle tab.
914
1019
915
1020
Then jump over to the Liquidity Provider tab and give your new function a try 💥
@@ -1148,10 +1253,23 @@ function _calculateProbability(uint256 tokensSold, uint256 totalSold) private pu
1148
1253
1149
1254
Run the following command to check if you have implemented all the functions correctly.
1150
1255
1256
+
<Tabs>
1257
+
<Tablabel="Hardhat">
1258
+
1151
1259
```sh
1152
1260
yarn test --grep "Checkpoint7"
1153
1261
```
1154
1262
1263
+
</Tab>
1264
+
<Tablabel="Foundry">
1265
+
1266
+
```sh
1267
+
yarn test --match-test "Checkpoint7"
1268
+
```
1269
+
1270
+
</Tab>
1271
+
</Tabs>
1272
+
1155
1273
## Checkpoint 8: 🔁💰 Buy and sell "Yes" or "No" Tokens for ETH
1156
1274
1157
1275
Now that you've built the pricing engine (`getBuyPriceInEth` / `getSellPriceInEth`), it’s time to bring the market to life with real trades! Let’s implement two key functions:
@@ -1347,10 +1465,23 @@ function sellTokensForEth(Outcome _outcome, uint256 _tradingAmount)
1347
1465
1348
1466
Run the following command to check if you have implemented all the functions correctly.
1349
1467
1468
+
<Tabs>
1469
+
<Tablabel="Hardhat">
1470
+
1350
1471
```sh
1351
1472
yarn test --grep "Checkpoint8"
1352
1473
```
1353
1474
1475
+
</Tab>
1476
+
<Tablabel="Foundry">
1477
+
1478
+
```sh
1479
+
yarn test --match-test "Checkpoint8"
1480
+
```
1481
+
1482
+
</Tab>
1483
+
</Tabs>
1484
+
1354
1485
And then run `yarn deploy` to test it in the front-end and see how the probability changes.
1355
1486
1356
1487
> ❗️💡 Make sure you **use another account as the liquidity provider**, otherwise you are **restricted from selling the tokens**
@@ -1452,10 +1583,23 @@ function redeemWinningTokens(uint256 _amount) external amountGreaterThanZero(_am
1452
1583
1453
1584
Run the following command to check if you have implemented the last function for this challenge correctly.
1454
1585
1586
+
<Tabs>
1587
+
<Tablabel="Hardhat">
1588
+
1455
1589
```sh
1456
1590
yarn test --grep "Checkpoint9"
1457
1591
```
1458
1592
1593
+
</Tab>
1594
+
<Tablabel="Foundry">
1595
+
1596
+
```sh
1597
+
yarn test --match-test "Checkpoint9"
1598
+
```
1599
+
1600
+
</Tab>
1601
+
</Tabs>
1602
+
1459
1603
Then run `yarn deploy` to test it on the front-end. Make sure to purchase some winning tokens beforehand and report the race. After that, you should be able to redeem your desired amount.
@@ -1466,8 +1610,19 @@ You’ve now built the full loop: provide liquidity, trade shares, resolve the m
1466
1610
1467
1611
## Checkpoint 10: 💾 Deploy your contracts! 🛰
1468
1612
1613
+
<Tabs>
1614
+
<Tablabel="Hardhat">
1615
+
1469
1616
📡 Edit the `defaultNetwork` to your choice of **Sepolia or Optimism Sepolia** in `packages/hardhat/hardhat.config.ts`
1470
1617
1618
+
</Tab>
1619
+
<Tablabel="Foundry">
1620
+
1621
+
📡 Choose your target network (**Sepolia or Optimism Sepolia**)
1622
+
1623
+
</Tab>
1624
+
</Tabs>
1625
+
1471
1626
🔐 You will need to generate a **deployer address** using `yarn generate` This creates a mnemonic and saves it locally.
1472
1627
1473
1628
👩🚀 Use `yarn account` to view your deployer account balances.
@@ -1476,9 +1631,22 @@ You’ve now built the full loop: provide liquidity, trade shares, resolve the m
1476
1631
1477
1632
> 🚨🚨 **!!!Warning!!!** Before deploying, make sure to adjust the ETH amount in the constructor to suit your preferences. By default, the contract deploys with **1 ETH** and sets the token value to **0.01 ETH**. You might want to change this, for example, to **0.1 ETH** and **0.001 ETH,** depending on how much ETH you're willing to allocate. 🚨🚨
1478
1633
1634
+
<Tabs>
1635
+
<Tablabel="Hardhat">
1636
+
1479
1637
🚀 Run `yarn deploy` to deploy your smart contract to a public network (selected in `hardhat.config.ts`)
1480
1638
1481
-
> 💬 Hint: You can set the `defaultNetwork` in hardhat.config.ts to sepolia or optimismSepolia OR you can yarn deploy --network sepolia or yarn deploy --network optimismSepolia.
1639
+
> 💬 Hint: You can set the `defaultNetwork` in `hardhat.config.ts` to `sepolia` or `optimismSepolia` OR you can `yarn deploy --network sepolia` or `yarn deploy --network optimismSepolia`.
1640
+
1641
+
</Tab>
1642
+
<Tablabel="Foundry">
1643
+
1644
+
🚀 Run `yarn deploy --network sepolia` (or `yarn deploy --network optimismSepolia`) to deploy your smart contracts.
1645
+
1646
+
> 💬 Hint: Foundry uses `forge script` under the hood. The `yarn deploy` command handles this for you.
1647
+
1648
+
</Tab>
1649
+
</Tabs>
1482
1650
1483
1651
💻 View your front-end at [http://localhost:3000](http://localhost:3000/) and verify you see the correct network.
1484
1652
@@ -1490,17 +1658,29 @@ You’ve now built the full loop: provide liquidity, trade shares, resolve the m
1490
1658
1491
1659
> Follow the steps to deploy to Vercel. It'll give you a public URL.
1492
1660
1493
-
> 🦊 Since we have deployed to a public testnet, you will now need to connect using a wallet you own or use a burner wallet. By default 🔥 burner wallets are only available on Hardhat. You can enable them on every chain by setting burnerWalletMode: "allNetworks" in your front-end config (scaffold.config.ts in packages/nextjs/)
1661
+
> 🦊 Since we have deployed to a public testnet, you will now need to connect using a wallet you own or use a burner wallet. By default 🔥 burner wallets are only available on localhost. You can enable them on every chain by setting burnerWalletMode: "allNetworks" in your front-end config (scaffold.config.ts in packages/nextjs/)
1494
1662
1495
1663
**Configuration of Third-Party Services for Production-Grade Apps.**
1496
1664
1497
1665
By default, 🏗 Scaffold-ETH 2 provides predefined API keys for popular services such as Alchemy and Etherscan. This allows you to begin developing and testing your applications more easily, avoiding the need to register for these services. This is great to complete your **Speedrun Ethereum**.
1498
1666
1499
1667
For production-grade applications, it's recommended to obtain your own API keys (to prevent rate limiting issues). You can configure these at:
1500
1668
1669
+
<Tabs>
1670
+
<Tablabel="Hardhat">
1671
+
1501
1672
- 🔷`ALCHEMY_API_KEY` variable in `packages/hardhat/.env` and `packages/nextjs/.env.local`. You can create API keys from the [Alchemy dashboard](https://dashboard.alchemy.com/).
1502
1673
- 📃`ETHERSCAN_API_KEY` variable in `packages/hardhat/.env` with your generated API key. You can get your key [here](https://etherscan.io/myapikey).
1503
1674
1675
+
</Tab>
1676
+
<Tablabel="Foundry">
1677
+
1678
+
- 🔷`ALCHEMY_API_KEY` variable in `packages/foundry/.env` and `packages/nextjs/.env.local`. You can create API keys from the [Alchemy dashboard](https://dashboard.alchemy.com/).
1679
+
- 📃`ETHERSCAN_API_KEY` variable in `packages/foundry/.env` with your generated API key. You can get your key [here](https://etherscan.io/myapikey).
1680
+
1681
+
</Tab>
1682
+
</Tabs>
1683
+
1504
1684
> 💬 Hint: It's recommended to store envs for nextjs in Vercel/system env config for live apps and use .env.local for local testing.
1505
1685
1506
1686
## Checkpoint 11: 📜 Contract Verification
@@ -1572,3 +1752,16 @@ But what makes this space exciting is that there’s no “one-size-fits-all”
1572
1752
We’d love to see what you build next. Share your ideas, your forks, your experiments. This is just the beginning. 💥
1573
1753
1574
1754
What will your prediction market look like? Let us know! 🧪🔮
1755
+
1756
+
## AI-Guided Learning Mode (Optional)
1757
+
1758
+
This challenge supports an interactive AI learning mode. Instead of reading through all the checkpoints above, you can have an AI guide you through the challenge step by step.
1759
+
1760
+
### Quick Start
1761
+
1762
+
1. Open this project in [Claude Code](https://claude.ai/claude-code), Cursor, or another AI-enabled IDE
1763
+
2. Run `/start` in the AI chat
1764
+
3. The AI will teach you each concept and give you coding tasks
1765
+
4. Say **"check"** to validate your code, **"hint"** for help, or **"/skip"** to see the solution
1766
+
1767
+
The AI uses the same checkpoint structure as this README but provides personalized guidance, answers questions, and runs tests for you.
0 commit comments