LEARN SOLIDITY DEEP DIVE
Learn Solidity: From Smart Contracts to DeFi Pioneer
Goal: Deeply understand Solidity and the mindset required for smart contract development—from the fundamentals of the Ethereum Virtual Machine (EVM) to building secure, gas-efficient, and complex decentralized applications.
Why Learn Solidity?
Solidity is not just another programming language; it’s a language for creating “world computers.” Unlike traditional programs that run on a single server, Solidity contracts run on the Ethereum blockchain, a decentralized network of thousands of computers that all execute the same code and agree on the same state. This paradigm shift introduces unique challenges and opportunities.
Learning Solidity teaches you to think about:
- Code as Law: Deployed contract code is immutable and its execution is final. Bugs can have catastrophic financial consequences.
- Economics of Computation: Every single operation costs money (gas). Optimization is not about speed, but about cost-effectiveness.
- Adversarial Environment: Your public contracts will be probed and attacked by anonymous actors around the world. Security is not a feature; it is the prerequisite.
- Decentralized State: You are building systems that manage value and state without a central authority.
After completing these projects, you will:
- Understand the EVM and its constraints.
- Write, test, and deploy secure and optimized smart contracts.
- Master core DeFi concepts by building them yourself.
- Know how to use professional tools like Foundry, Hardhat, and Ethers.js.
- Think like a smart contract auditor, spotting potential vulnerabilities.
Core Concept Analysis
The Smart Contract Paradigm
┌─────────────────────────────────────────────────────────────────────────┐
│ SOLIDITY SOURCE CODE (.sol) │
│ │
│ contract SimpleBank { │
│ mapping(address => uint) public balances; │
│ function deposit() public payable { │
│ balances[msg.sender] += msg.value; │
│ } │
│ } │
└─────────────────────────────────────────────────────────────────────────┘
│
▼ The Solidity Compiler (solc)
┌─────────────────────────────────────────────────────────────────────────┐
│ EVM BYTECODE + ABI │
│ │
│ Bytecode: 608060405234801561001057600080fd5b... (for the machine) │
│ ABI: [{"constant":false,"inputs":[],"name":"deposit"...}] (for humans)│
│ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼ Deployment Transaction
┌─────────────────────────────────────────────────────────────────────────┐
│ ETHEREUM BLOCKCHAIN (Global State) │
│ │
│ A new contract account is created at a specific address. │
│ The bytecode is stored in its `code` field. │
│ Its `storage` is a persistent key-value store (2^256 slots). │
│ │
└─────────────────────────────────────────────────────────────────────────┘
│
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ STATE-CHANGING │ │ READ-ONLY │ │ SECURITY & COST │
│ TRANSACTIONS │ │ CALLS │ │ (The Rules) │
│ │ │ │ │ │
│ • Sent by a user │ │ • Free to execute│ │ • Gas mechanism │
│ • Cost gas │ │ • Cannot change │ │ • Immutability │
│ • Change storage │ │ state │ │ • Re-entrancy │
│ • Are atomic │ │ • Run on one node│ │ • Overflow checks│
└──────────────────┘ └──────────────────┘ └──────────────────┘
Key Concepts Explained
1. The EVM & Gas
The Ethereum Virtual Machine (EVM) is a fully isolated, deterministic, Turing-complete state machine. Every node on the network runs it.
- Gas: Every opcode in the EVM (e.g.,
ADD,SSTORE) has a cost in “gas”. A transaction’s total fee isgas_used * gas_price. This prevents infinite loops and pays the nodes for their work.SSTORE(writing to storage) is one of the most expensive operations.
2. Storage, Memory, and Calldata
Where data lives is critical in Solidity:
storage: Persistent on the blockchain. A key-value store of 256-bit slots. Extremely expensive to write to. State variables live here.memory: Temporary, exists only during a function’s execution. Cheaper. Used for complex computations within a function.calldata: A special, read-only location for data from an external transaction. Even cheaper thanmemory.
3. Key Data Types & Patterns
address: Holds a 20-byte Ethereum account.address payablecan receive Ether.uint256/int256: The standard integer sizes, as the EVM works with a 256-bit word size.mapping(key => value): A hash table. The most common way to store user-related data (e.g.,mapping(address => uint) balances).msg.sender/msg.value: Global variables available in every function call.msg.senderis the address that called the function, andmsg.valueis the amount of Ether sent with the call.
4. Visibility: public, external, internal, private
public: Anyone can call (user or other contract). The compiler automatically creates a “getter” function for public state variables.external: Can only be called from outside the contract (by users or other contracts). Often cheaper thanpublic.internal: Can only be called from within the contract or by derived contracts.private: Can only be called from within the contract itself (not derived contracts). Note:privateonly restricts access within the EVM. All data on the blockchain is public and can be read by anyone.
5. The Checks-Effects-Interactions Pattern
A crucial security pattern to prevent re-entrancy attacks. When writing a function:
- Checks: Perform all
require()statements first (validate inputs, check permissions). - Effects: Write to your contract’s state variables (
balances[user] = 0). - Interactions: Call other contracts or send Ether.
By updating your state before the external call, you prevent the external contract from calling back into your function and exploiting an inconsistent state.
Project List
The projects are designed to build upon each other, starting with Solidity basics and moving into core DeFi and security concepts.
Project 1: The Simple Bank & Faucet
- File: LEARN_SOLIDITY_DEEP_DIVE.md
- Main Programming Language: Solidity
- Alternative Programming Languages: Vyper
- Coolness Level: Level 2: Practical but Forgettable
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 1: Beginner
- Knowledge Area: Smart Contracts / State Management
- Software or Tool: Remix IDE, Sepolia Testnet
- Main Book: “Mastering Ethereum” by A. Antonopoulos & G. Wood - Chapter 7
What you’ll build: A basic smart contract that can accept Ether deposits from users, track their balances, allow them to withdraw their funds, and a “faucet” function to get a small amount of test currency.
Why it teaches Solidity: This is the “Hello, World!” of smart contracts. It forces you to learn the most fundamental concepts: state variables (mapping), payable functions, access control (msg.sender), and security invariants (require).
Core challenges you’ll face:
- Tracking balances → maps to using the
mapping(address => uint)pattern - Accepting Ether → maps to
payablefunctions andmsg.value - Ensuring security → maps to using
requirefor access control and preventing underflows - Sending Ether → maps to the
transferorcallmethods and the Checks-Effects-Interactions pattern
Key Concepts:
- State Variables: Solidity Docs - “State Variables”
payablemodifier: Solidity by Example - “Payable”require()statement: Solidity by Example - “Require”msg.sender: Solidity Docs - “Globally Available Variables”
Difficulty: Beginner Time estimate: Weekend Prerequisites: Basic programming knowledge.
Real world outcome:
A contract deployed on a testnet. You can use a wallet like MetaMask to call your deposit function with some test Ether and see your balance update. Then, you can call withdraw to get it back.
Implementation Hints:
- Use a mapping
mapping(address => uint) public balances;to store who owns what. Thepublickeyword will automatically give you a “getter” function. - Your
depositfunction must be markedpayable. Inside it, you’ll addmsg.valuetobalances[msg.sender]. - Your
withdrawfunction should take auint amountas an argument.- Check:
require(balances[msg.sender] >= amount, "Insufficient balance"); - Effect:
balances[msg.sender] -= amount; - Interaction:
payable(msg.sender).transfer(amount);(This is the basic, safe way to send Ether).
- Check:
Learning milestones:
- You can deposit and see your balance → You understand state variables and
payablefunctions. - You can’t withdraw more than you deposited → You understand
require. - Another user can’t withdraw your funds → You understand
msg.sender. - You write a
withdrawfunction that is safe from re-entrancy → You’ve learned the Checks-Effects-Interactions pattern.
Project 2: Your Own ERC-20 Token
- File: LEARN_SOLIDITY_DEEP_DIVE.md
- Main Programming Language: Solidity
- Coolness Level: Level 3: Genuinely Clever
- Business Potential: 2. The “Micro-SaaS / Pro Tool”
- Difficulty: Level 2: Intermediate
- Knowledge Area: Token Standards / Fungible Tokens
- Software or Tool: Hardhat, OpenZeppelin Contracts
- Main Book: “Mastering Ethereum” - Chapter 8
What you’ll build: A standard, fungible token (like USDC or SHIB). You will implement the ERC-20 interface from scratch to understand every function.
Why it teaches Solidity: ERC-20 is the most widespread token standard. Building one teaches you how to implement a standard interface, a crucial skill for interoperability. You’ll also learn the approve/transferFrom pattern, a cornerstone of DeFi composability.
Core challenges you’ll face:
- Implementing an interface → maps to adhering to a standard (the EIP-20 spec)
- Managing token supply → maps to minting, burning, and tracking
totalSupply - Handling allowances → maps to the
approveandtransferFromflow, which allows contracts to spend tokens on your behalf - Emitting events → maps to logging actions (
Transfer,Approval) for off-chain applications to consume
Key Concepts:
- ERC-20 Standard: EIP-20 official specification
- Interfaces: Solidity by Example - “Interfaces”
- Events: Solidity by Example - “Events”
- Using libraries: OpenZeppelin’s ERC20 implementation (as a reference)
Difficulty: Intermediate Time estimate: Weekend Prerequisites: Project 1.
Real world outcome:
You’ll have a token that can be added to MetaMask and transferred between accounts on a testnet. You can see the Transfer events being fired in a block explorer like Etherscan.
// A snippet of the interface you will implement
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
Implementation Hints:
- You’ll need two core mappings: one for balances (
mapping(address => uint)) and one for allowances (mapping(address => mapping(address => uint))). - The
transferfunction is straightforward: decrease sender’s balance, increase receiver’s balance, emitTransferevent. - The
approvefunction is where a user (msg.sender) allows aspenderto withdraw up toamounttokens from their account. - The
transferFromfunction is called by thespenderto execute the transfer. It must check the allowance, then perform the transfer and decrease the allowance. This is how you “pay” a DeFi protocol.
Learning milestones:
- You can
transfertokens between two accounts → You’ve implemented the core logic. - You can
approvea contract and it cantransferFromyour account → You understand how DeFi protocols interact with tokens. - Your token shows up correctly in MetaMask and Etherscan → You’ve correctly implemented the metadata functions (
name,symbol,decimals). - You understand why
approvecan be risky and learn aboutsafeApprove→ You’re starting to think about security nuances.
Project 3: A Crowdfunding Platform (Kickstarter)
- File: LEARN_SOLIDITY_DEEP_DIVE.md
- Main Programming Language: Solidity
- Coolness Level: Level 3: Genuinely Clever
- Business Potential: 3. The “Service & Support” Model
- Difficulty: Level 2: Intermediate
- Knowledge Area: State Machines / DeFi
- Software or Tool: Foundry, Ethers.js
- Main Book: N/A, use online tutorials and docs.
What you’ll build: A contract that allows a project creator to raise funds. Contributors can send Ether. If the goal is met by the deadline, the creator can withdraw the funds. If not, contributors can claim a refund.
Why it teaches Solidity: This project is all about managing state transitions and time-based logic. It forces you to create a robust state machine and handle different outcomes securely, which is a common pattern in more complex applications.
Core challenges you’ll face:
- Building a state machine → maps to using an
enumfor states (Funding,Successful,Failed) andrequirestatements to control transitions - Time-based logic → maps to using
block.timestampto manage deadlines - Secure fund withdrawal → maps to the “pull-over-push” pattern to prevent issues with sending Ether
- Differentiating roles → maps to storing the project creator’s address (
owner) and ensuring only they can perform certain actions
Key Concepts:
- State Machines: Solidity by Example - “State Machine”
block.timestamp: Solidity Docs - “Globally Available Variables”- Pull-over-Push Pattern: Consensys Best Practices - “Favor Pull over Push”
Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Project 1.
Real world outcome: A deployed contract where users can contribute, and depending on the outcome, either the project owner withdraws the full amount or the contributors get their money back. You can test this entire flow using a simple script.
Implementation Hints:
- Use an
enum State { Funding, Successful, Failed }and a state variable to track the current state. - Store the
owner,goal, anddeadlineas immutable variables set in theconstructor. - The
contributefunction should only work whenstate == State.Funding. - Create a
checkIfFundingCompletefunction that can be called after the deadline. It will checkaddress(this).balance >= goaland transition the state toSuccessfulorFailed. - Crucially, for refunds, don’t loop through contributors and send them Ether (a “push” pattern). Instead, create a
claimRefundfunction that allows each contributor to “pull” their funds back individually. This is safer and more gas-efficient.
Learning milestones:
- You can contribute, and the contract balance increases → Basic state change works.
- The contract correctly transitions from
FundingtoSuccessfulorFailedafter the deadline → Your state machine and time-based logic work. - The project owner can withdraw funds only if the goal was met → You’ve secured the owner’s function.
- Contributors can get a refund only if the goal failed → You’ve secured the contributors’ function and implemented the pull-over-push pattern.
Project 4: An NFT Collection (ERC-721)
- File: LEARN_SOLIDITY_DEEP_DIVE.md
- Main Programming Language: Solidity
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 4. The “Open Core” Infrastructure
- Difficulty: Level 2: Intermediate
- Knowledge Area: Token Standards / NFTs / Digital Identity
- Software or Tool: Hardhat, OpenZeppelin Contracts, IPFS
- Main Book: “Mastering Ethereum” - Chapter 8
What you’ll build: A generative art NFT collection where users can mint a unique token. Each token will have metadata (image, attributes) stored on IPFS.
Why it teaches Solidity: It introduces you to non-fungible tokens, the backbone of digital collectibles and identity. You’ll learn how to manage ownership of unique assets, associate them with off-chain data (like images), and handle the “minting” process.
Core challenges you’ll face:
- Implementing ERC-721 → maps to understanding the NFT standard and its differences from ERC-20
- Tracking ownership of unique IDs → maps to
mapping(uint256 => address)for owners - On-chain vs. Off-chain data → maps to understanding that you don’t store images on-chain, but a link (URI) to them
- Writing a secure minting function → maps to controlling supply, setting a price, and preventing exploits
Key Concepts:
- ERC-721 Standard: EIP-721 official specification
- IPFS (InterPlanetary File System): A decentralized storage system perfect for NFT metadata.
- URI and JSON Metadata: The ERC-721 metadata standard.
- Using Libraries: OpenZeppelin’s ERC721 contracts are the industry standard and a great reference.
Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Project 2.
Real world outcome: You’ll have an NFT collection visible on a marketplace like OpenSea (on a testnet). You can mint an NFT to your wallet, see the image associated with it, and transfer it to a friend.
Implementation Hints:
- You can inherit from OpenZeppelin’s
ERC721.solcontract to get most of the functionality for free. Your job is to write the custommintfunction. - First, create your images and JSON metadata files. A simple format is
1.json,2.json, etc. Upload the folder containing these files to a service like Pinata, which pins them to IPFS. You’ll get a single folder hash (a CID). - In your contract, store this IPFS CID as a
baseURI. - Your
tokenURI(uint256 tokenId)function (which you’ll override) will then simply return the string concatenation ofbaseURIandtokenId.toString(). - Your
safeMintfunction should:- Check that the max supply hasn’t been reached.
- Require the user to pay the correct minting fee (
require(msg.value == mintPrice)). - Call the internal
_safeMint(to, tokenId)function from the OpenZeppelin contract.
Learning milestones:
- You can mint an NFT to your address → Your
safeMintfunction and contract setup work. - The NFT shows up on OpenSea with the correct image and attributes → Your
tokenURIand metadata are set up correctly. - You can transfer the NFT to another wallet → The core ERC-721 ownership logic is working.
- You implement features like a presale list or a Dutch auction → You are building on the basic minting mechanic with more advanced strategies.
Project 5: A Multi-Signature Wallet
- File: LEARN_SOLIDITY_DEEP_DIVE.md
- Main Programming Language: Solidity
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 3. The “Service & Support” Model
- Difficulty: Level 3: Advanced
- Knowledge Area: Security / Access Control / Wallets
- Software or Tool: Foundry
- Main Book: N/A, reference Gnosis Safe’s architecture.
What you’ll build: A smart contract wallet that holds funds and requires M-of-N owners to approve a transaction before it can be executed. For example, requiring 2-of-3 signatures.
Why it teaches Solidity: This is a pure security and logic project. It’s how DAOs and companies manage their treasuries. It teaches you how to design a secure, multi-step workflow on-chain and how to protect against unauthorized access to critical functions.
Core challenges you’ll face:
- Managing owners and confirmations → maps to complex state management with multiple mappings and structs
- A two-step transaction process → maps to proposing a transaction vs. executing it
- Ensuring transaction integrity → maps to preventing replay attacks on signatures or proposals
- Executing arbitrary transactions → maps to using
address.call{value: ...}(data)to interact with any other contract
Key Concepts:
- Access Control Design: More advanced than a simple
onlyOwnermodifier. - Structs: Solidity by Example - “Structs”
- Executing External Calls: Solidity Docs - “address.call”
- Hashing for Integrity: Using
keccak256to create a unique ID for each proposed transaction.
Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Projects 1 & 3.
Real world outcome: A deployed wallet that you can send Ether to. To get the Ether out, you (and at least one other owner) must individually sign and submit an approval transaction. Only after enough approvals are collected can the transaction be executed.
Implementation Hints:
- In the
constructor, set the list ofownersand therequirednumber of signatures. - You’ll need a
struct Transactionto store the destination, value, data, and execution status. Store these in an arrayTransaction[] public transactions;. - You’ll also need a mapping
mapping(uint => mapping(address => bool)) public confirmations;to track who has confirmed which transaction index. - The
submitTransactionfunction allows an owner to propose a new transaction. It adds it to the array and returns its index. - The
confirmTransactionfunction allows other owners to approve a transaction index. It should check that the caller is an owner and hasn’t already confirmed. - The
executeTransactionfunction can be called by anyone. It will:- Check that the transaction has enough confirmations.
- Check that the transaction hasn’t already been executed.
- Mark the transaction as executed.
- Use
address(tx.to).call{value: tx.value}(tx.data).
Learning milestones:
- An owner can submit a transaction proposal → The basic data structures are working.
- Other owners can confirm the transaction, and the confirmation count increases → State is being updated correctly.
- The transaction cannot be executed with too few confirmations → Your core security logic is holding up.
- A fully confirmed transaction executes successfully and can’t be executed again → The full lifecycle is complete and secure.
Project 6: Exploiting and Fixing a Re-entrancy Vulnerability
- File: LEARN_SOLIDITY_DEEP_DIVE.md
- Main Programming Language: Solidity
- Coolness Level: Level 5: Pure Magic (Super Cool)
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 3: Advanced
- Knowledge Area: Security / Smart Contract Auditing
- Software or Tool: Foundry or Hardhat
- Main Book: “Mastering Ethereum” - Chapter 9 (Security)
What you’ll build: You will build two contracts. First, a simple EtherStore contract that is deliberately vulnerable to a re-entrancy attack. Second, an Attack contract that exploits this vulnerability to drain all the Ether from EtherStore. Finally, you will fix the EtherStore contract.
Why it teaches Solidity: It forces you to think like an attacker. Understanding how vulnerabilities are exploited is the best way to learn how to prevent them. This project will burn the Checks-Effects-Interactions pattern into your brain forever.
Core challenges you’ll face:
- Writing a vulnerable
withdrawfunction → maps to violating the Checks-Effects-Interactions pattern - Using a fallback/receive function for the attack → maps to understanding how a contract reacts when it receives Ether
- The re-entrant call → maps to the core logic of the attack, where the fallback function calls
withdrawagain before the first call completes - Fixing the vulnerability → maps to applying the Checks-Effects-Interactions pattern or using a re-entrancy guard
Key Concepts:
- Re-entrancy: Consensys Best Practices - “Re-entrancy”
- Fallback/Receive functions: Solidity by Example - “Fallback”
- Security Mindset: Thinking about worst-case scenarios.
- Re-entrancy Guards: OpenZeppelin’s
ReentrancyGuard.sollibrary.
Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Project 1.
Real world outcome:
You will write a test (using Foundry or Hardhat) that proves your Attack contract can drain the EtherStore contract. Then, you will modify EtherStore, and the same test will now fail, proving your fix works.
Implementation Hints:
- Vulnerable
EtherStore.sol:- Have a
depositfunction. - Write a
withdrawfunction that does the interaction before the effect:- Look up user’s balance.
- Send the Ether via
msg.sender.call{value: amount}(""). - Then, set the user’s balance to 0. This is the bug.
- Have a
Attack.sol:- Write an
attackfunction that deposits a small amount intoEtherStoreand then callsEtherStore.withdraw(). - Implement a
receive()payable function. This function will be triggered whenEtherStoresends Ether to yourAttackcontract. - Inside
receive(), add the logic to callEtherStore.withdraw()again if theEtherStorestill has a balance.
- Write an
- The Fix:
- Rewrite the
withdrawfunction inEtherStoreto follow Checks-Effects-Interactions. - Alternatively, add a
bool internal locked;flag and a modifiernonReentrantthat sets it totrueat the start of the function andfalseat the end, and requires it to befalseto enter.
- Rewrite the
Learning milestones:
- Your
EtherStorecontract can be drained by yourAttackcontract → You have successfully executed a re-entrancy attack. - You understand exactly why the re-entrant call works → You can trace the execution flow of the attack.
- You fix
EtherStoreby reordering the operations → You have learned the Checks-Effects-Interactions pattern by fixing a real bug. - You fix
EtherStoreagain using a re-entrancy guard modifier → You have learned how to use libraries to implement common security patterns.
Project 7: Upgradable Contracts (Proxy Pattern)
- File: LEARN_SOLIDITY_DEEP_DIVE.md
- Main Programming Language: Solidity
- Coolness Level: Level 5: Pure Magic (Super Cool)
- Business Potential: 4. The “Open Core” Infrastructure
- Difficulty: Level 4: Expert
- Knowledge Area: Contract Architecture / Proxies /
delegatecall - Software or Tool: Hardhat Upgrades plugin, Foundry
- Main Book: N/A, use OpenZeppelin docs and blog posts.
What you’ll build: A simple ERC-20 token contract that is upgradable. You will deploy a “proxy” contract and a “logic” contract (V1). Then you will deploy a V2 of the logic contract with a new function, and “upgrade” the proxy to point to V2, all without losing the token balances.
Why it teaches Solidity: It solves the “immutability” problem. Real-world projects need to fix bugs and add features. This project teaches the advanced but essential proxy pattern, which uses a low-level opcode called delegatecall to separate a contract’s state from its logic.
Core challenges you’ll face:
- Understanding
delegatecall→ maps to the core of the proxy pattern; it runs another contract’s code in the current contract’s context (storage,msg.sender) - Proxy contract logic → maps to a minimal contract that forwards all calls to a logic contract address
- Storage layout compatibility → maps to the most dangerous part: ensuring that V2 of your contract declares state variables in the same order as V1 to avoid corrupting storage
- The upgrade process → maps to calling a function on the proxy to change the logic contract address
Key Concepts:
- Proxy Patterns (UUPS, Transparent): OpenZeppelin Docs - “Proxy Patterns”
delegatecall: Solidity Docs - “delegatecall”- Storage Slots: The underlying key-value layout of contract storage.
Difficulty: Expert Time estimate: 2-3 weeks Prerequisites: Deep understanding of Solidity, especially storage. Project 2.
Real world outcome:
- You deploy
TokenV1and aProxypointing to it. You mint tokens to yourself. Your balance is stored in theProxy’s storage. - You deploy
TokenV2(which might have a newburnMyTokens()function). - You call an
upgradeTo(tokenV2Address)function on theProxy. - You can now call the new
burnMyTokens()function on theProxyaddress, and your token balance is still intact.
Implementation Hints:
- Use the OpenZeppelin Upgrades plugins for Hardhat or Foundry. They handle most of the complexity and safety checks. Your goal is to understand what they are doing under the hood.
- Proxy.sol: The core logic is a
fallback()function that gets the logic address and uses inline assembly todelegatecall. - LogicV1.sol: A normal ERC-20 contract, but its
constructoris replaced by aninitializefunction. This is because the constructor logic needs to run in the context of the proxy, not when the logic contract is deployed. - LogicV2.sol: A copy of V1, but with a new function added. Crucially, do not change the order or type of the existing state variables. You can only add new ones at the end.
Learning milestones:
- You have a working Proxy and LogicV1, and can interact with it → You understand the basic setup.
- You successfully upgrade the proxy to point to LogicV2 → You have performed a contract upgrade.
- You can call a V2 function, and the state from V1 (balances) is preserved → You have achieved a successful, state-preserving upgrade.
- You intentionally break storage layout in V3 and see the contract state get corrupted → You learn, through failure, why storage layout is the most critical part of upgradability.
Summary
| Project | Difficulty | Time | Main Concept | Main Tool |
|---|---|---|---|---|
| 1. Simple Bank | Beginner | Weekend | State, payable, require |
Remix |
| 2. ERC-20 Token | Intermediate | Weekend | Token Standards, Events | Hardhat |
| 3. Crowdfunding | Intermediate | 1-2 weeks | State Machines, Time | Foundry |
| 4. NFT Collection | Intermediate | 1-2 weeks | ERC-721, Off-chain data | Hardhat/IPFS |
| 5. Multi-Sig Wallet | Advanced | 2-3 weeks | Security, Access Control | Foundry |
| 6. Re-entrancy Hack | Advanced | 1-2 weeks | Security, Attack Vectors | Foundry |
| 7. Upgradable Contract | Expert | 2-3 weeks | Proxies, delegatecall |
Hardhat |