Skip to main content

constructor-patterns

Constructor and initializer patterns for deploying Solidity contracts. Use when designing deployment flows, upgradeable proxies, factory patterns, or CREATE2 deployments. Covers constructor validation, initializer safety, and factory patterns.

跳到安装

来源信息

仓库
ccashwell/evm-cortex
最近来源活动
2026年4月10日 16:31
检测到的 SKILL.md 语言
英语
星标
131
分支
18

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
constructor-patterns
description
Constructor and initializer patterns for deploying Solidity contracts. Use when designing deployment flows, upgradeable proxies, factory patterns, or CREATE2 deployments. Covers constructor validation, initializer safety, and factory patterns.
# Constructor & Initializer Patterns ## Constructor for Non-Upgradeable Contracts Constructors run once at deployment. Validate all parameters and set immutables. ```solidity contract Vault { error ZeroAddress(); error InvalidFee(uint256 fee); address public immutable TOKEN; address public immutable ORACLE; uint256 public immutable FEE_BPS; uint256 public immutable DEPLOYMENT_BLOCK; constructor(address token, address oracle, uint256 feeBps) { if (token == address(0)) revert ZeroAddress(); if (oracle == address(0)) revert ZeroAddress(); if (feeBps > 10_000) revert InvalidFee(feeBps); TOKEN = token; ORACLE = oracle; FEE_BPS = feeBps; DEPLOYMENT_BLOCK = block.number; } } ``` ## Initializer for Upgradeable Contracts Proxies never call the implementation's constructor. Use `initializer` from OpenZeppelin. ```solidity import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; contract VaultV1 is Initializable, OwnableUpgradeable, PausableUpgradeable { uint256 public depositCap; address public feeRecipient; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } function initialize( address admin, address _feeRecipient, uint256 _depositCap ) external initializer { if (admin == address(0)) revert ZeroAddress(); if (_feeRecipient == address(0)) revert ZeroAddress(); __Ownable_init(admin); __Pausable_init(); feeRecipient = _feeRecipient; depositCap = _depositCap; } } ``` ### Critical: Disable Initializers in Implementation Without `_disableInitializers()`, an attacker can call `initialize()` on the implementation contract directly and potentially `selfdestruct` it (pre-Dencun) or manipulate its state. ```solidity /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } ``` ### Reinitializer for Upgrades When adding new state in V2, use `reinitializer(2)` instead of `initializer`. ```solidity contract VaultV2 is VaultV1 { uint256 public withdrawalDelay; function initializeV2(uint256 _withdrawalDelay) external reinitializer(2) { withdrawalDelay = _withdrawalDelay; } } ``` ## Immutables + Initializers Immutables can coexist with initializers. They're set in the implementation's constructor. ```solidity contract VaultImplementation is Initializable, OwnableUpgradeable { address public immutable WETH; address public immutable FACTORY; /// @custom:oz-upgrades-unsafe-allow constructor constructor(address weth, address factory) { if (weth == address(0)) revert ZeroAddress(); if (factory == address(0)) revert ZeroAddress(); WETH = weth; FACTORY = factory; _disableInitializers(); } function initialize(address admin) external initializer { __Ownable_init(admin); } } ``` ## Factory Patterns ### Minimal Clone Factory (EIP-1167) Cheapest deployment for contracts with identical logic but different state. ```solidity import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; contract VaultFactory { using Clones for address; address public immutable IMPLEMENTATION; event VaultCreated(address indexed vault, address indexed admin); constructor(address implementation) { IMPLEMENTATION = implementation; } function createVault(address admin, bytes32 salt) external returns (address vault) { vault = IMPLEMENTATION.cloneDeterministic(salt); IVault(vault).initialize(admin); emit VaultCreated(vault, admin); } function predictVaultAddress(bytes32 salt) external view returns (address) { return IMPLEMENTATION.predictDeterministicAddress(salt); } } ``` ### CREATE2 Factory Deterministic addresses — compute the address before deployment. ```solidity contract Create2Factory { event Deployed(address indexed addr, bytes32 indexed salt); function deploy(bytes memory bytecode, bytes32 salt) external returns (address addr) { assembly { addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt) if iszero(addr) { revert(0, 0) } } emit Deployed(addr, salt); } function computeAddress(bytes memory bytecode, bytes32 salt) external view returns (address) { return address(uint160(uint256(keccak256(abi.encodePacked( bytes1(0xff), address(this), salt, keccak256(bytecode) ))))); } } ``` ### CREATE2 + Constructor Args ```solidity function deployWithArgs(bytes32 salt, address token, uint256 fee) external returns (address vault) { bytes memory bytecode = abi.encodePacked( type(Vault).creationCode, abi.encode(token, fee) ); assembly { vault := create2(0, add(bytecode, 0x20), mload(bytecode), salt) if iszero(vault) { revert(0, 0) } } } ``` ## Constructor vs Initializer Decision Tree ``` Is the contract upgradeable? ├── No → Use constructor + immutables └── Yes → Use initializer + _disableInitializers() │ Are there deploy-time constants (WETH, factory)? ├── Yes → Set as immutables in constructor └── No → Everything in initializer ``` ## Anti-Patterns - **Missing `_disableInitializers()`** — allows initialization of implementation - **Constructor logic in upgradeable contract** — runs on implementation, not proxy - **Initializer without reentrancy protection** — OpenZeppelin's `initializer` handles this - **Forgetting `reinitializer(n)`** for V2+ upgrades — reusing `initializer` fails silently - **Setting storage in constructor of upgradeable** — sets implementation storage, not proxy ## Checklist - [ ] Non-upgradeable: constructor sets immutables, validates all parameters - [ ] Upgradeable: `_disableInitializers()` in constructor - [ ] Upgradeable: `initializer` modifier on `initialize()` - [ ] Upgradeable V2+: `reinitializer(n)` for upgrade initialization - [ ] All address parameters checked against `address(0)` - [ ] Numeric parameters validated against bounds - [ ] Factory: deterministic deployment uses salt from meaningful context - [ ] CREATE2: address pre-computation tested before mainnet deployment
在 GitHub 查看