This repository contains my solutions to several Solidity & EVM based CTFs and challenges.
- Ethernaut | My Writeups
- (1) Fallback
- (2) Fallout
- (3) CoinFlip
- (4) Telephone
- (5) Token
- (6) Delegation
- (7) Force
- (8) Vault
- (9) King
- (10) Reentrancy
- (11) Elevator
- (12) Privacy
- (13) Gatekeeper One
- (14) Gatekeeper Two
- (15) Naught Coin
- (16) Preservation
- (17) Recovery
- (18) Magic Number
- (19) Alien Codex
- (20) Denial
- (21) Shop
- (22) Dex One
- (23) Dex Two
- (24) Puzzle Wallet
- (25) Motorbike
- (26) Double Entry Point
- (27) Good Samaritan
- (28) Gatekeeper Three
- QuillCTF | My Writeups
- EVM Puzzles | My Writeups
- (*) Puzzles
- More EVM Puzzles | My Writeups
- (*) More Puzzles
Just yarn
to install required packages (or npm install
). Then, you can do any of the following:
yarn compile
to compile the contracts.yarn test
to run all the challenges. You can pass in--grep <regex>
option to run specific tests, such as:yarn test --grep Ethernaut
runs all Ethernaut tests.yarn test --grep "QuillCTF 1"
runs the 1st challenge of QuillCTF.yarn test --grep "Delegation"
runs the Delegation challenge of Ethernaut. If there are multiple challenges with the same name, all will run.
yarn lint
to lint TypeScript.yarn node:start
to start a Hardhat node onlocalhost
.yarn node:run <path>
to run a script at the given path onlocalhost
.
Put your tests under the test
folder, put the contracts within contracts
folder and put your write-ups under docs
folder. I prefer having a separate folder for each CTF. You can also find some utility functions under utils
folder, such as increasing the block-time, reading storage slots, or setting the balance of an account.
Each test has the following format:
describe('<ctf name> <number>: <challenge name>', () => {
// accounts
let owner: SignerWithAddress;
let attacker: SignerWithAddress;
// contracts
let contract: SomeContract; // types via typechain
// constants
const CONSTRUCTOR_ARG = '0xBEEF';
// pre-hack setups
before(async () => {
[owner, attacker] = await ethers.getSigners();
contract = await ethers.getContractFactory('SomeContract', owner).then(f => f.deploy(CONSTRUCTOR_ARG));
});
// the pawnage commences
it('should hack the contract', async () => {});
// post-hack checks
after(async () => {
expect(attacker.address).to.be.properAddress;
expect(true).to.be.true;
});
});
If your test requires directly changing the balance of an account, make sure you reset the balance after the tests.