Launch Types

Locked LP Tokens

Last updated April 22, 2026

LP tokens created during bonding curve graduation are program-locked in a Genesis-owned bucket. No wallet can withdraw them.

Summary

When a Genesis bonding curve sells out and graduates into a Raydium CPMM pool, the resulting LP tokens are transferred to a PDA owned by the Genesis program. The vesting schedule on this bucket is set to Never, making the tokens inaccessible.

  • LP tokens are not burned — they are transferred to a Genesis bucket signer PDA with vesting set to never
  • Program-locked — the lpLockSchedule.startCondition and cliffCondition are both Never, so no instruction or authority can release them
  • Verifiable onchain — fetch the RaydiumCpmmBucketV2 account to confirm the lock schedule and token balance
  • Happens automatically — locking occurs as part of the graduation process with no manual step

How LP Token Locking Works

During graduation, LP tokens are deposited into an ATA owned by a bucket signer PDA — a program-derived address with no private key. The RaydiumCpmmBucketV2 account locks them by setting both lpLockSchedule.startCondition and cliffCondition to { __kind: 'Never' }, preventing any withdrawal.

Some platforms refer to this as "burning" LP tokens. In Genesis the LP tokens are not sent to a burn address — they remain in a verifiable onchain account. The term program-locked is more accurate because the tokens exist and can be audited, but no wallet can access them.

Graduation Flow

  1. Bonding curve sells out (baseTokenBalance reaches zero)
  2. Graduation fires automatically — accumulated SOL and tokens migrate to a Raydium CPMM pool
  3. Raydium returns LP tokens to the Genesis program
  4. LP tokens are deposited into the bucket signer's ATA
  5. The lpLockSchedule.startCondition and cliffCondition are set to Never — program-locking the LP tokens

Verifying the LP Token Lock

Fetch the RaydiumCpmmBucketV2 account and inspect the lpLockSchedule extension to confirm that LP tokens are locked.

Deriving the Accounts

Three accounts form the LP token lock:

AccountDescriptionHow to Derive
Raydium Bucket PDAThe RaydiumCpmmBucketV2 account storing graduation state and lock configfindRaydiumCpmmBucketV2Pda(umi, { genesisAccount, bucketIndex })
Bucket Signer PDAThe PDA that owns the LP token ATA — has no private keyfindRaydiumBucketSignerPda(umi, { bucket })
Bucket Signer ATAThe associated token account holding the locked LP tokensStandard ATA derivation using the bucket signer + LP mint

Fetching and Checking the Lock

verify-lp-lock.ts
1import {
2 genesis,
3 findRaydiumCpmmBucketV2Pda,
4 fetchRaydiumCpmmBucketV2,
5 findRaydiumBucketSignerPda,
6 findLpMintPda,
7 RAYDIUM_CP_SWAP_PROGRAM_ID_MAINNET,
8} from '@metaplex-foundation/genesis';
9import { publicKey } from '@metaplex-foundation/umi';
10import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
11import { findAssociatedTokenPda } from '@metaplex-foundation/mpl-toolbox';
12
13const umi = createUmi('https://api.mainnet-beta.solana.com').use(genesis());
14
15const genesisAccount = publicKey('YOUR_GENESIS_ACCOUNT_PUBKEY');
16
17// 1. Derive the Raydium bucket PDA
18const [raydiumBucketPda] = findRaydiumCpmmBucketV2Pda(umi, {
19 genesisAccount,
20 bucketIndex: 0,
21});
22
23// 2. Fetch the bucket account
24const raydiumBucket = await fetchRaydiumCpmmBucketV2(umi, raydiumBucketPda);
25
26// 3. Check the LP lock schedule
27const lpLockSchedule = raydiumBucket.extensions.lpLockSchedule;
28
29if (lpLockSchedule.__option === 'Some') {
30 const schedule = lpLockSchedule.value;
31 console.log('LP lock start condition:', schedule.startCondition.__kind);
32 console.log('LP lock cliff condition:', schedule.cliffCondition.__kind);
33 // Expected output: both "Never"
34}
35
36console.log('LP token balance:', raydiumBucket.lpTokenBalance);
37
38// 4. Derive the bucket signer PDA
39const [bucketSignerPda] = findRaydiumBucketSignerPda(umi, {
40 bucket: raydiumBucketPda,
41});
42
43console.log('Bucket signer (LP token owner):', bucketSignerPda);
44
45// 5. Derive the LP mint from the pool state
46const [lpMint] = findLpMintPda(umi, RAYDIUM_CP_SWAP_PROGRAM_ID_MAINNET, raydiumBucket.poolState);
47
48// 6. Derive the bucket signer's ATA for the LP mint
49const [bucketSignerAta] = findAssociatedTokenPda(umi, {
50 mint: lpMint,
51 owner: bucketSignerPda,
52});
53
54console.log('LP mint:', lpMint);
55console.log('Bucket signer ATA (holds LP tokens):', bucketSignerAta);

Expected Output

When LP tokens are program-locked, the output confirms:

LP lock start condition: Never
LP lock cliff condition: Never
LP token balance: 123456789
Bucket signer (LP token owner): <PDA address>
LP mint: <LP mint address>
Bucket signer ATA (holds LP tokens): <ATA address>

The startCondition.__kind value of Never confirms that no vesting begins, and the cliffCondition of Never confirms there is no cliff release. Together they prove the LP tokens cannot be withdrawn.

RaydiumCpmmBucketV2 Account Fields

Key fields on the RaydiumCpmmBucketV2 account relevant to LP token locking:

FieldTypeDescription
lpTokenBalancebigintNumber of LP tokens held in the bucket signer's ATA
lpClaimAuthorityOption<PublicKey>Authority that could claim LP tokens — None when no authority is set
lpTokensClaimedbigintCumulative LP tokens claimed (zero when fully locked)
bucketSignerPublicKeyPDA that owns the ATA holding LP tokens
extensions.lpLockScheduleOption<ClaimSchedule>Vesting schedule for LP tokens — startCondition set to Never
poolStatePublicKeyAddress of the Raydium CPMM pool state account (not the LP mint — read the pool state to obtain the LP mint)

ClaimSchedule Fields

The lpLockSchedule extension is a ClaimSchedule with these fields:

FieldTypeDescription
startConditionConditionWhen claims can begin — { __kind: 'Never' } for program lock
durationbigintVesting duration in seconds (irrelevant when start is Never)
periodbigintVesting period interval (irrelevant when start is Never)
cliffConditionConditionCliff condition for vesting — also { __kind: 'Never' } for LP lock
cliffAmountBpsnumberCliff unlock percentage in basis points (irrelevant when start is Never)

The duration, period, and cliffAmountBps fields are present in the ClaimSchedule struct but are functionally irrelevant when both startCondition and cliffCondition are Never. Neither vesting nor cliff release can begin.

FAQ

Are LP tokens burned or locked during graduation?

LP tokens are program-locked, not burned. They are transferred to an associated token account owned by a Genesis bucket signer PDA. The bucket's lpLockSchedule has both its startCondition and cliffCondition set to Never, meaning no wallet can claim them.

Can anyone withdraw the locked LP tokens?

No. The lpLockSchedule on the RaydiumCpmmBucketV2 account has both its startCondition and cliffCondition set to Never. There is no instruction or authority that can release them.

How can I verify that LP tokens are locked onchain?

Fetch the RaydiumCpmmBucketV2 account using the Genesis SDK and check that extensions.lpLockSchedule has both startCondition.__kind and cliffCondition.__kind set to Never. The lpTokenBalance field shows the exact number of LP tokens held. See Verifying the LP Token Lock for the full code example.

What is the difference between burning LP tokens and program-locking them?

Burning destroys tokens via an SPL token burn instruction, permanently removing them from circulation. Program-locking transfers them to a PDA with vesting set to Never — the tokens still exist onchain and can be verified, but no wallet can withdraw them. Both approaches make the liquidity permanent.

Glossary

TermDefinition
GraduationThe automatic process triggered when a bonding curve sells out — migrates accumulated SOL and tokens into a Raydium CPMM pool
LP TokenLiquidity provider token representing a share of a Raydium CPMM pool
Program-LockedTokens held in a PDA-owned account with no withdrawal path — inaccessible but verifiable onchain
Bucket Signer PDAA program-derived address that owns the ATA holding LP tokens; has no private key
ClaimScheduleA vesting configuration with start condition, duration, period, and cliff — used on the Raydium bucket to define LP token release rules
Condition: NeverA condition variant that can never be satisfied — used as both the startCondition and cliffCondition on lpLockSchedule to prevent LP token claims
RaydiumCpmmBucketV2The Genesis account that stores post-graduation state including the Raydium pool reference, LP token balance, and lock schedule