★ Public initialize() without initializer modifier
Uniswap (v2 + v3)'s assessment for RD-F-022 — scored green on the v1.7.0 rubric. The evidence below is the curator's reasoning for this score.
Evidence summary #
V2: UniswapV2Pair initialize() protected by `require(msg.sender == factory)` — only factory can call, in same deployment transaction. V3: UniswapV3Pool initialize() protected by `require(slot0.sqrtPriceX96 == 0, 'AI')` — cannot be re-initialized. Factory calls initialize() atomically. 5+ years, thousands of pools, zero exploit. Combined: green.
Detail #
V2 UniswapV2Pair.sol has an `initialize(address _token0, address _token1)` function gated by `require(msg.sender == factory)`. The factory deploys the pair via CREATE2 and immediately calls initialize() in the same transaction — there is no front-running window. V3 UniswapV3Pool.sol has an `initialize(uint160 sqrtPriceX96)` function that uses `require(slot0.sqrtPriceX96 == 0, 'AI')` as an equivalent re-initialization guard (since sqrtPriceX96 can only be 0 before the pool has been initialized). Source comment states 'not locked because it initializes unlocked' — deliberate design. Factory createPool() calls initialize() in the same transaction, eliminating any front-running window. Over 5 years with thousands of pools created, this pattern has never been exploited.
Sources #
- GitHubUniswapV2Pair.sol initialize() — msg.sender == factory guardUniswapV2Pair.sol initialize() with factory guardretrieved 2026-05-12
- UniswapV3Pool.sol initialize() — slot0.sqrtPriceX96 == 0 re-init protectionUniswapV3Pool.sol initialize() with sqrtPriceX96 == 0 guardretrieved 2026-05-12
Methodology #
Determine whether any implementation contract exposes `initialize(…)` without the OpenZeppelin `initializer` modifier or equivalent initialization lock.
See the full factor methodology and distribution across all protocols →