This project is dedicated to learning and building upgradable smart contracts on Ethereum and compatible networks like Base. It serves as an educational resource for developers interested in implementing upgradeable contract patterns using Foundry and OpenZeppelin libraries. More contracts and examples will be added over time.
- What are Upgradable Contracts?
- Types of Upgradable Contracts
- Proxy Methodologies
- Learning Modules
- Examples Included
- Study Resources
- Project Structure
- Getting Started
- Contributing
- License
Upgradable contracts allow smart contracts to be modified or enhanced after deployment, which is crucial since blockchain data is immutable but contract logic can be updated through proxy patterns. This enables fixing bugs, adding features, and improving functionality without losing the contract's state or address.
Key benefits:
- Bug Fixes: Correct vulnerabilities post-deployment
- Feature Additions: Implement new functionalities
- Optimization: Improve gas efficiency or performance
- Maintenance: Adapt to changing requirements
- Uses a proxy contract that delegates calls to an implementation contract
- Admin can upgrade the implementation address
- Users interact directly with the proxy
- OpenZeppelin provides
TransparentUpgradeableProxy
- Upgrade logic is embedded in the implementation contract itself
- More gas-efficient than transparent proxies
- Implementation contract contains the upgrade function
- OpenZeppelin provides
UUPSUpgradeable
- Uses a beacon contract that points to the current implementation
- Multiple proxies can share the same beacon
- Allows upgrading multiple contracts simultaneously
- Useful for factory patterns
- Multi-faceted proxy supporting multiple implementation contracts
- Allows selective upgrades of specific functions
- More complex but highly flexible
- Enables modular contract design
- Proxies use
delegatecallto execute implementation logic in the proxy's context - State is stored in the proxy contract
- Implementation contracts should not have constructors that initialize state
- Critical to maintain compatible storage layouts between versions
- Use inheritance and careful slot management
- OpenZeppelin provides utilities for storage gaps
- Use
initializermodifier instead of constructors - Prevents re-initialization attacks
- Ensure proper access control for upgrade functions
- Implement timelocks for upgrades
- Use multi-signature wallets for admin functions
- Thoroughly test upgrade mechanisms
- Consider using formal verification
This project is structured as a progressive learning path for understanding upgradable contracts:
- Delegatecall Mechanism: Understanding how proxies execute logic in their own context
- Storage Layout: Critical concepts for maintaining state compatibility
- Initialization Patterns: Using
initializerinstead of constructors
- SmallProxy.sol: A minimal proxy contract demonstrating core concepts
- DelegateCallExample.sol: Practical examples of delegatecall usage
- State Management: How to handle contract state across upgrades
- Transparent Proxy Pattern: Using
TransparentUpgradeableProxy - UUPS Pattern: Implementing
UUPSUpgradeablefor gas efficiency - Beacon Pattern: Understanding shared implementation contracts
- Box Contract Example: V1 to V2 upgrade demonstration
- Factory Patterns: Creating multiple upgradeable instances
- Access Control: Admin roles and upgrade permissions
- Common Vulnerabilities: Avoiding upgrade-related security issues
- Testing Strategies: Comprehensive testing for upgradeable contracts
- Audit Considerations: What auditors look for in upgradeable systems
- SmallProxy.sol: Minimal proxy implementation for learning delegatecall
- DelegateCallExample.sol: Demonstrates delegatecall functionality and state management
- BoxV1.sol & BoxV2.sol: Complete upgrade example showing version transitions
- Integration with
openzeppelin-contracts-upgradeable - Foundry upgrades plugin (
openzeppelin-foundry-upgrades) - Deployment and verification scripts
- Unit tests for all contract functionality
- Upgrade-specific test scenarios
- Security-focused test cases
- OpenZeppelin Upgrades Documentation
- EIP-1967: Proxy Storage Slots
- EIP-1822: Universal Upgradeable Proxy Standard
- Beginner: Start with SmallProxy.sol and DelegateCallExample.sol
- Intermediate: Study BoxV1/V2 upgrade pattern
- Advanced: Implement custom upgradeable contracts with security considerations
- Delegatecall vs Call: Understanding execution contexts
- Storage Slots: EIP-1967 standard for proxy storage
- Initialization Guards: Preventing re-initialization attacks
- Upgrade Authorization: Who can upgrade and when
- "Mastering Ethereum" - Chapter on Upgradeable Contracts
- OpenZeppelin blog posts on proxy patterns
- Security audits of major upgradeable contract deployments
├── src/
│ ├── BoxV1.sol # Version 1 of upgradeable Box contract
│ ├── BoxV2.sol # Version 2 with additional functionality
│ └── sublesson/
│ ├── SmallProxy.sol # Basic proxy implementation
│ └── DelegateCallExample.sol # Delegatecall demonstration
├── test/
│ └── ... # Comprehensive test suites
├── script/
│ └── ... # Deployment and upgrade scripts
├── lib/
│ ├── forge-std/ # Foundry standard library
│ ├── openzeppelin-contracts/ # Standard OpenZeppelin contracts
│ ├── openzeppelin-contracts-upgradeable/ # Upgradeable contract variants
│ └── openzeppelin-foundry-upgrades/ # Foundry upgrade tools
├── foundry.toml # Foundry configuration
├── remappings.txt # Import remappings
├── commit-upgradable-contracts.ps1 # Git commit automation script
└── README.md # This comprehensive guide
- Clone the repository:
git clone <repository-url>
cd upgradable-contract-genesis- Install dependencies:
forge installThis will install:
forge-std: Foundry's standard testing libraryopenzeppelin-contracts: Standard OpenZeppelin contractsopenzeppelin-contracts-upgradeable: Upgradeable variants of OpenZeppelin contractsopenzeppelin-foundry-upgrades: Foundry-specific upgrade tools
forge buildforge testforge fmtforge snapshotStart a local Ethereum node:
anvilDeploy using scripts in the script/ directory:
forge script script/YourScript.s.sol --rpc-url <your_rpc_url> --private-key <your_private_key>-
Start with Fundamentals:
- Read the theoretical sections above (What are Upgradable Contracts?, Types, Methodologies)
- Study
src/sublesson/SmallProxy.sol- understand the basic proxy mechanism - Examine
src/sublesson/DelegateCallExample.sol- learn about delegatecall
-
Practice with Examples:
- Analyze
src/BoxV1.solandsrc/BoxV2.sol- see a complete upgrade scenario - Run the tests to understand expected behavior
- Try modifying the contracts and see how upgrades work
- Analyze
-
Deep Dive into OpenZeppelin:
- Explore the upgradeable contracts in
lib/openzeppelin-contracts-upgradeable/ - Study the Foundry upgrade tools in
lib/openzeppelin-foundry-upgrades/ - Implement your own upgradeable contract using these libraries
- Explore the upgradeable contracts in
-
Testing & Security:
- Review the test files to understand testing patterns for upgradeable contracts
- Learn about common security pitfalls and how to avoid them
- Experiment: Modify the example contracts and observe the results
- Test Thoroughly: Always run tests after making changes
- Read the Code: Each contract includes detailed comments explaining the concepts
- Build Incrementally: Start simple and gradually add complexity
- Why do we need proxies instead of direct upgrades?
- What happens to contract state during upgrades?
- How does delegatecall differ from regular calls?
- What are the security implications of each pattern?
Contributions are welcome! This is a learning project, so feel free to:
- Add new upgradable contract examples
- Improve documentation
- Submit bug fixes
- Enhance test coverage
Please ensure all code follows Solidity best practices and includes comprehensive tests.
This project is licensed under the MIT License - see the LICENSE file for details.