Skip to main content
Version: V3

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_TICK and TickMath.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 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 frontrunning attack designed to execute the mint call 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);        }    }