function _createPosition2(PoolKey calldata key, uint160 sqrtRatioX96, uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint128 liquidity) internal { // Calculate amounts of each token // I can't use LiquidityAmounts.getAmountsForLiquidity because the function does not exist in the v4 // library. See below links, there is a discrepency between the source and the documentation: // https://docs.uniswap.org/contracts/v4/reference/core/libraries/liquidity-amounts // https://github.com/Uniswap/v4-periphery/blob/main/src/libraries/LiquidityAmounts.sol // In any case, I included my own implementation in my utils contract: // http://etherscan.io/address/0xC97B6F6DC641C0f59c724657B91569aF00C28B7D // It's based on the UniswapV3 code so I'm pretty sure it works fine. (uint256 amount0, uint256 amount1) = utils.getAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, liquidity); // This function reverts if the amounts of the tokens are less than amount0 and amount1 by the end of the call // From this, we can be sure that we have enough of the tokens, assuming that amount0 and amount1 are correct obtainTokens(Currency.unwrap(key.currency0), Currency.unwrap(key.currency1), amount0, amount1); // Grant allowance to the position manager if (Currency.unwrap(key.currency0) != address(0)) permit2.approve(Currency.unwrap(key.currency0), address(v4PositionManager), uint160(amount0), 0); permit2.approve(Currency.unwrap(key.currency1), address(v4PositionManager), uint160(amount1), 0); // Based on code from: // https://docs.uniswap.org/contracts/v4/quickstart/manage-liquidity/mint-position // https://docs.uniswap.org/contracts/v4/guides/position-manager bytes memory actions = abi.encodePacked( uint8(Actions.MINT_POSITION), uint8(Actions.SETTLE_PAIR) ); bytes[] memory params = new bytes[](2); unchecked { params[0] = abi.encode( key, (TickMath.getTickAtSqrtPrice(sqrtRatioAX96) / key.tickSpacing) * key.tickSpacing, (TickMath.getTickAtSqrtPrice(sqrtRatioBX96) / key.tickSpacing) * key.tickSpacing, (liquidity * 1015) >> 10, // reduces the inserted liquidity a bit, just in case we barely don't have enough amount0, amount1, address(this), abi.encode(address(this)) ); } params[1] = abi.encode(key.currency0, key.currency1); unchecked { uint256 deadline = block.timestamp + 60; uint256 valueToPass = key.currency0.isAddressZero() ? amount0 : 0; // Our revert happens inside this call, apparently before SETTLE_PAIR is ever reached: v4PositionManager.modifyLiquidities{value: valueToPass}(abi.encode(actions, params), deadline); } }