I’m working on a project calling UniSwap V3 contracts. I’ve read the whitepaper, docs and everything I can find online to solve this problem, but get nothing. So I’m here to seek help, not only to solve the problem itself but to fully understand where I miss in the documentations.
First thing is initializing the pool. When I tried to mint a new position I found the comments on the function min() stating:
/// @notice Creates a new position wrapped in a NFT
/// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized
/// a method does not exist, i.e. the pool is assumed to be initialized.
/// @param params The params necessary to mint a position, encoded as `MintParams` in calldata
/// @return tokenId The ID of the token that represents the minted position
/// @return liquidity The amount of liquidity for this position
/// @return amount0 The amount of token0
/// @return amount1 The amount of token1
function mint(MintParams calldata params)
external
payable
returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
However, there’s nowhere in the docs to find how I should initialize this pool. Luckily enough, I found a github project demonstrating this feature:
// Creates the new pool
pool = IUniswapV3Pool(
v3Factory.createPool(address(token0), address(token1), fee)
);
/*
Calculates the initial price (in sqrtPriceX96 format)
https://docs.uniswap.org/sdk/guides/fetching-prices
sqrtPriceX96 = sqrt(price) * 2 ** 96
*/
// Lets set the price to be 1000 token0 = 1 token1
uint160 sqrtPriceX96 = encodePriceSqrt(1, 1000);
pool.initialize(sqrtPriceX96);
However, when I used this code to initialize the pool, it never worked out as expected. I’ll explain how.
The intended function is to send a pair of n ETH and m MING to the Uniswap. Here’s how I implement it:
pool = IUniswapV3Pool(
v3Factory.createPool(Address.WETH, addressOfMing, fee)
);
// for testing purpose
// only 1 to 1 worked.
// uint160 sqrtPriceX96 = encodePriceSqrt(1, 1);
uint160 sqrtPriceX96 = encodePriceSqrt(amountOfMing, amountOfETH);
pool.initialize(sqrtPriceX96);
tickSpacing = pool.tickSpacing();
(uint256 _tokenId, , , ) =
mintNewPosition(amountOfETH, amountOfMing);
Here’s the implementation of mintNewPosition():
IERC20 ming = IERC20(addressOfMing);
console.log("amount to mint new position: %s -> %s", amountOfETH, amountOfMing);
require(
ming.balanceOf(address(this)) >= amountOfMing,
"insufficient Ming"
);
require(
weth.balanceOf(address(this)) >= amountOfETH,
"insufficient WETH"
);
// Approve the position manager
TransferHelper.safeApprove(Address.WETH, address(nonfungiblePositionManager), amountOfETH);
TransferHelper.safeApprove(addressOfMing, address(nonfungiblePositionManager), amountOfMing);
// Get tick spacing
(, int24 curTick, , , , , ) = pool.slot0();
curTick = curTick - (curTick % tickSpacing);
int24 lowerTick = curTick - (tickSpacing * 2);
int24 upperTick = curTick + (tickSpacing * 2);
require(curTick % tickSpacing == 0, 'tick error');
INonfungiblePositionManager.MintParams memory params =
INonfungiblePositionManager.MintParams({
token0: pool.token0(),
token1: pool.token1(),
fee: poolFee,
tickLower: lowerTick,
tickUpper: upperTick,
amount0Desired: amountOfETH,
amount1Desired: amountOfMing,
amount0Min: 0,
amount1Min: 0,
recipient: address(this),
deadline: block.timestamp
});
// Note that the pool defined by DAI/USDC and fee tier 0.3% must already be
// created and initialized in order to mint
(tokenId, liquidity, amount0, amount1) = nonfungiblePositionManager.mint(params);
console.log("new position minted: %s, %s", amount0, amount1);
To my understanding, when I called pool.initialize(sqrtPriceX96), it decided the price of MING regarding ETH. Therefore, upon calling mint() using the same amount of ETH/MING pair, the amount of ETH/MING returned should be the same as there’s no slippages in this process.
However, this is what I got:
amount to mint new position: 3000000000000000000 -> 222222222222222000000000000000000
new position minted: 3000000000000000000, 25559352082992436084
Did I do anything wrong?
---------------------------update1------------------------- I found in the library PoolAddress: require(key.token0 < key.token1) therefore I edited the following code in the pool creation:
if(Address.WETH < addressOfMing){
console.log("init pool");
pool = IUniswapV3Pool(
v3Factory.createPool(Address.WETH, addressOfMing, fee)
);
uint160 sqrtPriceX96 = encodePriceSqrt(amountOfMing, amountOfETH);
pool.initialize(sqrtPriceX96);
tickSpacing = pool.tickSpacing();
(uint256 _tokenId, , , ) = mintNewPosition(amountOfETH, amountOfMing);
tokenId = _tokenId;
}else{
console.log("init pool reversed");
pool = IUniswapV3Pool(
v3Factory.createPool(addressOfMing, Address.WETH, fee)
);
uint160 sqrtPriceX96 = encodePriceSqrt(amountOfETH, amountOfMing);
pool.initialize(sqrtPriceX96);
tickSpacing = pool.tickSpacing();
(uint256 _tokenId, , , ) = mintNewPosition(amountOfMing, amountOfETH);
tokenId = _tokenId;
}
The issue is still the same. However, when I increased the lowerTick&upperTick by 1000x
int24 lowerTick = curTick - (tickSpacing * 1000);
int24 upperTick = curTick + (tickSpacing * 1000);
I'm getting a way better result as expected.
amount to mint new position 222222222222222000000000000000000, 3000000000000000000
new position minted -> 222222222222221995864146677687271, 2999106664470400961
Yet, it's not working as expected. As long the price is determined in pool initialization, by giving the same amount of tokens pair, upper&lower ticks shouldn't affect (greatly) the amount of tokens being added to liquidity.
I had the same issue as you,i've had to write code myself based on your findings to find the initial price
I've written it in solidity so you may need to reimplement it in js for your use
In my library _tokenbase is the base asset,in my case it is WETH and tokenSwap is the token you want to pair against weth,say it be dai,usdc or something else
This code assumes both base and swap token is 18 decimals,you will need to handle the case either token being under 18 decimals with rounding of the amount potentially