fix: Security audit fixes for PoolInitializer.sol#45
Conversation
Audit findings and fixes for PoolInitializer.createAndInitializePoolIfNecessary(): - V1: Add zero-address validation for all four token parameters (token0_20, token1_20, token0_223, token1_223) to prevent pool creation with invalid tokens - V2: Add descriptive revert reason to token ordering require (was bare require) - V3: Add sqrtPriceX96 != 0 validation to prevent initializing pools with an invalid zero price - V4: Add PoolCreatedAndInitialized event for off-chain observability - V5: Restore missing Revenue_old.sol and RevenueV1.sol compiler overrides in hardhat.config.ts (pre-existing build fix) Co-authored-by: Cursor <cursoragent@cursor.com>
Calling with A Pool can not be created if a pair of addresses is not validated as a pair of "original token"->"wrapper of that token" in the Converter.
Ethereum has a 24KB contract code size limit. We can't add more plain text to the contract since it will make it surpass this limit.
The same applies to Uniswap but it works without problems. We are just using their model here.
We are stretching the bytecode size limit and that's the reason why we had to sacrifice readability. |
Security Audit Report:
PoolInitializer.solFile:
contracts/dex-periphery/base/PoolInitializer.solAuditor: AI-assisted security review
Scope:
createAndInitializePoolIfNecessary()function and its interaction withIDex223FactoryandIUniswapV3PoolExecutive Summary
The
PoolInitializerabstract contract is responsible for creating and initializing Uniswap V3-style liquidity pools in the DEX-223 ecosystem. The audit identified 5 vulnerabilities ranging from medium to low severity. All have been remediated in this PR while maintaining full backward compatibility with theIPoolInitializerinterface.Vulnerability Details
V1 - Missing Zero-Address Validation for Token Parameters (Medium)
Location:
createAndInitializePoolIfNecessary(), all four token address parametersSeverity: Medium
Status: Fixed
Description:
The original code performed no validation on
token0_20,token1_20,token0_223, ortoken1_223other than a bare ordering check (require(token0_20 < token1_20)). Sinceaddress(0)sorts lower than any non-zero address, calling withtoken0_20 = address(0)and any validtoken1_20would pass the ordering check and create a pool paired against the zero address via the factory.This could lead to:
address(0)as a token, which would be permanently unusableFor the ERC-223 addresses (
token0_223,token1_223), passingaddress(0)would silently create a pool with missing ERC-223 counterparts, breaking the dual-standard design of DEX-223.Fix:
V2 - Missing Revert Reason on Token Ordering Check (Low)
Location:
createAndInitializePoolIfNecessary(), line 21 (original)Severity: Low
Status: Fixed
Description:
The original code used a bare
require(token0_20 < token1_20)with no revert reason string. When this check fails, the transaction reverts with no diagnostic information, making it difficult for integrators and front-ends to determine why a call was rejected.Fix:
V3 - Missing sqrtPriceX96 Validation (Medium)
Location:
createAndInitializePoolIfNecessary(), thesqrtPriceX96parameterSeverity: Medium
Status: Fixed
Description:
The
sqrtPriceX96parameter was passed directly toIUniswapV3Pool.initialize()without any validation. A value of0is not a valid Q64.96 square-root price representation - it would represent a price of zero, which is mathematically undefined in the x*y=k AMM model.While the Uniswap V3 pool own
initialize()function does checksqrtPriceX96 > 0internally, relying on a downstream contract to validate inputs is a defense-in-depth violation. Validating early inPoolInitializer:PI: ZERO_PRICEvs. a generic pool revert)createPoolcall when the subsequentinitializewould revert anywayFix:
V4 - No Event Emission for Pool Creation/Initialization (Low)
Location:
createAndInitializePoolIfNecessary(), entire functionSeverity: Low (Informational)
Status: Fixed
Description:
The original function emitted no events. While the factory
createPooland the poolinitializeemit their own events, thePoolInitializercontract itself provided no observability. This makes it difficult for off-chain indexers and monitoring systems to:createAndInitializePoolIfNecessaryactually created/initialized a pool, or was a no-op (pool already existed and was already initialized)Fix:
Added a
PoolCreatedAndInitializedevent withcreatedandinitializedboolean flags:V5 - Pre-existing Build Configuration Issue (Low)
Location:
hardhat.config.tsSeverity: Low (Build)
Status: Fixed
Description:
The
hardhat.config.tswas missing compiler version overrides forcontracts/dex-periphery/Revenue_old.solandcontracts/dex-periphery/RevenueV1.sol. Both files usepragma solidity ^0.8.13but the base compiler is configured as0.7.6, causingnpx hardhat compileto fail withError HH606. This is a pre-existing issue on themainbranch, not introduced by this audit.Fix:
Added the missing overrides with
version: "0.8.19"(matching the existingTokenConverter.soloverride pattern).Files Changed
contracts/dex-periphery/base/PoolInitializer.solhardhat.config.tsBackward Compatibility
IPoolInitializerinterface is unchanged - the function signature remains identicalNonfungiblePositionManager,V3Migrator) are unaffectedrequirestatements only reject inputs that would have caused reverts downstream or produced invalid stateTest Plan
npx hardhat compilesucceeds forPoolInitializer.soland all contracts inheriting it (NonfungiblePositionManager.sol,V3Migrator.sol)createAndInitializePoolIfNecessarywithtoken0_20 = address(0)reverts withPI: ZERO_TOKEN0_20token1_20 = address(0)reverts withPI: ZERO_TOKEN1_20token0_223 = address(0)reverts withPI: ZERO_TOKEN0_223token1_223 = address(0)reverts withPI: ZERO_TOKEN1_223token0_20 > token1_20reverts withPI: TOKEN_ORDERsqrtPriceX96 = 0reverts withPI: ZERO_PRICEPoolCreatedAndInitializedwithcreated=true, initialized=truecreated=false, initialized=truecreated=false, initialized=falseNonfungiblePositionManager.spec.tstest suite (specifically the#createAndInitializePoolIfNecessarysection) to confirm no regressions