Skip to content

Commit fbfabbc

Browse files
authored
Merge pull request #439 from scaffold-eth/challenge-prediction-markets-foundry
Foundry support - Challenge-prediction-markets
2 parents 08a31a5 + 05ca688 commit fbfabbc

14 files changed

Lines changed: 1527 additions & 323 deletions

README.md

Lines changed: 199 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,18 @@ Before you begin, you need to install the following tools:
7575
- [Node (>= v20.18.3)](https://nodejs.org/en/download/)
7676
- Yarn ([v1](https://classic.yarnpkg.com/en/docs/install/) or [v2+](https://yarnpkg.com/getting-started/install))
7777
- [Git](https://git-scm.com/downloads)
78+
- [Foundry](https://book.getfoundry.sh/getting-started/installation) (only if choosing Foundry as framework)
7879

7980
Then download the challenge to your computer and install dependencies by running:
8081

8182
```sh
82-
npx create-eth@2.0.13 -e challenge-prediction-markets challenge-prediction-markets
83+
npx create-eth@2.0.18 -e challenge-prediction-markets challenge-prediction-markets
8384
cd challenge-prediction-markets
8485
```
8586

86-
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):
8790
8891
```sh
8992
yarn chain
@@ -134,14 +137,29 @@ At its core, our prediction market has three essential parts:
134137

135138
In our version, when you deploy the market, it spins up two ERC20 tokens — one for "Yes", one for "No".
136139

140+
<Tabs>
141+
<Tab label="Hardhat">
142+
137143
🧱 You’ll find the token logic in `packages/hardhat/contracts/PredictionMarketToken.sol`
138144

145+
You’ll be working directly in `packages/hardhat/contracts/PredictionMarket.sol`.
146+
147+
</Tab>
148+
<Tab label="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+
139159
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 👀
140160

141161
### 🛠️ The Main Contract: PredictionMarket.sol
142162

143-
You’ll be working directly in `packages/hardhat/contracts/PredictionMarket.sol`.
144-
145163
Our protocol revolves around three roles:
146164

147165
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
241259
- **🏆 `s_ethCollateral`**: the total ETH backing the tokens — think of this as your prize pool
242260
- **💸 `s_lpTradingRevenue`** - tracks the fees earned from users buying/selling tokens — the LP’s reward
243261

262+
<Tabs>
263+
<Tab label="Hardhat">
264+
244265
> ❗️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`.)
245266
267+
</Tab>
268+
<Tab label="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+
246275
> ⏰ 🚨 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.
247276
248277
> 💡 `i_<variableName>` indicates an immutable variable, whereas `s_<variableName>` is a normal state variable that can be modified.
@@ -342,10 +371,23 @@ constructor(
342371

343372
Run the following command to check if you have implemented all variables and checks correctly.
344373

374+
<Tabs>
375+
<Tab label="Hardhat">
376+
345377
```sh
346378
yarn test --grep "Checkpoint2"
347379
```
348380

381+
</Tab>
382+
<Tab label="Foundry">
383+
384+
```sh
385+
yarn test --match-test "Checkpoint2"
386+
```
387+
388+
</Tab>
389+
</Tabs>
390+
349391
> 🚨 Before we deploy the contract we need to finish implementing the constructor in the next Checkpoint 3.
350392
351393
## Checkpoint 3: 🔨🪙 Mint the tokens
@@ -525,10 +567,23 @@ constructor(
525567

526568
Run the following command to check if you've implemented all variable and checks correctly.
527569

570+
<Tabs>
571+
<Tab label="Hardhat">
572+
528573
```sh
529574
yarn test --grep "Checkpoint3"
530575
```
531576

577+
</Tab>
578+
<Tab label="Foundry">
579+
580+
```sh
581+
yarn test --match-test "Checkpoint3"
582+
```
583+
584+
</Tab>
585+
</Tabs>
586+
532587
### ✅ Tests Passed? You're So Close!
533588

534589
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 {
650705

651706
Run the following command to check if you have implemented the functions correctly.
652707

708+
<Tabs>
709+
<Tab label="Hardhat">
710+
653711
```sh
654712
yarn test --grep "Checkpoint4"
655713
```
656714

715+
</Tab>
716+
<Tab label="Foundry">
717+
718+
```sh
719+
yarn test --match-test "Checkpoint4"
720+
```
721+
722+
</Tab>
723+
</Tabs>
724+
657725
## Checkpoint 5: 🔮 Let the oracle report
658726

659727
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
781849

782850
Run the following command to check if the report function for the oracle is implemented correctly.
783851

852+
<Tabs>
853+
<Tab label="Hardhat">
854+
784855
```sh
785856
yarn test --grep "Checkpoint5"
786857
```
787858

859+
</Tab>
860+
<Tab label="Foundry">
861+
862+
```sh
863+
yarn test --match-test "Checkpoint5"
864+
```
865+
866+
</Tab>
867+
</Tabs>
868+
788869
✅ Tests Passed? You’re Almost There!
789870

790871
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:
797878

798879
Now head over to the oracle tab in the UI and report the outcome (after you watched the race of course:)) 🏎️ 🏁
799880

881+
<Tabs>
882+
<Tab label="Hardhat">
883+
800884
> 💡 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`.)
801885
886+
</Tab>
887+
<Tab label="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`.)
890+
891+
</Tab>
892+
</Tabs>
893+
802894
![ch-6-oracle2](https://raw.githubusercontent.com/scaffold-eth/se-2-challenges/challenge-prediction-markets/extension/packages/nextjs/public/oracle2.png)
803895

804896
## Checkpoint 6: 📉💧 Resolve market and withdraw liquidity and trading revenue
@@ -906,10 +998,23 @@ function resolveMarketAndWithdraw() external onlyOwner predictionReported return
906998

907999
Run the following command to check if the `resolveMarketAndWithdraw` function is implemented correctly.
9081000

1001+
<Tabs>
1002+
<Tab label="Hardhat">
1003+
9091004
```sh
9101005
yarn test --grep "Checkpoint6"
9111006
```
9121007

1008+
</Tab>
1009+
<Tab label="Foundry">
1010+
1011+
```sh
1012+
yarn test --match-test "Checkpoint6"
1013+
```
1014+
1015+
</Tab>
1016+
</Tabs>
1017+
9131018
Make sure to redeploy the contract and report the outcome again using the Oracle tab.
9141019

9151020
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
11481253

11491254
Run the following command to check if you have implemented all the functions correctly.
11501255

1256+
<Tabs>
1257+
<Tab label="Hardhat">
1258+
11511259
```sh
11521260
yarn test --grep "Checkpoint7"
11531261
```
11541262

1263+
</Tab>
1264+
<Tab label="Foundry">
1265+
1266+
```sh
1267+
yarn test --match-test "Checkpoint7"
1268+
```
1269+
1270+
</Tab>
1271+
</Tabs>
1272+
11551273
## Checkpoint 8: 🔁💰 Buy and sell "Yes" or "No" Tokens for ETH
11561274

11571275
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)
13471465

13481466
Run the following command to check if you have implemented all the functions correctly.
13491467

1468+
<Tabs>
1469+
<Tab label="Hardhat">
1470+
13501471
```sh
13511472
yarn test --grep "Checkpoint8"
13521473
```
13531474

1475+
</Tab>
1476+
<Tab label="Foundry">
1477+
1478+
```sh
1479+
yarn test --match-test "Checkpoint8"
1480+
```
1481+
1482+
</Tab>
1483+
</Tabs>
1484+
13541485
And then run `yarn deploy` to test it in the front-end and see how the probability changes.
13551486

13561487
> ❗️💡 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
14521583

14531584
Run the following command to check if you have implemented the last function for this challenge correctly.
14541585

1586+
<Tabs>
1587+
<Tab label="Hardhat">
1588+
14551589
```sh
14561590
yarn test --grep "Checkpoint9"
14571591
```
14581592

1593+
</Tab>
1594+
<Tab label="Foundry">
1595+
1596+
```sh
1597+
yarn test --match-test "Checkpoint9"
1598+
```
1599+
1600+
</Tab>
1601+
</Tabs>
1602+
14591603
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.
14601604

14611605
![ch-6-user3](https://raw.githubusercontent.com/scaffold-eth/se-2-challenges/challenge-prediction-markets/extension/packages/nextjs/public/user3.png)
@@ -1466,8 +1610,19 @@ You’ve now built the full loop: provide liquidity, trade shares, resolve the m
14661610

14671611
## Checkpoint 10: 💾 Deploy your contracts! 🛰
14681612

1613+
<Tabs>
1614+
<Tab label="Hardhat">
1615+
14691616
📡 Edit the `defaultNetwork` to your choice of **Sepolia or Optimism Sepolia** in `packages/hardhat/hardhat.config.ts`
14701617

1618+
</Tab>
1619+
<Tab label="Foundry">
1620+
1621+
📡 Choose your target network (**Sepolia or Optimism Sepolia**)
1622+
1623+
</Tab>
1624+
</Tabs>
1625+
14711626
🔐 You will need to generate a **deployer address** using `yarn generate` This creates a mnemonic and saves it locally.
14721627

14731628
👩‍🚀 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
14761631

14771632
> 🚨🚨 **!!!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. 🚨🚨
14781633
1634+
<Tabs>
1635+
<Tab label="Hardhat">
1636+
14791637
🚀 Run `yarn deploy` to deploy your smart contract to a public network (selected in `hardhat.config.ts`)
14801638

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+
<Tab label="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>
14821650

14831651
💻 View your front-end at [http://localhost:3000](http://localhost:3000/) and verify you see the correct network.
14841652

@@ -1490,17 +1658,29 @@ You’ve now built the full loop: provide liquidity, trade shares, resolve the m
14901658
14911659
> Follow the steps to deploy to Vercel. It'll give you a public URL.
14921660
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/)
14941662
14951663
**Configuration of Third-Party Services for Production-Grade Apps.**
14961664

14971665
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**.
14981666

14991667
For production-grade applications, it's recommended to obtain your own API keys (to prevent rate limiting issues). You can configure these at:
15001668

1669+
<Tabs>
1670+
<Tab label="Hardhat">
1671+
15011672
- 🔷`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/).
15021673
- 📃`ETHERSCAN_API_KEY` variable in `packages/hardhat/.env` with your generated API key. You can get your key [here](https://etherscan.io/myapikey).
15031674

1675+
</Tab>
1676+
<Tab label="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+
15041684
> 💬 Hint: It's recommended to store envs for nextjs in Vercel/system env config for live apps and use .env.local for local testing.
15051685
15061686
## Checkpoint 11: 📜 Contract Verification
@@ -1572,3 +1752,16 @@ But what makes this space exciting is that there’s no “one-size-fits-all”
15721752
We’d love to see what you build next. Share your ideas, your forks, your experiments. This is just the beginning. 💥
15731753

15741754
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

Comments
 (0)