chapters· Liquidity
24 · Build & manage

Add & remove liquidity

Funding rules, harvest-first settlement, native refunds, and the locked-forever case.

The program is one hook-held V4 position with a tick range fixed at creation. The owner grows it, shrinks it, or locks it forever — and every add or remove harvests first, so principal and fees never mix.

Funding rules — the same everywhere

A position settles from its two sides independently, and each side follows the rule for its own kind of currency. An ERC20/ERC20 pool simply has two allowance-funded sides; a pool with a native side has one of each:

if a side is…it settles like this
an ERC20the position pulls the exact amount it needs straight from your allowance to the hook — approve first, no dust left behind
the native tokenprepaid with msg.value and the unused excess refunded — the attached value is a hard cap. (When a pool has a native side, V4 sorts it as currency0 — native is address(0), so it can never sort second.)
pot money never funds positions

The hook's own inventory is pot money. Every position is funded by its caller, every time — the machine's solvency accounting depends on it and the invariant suite proves it.

Growing the position

// owner-only; harvests pending fees FIRST, then settles pure principal hook.addProgramLiquidity{value: cap}(key, liquidity);

Shrinking the position

// owner-only; harvests first, then returns pure principal to `to` (uint256 a0, uint256 a1) = hook.removeProgramLiquidity(key, liquidity, to);

A live owner can always withdraw — no lock is implied by anything except the owner's own surrender. An ownerless program's liquidity is locked forever; that is the whole point of surrendering.

Why harvest-first matters

pending fees exist on the position
add/remove harvests them through the program's own split
THEN the principal moves
result: fee value always obeys the split — principal is always pure

Without this, an owner could time adds and removes to skim fee value past the split (past a burn share, past the pot's fuel). With it, the split is unavoidable: fees settle as fees, principal settles as principal.

The tick range

Fixed at creation, forever — (0, 0) resolves to full range. A range position concentrates the program's depth (and its fee earnings) around the price you choose; full range never goes out of range and suits the lock-forever shapes. Choose once, choose deliberately.

FAQ

How do amounts turn into a liquidity value?+

The app computes L from your typed amounts at the pool's live price over the position's range (Uniswap's own formulas), then shaves a hair so on-chain round-up never exceeds what you typed.

Why did adding liquidity trigger a buyback?+

Your deposit's swap-less price touch still runs the hook's callbacks — a pending harvest or a compound-carry mint can settle inside your transaction. It's your own machine doing scheduled work, not a fee.

What happens to the extra native I send?+

The native side is a hard cap: whatever the mint doesn't consume is refunded in the same transaction. You never overpay for a two-sided mint.

Who can add or remove liquidity?+

Only the program owner — adds top up the one hook-held position, removals settle harvest-first so pending fees are never orphaned. A surrendered owner means removals are impossible forever.

What does removing ALL liquidity leave behind?+

The program, its config, its carry and the pot all survive. Pumps keep splitting into the carry, and the next add re-arms the full machine with the carry re-minted at the next harvest.