code-423n4 / 2024-06-vultisig-findings

2 stars 0 forks source link

UniswapV3 `slot0` should not be used as it can be manipulated. #180

Closed howlbot-integration[bot] closed 3 months ago

howlbot-integration[bot] commented 3 months ago

Lines of code

https://github.com/code-423n4/2024-06-vultisig/blob/main/src/ILOManager.sol#L115 https://github.com/code-423n4/2024-06-vultisig/blob/main/src/ILOManager.sol#L189

Vulnerability details

The use of slot0 to obtain sqrtPriceX96Existing and sqrtPriceX96 is heavily discouraged as it is easy to manipulate as it represents the current price rather a time weighted price. An attacker can therefore use flash loans to shift the slot0 by doing large swaps on Uniswap.

Impact

UniswapV3 slot0 should not be used in _initUniV3PoolIfNecessary and launch function in the ILOManager.sol contract as it can be manipulated.

Proof of Concept

slot0 is used to obtain sqrtPriceX96Existing and sqrtPriceX96 as seen below.

ILOManager.sol#L115

    function _initUniV3PoolIfNecessary(PoolAddress.PoolKey memory poolKey, uint160 sqrtPriceX96) internal returns (address pool) {
        pool = IUniswapV3Factory(UNIV3_FACTORY).getPool(poolKey.token0, poolKey.token1, poolKey.fee);
        if (pool == address(0)) {
            pool = IUniswapV3Factory(UNIV3_FACTORY).createPool(poolKey.token0, poolKey.token1, poolKey.fee);
            IUniswapV3Pool(pool).initialize(sqrtPriceX96);
        } else {
-->          (uint160 sqrtPriceX96Existing, , , , , , ) = IUniswapV3Pool(pool).slot0();
            if (sqrtPriceX96Existing == 0) {
                IUniswapV3Pool(pool).initialize(sqrtPriceX96);
            } else {
                require(sqrtPriceX96Existing == sqrtPriceX96, "UV3P");
            }
        }
    }

ILOManager.sol#L189

function launch(address uniV3PoolAddress) external override {
        require(block.timestamp > _cachedProject[uniV3PoolAddress].launchTime, "LT");
-->     (uint160 sqrtPriceX96, , , , , , ) = IUniswapV3Pool(uniV3PoolAddress).slot0();
        require(_cachedProject[uniV3PoolAddress].initialPoolPriceX96 == sqrtPriceX96, "UV3P");
        address[] memory initializedPools = _initializedILOPools[uniV3PoolAddress];
        require(initializedPools.length > 0, "NP");
        for (uint256 i = 0; i < initializedPools.length; i++) {
            IILOPool(initializedPools[i]).launch();
        }

        emit ProjectLaunch(uniV3PoolAddress);
    }

References : RealWagmi , Maia DAO Ecosystem

Tools Used

Manual review.

Recommended Mitigation Steps

The protocol should consider using the TWAP price.

Assessed type

Uniswap

c4-judge commented 3 months ago

alex-ppg marked the issue as unsatisfactory: Invalid