Joseon docs
AppExplorerLaunch app

Protocol

Security

Audit surface, invariants, and what is still centralized.

Status#

Testnet. Unaudited by a third party. Admin keys are a single EOA.

Do not put real value into this deployment. It runs on GIWA Sepolia with test ETH, and the items under open items are not theoretical.

What has been done: an internal review with fuzzing, invariant testing, and static analysis, plus source verification of every deployed contract. What has not: an external audit, and a multisig over admin powers.

What is tested#

ToolScope
Hardhat unit testsbehaviour and integration across the launch and DEX contracts
Foundry fuzzcurve quoting, fee splitting, and guard conditions
Foundry invariantssupply, reserve, and fee-ledger properties over ~16k call sequences
Slitherstatic analysis, with every finding triaged and reasoned
Gas snapshotsregression tracking on hot paths

Curve properties proven by fuzzing#

  • The buy fee is exactly 2% floored, and net + fee == gross. No dust vanishes.
  • A buy never oversells the 800M allocation, and ethUsed + refund == gross.
  • Graduation uses the minimal ETH: one wei less must not graduate. The last buyer is not overcharged.
  • A buy followed immediately by a sell is never profitable, so the curve cannot be cycled dry.
  • Fees split 70/30 with no dust, and only the registry's recipient can claim.
  • A CTO redirect cannot take fees the previous recipient already accrued.
  • Selling above circulating supply reverts, so ETH cannot be minted out of virtual reserves.
  • Slippage and deadline guards hold; nothing trades after graduation.
  • A claim to a contract that rejects ETH reverts without zeroing the balance.

Invariants held#

InvariantWhy it matters
totalSupply never changesfixed supply is a hard promise
balanceOf(pool) >= realTokenReserve + dexTokenReservethe pool always covers what it owes
pool.balance >= realEthReserveaccounting cannot exceed custody
realEthReserve >= creatorFeeAccrued + protocolFeeAccruedsells cannot spend earmarked fees
Σ claimableCreatorFees == creatorFeeAccruedthe fee ledger balances across redirects
state never regresses from GRADUATING / DEX_ACTIVEone-way lifecycle
LP share value never decreasesswap fees, donations, and rounding accrue to the pool, never leak
balanceOf(0) >= MINIMUM_LIQUIDITYa pool cannot be drained and re-primed at a chosen ratio

Design decisions that carry security weight#

Atomic graduation. Pair creation, funding, minting, and locking happen in one transaction. There is no window in which a half-migrated pool can be traded.

Empty-pair assertion. The migration requires both reserves and both balances to be exactly zero before funding. Strict equality is the point — it is the defence against someone pre-seeding the pair at a fake ratio.

Launch-pair gating. Only the launch factory can create a launch pair, and only the migration manager can seed it. Both halves of a front-run are closed.

Creator set, not a slot. An earlier factory stored one launchPairCreator, so authorising a new launch factory would have revoked the old one in the same transaction and left in-flight curves unable to graduate — their ETH stranded with no migration path. That was caught during review, with a live launch mid-curve at the time. The current factory uses a set with per-creator revocation.

Fees booked, not transferred. A swap makes no external call beyond its two ERC-20 transfers, and the fee recipient is read from the registry only at claim time. No registry call sits on the swap path.

No upgrades. Nothing is a proxy and nothing has an admin-controlled implementation slot. A deployed launch cannot have its rules changed underneath it. The cost is that fixes ship as a new stack.

No metadata setter. tokenURI() derives from the token address against an immutable base, so a live token's metadata cannot be repointed by anyone.

Open items#

These are known and tracked, not surprises.

  1. No multisig or timelock on admin powers. feeToSetter, launch-pair authorisation, and setupAdmin are held by one EOA. This must change before mainnet. It now guards a real fee stream, which raises the priority rather than changing the requirement.
  2. External audit outstanding. Internal review only so far.
  3. The router has no dedicated invariant suite. Pairs and the curve do; the router is covered by unit tests only.
  4. Per-launch contracts start unverified. Blockscout does not match new token and curve deployments against already-verified bytecode. A repo script submits them — see network.

Graduation has now been exercised on these addresses: GIWA graduated at block 31727133, migrating 3.6 WETH and 200,000,000 tokens, with the resulting LP position held by the locker. You can verify that yourself by reading PermanentLiquidityLocker.lockedPair(token) and lockedLiquidity(token).

What is centralized#

Being direct about this, since "decentralized" gets used loosely:

PowerWho holds itCan it affect your tokens?
feeTo — protocol fee destinationdeploy EOAno, it only redirects the protocol's own share
Launch-pair authorisationdeploy EOAit gates which launch factories can graduate
setupAdmin — one-time wiringdeploy EOAalready used; configured blocks reuse
Fee recipient per tokenthe token's creatoryes, for that token's creator fees
Locked initial LPnobodyit cannot be withdrawn at all
Token supply and metadatanobodyno mint, no burn, no URI setter

The admin powers cannot mint tokens, move your balances, drain a curve, or unlock liquidity.

Reporting something#

Found a problem? Please report it privately rather than opening a public issue, and give us a chance to deploy a fixed stack first. Since nothing is upgradeable, a fix means a redeploy and a front-end cutover, which takes coordination.