Skip to main content

Documentation

Similar to other concentrated-liquidity position manager patterns, liquidity positions are minted as ERC-721 tokens and depend on a periphery contract.

Structured reference
TOC available

Mint Position

Similar to other concentrated-liquidity position manager patterns, liquidity positions are minted as ERC-721 tokens and depend on a periphery contract. v4's PositionManager contract will facilitate liquidity management

Context​

Please note that PositionManager is a command-based contract, where integrators will be encoding commands and their corresponding parameters.

Setup​

See the setup guide

Guide​

Below is a step-by-step guide for minting a v4 liquidity position, in solidity

1. Import and define IPositionManager​

import {IPositionManager} from "v4-periphery/src/interfaces/IPositionManager.sol";

// inside a contract, test, or foundry script:
IPositionManager posm = IPositionManager(<address>);

2. Encode Actions​

To mint a position, two actions are required:

  • mint operation - the creation of the liquidity position
  • settle pair - the two tokens to be paid by msg.sender

If providing ETH liquidity, a third action is required:

  • sweep - to recover excess eth sent to the position manager
import {Actions} from "v4-periphery/src/libraries/Actions.sol";

bytes memory actions = abi.encodePacked(uint8(Actions.MINT_POSITION), uint8(Actions.SETTLE_PAIR));

// For ETH liquidity positions
bytes memory actions = abi.encodePacked(uint8(Actions.MINT_POSITION), uint8(Actions.SETTLE_PAIR), uint8(Actions.SWEEP));

3. Encode Parameters​

bytes[] memory params = new bytes[](2); // new bytes[](3) for ETH liquidity positions

The MINT_POSITION action requires the following parameters:

ParameterTypeDescription
poolKeyPoolKeywhere the liquidity will be added to
tickLowerint24the lower tick boundary of the position
tickUpperint24the upper tick boundary of the position
liquidityuint256the amount of liquidity units to mint
amount0Maxuint128the maximum amount of currency0 msg.sender is willing to pay
amount1Maxuint128the maximum amount of currency1 msg.sender is willing to pay
recipientaddressthe address that will receive the liquidity position (ERC-721)
hookDatabytesarbitrary data that will be forwarded to hook functions
Currency currency0 = Currency.wrap(<tokenAddress1>); // tokenAddress1 = 0 for native ETH
Currency currency1 = Currency.wrap(<tokenAddress2>);
PoolKey poolKey = PoolKey(currency0, currency1, 3000, 60, IHooks(hook));

params[0] = abi.encode(poolKey, tickLower, tickUpper, liquidity, amount0Max, amount1Max, recipient, hookData);

The SETTLE_PAIR action requires the following parameters:

  • currency0 - Currency, one of the tokens to be paid by msg.sender
  • currency1 - Currency, the other token to be paid by msg.sender
params[1] = abi.encode(currency0, currency1);

The SWEEP action requires the following parameters:

  • currency - Currency, token to sweep - most commonly native Ether: CurrencyLibrary.ADDRESS_ZERO
  • recipient - address, where to send excess tokens
params[2] = abi.encode(currency, recipient);

4. Submit Call​

The entrypoint for all liquidity operations is modifyLiquidities()

uint256 deadline = block.timestamp + 60;

uint256 valueToPass = currency0.isAddressZero() ? amount0Max : 0;

posm.modifyLiquidities{value: valueToPass}(
abi.encode(actions, params),
deadline
);

Additional notes:​

  • To obtain balance changes, callers should read token balances before and after the .modifyLiquidities() call