Graduation to Uniswap V4
Deliberately two phases, so a failed pool seed can never strand a curve's reserves. The curve already holds the asset its pool will use, so graduation converts nothing, needs no router and needs no oracle.
Phase 1 — graduate(token)
- Permissionless. Checks the curve is ready, then runs the seed preflight through
AgiV2GraduationGuardbefore anything irreversible happens. - Sweeps pending fees, halts the curve and moves its quote and token reserves into the factory. The factory records what it actually received, not what the curve said it sent, so an under-delivering asset cannot draw the shortfall from other launches' escrowed balances.
- Because it is purely internal bookkeeping, the curve calls it automatically from inside the buy that crosses the threshold. If that inner call fails, it is swallowed (
AutoGraduationFailed) so a graduation problem cannot take the crossing buy down with it. - The launch moves to phase
Swept.
Phase 2 — createGraduatedPool(token)
- Permissionless and retryable: the launch stays in
Sweptuntil a seed succeeds, so a transient failure never strands anything. - Only the token amount that preserves the curve's terminal price is seeded:
poolTokens = sweptTokens × sweptQuote / (sweptQuote + phantomQuote). The remainder — the part that backed the phantom reserve — is permanently locked inAgiV2LaunchLockerand never circulates (GraduationTokensPermanentlyLocked). - The pool is initialized at the price those two amounts imply, registered with
AgiV2MemeHook, and a full-range position is minted directly to the locker byAgiV2GraduationExecutor, which exists only to keep the factory under the 24 KB contract size limit. - The launch moves to
PoolCreated. From then on the hook charges the fee and the escrow pays it out. - In practice our robot sends this call and pays its gas (see Operations). The call stays permissionless: if the robot is down, anyone can send it and the result is identical.
The guard
AgiV2GraduationGuard is a stateless preflight that models the rejections of the real mint: the
seed amounts against V4's signed int128 bound (the PositionManager's ABI takes
uint128, so an amount in between passes every field check and still reverts inside core), the
derived square-root price against V4's own limits, and the full-range liquidity against zero. It exists
because phase one is irreversible: a seed that passes here and reverts inside V4 would leave a launch
permanently unseedable.
When it goes wrong
forceSweptGraduationGraduationStillViable, so it cannot be used to take a healthy launch's reserves instead of seeding its pool.rescueSweptGraduationcreateGraduatedPool. The phase becomes Rescued, which is terminal.rescueCurveFeesgraduate sweeps fees before handing over reserves.