# Contract Upgrade Pattern Comparison ## Quick Overview of Three Mainstream Patterns | Pattern | Gas (Deploy) | Gas (Call) | Upgrade Authority | Complexity | Recommended Use Case | |------|------------|------------|---------|--------|---------| | **Transparent Proxy** | High | Medium | Admin account | Medium | General projects | | **UUPS** | Low | Low | Implementation contract | Low | **First choice for new projects** | | **Diamond (EIP-2535)** | Very High | Low | Custom | Very High | Large-scale protocols | --- ## 1. Transparent Proxy (Most Traditional) **Principle:** Proxy contract intercepts all calls; admin calls go to proxy management functions, other calls delegatecall to implementation contract. ```solidity // Deploy const proxy = await upgrades.deployProxy(MyContractV1, [initArgs], { kind: 'transparent' }) // Upgrade await upgrades.upgradeProxy(proxy.address, MyContractV2) ``` **Drawbacks:** - Every call must check if msg.sender is admin (extra gas) - Admin and regular users must be different addresses (otherwise admin operations cannot delegate) --- ## 2. UUPS (Recommended) **Principle:** Upgrade logic is placed in the **implementation contract**, proxy itself is minimal (only does delegatecall). ```solidity import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; contract MyContractV1 is UUPSUpgradeable, OwnableUpgradeable { /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } function initialize(address initialOwner) public initializer { __Ownable_init(initialOwner); __UUPSUpgradeable_init(); } // Control who can upgrade function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} } ``` ```bash # Deploy const proxy = await upgrades.deployProxy(MyContractV1, [owner], { kind: 'uups' }) # Upgrade await upgrades.upgradeProxy(proxy.address, MyContractV2) ``` **Advantages:** - Smaller proxy contract (lower deployment gas) - Shorter call path (lower gas) - More flexible upgrade authority **Risk:** Implementation contract must include `_authorizeUpgrade`, otherwise upgrades are permanently locked. --- ## 3. Diamond Pattern (EIP-2535) **Principle:** One proxy + multiple facets (functional modules), each selector routes to a different facet. Breaks through the 24KB contract size limit. ```solidity // Diamond structure Diamond (proxy) └── DiamondCut Facet // Manage facet addition/removal/modification └── DiamondLoupe Facet // Query current facet information └── Ownership Facet // Permission management └── YourFeature Facet // Business functionality └── ... ``` **Use Cases:** - Contract logic exceeds 24KB limit - Need fine-grained feature upgrades (upgrade only specific modules) - Long-term large-scale protocols (Aavegotchi, etc.) **Not recommended for:** Regular projects, complexity cost far exceeds benefits. --- ## Storage Layout Safety **Rules that must be followed:** ```solidity // V1 contract MyContractV1 { uint256 public value; // slot 0 address public owner; // slot 1 } // ✅ V2 can only append at the end contract MyContractV2 is MyContractV1 { uint256 public newValue; // slot 2 (safe) } // ❌ Never insert in the middle contract MyContractV2BAD { uint256 public newValue; // slot 0 (will overwrite value!) uint256 public value; // slot 1 (will overwrite owner!) } ``` **Validation tools:** ```bash npx hardhat check --network mainnet # hardhat-upgrades automatically checks storage compatibility ``` ## Upgrade Best Practices 1. Fully test on fork testnet before upgrading 2. Use multisig (Gnosis Safe) to manage upgrade authority, not EOA 3. Set timelock (24-48h delay) to give community reaction time 4. Immediately verify new implementation contract after each upgrade