pashov-2024-10-groupcoin-m06

[M-06] Griefing the launch to UniswapV3 by creating the pool first

보고서

Summary

UniV3 풀이 이미 있는지 확인하지 않고 uniswapV3Factory.createPool를 호출하는데, 이 함수는 이미 풀이 있다면 트랜잭션을 실패시킨다. uniswapV3Factory.createPool는 누구나 자유롭게 호출할 수 있으므로, 이미 풀이 있는지를 먼저 확인하고 핸들링하도록 하자.

Keyword

uniswap v3, griefing, dos

Vulnerability

토큰을 구매하고 토큰의 가치가 일정 임계값에 도달하면 launchGroupCoinToUniswapV3를 호출할 수 있다. launchGroupCoinToUniswapV3 함수에서는 풀이 이미 있는지 확인하지 않고 uniswapV3Factory.createPool 를 호출하고 있다.

    address pool = uniswapV3Factory.createPool(address(token), WETH, config.fee);
    IUniswapV3Pool(pool).initialize(config.sqrtPriceX96);

그런데 UniswapV3Factory.createPool 에서는 풀이 이미 배포되어 있다면 트랜잭션을 취소시킨다.

 function createPool(
        address tokenA,
        address tokenB,
        uint24 fee
    ) external override noDelegateCall returns (address pool) {
        require(tokenA != tokenB);
        (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        require(token0 != address(0));
        int24 tickSpacing = feeAmountTickSpacing[fee];
        require(tickSpacing != 0);
@>      require(getPool[token0][token1][fee] == address(0));
        pool = deploy(address(this), token0, token1, fee, tickSpacing);
@>      getPool[token0][token1][fee] = pool;
        // populate mapping in the reverse direction, deliberate choice to avoid the cost of comparing addresses
        getPool[token1][token0][fee] = pool;
        emit PoolCreated(token0, token1, fee, tickSpacing, pool);
    }

따라서 악성 유저가 직접 UniswapV3Factory.createPool를 호출해 동일한 수수료 설정으로 풀을 먼저 생성하면 launchGroupCoinToUniswapV3 함수는 항상 실패하게 된다.

Impact

launchGroupCoinToUniswapV3가 기능할 수 없도록 한다.

Mitigation

다음 함수를 사용하면 풀이 이미 생성되었는지 여부와 상관없이 처리할 수 있다. 초기화된 가격 조건은 동일한지 확인하도록 하자.

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");
        }
    }
}

tags: bughunting, groupcoin, smart contract, solidity, uniswap-v3 integration, griefing attack, dos, severity medium