Uniswap V3仓位铸造时computeAddress计算池地址错误排查求助
Uniswap V3 铸造仓位时池地址计算不一致问题排查
在Uniswap V3开发过程中,调用NonfungiblePositionManager.sol的mint函数铸造仓位时,出现PoolAddress.computeAddress计算出的池地址与UniswapV3PoolFactory.getPool返回地址不一致的问题,导致LiquidityManagement.sol的addLiquidity方法中后续调用池合约的操作全部失败。
已完成的排查项:
- 确认代币传入顺序符合
token0 < token1的要求 - 通过Hardhat本地链日志验证,
computeAddress与getPool所用参数完全一致 - 未修改
PoolAddress.sol中的POOL_INIT_CODE_HASH常量 - 所有合约均使用0.7.6版本编译器编译
相关代码片段
NonfungiblePositionManager.sol
function mint(MintParams calldata params) external payable override checkDeadline(params.deadline) returns ( uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1 ) { IUniswapV3Pool pool; // 此处调用失败 (liquidity, amount0, amount1, pool) = addLiquidity( AddLiquidityParams({ token0: params.token0, token1: params.token1, fee: params.fee, recipient: address(this), tickLower: params.tickLower, tickUpper: params.tickUpper, amount0Desired: params.amount0Desired, amount1Desired: params.amount1Desired, amount0Min: params.amount0Min, amount1Min: params.amount1Min }) ); _mint(params.recipient, (tokenId = _nextId++)); bytes32 positionKey = PositionKey.compute(address(this), params.tickLower, params.tickUpper); (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey); // 幂等设置池ID uint80 poolId = cachePoolKey( address(pool), PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee}) ); _positions[tokenId] = Position({ nonce: 0, operator: address(0), poolId: poolId, tickLower: params.tickLower, tickUpper: params.tickUpper, liquidity: liquidity, feeGrowthInside0LastX128: feeGrowthInside0LastX128, feeGrowthInside1LastX128: feeGrowthInside1LastX128, tokensOwed0: 0, tokensOwed1: 0 }); emit IncreaseLiquidity(tokenId, liquidity, amount0, amount1); }
LiquidityManagement.sol
function addLiquidity(AddLiquidityParams memory params) internal returns ( uint128 liquidity, uint256 amount0, uint256 amount1, IUniswapV3Pool pool ) { PoolAddress.PoolKey memory poolKey = PoolAddress.PoolKey({ token0: params.token0, token1: params.token1, fee: params.fee }); // 池地址计算逻辑 pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); // 后续操作全部失败 { (uint160 sqrtPriceX96, , , , , , ) = pool.slot0(); console.log("liquidity 4"); uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(params.tickLower); console.log("liquidity 5"); uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(params.tickUpper); console.log("liquidity 6"); liquidity = LiquidityAmounts.getLiquidityForAmounts( sqrtPriceX96, sqrtRatioAX96, sqrtRatioBX96, params.amount0Desired, params.amount1Desired ); console.log("liquidity 7"); } (amount0, amount1) = pool.mint( params.recipient, params.tickLower, params.tickUpper, liquidity, abi.encode(MintCallbackData({poolKey: poolKey, payer: msg.sender})) ); require(amount0 >= params.amount0Min && amount1 >= params.amount1Min, "Price slippage check"); }
PoolAddress.sol
bytes32 internal constant POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54; function computeAddress(address factory, PoolKey memory key) internal view returns (address pool) { require(key.token0 < key.token1); pool = address( // uint160( uint256( keccak256( abi.encodePacked( hex"ff", factory, keccak256(abi.encode(key.token0, key.token1, key.fee)), POOL_INIT_CODE_HASH ) ) ) // ) ); }
问题排查与解决方向
修复
computeAddress的地址转换逻辑
你提供的PoolAddress.sol中,注释掉了uint160()的转换步骤,这是核心错误。以太坊地址是160位的,官方实现中必须将keccak256计算出的256位哈希转换为uint160后再转为地址,否则生成的地址与Factory合约createPool方法生成的地址会不一致。修复后的代码:
function computeAddress(address factory, PoolKey memory key) internal view returns (address pool) { require(key.token0 < key.token1); pool = address( uint160( uint256( keccak256( abi.encodePacked( hex"ff", factory, keccak256(abi.encode(key.token0, key.token1, key.fee)), POOL_INIT_CODE_HASH ) ) ) ) ); }验证Factory合约的一致性
确认部署的UniswapV3PoolFactory是官方未修改的版本,其内部使用的POOL_INIT_CODE_HASH必须与PoolAddress.sol中的常量完全匹配。如果Factory合约被修改过,会导致地址生成逻辑不一致。检查Pool合约的字节码哈希
如果自定义了Pool合约(哪怕是微小修改),其部署字节码的哈希会变化,需要重新计算并更新PoolAddress.sol中的POOL_INIT_CODE_HASH常量。
内容的提问来源于stack exchange,提问作者Володимир Севастьянов
相关产品推荐
相关产品推荐

