chapters· Launch a pool
22 · Build & manage

Launch a pool

launchPool in one transaction — or the three standalone steps, your choice.

One entry does the whole thing: launchPool initializes the pool on the PoolManager, declares the pot's roles and creates the LP program with its seed liquidity — atomically, in one transaction. The three standalone steps still exist for pools that want them separately (or want no program at all).

The one-transaction launch

launchPool — anyone, on a pool that does not exist yet
function launchPool( PoolKey key, // must name this hook uint160 sqrtPriceX96, // the launch price, Q64.96 address main, // the defended currency address recipient, // 0x0 = burn (ERC20 main only) int24 tickLower, // (0,0) = full range int24 tickUpper, uint128 liquidity, // the program's seed address owner, // 0x0 = surrendered at birth ProgramConfig config // the split rules ) external payable returns (uint256 amount0, uint256 amount1);

The caller becomes the pot admin — exactly as if they had called PoolManager.initialize themselves — then the initPot and addLiquidityAdvanced bodies run with the same validation, events and funding rules as the standalone entries. It reverts if the pool already exists.

launch configured, not empty

launchPool takes the FULL ProgramConfig — the same struct addLiquidityAdvanced takes. Set it here and the machine is complete from the first trade: fees compound, the pot self-funds from the buyback share, the burn burns — no second transaction, no window where the pool trades with the split at zero. A zeroed config is a plain LP position that keeps everything for you until the operator edits it later.

Setting up the settings — the config, field by field

Ten fields, validated at write-time, editable later by the operator (unless you surrender the roles). Shares are WAD numbers: 1e18 = 100%.

a real launch config — flywheel with a treasury remainder
IGlueHook.ProgramConfig({ buybackShareWad: 0.30e18, // 30% of secondary-side fees fuel the pot burnShareWad: 0.20e18, // 20% of main-side fees walk the burn cascade compoundShareWad: 0.40e18, // 40% of BOTH sides re-mint as liquidity potCompoundShareWad: 0.50e18, // half of what every buyback buys joins the LP carry potBurnShareWad: 0, // none force-burned — the rest follows the pot's recipient publicHarvest: true, // anyone may trigger a harvest secondaryRecipient: treasury, // remainder of the secondary side (the 30% left) mainRecipient: treasury, // remainder of the main side (the 40% left) minMain: 100e18, // auto-harvest arms when either side's parked minSecondary: 0.05e18 // fees cross its threshold })
fieldwhat it does at launch
buybackShareWadthe flywheel's fuel line — this slice of secondary-side fees lands in the pot every harvest, so pumping and shielding never depend on donations
burnShareWadmain-side fees sent down the burn cascade — must be 0 when main is native
compoundShareWadboth sides — the share that becomes position liquidity again (the autocompound engine)
potCompoundShareWad / potBurnShareWadthe buyback split: how what the pot BUYS is carved between the LP carry, the burn cascade, and the pot's recipient
publicHarvestopen the manual harvest to keepers and the community, or keep it owner/operator-only
secondaryRecipient / mainRecipienteach side's remainder needs a live recipient whenever the shares sum below 100%
minMain / minSecondarythe auto-harvest trigger — type(uint256).max on both = disarmed, harvests are manual only

The write-time laws: each side's shares must fit in its own gross (compound + buyback ≤ 100%, compound + burn ≤ 100%, potCompound + potBurn ≤ 100%), a native main rejects burn shares, and every leg that can carry value needs a live recipient. A config that validates at launch keeps validating forever — and the swaps never depend on it: a recipient that starts refusing later just parks its money. Full lever-by-lever depth in Manage your program, proven presets included.

One transaction is also cheaper — and atomic

pathexecution gasall-in (incl. 21k base per tx)
launchPool — one transaction530,298551,298
the three-step path (3 transactions)514,909577,909

The launch orchestration costs ~15k extra execution gas but saves two transaction base costs — ~27k cheaper all-in. More importantly it is atomic: a failed step rolls the whole launch back (no pool, no half-configured pot left behind), where the three-step path can strand a pool between transactions with its roles undeclared.

why the admin capture stays sound

The PoolManager skips hook callbacks when the hook itself is the caller, so beforeInitialize never runs during a launch — and a successful initialize proves the pool was fresh, so the admin slot is provably virgin. launchPool records its own caller as the admin: exactly what the callback would have recorded had the launcher initialized the pool directly. Audited as the LA1–LA10 suite, including the no-spoof corner.

The checklist

1
Build the PoolKey with the hook address
Sort the two currencies by address — any ERC20 pair works; if one side is native it's address(0), which always sorts as currency0. Pick a non-zero fee tier and a tick spacing, set hooks = 0xb216…60C8. A zero-fee pool would never pump.
2
Pick the price
sqrtPriceX96 is the launch price in V4's Q64.96 square-root format. For a fresh token this IS the market's starting point; for an existing token match the live market or arbitrage will do it for you (at your LP's expense).
3
Declare the machine
main must be one of the key's two currencies — the other becomes secondary automatically. A native main must name a live recipient.
4
Write the split rules
Fill the ProgramConfig (the section above, field by field): the buyback share that self-funds the pot, the compound share, the burn share, the buyback split, the recipients, the auto-harvest trigger. Launching configured means the flywheel turns from the first trade — a zeroed config is just a plain LP until the operator edits it.
5
Fund the seed
Each side settles by its own kind: an ERC20 side pulls the exact amount from your allowance to the hook (an ERC20/ERC20 pool has two of these); a native side, if the pool has one, is prepaid with msg.value and the unused excess is refunded — the attached value is a hard cap.

The three-step manual path

the same result, decomposed
// 1. initialize the pool — YOU become the pot admin poolManager.initialize(key, sqrtPriceX96); // 2. declare the roles (one-shot, admin-only) hook.initPot(key, main, recipient); // 3. create the program WITH its settings (admin-only, one per pool) hook.addLiquidityAdvanced(key, 0, 0, liquidity, owner, config); // (bare shortcut: a plain position, all shares zero, you keep everything — // owner AND operator = the caller, so the settings stay editable later) hook.addLiquidity(key, 0, 0, liquidity, owner);
a pool with no program at all

Stop after step 2. The pot, the pump and the shield work standalone — the LP program is optional. It can be created later at any time (still admin-only, still one per pool).

After launch

trade — the machine runs on traffic· a buyback share self-funds the pot from the very first harvest
donate — optional turbo for the pot (you, the community, another contract)
tune — the operator edits the split any time· or freezes it forever

FAQ

What does launchPool do in one transaction?+

Creates the V4 pool with the hook attached, initializes the pot, creates the LP program with YOUR ProgramConfig, and seeds the first liquidity — configured from block one.

Should I use plain addLiquidity or the advanced path?+

Prefer addLiquidityAdvanced (or launchPool with a config): it writes your split rules atomically with the program. Plain addLiquidity is the bare shortcut — zeroed shares, you as owner/operator.

What are the plain addLiquidity defaults?+

The caller becomes owner AND operator (so everything stays editable later), all shares start at zero, and the pot recipient defaults toward burn. Nothing is locked by accident.

Why did the launch revert with BadConfig?+

A side's shares sum above 100%, a burn share on a native main, a value-carrying side without a live recipient, or a malformed liquidity request. All validation is at write-time by design.

Can a pool have more than one LP program?+

No — one program per pool, created once (PotAlreadyReady on a second attempt). Later deposits go through addProgramLiquidity, which tops up the existing position.