Overview

How a launch works

A launch is an ordinary Raydium LaunchLab pool with one difference: the account it forwards creator fees to is a program-derived address whose behaviour is fixed in advance. Here is the whole path.

The short version#

deployer ─▶ LaunchLab initialize_with_token2022
                 creator = the Stags policy PDA
                 quote   = a tokenized stock
                 ▼
           the pool trades, StonkFun collects the whole 1% fee
                 ▼
           StonkFun forwards the creator's share, off chain,
           into ATA(creator, quote_mint) = the policy's quote_escrow
                 ▼
     anyone ─▶ route  ─▶ protocol cut, bankroll share, LP share
     anyone ─▶ compound_lp, sweep_profit, buyback_and_burn, burn_base_leg

Every money instruction after the launch is permissionless, with two exceptions that are named on the instruction reference. There is no privileged keeper. If the one running stops, anybody can call the same instruction with the same result.

A launch is two transactions. Before either account exists, the policy address is computed offline from the base mint alone, because it derives from the seeds policy and base mint, and this works because LaunchLab's creator field is not a signer, so a pool may name an address that does not exist yet. Transaction one creates the LaunchLab pool naming that computed address as its creator. Transaction two calls init policy, which creates the policy at exactly that address and verifies the pool back: it re-derives the pool address, checks the owning program, the account length, the state discriminator and both mints, then reads the creator field and requires it to equal the policy address.BEFORE EITHER ACCOUNT EXISTSCompute the policy address["policy", base_mint]the mint keypair is the only inputcreator is a field, not a signerso a pool may name an address that does not existTRANSACTION 1LaunchLab initialize_with_token2022creator = the computed policy addresspool PDA is ["pool", base_mint, quote_mint]TRANSACTION 2init_policy verifies the pool backaddress, owner, length, discriminator, both mints,then creator must equal the policy address

The check matters because whoever the pool names as creator receives every fee the launch ever earns, and a policy pointing at the wrong pool would look perfectly healthy from the outside.

1. The pool#

The launch is a Raydium LaunchLab pool created with initialize_with_token2022. That instruction takes a creator, described by Raydium as "the wallet fees are forwarded to". Stags passes the policy PDA there, so creator fees accrue to a program-derived address from the first trade. There is nothing to register afterwards and no wallet in the path.

The quote asset is one of StonkFun's launchable pairs, which are dominated by tokenized stocks. See quote assets and pairs for which of those pairs can actually be used, because not all of them can.

2. The policy account#

In the same flow the deployer calls init_policy. This creates the Policy account at seeds ["policy", mint] and, with it, four token accounts owned by that PDA and one associated token account for the fee key. The policy records the splits, the mode, the model, the risk setting, the target asset and the leverage cap, plus three keys: the authority, the agent and the guardian.

The mode is fixed here and nowhere else

init_policy is the only place Mode is ever written. set_policy can change the model, the splits, the risk, the target asset and the leverage, but not the mode, so a launch sold as Passive can never quietly become an Agent launch.

It proves the fee stream rather than assuming it#

init_policy takes the LaunchLab pool account and refuses to create a policy that is not actually the pool's creator. Whoever the pool names as creator receives every fee the launch ever earns, so a policy pointing at the wrong pool could never be paid, and it would look perfectly healthy from the outside.

Five things are established before a single field is trusted:

  1. the account sits at the address LaunchLab would have derived for this pair, ["pool", base_mint, quote_mint];
  2. it is owned by the pinned launchlab_program;
  3. it is long enough to hold the struct;
  4. it carries LaunchLab's own PoolState discriminator;
  5. the base and quote mints read back out of it match the pair, which is belt and braces on top of the seeds.

Only then is creator read, and it must equal the policy address. Anything else fails with NotTheLaunchPool or PoolCreatorNotPolicy. Three field offsets are read out of the account directly rather than pulling in Raydium's crate, and they were confirmed against a real mainnet pool.

3. The Fee Key NFT, which turns out not to exist here#

The program was built expecting LaunchLab's claim to be gated by a per-launch Fee Key NFT, so init_policy creates fee_key_account, an associated token account for fee_key_mint owned by the policy PDA, and no instruction in the program ever debits it.

Reading LaunchLab's actual IDL corrected this

The claim instruction is claim_creator_fee: no arguments, eight accounts, and no NFT anywhere in it. The claim is authorised by the creator being a signer, which the policy PDA is. The NFT in this system is Raydium's locked-liquidity fee NFT, minted at migration, which is a different object entirely.

Policy.fee_key_mint and fee_key_account are still on the account and still created at launch. They are simply not part of the claim path. This documentation describes what the program holds and what the venue actually requires, and says where they disagree.

claim_creator_fee, from LaunchLab's own IDL
discriminator [26, 97, 138, 203, 132, 171, 141, 252]

0 creator                  signer, writable
1 fee_vault_authority      PDA["creator_fee_vault_auth_seed"]
2 creator_fee_vault        PDA[creator, quote_mint]   writable
3 recipient_token_account  ATA(creator, token_program, quote_mint)   writable
4 quote_mint
5 token_program
6 system_program
7 associated_token_program

4. How the fee arrives, and routing#

The program keeps two escrows, a quote leg and a base leg, and handles them differently:

  • The quote leg, which is the stock, is money. It lands in quote_escrow and is split by route.
  • The base leg, which is the launched token, is already the thing holders want destroyed, so it needs no swap. It lands in base_escrow and is burned by burn_base_leg or by buyback_and_burn.

The quote leg arrives by forwarding, straight into the escrow

A Stags launch is StonkFun's standard mode, which has no creator position to claim from: StonkFun takes the whole 1% at the protocol level and forwards the creator's share itself. Observed on chain across five real launches, a forwarding transaction is two instructions signed by StonkFun's own platform_fee_wallet, 5CEbueQnq1Ym2uSSx2xXds3jQAqT1BDnkA59RZobSPAG: a createIdempotent for the creator's quote associated token account, which StonkFun pays for, then a transferChecked into it.

That destination is ATA(creator, quote_mint), and the creator is the policy, so it is quote_escrow. Nothing needs to move the money into the program's reach; route reads it where it lands. The cadence and the caveats are on the fee split.

So claim_fees finds nothing, and fees_claimed stays at zero

claim_fees forwards LaunchLab's claim_creator_fee, which drains a creator fee vault. This launch type has no such vault, so the call reverts with NothingToDo by construction, and Policy.fees_claimed, which only that instruction increments, reads zero forever while real fees flow. Use route. This is on what is not built yet because the counter is misleading and the fix belongs in the program.

Account 3 is why the escrow is an associated token account

claim_creator_fee constrains its recipient to the creator's associated token account, and the constraint is enforced: a mainnet simulation with a deliberately wrong but otherwise valid recipient returns Anchor error 2015 ConstraintAssociated. For a while quote_escrow was a bespoke program address, so the two could never have matched. It is now ATA(policy, quote_mint), which is also exactly where the forwarding lands. One address, two mechanisms.

claim_fees may forward exactly one instruction, and nothing else

It is the one passthrough that has no choice but to sign as the policy, because LaunchLab demands the pool's creator signs. So the bound is on which instruction: the forwarded data must equal claim_creator_fee's eight byte discriminator, [26, 97, 138, 203, 132, 171, 141, 252]. That instruction takes no arguments, so its data is its discriminator and there is no argument surface left to steer. Before that check existed, a caller could have handed LaunchLab create_vesting_account instead. See the safety model.

Creator fees are quote only, not both legs

An earlier reading of StonkFun's documentation recorded that fees arrive in both tokens of the pair. That describes the older Raydium launchpad type, not this one. On this path both mechanisms are quote only: claim_creator_fee moves one mint, with vault seeds of (creator, mint) and no pool in them, and every forwarded credit observed was in the pool's quote token. The base escrow is real, and nothing fills it pre-migration.

What a launch costs#

About 0.0176 SOL, and that figure is decoded from a real StonkFun launch on mainnet rather than estimated. It answers a question worth answering out loud, because the number people expect is much larger.

SideItemSOL
LaunchLabthe base mint0.002718
LaunchLabthe pool state0.002830
LaunchLabquote vault0.001488
LaunchLabbase vault0.001488
LaunchLabnetwork fee0.000040
LaunchLab total0.008564
Stagsthe policy account0.002682
Stagsfour token accounts0.006238
Stagsmint and metadata0.002682
Stagstwo signatures0.000010
Stags total0.011613

The two subtotals do not simply add: the mint appears on both sides, because LaunchLab creates it. The honest total is about 0.0176 SOL per launch. LaunchLab's side was decoded from the newest live launch at the time of reading, mint 5JcJTPz7XAeV75iocd2D32oyFpTrtnt752eXFRikA8TE; the Stags side was read live by the preflight script. Rent moves, so re-read it rather than trusting this table.

One thing a launch integrator has to get right: the compute limit

init_policy creates five associated token accounts and lands at roughly 195,000 to 200,000 compute units against the 200,000 default. Which side of the line it falls on is a property of the randomly generated mint, because deriving a program address walks down from bump 255 at around 1,500 units an iteration. The test suite passed it four times and failed the fifth with consumed 200000 of 200000: a real launch would have failed on nothing but its mint.

Both senders now set an explicit limit of 400,000. A transaction may carry at most one compute limit instruction, so a caller adding its own must tell the SDK builder not to prepend one.

What the deployer decides#

These are the arguments to init_policy, in order. Everything else about a launch is either LaunchLab's or the protocol's.

mode
Passive | Agent
Fixed forever at launch. Passive runs a deterministic keeper with no model at all; Agent runs a model from the registry. See modes.
model_id
u16
Which registry entry trades the bankroll. Validated against the on-chain Model account only when the mode is Agent, and the model must be enabled. Meaningless in Passive mode.
to_lp_bps / to_bankroll_bps
u16, u16
The fee split, after the protocol cut. Must sum to exactly 10000, and to_bankroll_bps may not exceed the protocol's max_bankroll_bps ceiling.
profit_to_lp_bps / profit_to_holders_bps / profit_to_bankroll_bps
u16 x 3
The profit split above the high water mark. Must sum to exactly 10000.
risk
u8, 0 to 100
Caps what share of vault equity may sit at venues at once, and gates which asset classes are reachable at all. Must not exceed the origin's max_risk. See risk, origins and asset classes.
target_asset
Pubkey
A mint. In Passive mode it is what the keeper buys, and passive_deploy refuses any venue account whose asset_mint is not this exact key.
target_leverage_x
u8
Capped at MAX_LEVERAGE = 5 at the point it is written, and again on every remote equity attestation.

Three keys are also recorded from the accounts passed in: authority (the deployer, who can change an unlocked policy), agent (which may move funds only to pre-registered venues) and guardian (which may pause and may not unpause). The agent and guardian are recorded as raw keys and are not checked at launch, so they may be addresses the deployer does not control yet.

The accounts a launch creates#

All five token accounts are associated token accounts, not bespoke PDAs. That is a deliberate change, and the reason is on what is not built yet: the on-chain associated token program sizes an account against the live Token-2022 program, which is what lets a policy be created against a real tokenized stock at all.

AccountDerivationHolds
Policy["policy", mint]The splits, mode, model, risk, target, counters and the three keys
quote_escrowATA(policy, quote_mint)Quote fees awaiting route, and the LP share awaiting compound_lp
base_escrowATA(policy, base_mint)Base fees, and bought-back tokens, both awaiting the burn
bankrollATA(bankroll_auth, quote_mint)The vault itself, in the quote mint
holder_rewardsATA(holder_auth, quote_mint)Profit staged for buyback, spendable only by buyback_and_burn
fee_key_accountATA(fee_key_auth, fee_key_mint)The fee key account. Never debited.

Each *_auth is a program address at ["<name>_auth", policy]. Three of them are needed because only one associated token account can exist per owner and mint, and the three quote-side accounts would otherwise collide on a single address.

Why the fee key account has its own authority

The launch flow sets fee_key_mint = mint as its "no fee key" sentinel, which would have made ATA(policy, fee_key_mint) byte-identical to base_escrow. init_policy would then have tried to initialise two accounts at one address and failed on every real launch, while the test suite, which uses a separate fee key mint, stayed green.

Every quote-denominated account belongs to one policy's own quote_mint. There is no protocol-wide quote mint, because every launch pairs a different stock.

Why no StonkFun integration is needed#

StonkFun is not an AMM. It is a front end and adoption layer over LaunchLab. In its own words, you can skip it entirely and construct the launch yourself against LaunchLab with your own mint keypair and your own instructions, and still get everything the platform does around a launch. It scans pools carrying its platform id every minute and adopts the ones it has no record of, and an adopted launch is indistinguishable from an API-created one.

So Stags builds the LaunchLab initialize itself and bakes in the platform id. No key, no signup, no business relationship.

FieldAddress
LaunchLab programLanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj
GlobalConfigB7ctMMdGvy46Am56myTtzfkNzt9kWZVTNGM2BWrJ9adg
Platform id, standard4E876qZTE9FJMrBzgVtBrSrzz2TLivB5Y5QXPjB4gZL7
Platform id, reward6BwHHDg3u1854jC8PDLXvR4spTcLNaoBxLJNGC4nTESt
Curve rule, standardQYZp1YzqEHU67ngXphF9LAkxkxWGvpWEv3rXh4yDbWA
Curve rule, reward7MNLMFMmFVhN9Z3sj51QZso28oD2Ta3zDPwNAmfrs2vk
Pool PDA["pool", base_mint, quote_mint]

Read from StonkFun's developer documentation and its open API on 2026-09-22, quoting the SPYX pair. The pool PDA is confirmed twice independently: LaunchLab's own IDL declares exactly those seeds on initialize, initialize_v2, initialize_with_token_2022 and migrate_to_cpswap, and the derivation reproduces the pool address StonkFun reports for 25 of 25 live launches. No pool address needs storing: the two mints already on the Policy are enough.

The curve, as StonkFun configures it#

FieldValue
CurveConstantCurve, migrates to Raydium CPMM
Base decimals6
Base supply1000000000000000
totalSellA793100000000000
Opening market cap$3,305
Graduation market cap$48,567
Implied run14.69x
Reward tax tiers100 or 300 bps, and only those

Read 2026-09-22 against the SPYX pair. These are StonkFun's numbers, not ours: Stags does not choose the curve.

The raise amount is in the quote's decimals

StonkFun's own warning: the Raydium SDK's 85 SOL constant means 850 units on an 8-decimal stock and 85,000 on a 6-decimal stablecoin, so a launch that copies the constant blindly will "launch happily and graduate at a market cap nobody intended". raise.raw must come from StonkFun's /launchlab/pricing endpoint for the specific quote.

How much of a trade reaches the policy#

Two fields decide the mechanism, and they are separate rather than one list of three types. launchpad is launchlab here, or raydium for the older locked-position type with its Fee Key NFT, and mode is standard or reward. A Stags launch is launchlab, standard: no creator position, and StonkFun forwards the creator's share itself. Reward mode has no creator fee at all.

How much, and how often

The share is whatever StonkFun forwards, and it is not fixed by anything on chain. What was measured, on 2026-09-23, is the arrival: 35 of 35 credits on one migrated launch over 3 hours 19 minutes with a median gap of 299 seconds, 25 of 25 on another every 15 to 50 minutes, and 8 of 8 on a still-bonding SPYX launch with a median gap of 1035 seconds. All from the platform fee wallet, all in the pool's quote token.

The older Raydium launchpad type is the one with a documented share, about half of the 1% pool fee and about three quarters on the 2% tier, arriving in both tokens of the pair and claimed through a Fee Key NFT. It is not the type this launch flow builds.

The on-chain fee fields are consistent with that and are worth showing, because they look at first glance like the creator is paid nothing. Both live platform configs, decoded from mainnet on 2026-09-22:

FieldStandard configReward config
creator_fee_rate00
fee_rate10000 (1%, StonkFun's own cut)10000
creator_scale00
platform_scale1000000 (100% to StonkFun)1000000
burn_scale00

Every creator fee vault behind all 25 live launches reads exactly 0, and a mainnet claim simulation fails inside LaunchLab's own handler with nothing to transfer.

That is exactly what you would expect when the platform takes the whole 1% at the protocol level and then forwards the creator's share itself. The creator fee vaults are empty because that path is not the one in use, not because nobody is paid, and on every one of the five forwarded launches watched the vault read zero while credits arrived. The practical consequence for this program is that route, not claim_fees, is the instruction that does the work.

what StonkFun's reward mode does, which is not what Stags does
StonkFun reward mode:  a 100 or 300 bps TRANSFER TAX on the mint,
                       collected by StonkFun's own withhold authority,
                       and distributed by them on their reward cycle.

Stags:                 profit from the bankroll buys the token back
                       and burns it. Nothing is paid to a wallet.

Both can run on the same launch. They are different streams from different sources, and this documentation never calls them the same thing.