Protocol
JoseonSwap
A Uniswap V2-style AMM with launch-pair protection.
A V2 AMM with two additions#
JoseonSwap is a constant-product AMM in the Uniswap V2 shape: a factory that deploys one pair per token pair, pairs that hold reserves and mint LP tokens, and a router that handles paths, deadlines, and ETH wrapping. If you have integrated with V2 before, the mental model transfers directly.
Two things are different:
- Launch pairs are reserved. A graduating token's pair can only be created by the launch factory and can only be seeded by the migration manager, so nobody can front-run a graduation.
- Fees are booked, not compounded. The 0.30% fee is accrued outside the reserves and pulled by whoever earned it, instead of accruing entirely to LP share value.
Pricing#
amountInWithFee = amountIn × 997
amountOut = (amountInWithFee × reserveOut) / (reserveIn × 1000 + amountInWithFee)997/1000 is the same 0.30% as V2. Splitting the fee three ways changed who receives it, not what a trader gets: an
AMM prices a swap against the input net of the whole fee regardless of destination. A unit test runs the same swap
through a V2 pair and a V3 pair and asserts identical output.
Fee accounting#
On every swap the pair computes the fee on the input side and books the creator and protocol shares into storage
separate from the reserves. swap performs no external calls beyond its two ERC-20 transfers — no registry lookup, no
fee transfer.
fee = amountIn × 30 / 10000
creator = fee × creatorShareBps / 10000 // launch pairs only
protocol = fee × 3000 / 10000
// the remainder stays in the reserves, which is the LP share| Storage | Meaning |
|---|---|
creatorFees0 / creatorFees1 | owed to the launch token's creator |
protocolFees0 / protocolFees1 | owed to the factory's feeTo |
Claims are pull-based and permissionless — the destination comes from the registry or the factory, never from the caller:
pair.claimCreatorFees(); // pays LaunchRegistry.feeRecipientOf(launchToken)
pair.claimProtocolFees(); // pays factory.feeTo()Reading the recipient at claim time is what makes a CTO handover work without redeploying the pair.
See fees for the split by pair type and the reasoning behind it.
Launch-pair protection#
A launch pair is deployed in the UNSEEDED state, in which swap, burn, skim, and sync all revert. Only the assigned
seedManager — the migration manager — can perform the first mint, and that mint flips the pair to OPEN, after which
it behaves exactly like a plain pair.
UNSEEDED ──(migration manager's first mint)──▶ OPEN
│ │
swap / burn / skim / sync revert everything permittedAccess is layered: the swap factory's createLaunchPair requires an authorised creator (the launch factory), and the
pair itself requires the seed manager for its first mint. Front-running a graduation therefore has no entry point —
creating the pair, and seeding it at a fake ratio, are both gated.
isLaunchPairCreator is a set rather than a single address, so granting a new launch factory does not revoke an
older one. Curves still in flight on a previous launch factory can graduate normally.
Routes#
ETH → WETH → Token
Token → WETH → ETH
Token A → Token B
Token A → WETH → Token BPaths are capped at two hops. That keeps gas predictable and quoting simple, at the cost of not finding exotic routes.
ETH is wrapped and unwrapped by the router; pairs only ever hold WETH.
Each stack ships its own WETH and its own factory. A token that graduated on this stack has no pair on an older router, and routing through the wrong one fails quietly instead of reverting. Use the addresses on network.
Liquidity#
Adding and removing liquidity works the way it does on V2: deposit both sides in ratio, receive LP tokens, burn them to
withdraw. MINIMUM_LIQUIDITY (1,000 wei of LP) is retained on the first mint to keep the share price well-defined.
The only position that behaves differently is the initial LP from a graduation, which is held by the
PermanentLiquidityLocker and can never be withdrawn. Everything you add on top of it is yours.