Protocol
Contracts
Every contract in the live stack and what it is responsible for.
The stack#
Seven contracts, deployed together and wired to each other once at deploy time. Nothing is a proxy and nothing is upgradeable, so a new version means a new stack. Live addresses, explorer links, and ABI access are on network.
| Contract | Role |
|---|---|
| Launch factory | Creates each token and curve, reserves the future JoseonSwap pair, and optionally runs the creator's first buy. |
| Registry | Launch state, fee recipient of record, and ordered lifecycle transitions. |
| Migration manager | Funds the reserved pool from the curve and locks the opening liquidity. |
| Liquidity locker | Holds the principal LP forever — no withdraw. |
| Swap factory | Creates pairs; launch pairs are reserved by the launch factory. |
| Router | Paths, deadlines, and ETH ⇄ WETH entry points for post-graduation swaps. |
| WETH9 | Per-stack wrapped ETH. |
Each launch also gets its own token, curve, and (reserved) pair. Those are not part of the stack table above.
launch factory
├── deploys the token and its curve, one pair per launch
├── reserves the DEX pool the launch will graduate into
└── registers the launch
registry ──── launch state and the fee recipient of record
migration manager
├── funds the reserved pool from the curve
└── locks the opening liquidity
swap factory ──▶ one pair contract per token pair
router ──▶ paths, deadlines, ETH ⇄ WETH
WETH9 ──▶ per-stack wrapped ETHLaunchpad#
Launch factory#
Creates a launch in one transaction: deploys the token, deploys its curve, funds the curve with the full supply, reserves the DEX pool, registers everything, and optionally runs the creator's first buy.
createToken(string name, string symbol, uint256 initialBuyMinOut, uint256 deadline)
returns (address token, address curve)
curveFor(address token) returns (address)
pairFor(address token) returns (address)
allTokens(uint256 index) returns (address)
allTokensLength() returns (uint256)Its constants, identical for every launch:
| Constant | Value |
|---|---|
TOTAL_SUPPLY | 1,000,000,000e18 |
CURVE_SUPPLY | 800,000,000e18 |
DEX_SUPPLY | 200,000,000e18 |
INITIAL_VIRTUAL_ETH_RESERVE | 1.2e18 |
INITIAL_VIRTUAL_TOKEN_RESERVE | 1,066,666,666.666666666666666666e18 |
The token#
Fixed-supply ERC-20, 18 decimals, no mint and no burn. It adds an ERC-1046 tokenURI() derived from the token's own
address, so wallets and explorers can find the logo and description straight from the chain.
That pointer has no setter and no owner. Nobody, us included, can repoint a live token's metadata.
The curve pool#
One per launch. Prices trades on virtual reserves, holds the real ETH and tokens, accrues fees, and hands its assets to the migration exactly once. Full maths in bonding curve.
quoteBuy(uint256 grossEthIn) returns (
uint256 ethUsed, uint256 ethRefunded, uint256 fee, uint256 netEthIn,
uint256 tokenOut, uint256 nextVirtualEthReserve,
uint256 nextVirtualTokenReserve, bool willGraduate)
quoteSell(uint256 tokenIn) returns (...)
buy(uint256 minTokensOut, uint256 deadline) payable
sell(uint256 tokenIn, uint256 minEthOut, uint256 deadline)
getCurrentPrice() returns (uint256) // WAD, eth per token
getProgress() returns (uint256) // WAD, 1e18 == 100%
claimableCreatorFees(address recipient) returns (uint256)
claimCreatorFees(address recipient) returns (uint256)The handover of assets to a graduation can only be triggered by the migration manager, only in the graduating state, and only once.
The registry#
The record of what exists and who gets paid: creator, curve, pool, timestamps, supplies, state, and the fee recipient that both the curve and the pool consult. It enforces that lifecycle transitions happen in order.
Only the immutable feeAdmin may reassign a launch's fee recipient. That is the CTO path — see fees.
setupAdmin wires the stack once and cannot touch fee recipients.
The migration manager#
Owns graduation. Full sequence in graduation.
getMigrationQuote(address token)
returns (uint256 wethAmount, uint256 tokenAmount, uint256 initialDexPrice)
graduate(address token)It also enforces the two checks that make the handoff safe: the pool must be untouched, and its opening price must match the curve's closing price within a rounding margin.
The liquidity locker#
Takes the opening position from a graduation and keeps it. It accepts deposits only from the migration manager, only for a pool it has verified belongs to that launch, and only once per token.
There is no withdraw function. You can read what it holds:
lockedPair(address token) returns (address)
lockedLiquidity(address token) returns (uint256)DEX#
The swap factory#
createPair(address tokenA, address tokenB) returns (address pair)
getPair(address tokenA, address tokenB) returns (address)
allPairs(uint256 index) returns (address)
allPairsLength() returns (uint256)
isLaunchPair(address pair) returns (bool)
feeTo() returns (address)Pools for launch tokens are created by the launch factory, not by this entry point. Ordinary createPair refuses any
token the factory already knows as a launch token, and also requires both addresses to already hold code — so a
predicted-but-not-yet-deployed launch token cannot have its pair slot occupied in advance.
The pair#
Constant-product pair with V2 semantics, plus fees booked outside the reserves and the reservation gate that keeps a launch pool closed until its graduation opens it.
mint(address to) returns (uint256 liquidity)
burn(address to) returns (uint256 amount0, uint256 amount1)
swap(uint256 amount0Out, uint256 amount1Out, address to, bytes data)
getReserves() returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)
sync()
skim(address to)
claimCreatorFees() returns (uint256 amount0, uint256 amount1)
claimCreatorFeesFor(address recipient) returns (uint256 amount0, uint256 amount1)
claimProtocolFees() returns (uint256 amount0, uint256 amount1)
creatorFeesOwed(address recipient) returns (uint256 amount0, uint256 amount1)
feeShares() returns (uint256 creatorBps, uint256 protocolBps, uint256 lpBps)claimCreatorFees() is shorthand for claimCreatorFeesFor(currentRecipient). After a CTO reassignment the outgoing
recipient is no longer current, so claimCreatorFeesFor(theirAddress) is how they collect what they earned. Both are
permissionless: the address argument is the ledger key and the payout destination, never the caller.
| Constant | Value |
|---|---|
TOTAL_FEE_BPS | 30 (0.30%) |
LAUNCH_CREATOR_SHARE_BPS | 5,000 |
LAUNCH_PROTOCOL_SHARE_BPS | 3,000 |
PLAIN_PROTOCOL_SHARE_BPS | 3,000 |
MINIMUM_LIQUIDITY | 1,000 |
The router#
A standard V2-style router: quoting helpers, the swapExact… family, the ETH variants, and liquidity add/remove. Paths
are capped at two hops.
WETH9#
Canonical WETH, redeployed per stack. Byte-identical to previous versions but at a different address — which is exactly why mixing addresses across stacks breaks routing.
Events worth knowing#
If you are reading the chain yourself rather than the Indexer API, these are the lifecycle events to follow. Filter by the current deployment addresses. Use the launch token as the stable market key.
// Launch factory
event TokenCreated(address indexed token, address indexed creator, address indexed bondingCurve, uint256 initialBuy);
event LaunchPairReserved(address indexed token, address indexed pair);
// Curve
event TokenBought(address indexed buyer, uint256 ethUsed, uint256 fee, uint256 tokenOut, uint256 ethRefunded);
event TokenSold(address indexed seller, uint256 tokenIn, uint256 grossEthOut, uint256 fee, uint256 netEthOut);
event CurveCompleted(uint256 realEthReserve, uint256 creatorFeeAccrued, uint256 protocolFeeAccrued);
// Registry / migration / locker
event GraduationStarted(address indexed token);
event GraduationCompleted(address indexed token, address indexed dexPair);
event TokenGraduated(address indexed token, address indexed pair, uint256 migrationWeth, uint256 migrationToken, uint256 liquidity);
event LiquidityLocked(address indexed token, address indexed pair, uint256 liquidity);
// Pair (post-graduation)
event Swap(address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to);CurveCompleted means the curve sold out; do not treat the pair as tradeable until GraduationCompleted /
TokenGraduated. On pair Swap, sender is often the router and to is the output receiver — neither is guaranteed
to be the human trader.
Reading the real thing#
Every contract in the stack is source-verified, so the authoritative version of this page is the explorer. Open any address from network and read the code — and the verified ABI — that is actually running rather than trusting a summary of it.