Fees

One pool fee, three shares, and yours is the largest of them.

The split

Every trade pays the pool fee of the tier you launched at — 1% at tick spacing 200, 2% at 220 — and Cetus takes its cut before any liquidity provider sees the rest. Cetus's GlobalConfig sets protocol_fee_rate to 2,000 of 10,000, so 20% of every fee never reaches a position. Your launch's position earns the other 80%, and that is what Maelstrom splits:

of what the position earnsof the pool feeof volume, 1% tierof volume, 2% tier
You, the creator80%64%0.64%1.28%
Protocol20%16%0.16%0.32%
Cetus—20%0.20%0.40%

It is easy to read "80% to the creator" and multiply it by 1%. That gives 0.80%, and it is wrong by exactly Cetus's share. The figure to hold on to is 0.64% of volume at the 1% tier.

The Cetus row is Cetus's to change: its administrators can move the protocol fee rate (up to 3,000 of 10,000) and an individual pool's fee rate, which would move every percentage above except the first column. The first column is the launchpad's, and for an existing launch it does not move.

Fees accrue on both sides of the pair. A buy pays its fee in the pair asset, a sell pays it in your coin — so a coin that trades in both directions earns you both.

Where the protocol's 20% goes

Launchpad.buyback_bps of it goes to the buyback sink, the rest to the treasury. buyback_bps is 10,000 today, so all of it goes to the sink — the stream set aside for $STROM — and the treasury receives none. The treasury's income is the launch fee: 2 SUI per launch.

Your share cannot be cut after the fact

Launch.creator_fee_bps is copied onto your launch from the launchpad at the moment it happens. The owner's set_creator_fee_bps changes the figure for future launches only; collect_as_a and collect_as_b read the launch's own copy, never the global one. A creator commits to a launch on the strength of a number, and a protocol that could revise that number later would not really have given them anything. You can read any launch's figure off its object, or with launchpad::launch_creator_fee_bps.

The protocol's own buyback_bps is not snapshotted, because it only divides the protocol's money.

The honest limit of this guarantee: it is a property of the launchpad's code as published, and the launchpad package can be upgraded. An upgrade cannot touch your liquidity, but it could change how fees are split. Who holds that power, and why the package has to stay upgradeable at all, is in Contracts.

The dev buy's fee

A computed first trade pays no pool fee, so the launch charges the part the protocol would have received explicitly: take_dev_buy_fee takes seed × fee rate × (1 − creator share) from the seed before it moves the price — 0.2% of the seed at the 1% tier — and splits it between the buyback sink and the treasury like any other protocol fee. Your own share of that fee is not taken, because it would only have been paid back to you. Cetus receives nothing on the computed trade.

What you can do with your share

Two settings on your Launch, both creator-only, both effective from the next collect — so you can change either one later, without relaunching. The one exception is holder rewards, which is final.

Where it goes

set_fee_recipient(pad, launch, recipient) sends your share somewhere other than the wallet you launched from — a multisig, a treasury, a splitter. 0x0 resets it to you. It moves only your share.

Or: buy back and burn

set_fee_route(pad, launch, 1) stops paying you and destroys your share instead:

  • The part in your coin is burned directly. Coins launched here have a burn-only supply, so Currency::burn_balance really shrinks the coin's total supply; Launch.burned counts every coin destroyed this way.
  • The part in the pair asset buys your coin in its own pool — a flash swap against your launch's pool — and burns what it buys, so the buyback walks the price up through the same range your launch seeded.

One collect's buy may move the pool's square root price by at most a quarter of the pool fee. Collects are permissionless, so a sandwich — trade, collect, trade back — costs nothing to arrange; it only pays when the collect moves the price by more than the round trip costs in fees, and the cap keeps every one underwater.

What the cap holds back, and an amount too small to buy even one base unit of your coin, is not wasted and does not abort the sweep: it waits in Launch.pending_buyback and joins the next collect's buy.

Either route also burns the launch's dust — the few coins the first deposit's rounding left behind — at the first collect.

Each such collect also emits BoughtBackAndBurned, with what was spent, bought and destroyed.

set_fee_route(pad, launch, 0) switches back to being paid, and anything still waiting in pending_buyback goes out with the next payout rather than sitting in the launch forever.

Or: reward your holders

set_fee_route(pad, launch, 2) gives your share to the coin's holders, paid in the pair asset — a coin paired with SUI pays its holders SUI, one paired with $STROM pays $STROM.

  • The part in the pair asset goes to the holders as it is.
  • The part in your coin is sold for the pair asset in the coin's own pool, inside the collect, under the same price cap as a buyback. What the cap holds back waits on the launch (launchpad::pending_rewards) and is sold at the next collect.

Both go into a rewards pot on your Launch (launchpad::rewards_pot), on chain and visible to anyone. Each collect that adds to it emits HolderRewardsAdded. Anything still waiting in pending_buyback from a buyback route goes into the pot too.

This route is final. A launch can move onto it from either other route, and never off it — not back to paying you, not to a burn. Holders are told your share is theirs; a creator who could switch it back could make that promise and break it the same day.

It can only be chosen once the launchpad has a distributor: the one address allowed to pay out of pots, set by the owner with set_holder_rewards_distributor. Until then the route is refused (EHolderRewardsOff).

How holders are paid

Who holds a coin is not something a Sui contract can read — a balance is spread over coin objects that only an indexer sees together — so the payout is computed off chain, by the project's distributor (distributor/ in the repository), and settled on chain by pay_holder_rewards(pad, launch, recipients, amounts):

  1. Every few hours, at a random moment, it reads every holder of the coin straight from the chain, through Sui's GraphQL: coins held as coin objects and as address balances alike.
  2. Each holder counts for what they held through the whole interval since the previous round — the smaller of their two balances. Buying just before a snapshot and selling just after earns nothing.
  3. What reached the pot since the previous round is split pro rata to that, and paid to everyone owed more than a few cents; smaller amounts carry over until they are worth a transaction.

Left out: the pool, the launch, the protocol's treasury and buyback sink, the distributor, and addresses that are contracts rather than wallets. Coins deposited in another market or contract — another pool, a vault — are that contract's, not a wallet's, and earn nothing while they are there.

What the contract guarantees: nothing leaves a pot except to the addresses a payout names, never more than the pot holds, only when the distributor sends it, and every recipient and amount is published in HolderRewardsPaid. What it cannot guarantee is the split itself — holders trust the distributor's arithmetic, and the events are there so anyone can check it. If the distributor's key is ever in doubt, the owner names a new one; the pots do not move.

Collecting

collect_as_a and collect_as_b are permissionless. Anyone can call them for any launch — a bot, a buyer, you — and it does not matter who does, because the proceeds can only reach the recorded fee recipient or the launch's rewards pot, the buyback sink and the treasury, or the burn. There is no path by which the caller pays themselves. A collect with nothing earned is a no-op, not an abort.

Nothing on chain calls them on a schedule, so the project runs a keeper (keeper/ in the repository) that sweeps every launch owed more than a dollar, in batches. If it stops, fees keep accruing in the pool and the next collect, from anyone, sweeps all of it.

Outside holder rewards there is no "owed" balance and no claim step. A transfer on Sui is a new object in the recipient's name; there is no receiving contract that can refuse it. The one exception worth knowing is a regulated pair asset — USDC on Sui is one — whose issuer can deny an address: from the next epoch a denied address cannot receive that coin, and a collect paying it aborts. Nothing is lost — the fees stay in the position — and the creator can point set_fee_recipient elsewhere.