Raydium Swap —— Pool 指令详解

本文档详细拆解 Raydium CP-Swap 协议中流动性池(Pool)相关指令的代码实现。

1. initialize

初始化一个新的流动性池,设置初始代币数量和开放交易时间。任何用户都可以调用创建新池子。

1.1 账户约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// programs/cp-swap/src/instructions/initialize.rs
use crate::curve::CurveCalculator;
use crate::error::ErrorCode;
use crate::states::*;
use crate::utils::*;
use anchor_lang::prelude::*;
use anchor_spl::{
associated_token::AssociatedToken,
token::Token,
token_interface::{Mint, TokenAccount, TokenInterface},
};

#[derive(Accounts)]
pub struct Initialize<'info> {
/// 池子创建者,支付所有费用
#[account(mut)]
pub creator: Signer<'info>,

/// 池子所属的 AMM 配置
pub amm_config: Box<Account<'info, AmmConfig>>,

/// 池子权限账户(PDA),用于 vault 和 lp mint 的签名
/// Seeds: ["vault_and_lp_mint_auth_seed"]
#[account(
seeds = [crate::AUTH_SEED.as_bytes()],
bump,
)]
pub authority: UncheckedAccount<'info>,

/// 池子状态账户
/// PDA Seeds: ["pool", amm_config, token_0_mint, token_1_mint]
/// 或者由 CLI 签名的随机账户
#[account(mut)]
pub pool_state: UncheckedAccount<'info>,

/// Token_0 的 Mint,key 必须小于 token_1_mint
#[account(
constraint = token_0_mint.key() < token_1_mint.key(),
mint::token_program = token_0_program,
)]
pub token_0_mint: Box<InterfaceAccount<'info, Mint>>,

/// Token_1 的 Mint,key 必须大于 token_0_mint
#[account(mint::token_program = token_1_program)]
pub token_1_mint: Box<InterfaceAccount<'info, Mint>>,

/// LP 代币 Mint
/// Seeds: ["pool_lp_mint", pool_state]
#[account(
init,
seeds = [POOL_LP_MINT_SEED.as_bytes(), pool_state.key().as_ref()],
bump,
mint::decimals = 9,
mint::authority = authority,
payer = creator,
mint::token_program = token_program,
)]
pub lp_mint: Box<InterfaceAccount<'info, Mint>>,

/// 创建者的 token_0 账户
#[account(
mut,
token::mint = token_0_mint,
token::authority = creator,
)]
pub creator_token_0: Box<InterfaceAccount<'info, TokenAccount>>,

/// 创建者的 token_1 账户
#[account(
mut,
token::mint = token_1_mint,
token::authority = creator,
)]
pub creator_token_1: Box<InterfaceAccount<'info, TokenAccount>>,

/// 创建者的 LP 代币账户(自动创建)
#[account(
init,
associated_token::mint = lp_mint,
associated_token::authority = creator,
payer = creator,
token::token_program = token_program,
)]
pub creator_lp_token: Box<InterfaceAccount<'info, TokenAccount>>,

/// 池子的 token_0 储备账户
/// Seeds: ["pool_vault", pool_state, token_0_mint]
#[account(
mut,
seeds = [POOL_VAULT_SEED.as_bytes(), pool_state.key().as_ref(), token_0_mint.key().as_ref()],
bump,
)]
pub token_0_vault: UncheckedAccount<'info>,

/// 池子的 token_1 储备账户
/// Seeds: ["pool_vault", pool_state, token_1_mint]
#[account(
mut,
seeds = [POOL_VAULT_SEED.as_bytes(), pool_state.key().as_ref(), token_1_mint.key().as_ref()],
bump,
)]
pub token_1_vault: UncheckedAccount<'info>,

/// 创建池子费用接收账户
#[account(mut, address = crate::create_pool_fee_reveiver::ID)]
pub create_pool_fee: Box<InterfaceAccount<'info, TokenAccount>>,

/// 价格预言机状态账户
/// Seeds: ["observation", pool_state]
#[account(
init,
seeds = [OBSERVATION_SEED.as_bytes(), pool_state.key().as_ref()],
bump,
payer = creator,
space = ObservationState::LEN
)]
pub observation_state: AccountLoader<'info, ObservationState>,

pub token_program: Program<'info, Token>,
pub token_0_program: Interface<'info, TokenInterface>,
pub token_1_program: Interface<'info, TokenInterface>,
pub associated_token_program: Program<'info, AssociatedToken>,
pub system_program: Program<'info, System>,
pub rent: Sysvar<'info, Rent>,
}

1.2 指令逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// programs/cp-swap/src/instructions/initialize.rs
pub fn initialize(
ctx: Context<Initialize>,
init_amount_0: u64, // 初始存入的 token_0 数量
init_amount_1: u64, // 初始存入的 token_1 数量
mut open_time: u64, // 池子开放交易的时间戳
) -> Result<()> {
// 1. 验证代币是否支持
if !(is_supported_mint(&ctx.accounts.token_0_mint).unwrap()
&& is_supported_mint(&ctx.accounts.token_1_mint).unwrap())
{
return err!(ErrorCode::NotSupportMint);
}

// 2. 检查是否允许创建池子
if ctx.accounts.amm_config.disable_create_pool {
return err!(ErrorCode::NotApproved);
}

// 3. 调整开放时间(不能早于当前时间)
let block_timestamp = clock::Clock::get()?.unix_timestamp as u64;
if open_time <= block_timestamp {
open_time = block_timestamp + 1;
}

// 4. 创建 token vault 账户
create_token_account(/* token_0_vault */)?;
create_token_account(/* token_1_vault */)?;

// 5. 创建池子状态账户
let pool_state_loader = create_pool(/* ... */)?;
let pool_state = &mut pool_state_loader.load_init()?;

// 6. 初始化预言机状态
let mut observation_state = ctx.accounts.observation_state.load_init()?;
observation_state.pool_id = ctx.accounts.pool_state.key();

// 7. 转入初始代币到 vault
transfer_from_user_to_pool_vault(/* token_0 */)?;
transfer_from_user_to_pool_vault(/* token_1 */)?;

// 8. 验证初始供应量
CurveCalculator::validate_supply(token_0_vault.amount, token_1_vault.amount)?;

// 9. 计算初始流动性:liquidity = sqrt(token_0 * token_1)
let liquidity = U128::from(token_0_vault.amount)
.checked_mul(token_1_vault.amount.into())
.unwrap()
.integer_sqrt()
.as_u64();

// 10. 铸造 LP 代币(减去锁定量 100)
let lock_lp_amount = 100;
token::token_mint_to(
/* ... */
liquidity.checked_sub(lock_lp_amount).ok_or(ErrorCode::InitLpAmountTooLess)?,
/* ... */
)?;

// 11. 收取创建池子费用(如果配置了)
if ctx.accounts.amm_config.create_pool_fee != 0 {
invoke(&system_instruction::transfer(/* ... */))?;
}

// 12. 初始化池子状态
pool_state.initialize(
ctx.bumps.authority,
liquidity,
open_time,
ctx.accounts.creator.key(), // pool_creator
ctx.accounts.amm_config.key(),
/* ... */
CreatorFeeOn::BothToken, // 普通创建不启用创建者费用
false, // enable_creator_fee = false
);

Ok(())
}

1.3 初始流动性计算

1
2
初始流动性 = sqrt(init_amount_0 × init_amount_1)
用户获得 LP = 初始流动性 - 100(锁定量)

锁定 100 LP 的原因:防止首个流动性提供者通过精心构造的数值操控价格。

2. initialize_with_permission

需要 Permission 账户授权的池子创建方式,支持设置创建者费用收取模式。

2.1 账户约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// programs/cp-swap/src/instructions/initialize_with_permission.rs
#[derive(Accounts)]
pub struct InitializeWithPermission<'info> {
/// 支付者,需要有 Permission 授权
#[account(mut)]
pub payer: Signer<'info>,

/// 池子创建者(可以是任意地址,用于接收创建者费用)
/// CHECK: creator of pool
pub creator: UncheckedAccount<'info>,

/// 池子所属的 AMM 配置
pub amm_config: Box<Account<'info, AmmConfig>>,

/// 池子权限账户
#[account(seeds = [crate::AUTH_SEED.as_bytes()], bump)]
pub authority: UncheckedAccount<'info>,

/// 池子状态账户
#[account(mut)]
pub pool_state: UncheckedAccount<'info>,

/// Token_0 Mint(key 必须小于 token_1)
#[account(constraint = token_0_mint.key() < token_1_mint.key())]
pub token_0_mint: Box<InterfaceAccount<'info, Mint>>,

/// Token_1 Mint
pub token_1_mint: Box<InterfaceAccount<'info, Mint>>,

/// LP Mint
#[account(
init,
seeds = [POOL_LP_MINT_SEED.as_bytes(), pool_state.key().as_ref()],
bump,
mint::decimals = 9,
mint::authority = authority,
payer = payer,
)]
pub lp_mint: Box<InterfaceAccount<'info, Mint>>,

// ... 其他账户与 initialize 类似 ...

/// 权限验证账户(PDA)
/// Seeds: ["permission", payer]
/// 只有被管理员授权的地址才能调用此指令
#[account(
seeds = [PERMISSION_SEED.as_bytes(), payer.key().as_ref()],
bump,
)]
pub permission: Box<Account<'info, Permission>>,

// ... 程序账户 ...
}

2.2 指令逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// programs/cp-swap/src/instructions/initialize_with_permission.rs
pub fn initialize_with_permission(
ctx: Context<InitializeWithPermission>,
init_amount_0: u64,
init_amount_1: u64,
open_time: u64,
creator_fee_on: CreatorFeeOn, // 创建者费用收取模式
) -> Result<()> {
// 逻辑与 initialize 基本相同,区别在于:

// 1. payer 必须有 Permission 账户(由管理员创建)
// 2. creator 可以是任意地址(用于接收创建者费用)
// 3. 支持设置 creator_fee_on 模式
// 4. enable_creator_fee = true

pool_state.initialize(
/* ... */
ctx.accounts.creator.key(), // 可指定任意创建者
/* ... */
creator_fee_on, // 自定义费用收取模式
true, // enable_creator_fee = true
);

Ok(())
}

2.3 CreatorFeeOn 模式说明

1
2
3
4
5
pub enum CreatorFeeOn {
BothToken, // 0: 根据输入代币收取(token_0 或 token_1)
OnlyToken0, // 1: 只收取 token_0 作为创建者费用
OnlyToken1, // 2: 只收取 token_1 作为创建者费用
}

3. deposit

用户向池子存入代币,获得 LP 代币。

3.1 账户约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// programs/cp-swap/src/instructions/deposit.rs
use crate::curve::{CurveCalculator, RoundDirection};
use crate::error::ErrorCode;
use crate::states::*;
use crate::utils::token::*;
use anchor_lang::prelude::*;
use anchor_spl::token::Token;
use anchor_spl::token_interface::{Mint, Token2022, TokenAccount};

#[derive(Accounts)]
pub struct Deposit<'info> {
/// 存款用户
pub owner: Signer<'info>,

/// 池子权限账户
#[account(seeds = [crate::AUTH_SEED.as_bytes()], bump)]
pub authority: UncheckedAccount<'info>,

/// 池子状态
#[account(mut)]
pub pool_state: AccountLoader<'info, PoolState>,

/// 用户的 LP 代币账户
#[account(mut, token::authority = owner)]
pub owner_lp_token: Box<InterfaceAccount<'info, TokenAccount>>,

/// 用户的 token_0 账户
#[account(
mut,
token::mint = token_0_vault.mint,
token::authority = owner
)]
pub token_0_account: Box<InterfaceAccount<'info, TokenAccount>>,

/// 用户的 token_1 账户
#[account(
mut,
token::mint = token_1_vault.mint,
token::authority = owner
)]
pub token_1_account: Box<InterfaceAccount<'info, TokenAccount>>,

/// 池子的 token_0 储备
#[account(
mut,
constraint = token_0_vault.key() == pool_state.load()?.token_0_vault
)]
pub token_0_vault: Box<InterfaceAccount<'info, TokenAccount>>,

/// 池子的 token_1 储备
#[account(
mut,
constraint = token_1_vault.key() == pool_state.load()?.token_1_vault
)]
pub token_1_vault: Box<InterfaceAccount<'info, TokenAccount>>,

pub token_program: Program<'info, Token>,
pub token_program_2022: Program<'info, Token2022>,

/// token_0 的 Mint
#[account(address = token_0_vault.mint)]
pub vault_0_mint: Box<InterfaceAccount<'info, Mint>>,

/// token_1 的 Mint
#[account(address = token_1_vault.mint)]
pub vault_1_mint: Box<InterfaceAccount<'info, Mint>>,

/// LP Mint
#[account(
mut,
address = pool_state.load()?.lp_mint @ ErrorCode::IncorrectLpMint
)]
pub lp_mint: Box<InterfaceAccount<'info, Mint>>,
}

3.2 指令逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// programs/cp-swap/src/instructions/deposit.rs
pub fn deposit(
ctx: Context<Deposit>,
lp_token_amount: u64, // 希望获得的 LP 代币数量
maximum_token_0_amount: u64, // 最大 token_0 数量(滑点保护)
maximum_token_1_amount: u64, // 最大 token_1 数量(滑点保护)
) -> Result<()> {
require_gt!(lp_token_amount, 0);

let pool_state = &mut ctx.accounts.pool_state.load_mut()?;

// 1. 检查池子是否允许存款
if !pool_state.get_status_by_bit(PoolStatusBitIndex::Deposit) {
return err!(ErrorCode::NotApproved);
}

// 2. 计算池子当前储备(不含累积费用)
let (total_token_0_amount, total_token_1_amount) = pool_state.vault_amount_without_fee(
ctx.accounts.token_0_vault.amount,
ctx.accounts.token_1_vault.amount,
)?;

// 3. 根据 LP 数量计算所需代币数量(向上取整)
let results = CurveCalculator::lp_tokens_to_trading_tokens(
u128::from(lp_token_amount),
u128::from(pool_state.lp_supply),
u128::from(total_token_0_amount),
u128::from(total_token_1_amount),
RoundDirection::Ceiling, // 向上取整,防止套利
).ok_or(ErrorCode::ZeroTradingTokens)?;

// 4. 计算转账费用(Token-2022 支持)
let (transfer_token_0_amount, transfer_token_0_fee) = {
let transfer_fee = get_transfer_inverse_fee(&vault_0_mint, token_0_amount)?;
(token_0_amount + transfer_fee, transfer_fee)
};

// 5. 滑点检查
if transfer_token_0_amount > maximum_token_0_amount
|| transfer_token_1_amount > maximum_token_1_amount
{
return Err(ErrorCode::ExceededSlippage.into());
}

// 6. 从用户账户转入代币到 vault
transfer_from_user_to_pool_vault(/* token_0 */)?;
transfer_from_user_to_pool_vault(/* token_1 */)?;

// 7. 更新 LP 供应量并铸造 LP 代币
pool_state.lp_supply = pool_state.lp_supply.checked_add(lp_token_amount).unwrap();
token_mint_to(/* mint LP to owner */)?;

pool_state.recent_epoch = Clock::get()?.epoch;
Ok(())
}

3.3 存款计算公式

1
2
token_0_needed = lp_token_amount / lp_supply × total_token_0
token_1_needed = lp_token_amount / lp_supply × total_token_1

4. withdraw

用户销毁 LP 代币,按比例提取两种代币。

4.1 账户约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// programs/cp-swap/src/instructions/withdraw.rs
#[derive(Accounts)]
pub struct Withdraw<'info> {
/// 取款用户
pub owner: Signer<'info>,

/// 池子权限账户
#[account(seeds = [crate::AUTH_SEED.as_bytes()], bump)]
pub authority: UncheckedAccount<'info>,

/// 池子状态
#[account(mut)]
pub pool_state: AccountLoader<'info, PoolState>,

/// 用户的 LP 代币账户
#[account(mut, token::authority = owner)]
pub owner_lp_token: Box<InterfaceAccount<'info, TokenAccount>>,

/// 用户接收 token_0 的账户
#[account(mut, token::mint = token_0_vault.mint)]
pub token_0_account: Box<InterfaceAccount<'info, TokenAccount>>,

/// 用户接收 token_1 的账户
#[account(mut, token::mint = token_1_vault.mint)]
pub token_1_account: Box<InterfaceAccount<'info, TokenAccount>>,

/// 池子的 token_0 储备
#[account(
mut,
constraint = token_0_vault.key() == pool_state.load()?.token_0_vault
)]
pub token_0_vault: Box<InterfaceAccount<'info, TokenAccount>>,

/// 池子的 token_1 储备
#[account(
mut,
constraint = token_1_vault.key() == pool_state.load()?.token_1_vault
)]
pub token_1_vault: Box<InterfaceAccount<'info, TokenAccount>>,

pub token_program: Program<'info, Token>,
pub token_program_2022: Program<'info, Token2022>,
pub vault_0_mint: Box<InterfaceAccount<'info, Mint>>,
pub vault_1_mint: Box<InterfaceAccount<'info, Mint>>,

/// LP Mint
#[account(
mut,
address = pool_state.load()?.lp_mint @ ErrorCode::IncorrectLpMint
)]
pub lp_mint: Box<InterfaceAccount<'info, Mint>>,

/// Memo 程序(用于记录日志)
#[account(address = spl_memo::id())]
pub memo_program: UncheckedAccount<'info>,
}

4.2 指令逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// programs/cp-swap/src/instructions/withdraw.rs
pub fn withdraw(
ctx: Context<Withdraw>,
lp_token_amount: u64, // 要销毁的 LP 代币数量
minimum_token_0_amount: u64, // 最少提取的 token_0(滑点保护)
minimum_token_1_amount: u64, // 最少提取的 token_1(滑点保护)
) -> Result<()> {
require_gt!(lp_token_amount, 0);
require_gte!(ctx.accounts.owner_lp_token.amount, lp_token_amount);

let pool_state = &mut ctx.accounts.pool_state.load_mut()?;

// 1. 检查池子是否允许取款
if !pool_state.get_status_by_bit(PoolStatusBitIndex::Withdraw) {
return err!(ErrorCode::NotApproved);
}

// 2. 计算池子当前储备
let (total_token_0_amount, total_token_1_amount) = pool_state.vault_amount_without_fee(
ctx.accounts.token_0_vault.amount,
ctx.accounts.token_1_vault.amount,
)?;

// 3. 根据 LP 数量计算可提取代币数量(向下取整)
let results = CurveCalculator::lp_tokens_to_trading_tokens(
u128::from(lp_token_amount),
u128::from(pool_state.lp_supply),
u128::from(total_token_0_amount),
u128::from(total_token_1_amount),
RoundDirection::Floor, // 向下取整,防止超额提取
).ok_or(ErrorCode::ZeroTradingTokens)?;

// 4. 计算转账费用(扣除后的实际接收量)
let (receive_token_0_amount, token_0_transfer_fee) = {
let transfer_fee = get_transfer_fee(&vault_0_mint, token_0_amount)?;
(token_0_amount - transfer_fee, transfer_fee)
};

// 5. 滑点检查
if receive_token_0_amount < minimum_token_0_amount
|| receive_token_1_amount < minimum_token_1_amount
{
return Err(ErrorCode::ExceededSlippage.into());
}

// 6. 更新 LP 供应量并销毁用户的 LP 代币
pool_state.lp_supply = pool_state.lp_supply.checked_sub(lp_token_amount).unwrap();
token_burn(/* burn LP from owner */)?;

// 7. 从 vault 转账到用户账户
transfer_from_pool_vault_to_user(/* token_0 */)?;
transfer_from_pool_vault_to_user(/* token_1 */)?;

pool_state.recent_epoch = Clock::get()?.epoch;
Ok(())
}

4.3 取款计算公式

1
2
token_0_withdraw = lp_token_amount / lp_supply × total_token_0
token_1_withdraw = lp_token_amount / lp_supply × total_token_1

5. collect_creator_fee

池子创建者提取累积的创建者费用。只有通过 initialize_with_permission 创建且启用了创建者费用的池子才会有累积费用。

5.1 账户约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// programs/cp-swap/src/instructions/collect_creator_fee.rs
use crate::error::ErrorCode;
use crate::states::*;
use crate::utils::token::*;
use anchor_lang::prelude::*;
use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};

#[derive(Accounts)]
pub struct CollectCreatorFee<'info> {
/// 调用者必须是池子创建者
#[account(mut, address = pool_state.load()?.pool_creator)]
pub creator: Signer<'info>,

/// 池子权限账户
#[account(seeds = [crate::AUTH_SEED.as_bytes()], bump)]
pub authority: UncheckedAccount<'info>,

/// 池子状态,存储累积的创建者费用
#[account(mut)]
pub pool_state: AccountLoader<'info, PoolState>,

/// AMM 配置账户
#[account(address = pool_state.load()?.amm_config)]
pub amm_config: Account<'info, AmmConfig>,

/// 池子的 token_0 储备
#[account(
mut,
constraint = token_0_vault.key() == pool_state.load()?.token_0_vault
)]
pub token_0_vault: Box<InterfaceAccount<'info, TokenAccount>>,

/// 池子的 token_1 储备
#[account(
mut,
constraint = token_1_vault.key() == pool_state.load()?.token_1_vault
)]
pub token_1_vault: Box<InterfaceAccount<'info, TokenAccount>>,

/// token_0 的 Mint
#[account(address = token_0_vault.mint)]
pub vault_0_mint: Box<InterfaceAccount<'info, Mint>>,

/// token_1 的 Mint
#[account(address = token_1_vault.mint)]
pub vault_1_mint: Box<InterfaceAccount<'info, Mint>>,

/// 创建者接收 token_0 的账户(自动创建)
#[account(
init_if_needed,
associated_token::mint = vault_0_mint,
associated_token::authority = creator,
payer = creator,
associated_token::token_program = token_0_program,
)]
pub creator_token_0: Box<InterfaceAccount<'info, TokenAccount>>,

/// 创建者接收 token_1 的账户(自动创建)
#[account(
init_if_needed,
associated_token::mint = vault_1_mint,
associated_token::authority = creator,
payer = creator,
associated_token::token_program = token_1_program,
)]
pub creator_token_1: Box<InterfaceAccount<'info, TokenAccount>>,

pub token_0_program: Interface<'info, TokenInterface>,
pub token_1_program: Interface<'info, TokenInterface>,
pub associated_token_program: Program<'info, AssociatedToken>,
pub system_program: Program<'info, System>,
}

5.2 指令逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// programs/cp-swap/src/instructions/collect_creator_fee.rs
pub fn collect_creator_fee(ctx: Context<CollectCreatorFee>) -> Result<()> {
let mut pool_state = ctx.accounts.pool_state.load_mut()?;

// 1. 获取累积的创建者费用
let creator_fees_token_0 = pool_state.creator_fees_token_0;
let creator_fees_token_1 = pool_state.creator_fees_token_1;

// 2. 检查是否有费用可提取
if creator_fees_token_0 == 0 && creator_fees_token_1 == 0 {
return err!(ErrorCode::NoFeeCollect);
}

// 3. 构建 PDA 签名
let signer_seeds: &[&[u8]] = &[crate::AUTH_SEED.as_bytes(), &[auth_bump]];

// 4. 转账 token_0 费用到创建者账户
transfer_from_pool_vault_to_user(
ctx.accounts.authority.to_account_info(),
ctx.accounts.token_0_vault.to_account_info(),
ctx.accounts.creator_token_0.to_account_info(),
ctx.accounts.vault_0_mint.to_account_info(),
ctx.accounts.token_0_program.to_account_info(),
creator_fees_token_0,
ctx.accounts.vault_0_mint.decimals,
&[signer_seeds],
)?;

// 5. 转账 token_1 费用到创建者账户
transfer_from_pool_vault_to_user(
ctx.accounts.authority.to_account_info(),
ctx.accounts.token_1_vault.to_account_info(),
ctx.accounts.creator_token_1.to_account_info(),
ctx.accounts.vault_1_mint.to_account_info(),
ctx.accounts.token_1_program.to_account_info(),
creator_fees_token_1,
ctx.accounts.vault_1_mint.decimals,
&[signer_seeds],
)?;

// 6. 清零累积费用
pool_state.creator_fees_token_0 = 0;
pool_state.creator_fees_token_1 = 0;
pool_state.recent_epoch = Clock::get()?.epoch;

Ok(())
}

6. 总结

6.1 指令列表

指令 功能 调用者
initialize 创建流动性池 任何用户
initialize_with_permission 授权创建池子 被授权的地址
deposit 添加流动性 任何用户
withdraw 移除流动性 LP 持有者
collect_creator_fee 提取创建者费用 池子创建者

6.2 池子生命周期

1
2
3
4
5
1. initialize / initialize_with_permission  → 创建池子,存入初始流动性
2. deposit → 用户添加流动性
3. swap_base_input / swap_base_output → 用户交易(见 raydium_swap.md)
4. withdraw → 用户移除流动性
5. collect_creator_fee → 创建者提取费用(可选)

6.3 PDA 种子汇总

账户 Seeds
authority ["vault_and_lp_mint_auth_seed"]
pool_state ["pool", amm_config, token_0_mint, token_1_mint]
lp_mint ["pool_lp_mint", pool_state]
token_0_vault ["pool_vault", pool_state, token_0_mint]
token_1_vault ["pool_vault", pool_state, token_1_mint]
observation_state ["observation", pool_state]