Graduation & Liquidity Provisioning
When all 800,000 lots are sold, the token graduates into permanent, protocol-owned DEX liquidity. It happens atomically inside the final buy — no separate transaction, no keeper, no operator.
Trigger
Graduation runs inside the buy() that pushes current_supply_lots to 800,000. The buyer's transaction carries the cost. The FLAG_GRADUATED bit is set before the external calls, so the curve is already closed to trading when the hook is invoked.
What happens
raw_call with revert_on_failure=False. If the deployer's wallet cannot receive — a broken contract, a multisig that reverts on plain transfers — the fee is forfeited to the protocol rather than bricking graduation for everyone else.enableTrading() moves the token from STATE_LOCKED to STATE_TRADING. Until this point only the minter could move tokens.createPositionAndRegister() initializes the pool at INITIAL_SQRT_PRICE and mints a full-range position owned by the hook. The caller is verified against Factory.is_curve() — only a factory-born curve can graduate a token.The graduation envelope
On V4-family chains the envelope splits 90/10 between the two pools. The protocol token takes the full envelope with no split — it cannot be paired against itself.
| Amount | Base | BSC | Polygon | Robinhood | Gnosis |
|---|---|---|---|---|---|
Total native (GRADUATION_NATIVE) | 4 ETH | 12 BNB | 96,000 POL | 4 ETH | 9,600 xDAI |
| → primary pool | 3.6 ETH | 10.8 BNB | 86,400 POL | 3.6 ETH | 9,600 xDAI (no split) |
| → secondary pool | 0.4 ETH | 1.2 BNB | 9,600 POL | 0.4 ETH | — |
| Total tokens | 400,000,000 × 10⁹ (400M) | ||||
| → primary pool | 360,000,000 × 10⁹ (360M) | ||||
| → secondary pool | 40,000,000 × 10⁹ (40M) (V4 family only) | ||||
PRIMARY_GRADUATION_LIQUIDITY is exactly 0.9 × GRADUATION_LIQUIDITY — amounts scale linearly, and at INITIAL_SQRT_PRICE both sides of the position bind simultaneously, so the settle step pulls exactly the intended amounts.
Pool parameters
| Parameter | V4 family | Gnosis (V3) |
|---|---|---|
| Pool fee | 0 — all fees taken by the hook | 10000 (1% tier) |
| Tick spacing | 200 | 200 (implied by the 1% tier) |
| Tick range | Full range (−887200 … 887200) | Full range |
| Position owner | PrimaryHook | GraduationManager |
| Removable | No — there is no function that decreases liquidity | |
The LP position is owned by the hook or manager contract, and no code path removes or reduces it. The position can only grow, through fee reinvestment. This is a core guarantee of the protocol and is verifiable by reading the source: there is no decreaseLiquidity, no burn, no withdrawal.
Front-running the pool initialization
An attacker who initializes the pool first at a distorted price would corrupt the graduation mint. Both venue families close this:
- V4 family —
beforeInitializereverts unless a transient_initializingflag is set, and onlycreatePositionAndRegistersets it. The flag is cleared immediately after the multicall, not left to expire at end of transaction, so no later call in the same transaction can slip through it. - Gnosis — the pool is initialized atomically inside
deploy_pairviapreInitializePool(). A frontrunner who got there first causes the whole deployment to revert; the deployer retries with a fresh salt. At graduation the manager re-readsslot0and asserts the price still matches, as defense in depth.
Excess native
Anything sent beyond GRADUATION_NATIVE is credited to protocolBalance. This is normally the protocol's 10% cut of curve fees, arriving in the same call. The secondary pool's earmarked share is not excess — it is accounted separately and pushed on.