Protocol
Graduation internals
What _graduate does step by step, including the pair-price alignment against a pre-seeded pool.
Graduation runs inside the buy (or settleBatch) that makes tokensSold == supplyOnCurve. There is no separate migration transaction, so it cannot be front-run, delayed or skipped.
Sequence
graduatedAt = now
raised = usdgRaised
gradFee = raised × graduationFeeBps / BPS → treasury
floorUsdg = raised × floorBps / BPS → FloorReserve.deposit(token, floorUsdg)
liqUsdg = raised − gradFee − floorUsdg
liqTokens = lpSupply // 200M with the defaults
pair = factory.getPair(token, USDG) or createPair(…)
(liqTokens, liqUsdg) = _alignPairPrice(liqTokens, liqUsdg) // see below
router.addLiquidity(token, USDG, liqTokens, liqUsdg, 0, 0, curve, now)
if lpLockSeconds == 0: transfer LP to 0x…dEaD
else: lpLockId = locker.lock(pair, liquidity, now + lpLockSeconds, creator)
dustTokens = token.balanceOf(curve) − batchTokensUnclaimed → burned
dustUsdg = usdg.balanceOf(curve) − batchRefundUnclaimed → FloorReserve.deposit
emit Graduated(token, pair, usedUsdg, usedTokens, lpLockId | type(uint256).max, floorUsdg)
Unclaimed batch tokens and refunds are explicitly excluded from the dust sweep, so late claimers are never affected.
Pair-price alignment
Anyone can create the token/USDG pair on Uniswap before graduation and seed it at an arbitrary price. A naive addLiquidity would then be forced to the pool's ratio (Uniswap adds at the existing price), letting a griefer mis-price the migration. _alignPairPrice swaps against the pool first so its price equals the curve's target liqUsdg / liqTokens:
(rT, rU) = pool reserves; k = rT × rU
targetU = sqrt(k × liqUsdg / liqTokens) // USDG reserve the pool would have at our price with the same k
if targetU > rU: // pool too cheap → buy tokens with USDG
usdgIn = (targetU − rU) × 1000 / 997 + 1 // account for the 0.3% pool fee
skip if usdgIn ≥ liqUsdg / 2
tokensOut = rT − ceil(k / (rU + usdgIn × 997 / 1000)); swap; liqTokens += tokensOut; liqUsdg −= usdgIn
elif targetU < rU: // pool too expensive → sell tokens for USDG
targetT = sqrt(k × liqTokens / liqUsdg)
tokensIn = (targetT − rT) × 1000 / 997 + 1
skip if tokensIn ≥ liqTokens / 2
usdgOut = rU − ceil(k / (rT + tokensIn × 997 / 1000)); swap; liqTokens −= tokensIn; liqUsdg += usdgOut
Whoever seeded the pair at the wrong price pays for the correction (the arbitrage profit lands in the curve's liquidity), and the migration proceeds. If aligning would cost more than half of either side, the contract skips the swap and adds at the pool's price rather than revert — a launch can always graduate. Covered by test_graduation_survivesPreSeededSkewedPair.
Opening DEX price
p_dex = liqUsdg / liqTokens = (raised × (1 − graduationFeeBps/BPS − floorBps/BPS)) / lpSupply
Defaults with a 10% floor: 12,400 × 0.88 / 200M ≈ 0.0000546 USDG, versus the curve's final spot ≈ 0.0000609 (the fee and the floor come out of the pool). With floorBps = 0: ≈ 0.0000608.
After graduation
The app's trade panel routes through Uniswap V2 (swapExactTokensForTokens, path [USDG, token] or [token, USDG], router 0x89e5DB8B5aA49aA85AC63f691524311AEB649eba); the indexer follows the pair's Swap/Sync events; FloorReserve.redeem is available to every holder.