Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Port all contracts to Foundry #712

Merged
merged 35 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
652457d
🚧 Foundry init and move Hardhat contracts
cairoeth Mar 20, 2024
f35c572
📌 foundry openzeppelin multiple dependencies
cairoeth Mar 20, 2024
067986b
🎨 Foundry formatter
cairoeth Mar 20, 2024
4178f2c
✅ Main Ethernaut unit test
cairoeth Mar 20, 2024
efa8a05
✅ Finish Ethernaut core unit tests
cairoeth Mar 20, 2024
a793332
✅ Leaderboard unit tests
cairoeth Mar 20, 2024
af8bb67
✅ Level unit tests
cairoeth Mar 20, 2024
e521368
✅ Player core unit tests
cairoeth Mar 20, 2024
048a6b5
✨ CoinFlip foundry test
cairoeth Mar 21, 2024
75cb544
✨ Delegation foundry test
cairoeth Mar 21, 2024
590fdaa
🚧 Denial foundry test wip
cairoeth Mar 21, 2024
94a7d91
✨ Multiple challenges tests
cairoeth Mar 21, 2024
7bd0679
✨ foundry: token, reentrance, motorbik
cairoeth Mar 21, 2024
2b68cba
🔥 remove hardhat contracts
cairoeth Mar 21, 2024
64791e1
💚 foundry tests CI
cairoeth Mar 21, 2024
6b81a4d
✅ fix remaining foundry tests
cairoeth Mar 21, 2024
7217898
🔧 update compilation output paths
cairoeth Mar 22, 2024
a3d369e
🚧 add path to foundry for netlify
cairoeth Mar 22, 2024
e5fd3f0
🔧 move foundry init to package.json
cairoeth Mar 22, 2024
9371269
🧪 test bash with yarn init contracts
cairoeth Mar 22, 2024
09db1f8
🧪 bash for foundryup
cairoeth Mar 22, 2024
de957bc
🧪 path bin foundryup
cairoeth Mar 22, 2024
81e3879
🧪 foundryup yarn
cairoeth Mar 22, 2024
f590b69
🧪 netlify.toml bash
cairoeth Mar 22, 2024
b7c857d
🔨 update import paths of scripts and symlinks
cairoeth Mar 22, 2024
68f4813
📦️ remove unusued and build out for netlify
cairoeth Mar 22, 2024
1c6f218
🔨netlify use yarn run
cairoeth Mar 22, 2024
7a5ff79
🐛 fix build netlify command
cairoeth Mar 22, 2024
cb5d18b
➕ add sentry tracing dependency
cairoeth Mar 22, 2024
5826ee6
⚡️ optimize yarn site build
cairoeth Mar 22, 2024
5c3b77d
🐛 fix contracts path in client
cairoeth Mar 22, 2024
2b5bf86
⚰️ remove unusued var
cairoeth Mar 22, 2024
e9f7e92
🐛 fix how abi is consumed
cairoeth Mar 22, 2024
79692c8
🐛 fix deploy and supersede scripts
cairoeth Mar 22, 2024
3432dca
➖ remove duplicate command
cairoeth Mar 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
33 changes: 25 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
name: Test

on:
push:
branches: [master]
pull_request: {}

jobs:
test:
tests:
name: Foundry tests
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./contracts

steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16.x
- run: yarn
- run: yarn test:contracts
- uses: actions/checkout@v3
with:
submodules: true

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Install dependencies
run: forge install

- name: Check contract sizes
run: forge build --sizes --skip test
id: build

- name: Run tests
run: forge test -v
id: test
9 changes: 3 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ node_modules/
client/.env
contracts/.env

# Contracts
contracts/build/
contracts/cache/
contracts/node_modules/
contracts/.openzeppelin/

# Client
client/build/
client/node_modules/
client/src/gamedata/deploy.local.json
.env

# Local Netlify folder
.netlify
12 changes: 12 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[submodule "contracts/lib/forge-std"]
path = contracts/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "contracts/lib/openzeppelin-contracts-06"]
path = contracts/lib/openzeppelin-contracts-06
url = https://github.com/openzeppelin/openzeppelin-contracts
[submodule "contracts/lib/openzeppelin-contracts-08"]
path = contracts/lib/openzeppelin-contracts-08
url = https://github.com/openzeppelin/openzeppelin-contracts
[submodule "contracts/lib/openzeppelin-contracts-upgradeable"]
path = contracts/lib/openzeppelin-contracts-upgradeable
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## March 2024
- Updated all contracts (challenges, tests, etc.) to use Foundry instead of Hardhat

## August 2021
- Filter out repeated log messages for mined transactions
- Increased Sidebar width
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ If you would like to contribute in another way, please reach out to us via email

*A level is composed of the following elements:*

- A `<Level>Factory.sol` contract, where `<Level>` is replaced by the name of the level, that needs to extend [`Level.sol`](./contracts/contracts/levels/base/Level.sol). This factory contract will be deployed only once and registered on Ethernaut.sol by Ethernaut's owner. Players never interact with the factory directly. The factory is in charge of creating level instances for players to use (1 instance per player) and to check these instances to verify if the player has passed the level. Factories should not have state that can be changed by the player.
- A `<Level>Factory.sol` contract, where `<Level>` is replaced by the name of the level, that needs to extend [`Level.sol`](./contracts/src/levels/base/Level.sol). This factory contract will be deployed only once and registered on Ethernaut.sol by Ethernaut's owner. Players never interact with the factory directly. The factory is in charge of creating level instances for players to use (1 instance per player) and to check these instances to verify if the player has passed the level. Factories should not have state that can be changed by the player.
- A `level instance` contract named `<Level>.sol`, where `<Level>` is replaced by the name of the level, that is emitted by the factory for each player that requests it. Instances need to be completely decouppled from Ethernaut's architecture. Factories will emit them and verify them. That is, level instances don't know anything about their factories or Ethernaut. An instance's state can be completely demolished by players and even destroyed since they are not really part of the architecture, just a challenge for a player to use at will.
- A `description file` in [the descriptions directory](./client/src/gamedata/en/descriptions/levels) that the UI presents to the player and describes the level's objectives with some narrative and tips.
- A `description completion file` also located in [the descriptions directory](./client/src/gamedata/en/descriptions/levels) that the UI presents to the player when the level is passed, further information about the player, historical insights, further explanations or just a congrats message.
Expand All @@ -60,8 +60,8 @@ Let's suppose that we are creating the level "King" (which is already created an
2. Use the other levels as a basis, eg. duplicate DummyFactory.sol and Dummy.sol.
3. Rename and modify the contracts to KingFactory.sol and King.sol respectively.
4. Implement the desired instance and factory logic in solidity. See current levels and notes to understand how the game mechanics work.
5. Add the test file `contracts/test/levels/King.test.js`. Use other tests files as reference to see how tests might work.
6. Run `yarn test:contracts` and once all tests pass, register the level in [gamedata.json](client/src/gamedata/gamedata.json).
5. Add the test file `contracts/test/levels/King.t.sol`. Use other tests files as reference to see how tests might work.
6. Run `forge test` and once all tests pass, register the level in [gamedata.json](client/src/gamedata/gamedata.json).
7. The level should now show up in the ui. To start the UI, set the [ACTIVE_NETWORK](client/src/constants.js) to `NETWORKS.LOCAL` and run `yarn start`.
8. Add a description markdown file, in this case client/src/gamedata/levels/king.md (make sure gamedata.json points to it). This content will now be displayed in the ui for the level.
9. Add a completed description markdown file, in this case client/src/gamedata/levels/king_complete.md (make sure gamedata.json points to it). The level will display this as additional info once the level is solved, usually to include historical information related to the level.
Expand Down
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
"private": true,
"dependencies": {
"@sentry/react": "^7.0.0",
"@sentry/tracing": "^7.108.0",
"@truffle/contract": "^4.3.15",
"alchemy-sdk": "^2.2.3",
"axios": "^1.2.4",
"bad-words": "^3.0.4",
"bootstrap": "^5.0.0",
"contracts": "0.1.0",
"cross-env": "7.0.3",
"devtools-detect": "^4.0.0",
"dotenv": "^16.0.3",
Expand Down
1 change: 0 additions & 1 deletion client/public/contracts

This file was deleted.

10 changes: 5 additions & 5 deletions client/scripts/deploy_contracts.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import * as ethutil from "../src/utils/ethutil.js";
import * as constants from "../src/constants.js";
import HDWalletProvider from "@truffle/hdwallet-provider";
import * as gamedata from "../src/gamedata/gamedata.json" assert { type: "json" };
import * as EthernautABI from "contracts/build/contracts/Ethernaut.sol/Ethernaut.json" assert { type: "json" };
import * as ProxyAdminABI from "contracts/build/contracts/proxy/ProxyAdmin.sol/ProxyAdmin.json" assert { type: "json" };
import * as ImplementationABI from "contracts/build/contracts/metrics/Statistics.sol/Statistics.json" assert { type: "json" };
import * as ProxyStatsABI from "contracts/build/contracts/proxy/ProxyStats.sol/ProxyStats.json" assert { type: "json" };
import * as EthernautABI from "../src/contracts/out/Ethernaut.sol/Ethernaut.json" assert { type: "json" };
import * as ProxyAdminABI from "../src/contracts/out/ProxyAdmin.sol/ProxyAdmin.json" assert { type: "json" };
import * as ImplementationABI from "../src/contracts/out/Statistics.sol/Statistics.json" assert { type: "json" };
import * as ProxyStatsABI from "../src/contracts/out/ProxyStats.sol/ProxyStats.json" assert { type: "json" };

let web3;
let ethernaut;
Expand Down Expand Up @@ -127,7 +127,7 @@ async function deployContracts(deployData) {
// Deploy contract
const LevelABI = JSON.parse(
fs.readFileSync(
`contracts/build/contracts/levels/${
`contracts/out/${
level.levelContract
}/${withoutExtension(level.levelContract)}.json`,
"utf-8"
Expand Down
14 changes: 7 additions & 7 deletions client/scripts/supersede_level.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import HDWalletProvider from "@truffle/hdwallet-provider";
import Web3 from "web3";
import * as ethutil from "../src/utils/ethutil.js";
import * as constants from "../src/constants.js";
import * as EthernautABI from "contracts/build/contracts/Ethernaut.sol/Ethernaut.json" assert { type: "json" };
import * as ProxyStatsABI from "contracts/build/contracts/proxy/ProxyStats.sol/ProxyStats.json" assert { type: "json" };
import * as ProxyAdminABI from "contracts/build/contracts/proxy/ProxyAdmin.sol/ProxyAdmin.json" assert { type: "json" };
import * as ImplementationABI from "contracts/build/contracts/metrics/Statistics.sol/Statistics.json" assert { type: "json" };
import * as SupersederImplementationABI from "contracts/build/contracts/metrics/StatisticsLevelSuperseder.sol/StatisticsLevelSuperseder.json" assert { type: "json" };
import * as EthernautABI from "../src/contracts/out/Ethernaut.sol/Ethernaut.json" assert { type: "json" };
import * as ProxyStatsABI from "../src/contracts/out/ProxyStats.sol/ProxyStats.json" assert { type: "json" };
import * as ProxyAdminABI from "../src/contracts/out/ProxyAdmin.sol/ProxyAdmin.json" assert { type: "json" };
import * as ImplementationABI from "../src/contracts/out/Statistics.sol/Statistics.json" assert { type: "json" };
import * as SupersederImplementationABI from "../src/contracts/out/StatisticsLevelSuperseder.sol/StatisticsLevelSuperseder.json" assert { type: "json" };

import gamedata from "../src/gamedata/gamedata.json" assert { type: "json" };
const levels = gamedata.levels;
Expand Down Expand Up @@ -201,7 +201,7 @@ async function deployAndUpgradeStatisticsToStatisticsSuperseder() {

const props = {
gasPrice: parseInt(await web3.eth.getGasPrice() * 1.10),
gas: 45000000,
gas: 30000000,
};
let from = constants.ADDRESSES[constants.ACTIVE_NETWORK.name];
if (!from) from = (await web3.eth.getAccounts())[0];
Expand Down Expand Up @@ -258,7 +258,7 @@ async function deployLevel(level) {

const LevelABI = JSON.parse(
fs.readFileSync(
`contracts/build/contracts/levels/${level.levelContract}/${
`contracts/out/${level.levelContract}/${
level.levelContract.split(".")[0]
}.json`,
"utf-8"
Expand Down
4 changes: 2 additions & 2 deletions client/scripts/upgrade_proxy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import fs from 'fs';
import * as ethutil from '../src/utils/ethutil.js';
import * as constants from '../src/constants.js';
import HDWalletProvider from '@truffle/hdwallet-provider';
import * as ProxyAdminABI from 'contracts/build/contracts/proxy/ProxyAdmin.sol/ProxyAdmin.json' assert { type: 'json' };
import * as ImplementationABI from 'contracts/build/contracts/metrics/Statistics.sol/Statistics.json' assert { type: 'json' };
import * as ProxyAdminABI from '../src/contracts/out/ProxyAdmin.sol/ProxyAdmin.json' assert { type: 'json' };
import * as ImplementationABI from '../src/contracts/out/Statistics.sol/Statistics.json' assert { type: 'json' };

let web3;

Expand Down
2 changes: 1 addition & 1 deletion client/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export const GOOGLE_ANALYTICS_ID = "UA-85043059-4";

// Owner addresses
export const ADDRESSES = {
[NETWORKS.LOCAL.name]: undefined,
[NETWORKS.LOCAL.name]: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
[NETWORKS.MUMBAI.name]: "0x09902A56d04a9446601a0d451E07459dC5aF0820",
[NETWORKS.SEPOLIA.name]: "0x09902A56d04a9446601a0d451E07459dC5aF0820",
[NETWORKS.OPTIMISM_SEPOLIA.name]: "0x09902A56d04a9446601a0d451E07459dC5aF0820",
Expand Down
2 changes: 1 addition & 1 deletion client/src/containers/Level.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class Level extends React.Component {

let sourcesFile = null;
try {
sourcesFile = require(`contracts/contracts/levels/${level.instanceContract}`);
sourcesFile = require(`../contracts/src/levels/${level.instanceContract}`);
} catch (e) {
console.log(e);
}
Expand Down
1 change: 1 addition & 0 deletions client/src/contracts
2 changes: 1 addition & 1 deletion client/src/middlewares/loadEthernautContract.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as ethutil from '../utils/ethutil'
import EthernautABI from 'contracts/build/contracts/Ethernaut.sol/Ethernaut.json'
import EthernautABI from '../contracts/out/Ethernaut.sol/Ethernaut.json'
import * as actions from '../actions';
import { loadTranslations } from '../utils/translations'

Expand Down
4 changes: 2 additions & 2 deletions client/src/middlewares/loadLevelInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ const loadLevelInstance = (store) => (next) => (action) => {
if (!instanceAddress) return;
console.info(`=> ${strings.instanceAddressMessage}\n${instanceAddress}`);
const Instance = ethutil.getTruffleContract(
require(`contracts/build/contracts/levels/${action.level.instanceContract
}/${withoutExtension(action.level.instanceContract)}.json`),
require(`../contracts/out/${action.level.instanceContract
}/${withoutExtension(action.level.instanceContract)}.json`),
{
from: state.player.address,
gasPrice: 2 * state.network.gasPrice,
Expand Down
6 changes: 3 additions & 3 deletions client/src/utils/contractutil.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as constants from "../constants";
import { getWeb3, setWeb3, getTruffleContract, getNetworkFromId } from "./ethutil";
import { newGithubIssueUrl } from "./github";
import * as LocalFactoryABI from "contracts/build/contracts/factory/LocalFactory.sol/Factory.json";
import * as LocalFactoryABI from "../contracts/out/LocalFactory.sol/Factory.json";
import { deployAdminContracts, deployAndRegisterLevel } from "./deploycontract";
var levels = require(`../gamedata/gamedata.json`).levels;

Expand Down Expand Up @@ -62,7 +62,7 @@ export function getLevelKey(levelAddress) {

export function fetchLevelABI(level) {
const contractName = level.levelContract.split(".")[0];
return require(`contracts/build/contracts/levels/${level.levelContract}/${contractName}.json`);
return require(`../contracts/out/${level.levelContract}/${contractName}.json`);
}

// write windows finction to transfer ownership to a new user
Expand Down Expand Up @@ -175,7 +175,7 @@ export const verifyContract = async (contractAddress, level, chainId) => {
if (!network.explorer || !level.verificationDetails)
return;

const contractFile = await fetch(`contracts/levels/${level.instanceContract}`);
const contractFile = await fetch(`../contracts/src/levels/${level.instanceContract}`);
const contractCode = await contractFile.text();

const headers = new Headers();
Expand Down
2 changes: 1 addition & 1 deletion client/src/utils/deploycontract.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import colors from "colors";
import * as ethutil from "../utils/ethutil.js";
import * as LocalFactoryABI from "contracts/build/contracts/factory/LocalFactory.sol/Factory.json";
import * as LocalFactoryABI from "../contracts/out/LocalFactory.sol/Factory.json";
import { getGasFeeDetails } from "../utils/ethutil.js";
import {
cacheContract,
Expand Down
2 changes: 1 addition & 1 deletion client/src/utils/ethutil.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const getTruffleContract = (jsonABI, defaults = {}) => {
// // With this, MetaMask v9 deprecation warnings are removed.
// const TruffleContract = require('@truffle/contract');

const truffleContract = TruffleContract(jsonABI);
const truffleContract = TruffleContract({ abi: jsonABI.abi, bytecode: jsonABI.bytecode.object });
if (!defaults.gasPrice) defaults.gasPrice = 2000000000;
if (!defaults.gas) defaults.gas = 2000000;
truffleContract.defaults(defaults);
Expand Down
2 changes: 1 addition & 1 deletion client/src/utils/statsContract.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ const getProxyStatsContractAddressInNetwork = (networkId) => {
}

const getStatsContract = async (proxyStatsAddress, playerAddress) => {
const statsABI = require("contracts/build/contracts/metrics/Statistics.sol/Statistics.json");
const statsABI = require("../contracts/out/Statistics.sol/Statistics.json");
let statsContract;
if (playerAddress) {
statsContract = getTruffleContract(statsABI, { from: playerAddress });
Expand Down
13 changes: 13 additions & 0 deletions contracts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Compiler files
cache/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/

# Docs
docs/

# Dotenv file
.env
66 changes: 66 additions & 0 deletions contracts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
## Foundry

**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.**

Foundry consists of:

- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools).
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network.
- **Chisel**: Fast, utilitarian, and verbose solidity REPL.

## Documentation

https://book.getfoundry.sh/

## Usage

### Build

```shell
$ forge build
```

### Test

```shell
$ forge test
```

### Format

```shell
$ forge fmt
```

### Gas Snapshots

```shell
$ forge snapshot
```

### Anvil

```shell
$ anvil
```

### Deploy

```shell
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key>
```

### Cast

```shell
$ cast <subcommand>
```

### Help

```shell
$ forge --help
$ anvil --help
$ cast --help
```
Loading
Loading