54 runnable labs — 45 Solidity labs with Foundry tests, 4 Noir zero-knowledge labs and 4 Cairo/Starknet labs — from storage basics to reentrancy, flash loans, proxies, ERC-7201, fuzzing, invariants and mainnet forks — plus a bonus Hardhat lab. Every lab compiles and passes in CI.
Built by Blockchain Lab — blockchainlab.com
git clone --recursive https://github.com/Blockchains/blockchainlab-labs && cd blockchainlab-labs
curl -L https://foundry.paradigm.xyz | bash && foundryup # if you don't have Foundry
forge test # all 45 Solidity labs (131 tests)
forge test --match-path test/L09* # one lab
forge test --match-path test/L29* -vv # mainnet fork lab (uses https://ethereum-rpc.publicnode.com or MAINNET_RPC_URL)
cd hardhat && npm ci && npx hardhat test # bonus Hardhat lab| # | Lab | You learn | Exercise | Blockchain Lab |
|---|---|---|---|---|
| 01 | SimpleStorage · test | State variables, storage slot 0, first fuzz test | Add a uint256[] history and push every value; assert its length. |
read · tool |
| 02 | Counter · test | Events, custom errors, vm.expectEmit / expectRevert | Add reset() restricted to the deployer. |
read · tool |
| 03 | Ownable · test | Access control, two-step ownership | Add a renounceOwnership with a 2-day delay. |
read |
| 04 | ERC20Scratch · test | ERC-20 from first principles, infinite allowance | Add increaseAllowance and a test for the approve front-running problem. |
read · tool |
| 05 | OZToken · test | OpenZeppelin ERC20Capped + AccessControl roles | Add a PAUSER_ROLE using ERC20Pausable. | read |
| 06 | NFT · test | ERC-721 paid mint, supply cap, tokenURI | Add a Merkle-gated presale (combine with lab 11). | read · tool |
| 07 | MultiToken · test | ERC-1155 fungible + non-fungible items, batch transfer | Add a crafting function that burns 100 GOLD to mint a SWORD. | read |
| 08 | EtherVault · test | receive(), checks-effects-interactions, pull payments | Add a per-user daily withdrawal limit. | read |
| 09 | Reentrancy · test | Reentrancy exploit end-to-end and two fixes | Write a cross-function reentrancy variant and fix it. | read |
| 10 | Arithmetic · test | Checked vs unchecked maths, rounding direction | Find an input where rounding the wrong way lets a user extract value. | read · tool |
| 11 | MerkleAirdrop · test | OpenZeppelin StandardMerkleTree allowlist claim | Build your own tree in the Tools hash page and claim with it. | read · tool |
| 12 | Permit · test | EIP-2612 / EIP-712 typed signatures, replay & expiry | Implement a depositWithPermit vault. |
read · tool |
| 13 | Signatures · test | EIP-191 signed vouchers, ecrecover via ECDSA, nonces | Add an expiry and show signature malleability is handled by ECDSA. | read |
| 14 | MultiSig · test | M-of-N wallet: submit / confirm / execute | Add revoke-confirmation and owner rotation via self-call. | read |
| 15 | Timelock · test | Queued governance actions with delay and grace | Add cancel() and emit events for off-chain monitoring. |
read |
| 16 | CommitReveal · test | Hiding choices to resist front-running | Add a deposit that is slashed if a committer never reveals. | read · tool |
| 17 | DutchAuction · test | Linear price decay, refunds | Make the decay exponential and compare gas. | read |
| 18 | EnglishAuction · test | Bidding with pull refunds (DoS-safe) | Add anti-sniping: extend the end time on late bids. | read |
| 19 | Crowdfund · test | SafeERC20, goals, deadlines, refunds | Support fee-on-transfer tokens correctly. | read |
| 20 | StakingRewards · test | Reward-per-token accumulator (Synthetix pattern) | Add a reward period end and notifyRewardAmount. |
read |
| 21 | ConstantProductAMM · test | x·y=k swaps, 0.3% fee, LP shares, slippage | Add a TWAP price oracle accumulator. | read |
| 22 | FlashLoan · test | ERC-3156 flash lender and borrower | Use a flash loan to arbitrage two lab-21 pools. | read · tool |
| 23 | UpgradeableProxy · test | UUPS + ERC-1967 proxy, initialisers, upgrade auth | Add a storage-collision bug in V2 and catch it with a test. | read · tool |
| 24 | NamespacedStorage · test | ERC-7201 namespaced storage layout | Add a second namespace and prove they never collide. | read · tool |
| 25 | StorageLayout · test | Packing, mappings, arrays, short strings via vm.load | Store a 40-byte string and decode the long-string layout. | read · tool |
| 26 | GasOptimisation · test | Packing, caching, calldata, unchecked loops | Run forge snapshot and cut another 10%. |
read · tool |
| 27 | FuzzMath · test | Property-based fuzzing finds a precision bug | Write a fuzz test that fails on afterFeeBuggy. |
read |
| 28 | InvariantVault · test | Stateful invariant testing with a handler + ghost vars | Introduce a bug in withdraw and watch the invariant catch it. | read |
| 29 | MainnetFork · test | Fork tests against real USDC / WETH via public RPC | Fork Base and test real Base USDC. | read · tool |
| 30 | Create2Factory · test | CREATE2 deterministic deployment and address prediction | Predict the address off-chain with ethers getCreate2Address. |
read · tool |
| 31 | ERC4626Vault · test | ERC-4626 vaults and the first-depositor inflation attack vs virtual shares | Show the attack is still possible with offset 0 and a bigger donation, and compute its cost. | read |
| 32 | Governance · test | OZ Governor + ERC20Votes + TimelockController full lifecycle | Add GovernorPreventLateQuorum and test a late whale vote. | read |
| 33 | Vesting · test | VestingWalletCliff: cliff + linear release | Make a factory that deploys one vesting wallet per employee. | read |
| 34 | PaymentSplitter · test | Pull-based revenue splits for ETH and ERC-20 | Add a shareholder via a 2-of-3 vote. | read |
| 35 | Royalties · test | ERC-2981 default and per-token royalties | Add a marketplace that pays royalties on sale. | read · tool |
| 36 | Soulbound · test | ERC-5192 non-transferable credentials | Add issuer revocation with an event. | read |
| 37 | OracleConsumer · test | Chainlink AggregatorV3 consumer: staleness, decimals, bad answers | Add an L2 sequencer-uptime feed check. | read · tool |
| 38 | TransientStorage · test | EIP-1153 TSTORE/TLOAD reentrancy lock | Compare gas with an SSTORE-based guard using forge snapshot. | read · tool |
| 39 | SmartAccount · test | Meta-transactions and ERC-1271 contract signatures | Add a session key with an expiry and a spend limit. | read · tool |
| 40 | MinimalProxy · test | EIP-1167 clones, initializer locking, deterministic addresses | Measure deploy gas for clone vs full deploy in a script. | read · tool |
| 41 | Sandwich · test | MEV sandwich on an AMM, and slippage limits as the defence | Find the largest victim trade 0.5% slippage still protects. | read · tool |
| 42 | SpotOracleLending · test | Spot-price oracle manipulation leading to over-borrowing | Replace the spot price with a TWAP and re-run the attack. | read · tool |
| 43 | CrossChainReplay · test | Signature replay across chains/contracts; EIP-712 domain binding | Add a deadline and test expiry. | read · tool |
| 44 | YulBasics · test | Inline assembly: sload/sstore, calldata loops, scratch-space hashing | Write transfer for an ERC-20 entirely in Yul. |
read |
| 45 | CircuitBreaker · test | Guardian pause + per-window outflow rate limit | Allow the rate limit to be raised only through the timelock (lab 15). | read |
| H1 | Counter (Hardhat) · test | Same contract as lab 02, tested with Hardhat + ethers + chai | Port lab 04 to Hardhat | read |
| N1 | Noir: hash preimage | ZK proof of knowledge of a Pedersen preimage | Switch to Poseidon via the noir-lang/poseidon library. | read |
| N2 | Noir: age check | Selective disclosure: prove age ≥ 18 from a committed credential | Add an expiry date to the credential. | read |
| N3 | Noir: Merkle membership | Anonymous allowlist membership + nullifier (Semaphore pattern) | Raise depth to 20 and measure constraint count with nargo info. |
read |
| N4 | Noir: proof of reserves | Prove committed balances ≥ liabilities without revealing them | Add per-account non-negativity and a Merkle sum tree. | read |
| C1 | Cairo: integers & felts | felt252 modular wrap vs panicking uN integers, u256 limbs | Implement checked u256 sqrt. | read |
| C2 | Cairo: ownership & traits | Linear types, snapshots, arrays, structs, traits | Add a short-position variant with an enum. | read |
| C3 | Cairo: Starknet token | Starknet contract storage, events, dispatcher tests with deploy_syscall | Add approve/transfer_from. | read |
| C4 | Cairo: Poseidon Merkle | STARK-friendly Poseidon hashing and sorted-pair Merkle proofs | Write an airdrop contract that uses verify. |
read |
Each lab = one contract in src/ and one test in test/. Read the test first — it is the spec. Then do the exercise and make your new test pass.
Foundry (forge-std v1.17), Solidity 0.8.28 (Cancun), OpenZeppelin Contracts v5.4.0, Hardhat 2 for the bonus lab. Noir 1.0.0-rc.2 for ZK labs, Scarb 2.20.1 / Cairo 2.20 for Starknet labs. Run cd noir && nargo test or cd cairo && scarb test. Sui Move and Anchor (Solana) labs are planned.
- Blockchain developer roadmap · Interview questions
- Blockchain Lab Tools — decode the txs your labs produce once deployed; compute storage slots; build Merkle trees
- Whitepaper library · Glossary · Failure atlas
Educational code — not audited, do not deploy to mainnet with real funds. MIT.
For AI agents and builders: read
AGENTS.md(setup, commands, structure, rules),llms.txt(doc map) and the machine-readableblocks.json(schema). How all Blockchains blocks fit together: Build with Blocks · org catalogue: https://blockchains.github.io/blocks.json.
What it exports
| Export | Type | Install / access |
|---|---|---|
src/L<NN>_<Name>.sol |
solidity | forge install Blockchains/blockchainlab-labs |
noir/ |
file | cd noir && nargo test |
cairo/ |
file | cd cairo && scarb test |
Minimal example (compiled and passed forge test on 2026-10-04 in a fresh Foundry project)
// forge install Blockchains/blockchainlab-labs
// remappings.txt:
// @openzeppelin/contracts/=lib/blockchainlab-labs/lib/openzeppelin-contracts/contracts/
// labs/=lib/blockchainlab-labs/src/
import {ConstantProductAMM} from "labs/L21_ConstantProductAMM.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
ConstantProductAMM amm = new ConstantProductAMM(IERC20(tokenA), IERC20(tokenB));
// approve both tokens, then:
amm.addLiquidity(1000e18, 1000e18);
uint256 out = amm.swap(IERC20(tokenA), 10e18, 0); // ≈ 9.87e18 after 0.3% fee + price impactInputs → outputs
- In:
lab contract(Solidity source) constructor args per lab - Out:
tested contract patterns(Solidity/Noir/Cairo);exercises(Markdown table in README)
Composes with
- Blockchains/blockchain-dev-roadmap: roadmap stages link to these labs
- Blockchains/blockchain-interview-questions: answers link to labs
- Blockchains/blockchainlab-tools: labs reference the hash/storage tools
- Blockchains/blockchainlab-starters: production-style starters for the same stacks
- Blockchains/forge-usd-priced-membership-nft: combine lab patterns with composed contracts
Versioning & stability: stable. Teaching code: lab file names (L<NN>_<Name>.sol) are stable, internals may change to improve clarity. Not audited; do not deploy with real value without review.
No keys needed. MAINNET_RPC_URL optionally overrides the public RPC (https://ethereum-rpc.publicnode.com) used by the mainnet fork lab (test/L29_MainnetFork.t.sol).
MIT, see LICENSE.
Issues and pull requests are welcome. Please read the contributing guide, code of conduct and security policy first.
Built by Blockchain Lab — blockchainlab.com
