This entry covers an upcoming LaunchLab program update. It was verified against the local release branch before deployment. Confirm the deployed program before relying on the new instruction or the changed
MigrateToAmm layout.get_upgrade_timestamp, a hardcoded cut-over date that several checks compared the clock against; a deprecated Initialize that kept working for three days past that date; and MigrateToAmm’s OpenBook plumbing, which had nothing to talk to after AMM v4 removed its own OpenBook dependency in July. All three are gone. On mainnet the cut-over is months past, so the behavioural effect is nil — but the failure modes changed, and one account list changed hard.
The framework moved at the same time: Anchor 0.32.1 to =1.0.2, Agave 2.3.0 to 3.1.10. And, as on AMM v4 and CPMM, there is a new admin instruction for reclaiming rent.
Trading, fees, vesting, curve rules and platform configuration are untouched.
TL;DR for integrators
- No trade instruction changed its accounts, arguments or math.
BuyExactIn,BuyExactOut,SellExactIn,SellExactOutare byte-identical. No account layout changed. Initialize(the deprecated one) now always fails withNotApproved(6000), before reading any account. UseInitializeV2. Launches already created through it trade and graduate normally.MigrateToAmmis hard-breaking for the migration wallet. It lost all three arguments (base_lot_size,quote_lot_size,market_vault_signer_nonce) and nine accounts. See below.- The three trade
remaining_accountsare now unconditionally required, and thesystem_programslot is validated. A builder that omits them now always fails withNotEnoughRemainingAccounts(6018) instead of only after the cut-over. - One instruction is added:
CollectExcessLamports. Admin-only. Seeproducts/launchlab/instructions. - One error code is appended:
6031LamportsCalculateError. Codes6000–6030are unchanged. - Three
MigrateToCpswapaddress constraints moved into the instruction body, changing their error fromConstraintAddress(2012) toRequireKeysEqViolated(2502). - An IDL refresh is required. One new instruction, one removed argument set, nine removed accounts, one new error variant.
MigrateToAmm lost its OpenBook half
This is the change most likely to break something. Old instruction data carried 17 bytes of arguments after the discriminator; new data is the bare discriminator. Old account lists carried nine accounts that no longer exist in the struct, so everything after the first removal is misaligned.
Arguments removed: base_lot_size: u64, quote_lot_size: u64, market_vault_signer_nonce: u8. All three existed only to configure the OpenBook market that the program used to initialize by CPI. That CPI — initialize_openbook_market — is gone, along with the gen_vault_signer_key check that validated the nonce.
Accounts removed: openbook_program, request_queue, event_queue, bids, asks, market_vault_signer, market_base_vault, market_quote_vault, and amm_open_orders. The last one went because AMM v4’s Initialize2 no longer takes it.
The market account stays, in its original position. AMM v4 still records the market as a reference field on AmmInfo, so LaunchLab still forwards it. Two things about it changed: the program no longer initializes it, and it is now entirely unvalidated — its declaration is a bare #[account(mut)] with no owner, address or seeds constraint, because owner = openbook_program.key() came off together with the openbook_program account and nothing replaced it. Whatever the migration wallet passes there is forwarded straight into AMM v4’s Initialize2 CPI and recorded on the new pool. A caller that wants a genuine initialized market behind that field has to create it beforehand, and the program will not tell it otherwise.
The resulting 23-account list is documented in full on products/launchlab/instructions.
MigrateToCpswap is unaffected — it never had arguments, and its account list is unchanged.
The deprecated Initialize always fails
initialize previously ran a soft deprecation: it worked until get_upgrade_timestamp() + 3 days, then returned NotApproved. With the timestamp helper deleted, the failure is unconditional — the handler is now a msg! and err!(NotApproved) and nothing else.
One detail if you are reading logs: the Accounts struct is unchanged and still carries four init constraints, so Anchor’s generated account-validation prologue runs — and creates those accounts — before the handler returns. The transaction reverts either way, so nothing is actually created, but the failure surfaces after account validation rather than before it.
The instruction is retained rather than removed so its discriminator stays occupied and the IDL keeps a stable shape. Its argument and account definitions are still worth having documented for decoding historical transactions, and the page keeps them behind a warning.
The get_upgrade_timestamp gate is gone
The helper returned 0 under the local and devnet features and the hardcoded mainnet timestamp 1755522000 (2025-08-18 13:00 UTC) otherwise. Four instructions compared the clock against it, across five references in the source. Each becomes the post-cut-over branch unconditionally:
The mainnet timestamp is over a year past, so a correct, up-to-date builder sees no behaviour change. What changed is that a stale builder now fails deterministically instead of appearing to work against a devnet build. The new
system_program validation is genuinely new: that slot previously accepted any account.
CollectExcessLamports
Step 1 of SIMD-0437 landed on mainnet on 3 September 2026, cutting the rent-exempt minimum by 9% with four more steps to come. Every LaunchLab pool vault, fee vault and program-owned PDA created before a step is now over-funded.
The instruction takes four fixed accounts — the signer/destination wallet, one vault authority PDA, and both token programs — then any number of source accounts in remaining_accounts.
The authority slot is the part worth reading carefully. LaunchLab has three vault authority PDAs (vault_auth_seed, platform_fee_vault_auth_seed, creator_fee_vault_auth_seed), and the instruction resolves whichever one you passed by re-deriving all three and matching; a key matching none fails with InvalidOwner (6001). Because one call carries one authority and the token program requires each account’s actual owner to sign, source accounts must be grouped by authority — pool vaults, platform fee vaults and creator fee vaults sweep in separate transactions. Program-owned PDAs are debited directly and can ride along with any authority.
Wrapped SOL follows the same SyncNative → delta-sized UnwrapLamports → assert-unchanged sequence CPMM and AMM v4 use, with LamportsCalculateError (6031) if the round-trip does not net to zero. A SOL-quoted launch keeps its full quote reserve.
Base mints cannot be swept. InitializeV2 and InitializeWithToken2022 revoke MintTokens in the same instruction that mints the supply, so no key can sign a WithdrawExcessLamports for a base mint. Its rent is stranded by design.
The signer may be either the shared program admin or a dedicated collect-lamports wallet; addresses are in reference/program-addresses.
MigrateToCpswap constraint relocation
Three account constraints moved out of the Accounts struct and into the instruction body:
PoolState. Only the error surface differs: Anchor’s generic RequireKeysEqViolated (2502), reported without an account name, rather than ConstraintAddress (2012) naming the offending account. Update any error handling that matched on 2012 for these three accounts.
Toolchain and dependency changes
Anchor 1.0’s two call-site changes apply here too:
Context collapses from four lifetime parameters to one, and CpiContext::new takes the program’s Pubkey rather than its AccountInfo. See sdk-api/rust-cpi.
Two build-system details with no on-chain effect: the local feature was replaced by localnet, which compiles the local wallet in as admin from a LAUNCHPAD_LOCALNET_ADMIN environment variable (yarn test:local-admin wires it up), and the duplicate [profile.release] block in programs/launchpad/Cargo.toml was deleted — Cargo ignores [profile] outside the workspace root, so the root block was already the one in effect, including the fact that the program-level block’s panic = "abort" was never applied.
What did not change
- Every account layout.
PoolState,GlobalConfig,PlatformConfig,PlatformCurveRule,PlatformAllowConfig, vesting records — same sizes, same offsets. - Error codes
6000–6030, including the deliberately-retained6020. - Curve math, fee rates, fee accrual, vesting schedules, and the graduation LP split.
- Platform curve rules and the
GlobalConfigallowlist. Same instructions, same accounts, same semantics; only the clock gate around the curve-rule check is gone. MigrateToCpswap’s account list andremaining_accountsindices.- The Token-2022 transfer-fee authority handover at graduation.
- Program ID. Unchanged.
Pages updated
products/launchlab/instructions—CollectExcessLamportsadded with its account list, authority-resolution table and grouping warning;MigrateToAmm’s argument and account removals documented with the full new list;Initializefronted with an always-fails warning; new “Trade remaining accounts” section covering the now-unconditional three accounts and thesystem_programcheck;MigrateToCpswapnote on the permission-only path and the relocated constraints; inventory and state-change matrix rows.products/launchlab/overview— release banner; the “CPMM-only” and base-mint invariants corrected.products/launchlab/accounts— mint-authority revocation timing corrected to launch creation (it was documented at graduation);CollectExcessLamportsrow added.reference/error-codes—6031documented.reference/program-addresses— new “Excess-lamports collection wallets” section.solana-fundamentals/rent-and-reclaimable-rent— new “What the Raydium programs sweep on their own side” section.sdk-api/rust-cpi,solana-fundamentals/toolchain— Anchor 1.0 pins and the CPI migration notes.

