To mint a new position, we use the nonFungiblePositionManager and call mint.
For this exact example, we’re hard-coding the token amounts to be minted. In production, this would be a user-configurable function argument.
/// @notice Calls the mint function defined in periphery, mints the same amount of each token. For this example we are providing 1000 DAI and 1000 USDC in liquidity
/// @return tokenId The id of the newly minted ERC721/// @return liquidity The amount of liquidity for the position/// @return amount0 The amount of token0/// @return amount1 The amount of token1functionmintNewPosition()externalreturns (uint256 tokenId,uint128 liquidity,uint256 amount0,uint256 amount1 ) {// For this example, we will provide equal amounts of liquidity in both assets.// Providing liquidity in both assets means liquidity will be earning fees and is considered in-range.uint256 amount0ToMint =1000;uint256 amount1ToMint =1000;
Calling Mint
Here we approve the nonfungiblePositionManager to use the contracts' tokens, then populate the MintParams struct and assign it to a local variable params that will be passed to the nonfungiblePositionManager when we call mint.
By using TickMath.MIN_TICK and TickMath.MAX_TICK, we are providing liquidity across the whole pool’s range. In production, you may want to specify a more concentrated position.
We set amount0Min and amount1Min to zero for the example - but this would be a vulnerability in production. A function calling mint with no slippage protection would be vulnerable to a front running attack designed to execute the mint call at an inaccurate price.
For more reliable practice, the developer needs to implement a slippage assessment process.
Note that this function will not initialize a pool where one does not yet exist.
// Approve the position manager TransferHelper.safeApprove(DAI,address(nonfungiblePositionManager), amount0ToMint); TransferHelper.safeApprove(USDC,address(nonfungiblePositionManager), amount1ToMint); INonfungiblePositionManager.MintParams memory params = INonfungiblePositionManager.MintParams({ token0: DAI, token1: USDC, tickLower: TickMath.MIN_TICK, tickUpper: TickMath.MAX_TICK, amount0Desired: amount0ToMint, amount1Desired: amount1ToMint, amount0Min:0, amount1Min:0, recipient:address(this), deadline: block.timestamp });// Note that the pool defined by DAI/USDC must already be created and initialized in order to mint (tokenId, liquidity, amount0, amount1) = nonfungiblePositionManager.mint(params);
Updating The Deposit Mapping And Refunding The Calling Address
After the previous point, we can call the internal function we previously wrote in Setting Up Your Contract. After that, we can take any liquidity leftover from minting and refund it to msg.sender.
/// @notice Calls the mint function defined in periphery, mints the same amount of each token. For this example we are providing 1000 DAI and 1000 USDC in liquidity
/// @return tokenId The id of the newly minted ERC721/// @return liquidity The amount of liquidity for the position/// @return amount0 The amount of token0/// @return amount1 The amount of token1functionmintNewPosition()externalreturns (uint256 tokenId,uint128 liquidity,uint256 amount0,uint256 amount1 ) {// For this example, we will provide equal amounts of liquidity in both assets.// Providing liquidity in both assets means liquidity will be earning fees and is considered in-range.uint256 amount0ToMint =1000;uint256 amount1ToMint =1000;// Approve the position manager TransferHelper.safeApprove(DAI,address(nonfungiblePositionManager), amount0ToMint); TransferHelper.safeApprove(USDC,address(nonfungiblePositionManager), amount1ToMint); INonfungiblePositionManager.MintParams memory params = INonfungiblePositionManager.MintParams({ token0: DAI, token1: USDC, tickLower: TickMath.MIN_TICK, tickUpper: TickMath.MAX_TICK, amount0Desired: amount0ToMint, amount1Desired: amount1ToMint, amount0Min:0, amount1Min:0, recipient:address(this), deadline: block.timestamp });// Note that the pool defined by DAI/USDC must already be created and initialized in order to mint (tokenId, liquidity, amount0, amount1) = nonfungiblePositionManager.mint(params);// Create a deposit_createDeposit(msg.sender, tokenId);// Remove allowance and refund in both assets.if (amount0 < amount0ToMint) { TransferHelper.safeApprove(DAI,address(nonfungiblePositionManager),0);uint256 refund0 = amount0ToMint - amount0; TransferHelper.safeTransfer(DAI, msg.sender, refund0); }if (amount1 < amount1ToMint) { TransferHelper.safeApprove(USDC,address(nonfungiblePositionManager),0);uint256 refund1 = amount1ToMint - amount1; TransferHelper.safeTransfer(USDC, msg.sender, refund1); } }