4: Direct launchlab integration
For developers who want to integrate directly with the Raydium LaunchLab program without using this SDK.
Core Concepts
Program Information
Program ID: 9SkAtSxgNUMvT9bGb93v6rLU5MjW1XibykqoGtqT9dbg
Key Accounts:
Global Config:
6s1xP3hpbAfFoNtUNF8mfHsjr2Bd97JxFJRWLbL6aHuXPlatform Config:
3pcjttVo7W1eTYFjTfBTrWKm1YmWU2RXY6GKND3svqYiVault Authority:
WLHv2UAZm6z4KyaaELi5pjdbJh6RESMva1Rnn8pJVVhSOL Mint:
So11111111111111111111111111111111111111112
Program State Machine
Pools exist in three states:
Funding
0
Initial phase - accepting trades, fundraising in progress
Migrate
1
Fundraising complete - waiting for migration to AMM
Trade
2
Migrated to AMM - trading occurs on Raydium AMM V4
Critical Account Derivations
All accounts use Program Derived Addresses (PDAs) — deterministic addresses derived from seeds.
Pool State (Bonding Curve)
Main pool account that stores all state.
const [poolState, bump] = await PublicKey.findProgramAddress(
[
Buffer.from('pool'),
baseMint.toBuffer(), // Token mint
quoteMint.toBuffer(), // WSOL mint: So11111111111111111111111111111111111111112
],
LAUNCHLAB_PROGRAM_ID
);Seed components:
Constant:
"pool"(UTF-8 bytes)Base Mint: Token mint public key (32 bytes)
Quote Mint: WSOL mint public key (32 bytes)
Vault Authority
PDA that has authority over all vault operations.
const [authority, authBump] = await PublicKey.findProgramAddress(
[Buffer.from('vault_auth_seed')],
LAUNCHLAB_PROGRAM_ID
);Seed: "vault_auth_seed" (UTF-8 bytes)
Base Vault (Token Vault)
Holds the pool's base tokens (the tokens being sold).
const [baseVault, baseVaultBump] = await PublicKey.findProgramAddress(
[
Buffer.from('pool_vault'),
poolState.toBuffer(),
baseMint.toBuffer(),
],
LAUNCHLAB_PROGRAM_ID
);Seed components:
Constant:
"pool_vault"Pool State: Pool state PDA (32 bytes)
Base Mint: Token mint public key (32 bytes)
Quote Vault (SOL Vault)
Holds the pool's quote tokens (WSOL/SOL).
const [quoteVault, quoteVaultBump] = await PublicKey.findProgramAddress(
[
Buffer.from('pool_vault'),
poolState.toBuffer(),
wsolMint.toBuffer(), // So11111111111111111111111111111111111111112
],
LAUNCHLAB_PROGRAM_ID
);Platform Fee Vault
Collects platform fees.
const [platformVault, platformVaultBump] = await PublicKey.findProgramAddress(
[
platformConfig.toBuffer(),
wsolMint.toBuffer(),
],
LAUNCHLAB_PROGRAM_ID
);Creator Fee Vault
Collects creator fees.
const [creatorVault, creatorVaultBump] = await PublicKey.findProgramAddress(
[
creator.toBuffer(), // Creator wallet public key
wsolMint.toBuffer(),
],
LAUNCHLAB_PROGRAM_ID
);Event Authority
Used for emitting program events.
const [eventAuthority, eventBump] = await PublicKey.findProgramAddress(
[Buffer.from('__event_authority')],
LAUNCHLAB_PROGRAM_ID
);Seed: "__event_authority"
Metadata Account (Metaplex)
Stores token metadata (name, symbol, URI).
const [metadata] = await PublicKey.findProgramAddress(
[
Buffer.from('metadata'),
METAPLEX_TOKEN_METADATA_PROGRAM_ID.toBuffer(),
baseMint.toBuffer(),
],
METAPLEX_TOKEN_METADATA_PROGRAM_ID // metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s
);Manual Transaction Construction
Buy Tokens (buy_exact_in)
Instruction Discriminator: [250, 234, 13, 123, 213, 156, 19, 236]
Accounts (in order):
0
payer
✅
✅
User wallet
1
authority
❌
❌
Vault authority PDA
2
global_config
❌
❌
Global config
3
platform_config
❌
❌
Platform config
4
pool_state
✅
❌
Pool state PDA
5
user_base_token
✅
❌
User's token account (ATA)
6
user_quote_token
✅
❌
User's WSOL account (ATA)
7
base_vault
✅
❌
Pool's base vault
8
quote_vault
✅
❌
Pool's quote vault
9
base_token_mint
❌
❌
Token mint
10
quote_token_mint
❌
❌
WSOL mint
11
base_token_program
❌
❌
Token program
12
quote_token_program
❌
❌
Token program
13
event_authority
❌
❌
Event authority PDA
14
program
❌
❌
LaunchLab program ID
15
system_program
❌
❌
System program
16
platform_vault
✅
❌
Platform fee vault
17
creator_vault
✅
❌
Creator fee vault
Instruction data (8 bytes discriminator + 8 bytes amount_in + 8 bytes min_amount_out + 8 bytes share_fee_rate):
const discriminator = Buffer.from([250, 234, 13, 123, 213, 156, 19, 236]);
const amountInBuffer = Buffer.alloc(8);
amountInBuffer.writeBigUInt64LE(BigInt(amountIn.toString()), 0);
const minAmountOutBuffer = Buffer.alloc(8);
minAmountOutBuffer.writeBigUInt64LE(BigInt(minAmountOut.toString()), 0);
const shareFeeRateBuffer = Buffer.alloc(8);
shareFeeRateBuffer.writeBigUInt64LE(BigInt(0), 0); // Usually 0
const instructionData = Buffer.concat([
discriminator,
amountInBuffer,
minAmountOutBuffer,
shareFeeRateBuffer,
]);Pre-instructions required:
Full transaction example:
import {
Connection,
PublicKey,
Transaction,
TransactionInstruction,
SystemProgram,
} from '@solana/web3.js';
import {
createAssociatedTokenAccountIdempotentInstruction,
createSyncNativeInstruction,
TOKEN_PROGRAM_ID,
} from '@solana/spl-token';
// Constants
const LAUNCHLAB_PROGRAM_ID = new PublicKey('9SkAtSxgNUMvT9bGb93v6rLU5MjW1XibykqoGtqT9dbg');
const WSOL_MINT = new PublicKey('So11111111111111111111111111111111111111112');
const GLOBAL_CONFIG = new PublicKey('6s1xP3hpbAfFoNtUNF8mfHsjr2Bd97JxFJRWLbL6aHuX');
const PLATFORM_CONFIG = new PublicKey('3pcjttVo7W1eTYFjTfBTrWKm1YmWU2RXY6GKND3svqYi');
const VAULT_AUTHORITY = new PublicKey('WLHv2UAZm6z4KyaaELi5pjdbJh6RESMva1Rnn8pJVVh');
async function buyTokensManually(
connection: Connection,
payer: PublicKey,
tokenMint: PublicKey,
amountIn: bigint, // Lamports to spend
minAmountOut: bigint // Minimum tokens to receive (slippage protection)
): Promise<Transaction> {
const transaction = new Transaction();
// 1. Derive all PDAs
const [poolState] = await PublicKey.findProgramAddress(
[Buffer.from('pool'), tokenMint.toBuffer(), WSOL_MINT.toBuffer()],
LAUNCHLAB_PROGRAM_ID
);
const [baseVault] = await PublicKey.findProgramAddress(
[Buffer.from('pool_vault'), poolState.toBuffer(), tokenMint.toBuffer()],
LAUNCHLAB_PROGRAM_ID
);
const [quoteVault] = await PublicKey.findProgramAddress(
[Buffer.from('pool_vault'), poolState.toBuffer(), WSOL_MINT.toBuffer()],
LAUNCHLAB_PROGRAM_ID
);
const [eventAuthority] = await PublicKey.findProgramAddress(
[Buffer.from('__event_authority')],
LAUNCHLAB_PROGRAM_ID
);
const [platformVault] = await PublicKey.findProgramAddress(
[PLATFORM_CONFIG.toBuffer(), WSOL_MINT.toBuffer()],
LAUNCHLAB_PROGRAM_ID
);
// Get pool state to find creator
const poolAccountInfo = await connection.getAccountInfo(poolState);
if (!poolAccountInfo) throw new Error('Pool not found');
// Parse creator from pool state (offset 280, 32 bytes)
const creator = new PublicKey(poolAccountInfo.data.slice(280, 312));
const [creatorVault] = await PublicKey.findProgramAddress(
[creator.toBuffer(), WSOL_MINT.toBuffer()],
LAUNCHLAB_PROGRAM_ID
);
// 2. Get user token accounts (ATAs)
const [userTokenAccount] = await PublicKey.findProgramAddress(
[payer.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), tokenMint.toBuffer()],
new PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL')
);
const [userWsolAccount] = await PublicKey.findProgramAddress(
[payer.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), WSOL_MINT.toBuffer()],
new PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL')
);
// 3. Add pre-instructions
transaction.add(
createAssociatedTokenAccountIdempotentInstruction(
payer,
userTokenAccount,
payer,
tokenMint
)
);
transaction.add(
createAssociatedTokenAccountIdempotentInstruction(
payer,
userWsolAccount,
payer,
WSOL_MINT
)
);
transaction.add(
SystemProgram.transfer({
fromPubkey: payer,
toPubkey: userWsolAccount,
lamports: amountIn,
})
);
transaction.add(createSyncNativeInstruction(userWsolAccount));
// 4. Build buy instruction
const discriminator = Buffer.from([250, 234, 13, 123, 213, 156, 19, 236]);
const amountInBuffer = Buffer.alloc(8);
amountInBuffer.writeBigUInt64LE(amountIn, 0);
const minAmountOutBuffer = Buffer.alloc(8);
minAmountOutBuffer.writeBigUInt64LE(minAmountOut, 0);
const shareFeeRateBuffer = Buffer.alloc(8);
shareFeeRateBuffer.writeBigUInt64LE(0n, 0);
const instructionData = Buffer.concat([
discriminator,
amountInBuffer,
minAmountOutBuffer,
shareFeeRateBuffer,
]);
const buyInstruction = new TransactionInstruction({
programId: LAUNCHLAB_PROGRAM_ID,
data: instructionData,
keys: [
{ pubkey: payer, isSigner: true, isWritable: true },
{ pubkey: VAULT_AUTHORITY, isSigner: false, isWritable: false },
{ pubkey: GLOBAL_CONFIG, isSigner: false, isWritable: false },
{ pubkey: PLATFORM_CONFIG, isSigner: false, isWritable: false },
{ pubkey: poolState, isSigner: false, isWritable: true },
{ pubkey: userTokenAccount, isSigner: false, isWritable: true },
{ pubkey: userWsolAccount, isSigner: false, isWritable: true },
{ pubkey: baseVault, isSigner: false, isWritable: true },
{ pubkey: quoteVault, isSigner: false, isWritable: true },
{ pubkey: tokenMint, isSigner: false, isWritable: true },
{ pubkey: WSOL_MINT, isSigner: false, isWritable: false },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: eventAuthority, isSigner: false, isWritable: false },
{ pubkey: LAUNCHLAB_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
{ pubkey: platformVault, isSigner: false, isWritable: true },
{ pubkey: creatorVault, isSigner: false, isWritable: true },
],
});
transaction.add(buyInstruction);
return transaction;
}Sell Tokens (sell_exact_in)
Instruction Discriminator: [149, 39, 222, 155, 211, 124, 152, 26]
Accounts: Same as buy_exact_in (see above)
Instruction data:
const discriminator = Buffer.from([149, 39, 222, 155, 211, 124, 152, 26]);
const amountInBuffer = Buffer.alloc(8);
amountInBuffer.writeBigUInt64LE(BigInt(tokensToSell.toString()), 0);
const minAmountOutBuffer = Buffer.alloc(8);
minAmountOutBuffer.writeBigUInt64LE(BigInt(minLamportsOut.toString()), 0);
const shareFeeRateBuffer = Buffer.alloc(8);
shareFeeRateBuffer.writeBigUInt64LE(BigInt(0), 0);
const instructionData = Buffer.concat([
discriminator,
amountInBuffer,
minAmountOutBuffer,
shareFeeRateBuffer,
]);Post-instructions required:
createCloseAccountInstruction(
userWsolAccount, // Account to close
payer, // Destination for remaining lamports
payer // Owner of the account
);Create Token (initialize_v2)
Instruction Discriminator: [67, 153, 175, 39, 218, 16, 38, 32]
This is the most complex instruction. It requires:
Simplified example using Anchor Program interface:
import { Program, AnchorProvider } from '@coral-xyz/anchor';
// Use the Anchor program interface
const program = new Program(RaydiumLaunchlabIDL, LAUNCHLAB_PROGRAM_ID, provider);
const tokenKeypair = Keypair.generate();
const [poolState] = await PublicKey.findProgramAddress(
[Buffer.from('pool'), tokenKeypair.publicKey.toBuffer(), WSOL_MINT.toBuffer()],
LAUNCHLAB_PROGRAM_ID
);
// ... derive other PDAs ...
const tx = await program.methods
.initializeV2(
{
decimals: 6,
name: 'My Token',
symbol: 'MTK',
uri: 'https://ipfs.io/ipfs/...',
},
{
constant: {
data: {
supply: new BN('1000000000000000'),
totalBaseSell: new BN('793100000000000'),
totalQuoteFundRaising: new BN('30000000000'),
migrateType: 0,
},
},
},
{
totalLockedAmount: new BN('0'),
cliffPeriod: new BN('0'),
unlockPeriod: new BN('0'),
},
{ quoteToken: {} }
)
.accounts({
payer: payer,
creator: payer,
globalConfig: GLOBAL_CONFIG,
platformConfig: PLATFORM_CONFIG,
// ... other accounts
})
.instruction();Reading Pool State
Manual Deserialization
Pool state is stored in the PoolState account using Borsh serialization.
Account structure (selected fields):
epoch
0
8
u64
Update epoch
auth_bump
8
1
u8
Authority bump seed
status
9
1
u8
Pool status (0/1/2)
base_decimals
10
1
u8
Token decimals
quote_decimals
11
1
u8
WSOL decimals (always 9)
migrate_type
12
1
u8
Migration type
supply
16
8
u64
Total supply
total_base_sell
24
8
u64
Total base for sale
virtual_base
32
8
u64
Virtual base reserves
virtual_quote
40
8
u64
Virtual quote reserves
real_base
48
8
u64
Real base reserves
real_quote
56
8
u64
Real quote reserves
total_quote_fund_raising
64
8
u64
Fundraising goal
...
...
...
...
...
creator
280
32
Pubkey
Token creator
Using Borsh deserialization with Anchor coder:
import { BorshAccountsCoder } from '@coral-xyz/anchor';
const accountInfo = await connection.getAccountInfo(poolState);
if (!accountInfo) throw new Error('Pool not found');
const coder = new BorshAccountsCoder(RaydiumLaunchlabIDL);
const poolData = coder.decode('PoolState', accountInfo.data);
console.log('Status:', poolData.status);
console.log('Real Quote:', poolData.real_quote.toString());Manual parsing (without Anchor):
const accountInfo = await connection.getAccountInfo(poolState);
if (!accountInfo) throw new Error('Pool not found');
const data = accountInfo.data;
// Parse key fields
const status = data.readUInt8(9);
const baseDecimals = data.readUInt8(10);
const supply = data.readBigUInt64LE(16);
const virtualBase = data.readBigUInt64LE(32);
const virtualQuote = data.readBigUInt64LE(40);
const realBase = data.readBigUInt64LE(48);
const realQuote = data.readBigUInt64LE(56);
const totalQuoteFundRaising = data.readBigUInt64LE(64);
console.log({
status,
baseDecimals,
supply: supply.toString(),
virtualBase: virtualBase.toString(),
virtualQuote: virtualQuote.toString(),
realBase: realBase.toString(),
realQuote: realQuote.toString(),
totalQuoteFundRaising: totalQuoteFundRaising.toString(),
});Last updated
