Providing liquidity
Read how to mint, add to, move, split and withdraw a position through the v4 PositionManager, the way the Hookr app sends each one.
On this page
Introduction
Hookr pools take liquidity through the ordinary Uniswap v4 PositionManager. There is no Hookr-specific liquidity contract and no Hookr position NFT.
Minting a Position
Use PositionManager exactly as you would on any v4 pool.
Actions.MINT_POSITION (poolKey, tickLower, tickUpper, liquidity, amount0Max, amount1Max, owner, hookData)
Actions.SETTLE_PAIR (currency0, currency1)Pass empty hookData.
For an ERC-20 currency, approve the token to Permit2, then approve PositionManager as a Permit2 spender with an amount and an expiry. PositionManager pulls through Permit2 and a plain ERC-20 approval to it is not read.
What the Hookr App Sends
The app's mint is one modifyLiquidities call carrying three actions:
Actions.MINT_POSITION (poolKey, tickLower, tickUpper, liquidity, amount0Max, amount1Max, owner, hookData)
Actions.SETTLE_PAIR (currency0, currency1)
Actions.SWEEP (currency, owner)The two maxima carry a 1% allowance above the debts quoted at review, because the block that mines the mint can sit at a slightly different price and PositionManager reverts MaximumAmountExceeded the moment a debt exceeds its maximum. msg.value is amount0Max for a native leg, and SWEEP returns in the same transaction whatever SETTLE_PAIR did not take, so the allowance is never stranded in the PositionManager. An ERC-20 leg pulls only the exact debt through Permit2, so nothing is left to sweep there.
Managing a Position
Every action the app offers is one modifyLiquidities call. The actions, in the order they are encoded:
| In the app | Actions | Native value sent |
|---|---|---|
| Add liquidity | MINT_POSITION · SETTLE_PAIR · SWEEP | amount0Max |
| Split | MINT_POSITION × n · SETTLE_PAIR · SWEEP | the summed amount0Max |
| Increase a position | INCREASE_LIQUIDITY · SETTLE_PAIR · SWEEP | amount0Max |
| Withdraw part | DECREASE_LIQUIDITY · TAKE_PAIR | none |
| Collect fees | DECREASE_LIQUIDITY with zero liquidity · TAKE_PAIR | none |
| Move the range | BURN_POSITION · MINT_POSITION · CLOSE_CURRENCY × 2 · SWEEP | the net native debt |
| Rebalance into another pool | as a move, with the mint in the other PoolKey | the net native debt |
| Withdraw all | BURN_POSITION · TAKE_PAIR | none |
SWEEP appears only when currency0 is native; an ERC-20 pair has nothing to return. An increase keeps the position's range and token id; its share is of the liquidity the position already holds, and it is funded and swept like a mint. Every action is encoded from the Actions bytes in src/lib/lp-position-manager.ts, and the mint and burn sequences reproduce, byte for byte, the calldata a retained fork rehearsal broadcast.
Moving a Range
A move closes the old position and opens the new one inside one PoolManager unlock. BURN_POSITION credits the position's principal and its uncollected fees to the transient delta ledger, MINT_POSITION debits the new range's deposit against the same ledger, and CLOSE_CURRENCY settles each currency's net once: a negative net is paid, a positive net is taken to the owner. Nothing crosses as a transfer for the part that funds the new range in place.
The native value sent is the net debt, the mint's amount0Max less the burn's amount0Min, and never the mint's gross maximum. SWEEP returns what the pool does not take, so the value is never stranded; but a call funded with the gross asks the wallet to hold ETH that is already inside the position, and a wallet that cannot cover it cannot re-range. A rebalance is the same sequence with the mint pointed at another pool of the same pair, in the same currency order, which is the condition under which the two legs net inside one call.
The new position is a new token id. The receipt is accepted only when it carries the burn of the reviewed id and exactly one mint to the connected wallet.
Splitting a Deposit
The Split tile divides one deposit across three of the fixed plans: Full range, Sit still and Dip, then climb, opening on 10%, 30% and 60% of the currency0 amount. The shares are whole percentages that add up to 100; a share of zero drops its range rather than minting an empty position, and a split with one range left is refused. Each range is sized at the current price the way a single plan is and re-priced at submit time, so every position takes both currencies.
The call is MINT_POSITION once per range, one SETTLE_PAIR and one SWEEP, funded with the summed amount0Max. Each position keeps its own range, its own fees and its own token id. The receipt is accepted only when it carries exactly the reviewed number of mints to the connected wallet.