Mint a New Position
Input Parameters#
To mint a new position, we use the nonFungiblePositionManager and call mint.
For the sake of this example, were 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 token1 function mintNewPosition() external returns ( 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_TICKandTickMath.MAX_TICK, we are providing liquidity across the whole range of the pool. In production you may want to specify a more concentrated position.We set
amount0Minandamount1Minto zero for the example - but this would be a vulnerability in production. A function callingmintwith no slippage protection would be vulnerable to a frontrunning attack designed to execute themintcall at an inaccurate price.For a more secure practice the developer would need to implement a slippage estimation 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, fee: poolFee, 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 and fee tier 0.3% 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#
Now 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.
// 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); } }The Full Example#
/// @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 token1 function mintNewPosition() external returns ( 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, fee: poolFee, 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 and fee tier 0.3% 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); } }