Maelstrom
Launch coin

Launching a coin

Two wallet prompts. The first publishes your coin; the second opens its pool, seeds it with the whole float, locks the position and records the launch. If the second fails, none of it happened — and the coin from the first is still in your wallet, ready to try again.

What you choose

what it isbounds
name / symbolwritten into the coin's Currency in Sui's coin registry, permanentlyname 1–32 characters, symbol 1–10 printable ASCII
description / logoalso permanent; the logo is an https link, pinned to IPFS by the interfacedescription up to 280 characters
linkswebsite and socials, stored on the launch record as a small JSON documentat most 1,024 bytes; never read on-chain
pair assetwhat your coin trades againstanything the launchpad does not refuse — see Pair assets
fee tierthe pool fee on every trade1% (tick spacing 200) or 2% (tick spacing 220)
creator premineshare of supply sent straight to you0 by default, hard-capped at 20%
dev buypair asset spent buying your own coin as the pool's opening tradefrom a few base units up to just short of the whole float
fee routingwhat happens to your share of fees — see Feeskeep it, or buy back and burn; changeable later
fee recipientwhere your share goesdefaults to the launching wallet; changeable later

What you do not choose:

  • Supply is 1,000,000,000 coins at 9 decimals, for every coin. It is a constant in the template, not a field, so no two coins launched here differ in anything but their four strings.
  • The top of the range is always the last usable tick. There is no ceiling to set.
  • The opening valuation is the same for every launch in the interface: a $4,000 market cap, converted into the pair asset at its live price. The contract takes a tick and would accept any; the interface fixes the number because it is denominated in the pair asset, and a figure typed against USDC and the same figure typed against CETUS are orders of magnitude apart.

The launch fee is 2 SUI, paid to the treasury in the second transaction. The contract caps it at 100 SUI (MAX_LAUNCH_FEE), so it can never quietly become a tax, and returns whatever you send above it.

The two prompts

1 — publish. Nothing is compiled per coin. The interface takes the template's checked-in bytecode, renames its module and one-time witness after your symbol ($WAVE becomes …::wave::WAVE), swaps your strings in for the placeholders with @mysten/move-bytecode-template, and asks your wallet to publish it and call 0x2::package::make_immutable on the resulting UpgradeCap. When it lands you hold the entire supply, and the coin's Currency<T> is waiting in the registry for its final registration.

2 — launch. One programmable transaction:

coin_registry::finalize_registration<T>          if the Currency is still pending
<Cetus aggregator swap: SUI → pair asset>        if you pay for the dev buy in SUI
launchpad::launch_as_a<T, Q> / launch_as_b<T, Q>

finalize_registration is permissionless, so somebody else may have done it already; the interface checks rather than assumes. The swap, when present, carries a 1% slippage bound, and its output becomes the seed — so there is no moment at which you hold the pair asset and the launch has not happened.

The dev buy

The seed you pass is the dev buy, computed on-chain along the position's own curve — see What it is. Before it moves the price, two things come off it:

  1. The protocol's share of the pool fee the computed trade does not pay: seed × fee rate × (1 − creator share), which is 0.2% of the seed at the 1% tier.
  2. A rounding margin of seed / 1000 + 10 base units, held back from the price computation because Cetus rounds the first deposit's pair-asset requirement up. Whatever is not needed comes back to you in the same transaction.

min_coins_out is the least the dev buy may return. It is not protection from other traders — nobody can trade before this, because the pool does not exist yet. It guards against the chain and the interface disagreeing: the launchpad's creator share changing between preview and signing, or the aggregator swap delivering less than planned. The interface's preview replays the contract's arithmetic exactly (previewLaunch in the SDK), so it sets the bound 2% under the preview rather than guessing at slippage.

A launch with no dev buy still needs a seed: the few base units that open the pool inside the range. They buy you a sliver of coin, which is correct — that is what they would buy.

Which side of the pair

Cetus requires a pool's coin A to sort above coin B by type name, and your coin's type name starts with its freshly published package address. So which function the launch calls is read off the chain after the first prompt, not chosen:

factory::is_right_order<T, Q>()the callpoolprice reads as
truelaunch_as_aPool<T, Q>pair asset per coin; buys move it up
falselaunch_as_bPool<Q, T>coin per pair asset; buys move it down

Against SUI it is always launch_as_a. The boundary tick flips with the side — the bottom of the range for coin A, the top for coin B — and the SDK's boundaryTick and previewLaunch handle both, including pair assets that do not have 9 decimals. USDC has 6 and BLUB has 2; a tick is a ratio of raw units, so getting that correction wrong by hand opens a launch orders of magnitude off.

What can go wrong

A launch aborts with one of the launchpad's errors, named in the wallet and the explorer:

abortwhat happened
EPausedthe owner has paused launches (set_paused); fee collection is unaffected
EQuoteNotAllowedthe launchpad's registry refuses this pair asset
ETickSpacingNotAllowedthat fee tier is not offered
ECreatorShareTooHighpremine above 20%
EBadFeeRoutea fee route other than 0 (wallet) or 1 (buyback-burn)
EMetadataTooLonglinks above 1,024 bytes
EAlreadyLaunchedthis coin already has a launch — against any pair asset, at any tier
EWrongSidelaunch_as_a for a coin that sorts below its pair asset, or the reverse
EInsufficientLaunchFeethe fee coin is below the launch fee, which may have changed since the page loaded
ETickNotAlignedthe boundary tick is not a multiple of the tick spacing
ETickOutOfRangethe boundary tick leaves no room for a range before the edge of the pool
EZeroSupplythe supply coin is empty
ESeedTooSmallthe seed cannot move the price off the boundary, or is not larger than its own rounding margin
ESeedTooLargethe seed would buy the entire float
EDevBuyBelowMinimumthe dev buy would return less than min_coins_out
EWrongVersionthe Launchpad object and the package disagree on version — an upgrade without migrate

And a few can come from Cetus inside the same call:

Cetus abortwhat happened
config::EPackageVersionDeprecateCetus retired the version the launchpad links against, or paused itself; the launchpad needs an upgrade (see Contracts)
factory::EPoolAlreadyExista pool for this coin, pair asset and tier already exists. Opening one takes some of the coin, which only you hold — unless you have sent some away
factory::ECoinTypeNotAllowedCetus has put the coin or the pair asset on its deny list for new pools

None of these cost more than gas. Every abort undoes the whole second transaction: the supply, the seed and the fee stay in your wallet, and the coin can still be launched.