Resource Notes
Use, understand, and apply the model
Key takeaway
Four upgrade patterns: No Upgrade (immutable — maximum trust, no bug fixes), Transparent Proxy (EIP-1967 — robust admin/user conflict resolution), UUPS Proxy (EIP-1822 — recommended for new projects, best gas efficiency), Diamond (EIP-2535 — unlimited contract size, granular upgrades). All production upgrades require a timelock (minimum 48 hours). UUPS is OpenZeppelin's recommended default for new deployments.
Pattern 1: No Upgrade (Immutable)
Use when: Protocol parameters are well-defined and unlikely to change. Protocol seeks maximum trust (no admin keys). Protocol is simple enough that bugs are unlikely. Examples: Uniswap V2 core contracts, WETH, most token contracts. Pros: Maximum trustlessness, no upgrade risk, gas-efficient (no proxy overhead). Cons: Bugs cannot be fixed, features cannot be added.
Pattern 2: Transparent Proxy (EIP-1967)
Use when: Protocol needs upgradeability, multiple implementation contracts, or the admin and user function selectors may conflict. Pros: Robust against admin/user selector conflicts, standard and well-audited. Cons: Admin cannot call implementation functions, slightly more gas than UUPS.
Pattern 3: UUPS Proxy (EIP-1822)
Use when: Most new DeFi protocols. Best balance of upgradeability and gas efficiency. Pros: More gas efficient (upgrade logic in implementation, not permanent proxy overhead), implementation can be self-destroyed to make protocol immutable. Cons: If `_authorizeUpgrade` has a bug, the protocol can become permanently unupgradeable.
Pattern 4: Diamond (EIP-2535)
Use when: Protocol logic exceeds 24KB contract size limit. Protocol needs many modular functions that can be added/removed independently. Very complex protocol with multiple facets. Pros: Unlimited contract size, granular upgrades. Cons: Significantly more complex, harder to audit, higher gas overhead.
Upgrade Timelock (Required for Production)
import "@openzeppelin/contracts/governance/TimelockController.sol";
TimelockController timelock = new TimelockController(
48 hours, // Min delay
[multisig], // Proposers
[multisig, address(0)], // Executors
address(0)
);