Raydium Swap —— Admin 指令详解

本文档详细拆解 Raydium CP-Swap 协议中所有管理员(Admin)相关指令的代码实现。

1. create_config

在 Raydium Swap 系统中,AmmConfig 是全局配置中心,用于统一管理费率参数、权限开关和资金接收地址,是各个流动性池运行的基础控制单元。

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
// programs/cp-swap/src/states/config.rs
use anchor_lang::prelude::*;

pub const AMM_CONFIG_SEED: &str = "amm_config"; // AMM 配置账户的种子前缀

#[account]
#[derive(Default, Debug)]
pub struct AmmConfig {
pub bump: u8, // PDA的bump种子
pub disable_create_pool: bool, // 是否禁止创建新池子
pub index: u16, // 配置索引,用于区分不同配置
pub trade_fee_rate: u64, // 交易手续费率,单位:百万分之一,例如2500表示0.25%
pub protocol_fee_rate: u64, // 协议费率,从交易费中抽取的比例,例如200000表示20%
pub fund_fee_rate: u64, // 基金费率,从交易费中抽取的比例,例如300000表示30%
pub create_pool_fee: u64, // 创建新池子需要支付的费用,单位:lamports
pub protocol_owner: Pubkey, // 协议费用接收地址
pub fund_owner: Pubkey, // 基金费用接收地址
pub creator_fee_rate: u64, // 池子创建者费率,单位:百万分之一,例如5000表示0.5%
pub padding: [u64; 15], // 预留空间,用于未来扩展功能而不需要迁移账户
}

impl AmmConfig {
pub const LEN: usize = 8 + 1 + 1 + 2 + 4 * 8 + 32 * 2 + 8 + 8 * 15; // 账户总大小
}

费率计算说明

  • FEE_RATE_DENOMINATOR_VALUE = 1_000_000(百万分之一为单位)
  • 例如:trade_fee_rate = 2500 表示 0.25% 的交易手续费

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
// programs/cp-swap/src/instructions/admin/create_config.rs
use crate::error::ErrorCode;
use crate::states::*;
use anchor_lang::prelude::*;
use std::ops::DerefMut;

#[derive(Accounts)]
#[instruction(index: u16)] // 从指令参数中获取index,用于生成PDA
pub struct CreateAmmConfig<'info> {
/// 配置创建者,必须是管理员地址
#[account(
mut,
address = crate::admin::ID @ ErrorCode::InvalidOwner // 验证必须是管理员
)]
pub owner: Signer<'info>,

/// AMM 配置账户,使用PDA确保每个index只能创建一个配置
/// Seeds: ["amm_config", index.to_be_bytes()]
#[account(
init, // 初始化新账户
seeds = [
AMM_CONFIG_SEED.as_bytes(), // "amm_config"
&index.to_be_bytes() // 配置索引(大端序)
],
bump, // PDA bump,由Anchor自动查找
payer = owner, // 支付账户租金的账户
space = AmmConfig::LEN // 账户所需空间大小
)]
pub amm_config: Account<'info, AmmConfig>,

pub system_program: Program<'info, System>, // Solana系统程序,用于创建账户
}

1.3 指令逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// programs/cp-swap/src/instructions/admin/create_config.rs
pub fn create_amm_config(
ctx: Context<CreateAmmConfig>,
index: u16,
trade_fee_rate: u64,
protocol_fee_rate: u64,
fund_fee_rate: u64,
create_pool_fee: u64,
creator_fee_rate: u64,
) -> Result<()> {
let amm_config = ctx.accounts.amm_config.deref_mut();
amm_config.protocol_owner = ctx.accounts.owner.key(); // 设置协议费用接收者为创建者
amm_config.bump = ctx.bumps.amm_config; // 记录PDA bump
amm_config.disable_create_pool = false; // 默认允许创建池子
amm_config.index = index; // 记录配置索引
amm_config.trade_fee_rate = trade_fee_rate; // 设置交易手续费率
amm_config.protocol_fee_rate = protocol_fee_rate; // 设置协议费率
amm_config.fund_fee_rate = fund_fee_rate; // 设置基金费率
amm_config.create_pool_fee = create_pool_fee; // 设置创建池子费用
amm_config.fund_owner = ctx.accounts.owner.key(); // 设置基金费用接收者为创建者
amm_config.creator_fee_rate = creator_fee_rate; // 设置创建者费率
Ok(())
}

1.4 入口函数与参数校验

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
// programs/cp-swap/src/lib.rs
pub fn create_amm_config(
ctx: Context<CreateAmmConfig>,
index: u16,
trade_fee_rate: u64,
protocol_fee_rate: u64,
fund_fee_rate: u64,
create_pool_fee: u64,
creator_fee_rate: u64,
) -> Result<()> {
// 交易费率 + 创建者费率 不能超过 100%
assert!(trade_fee_rate + creator_fee_rate < FEE_RATE_DENOMINATOR_VALUE);
// 协议费率不能超过 100%
assert!(protocol_fee_rate <= FEE_RATE_DENOMINATOR_VALUE);
// 基金费率不能超过 100%
assert!(fund_fee_rate <= FEE_RATE_DENOMINATOR_VALUE);
// 协议费率 + 基金费率 不能超过 100%(它们共享交易费)
assert!(fund_fee_rate + protocol_fee_rate <= FEE_RATE_DENOMINATOR_VALUE);

instructions::create_amm_config(
ctx,
index,
trade_fee_rate,
protocol_fee_rate,
fund_fee_rate,
create_pool_fee,
creator_fee_rate,
)
}

2. update_config

用于动态更新 AMM 配置参数,包括费率调整和所有者变更。

2.1 账户约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// programs/cp-swap/src/instructions/admin/update_config.rs
use crate::curve::fees::FEE_RATE_DENOMINATOR_VALUE;
use crate::error::ErrorCode;
use crate::states::*;
use anchor_lang::prelude::*;

#[derive(Accounts)]
pub struct UpdateAmmConfig<'info> {
/// 调用者必须是管理员
#[account(address = crate::admin::ID @ ErrorCode::InvalidOwner)]
pub owner: Signer<'info>,

/// 要修改的配置账户
#[account(mut)]
pub amm_config: Account<'info, AmmConfig>,
}

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/admin/update_config.rs
pub fn update_amm_config(ctx: Context<UpdateAmmConfig>, param: u8, value: u64) -> Result<()> {
let amm_config = &mut ctx.accounts.amm_config;
let match_param = Some(param);
match match_param {
Some(0) => update_trade_fee_rate(amm_config, value), // 更新交易费率
Some(1) => update_protocol_fee_rate(amm_config, value), // 更新协议费率
Some(2) => update_fund_fee_rate(amm_config, value), // 更新基金费率
Some(3) => {
// 更新协议费用所有者(从 remaining_accounts 获取新地址)
let new_procotol_owner = *ctx.remaining_accounts.iter().next().unwrap().key;
set_new_protocol_owner(amm_config, new_procotol_owner)?;
}
Some(4) => {
// 更新基金费用所有者(从 remaining_accounts 获取新地址)
let new_fund_owner = *ctx.remaining_accounts.iter().next().unwrap().key;
set_new_fund_owner(amm_config, new_fund_owner)?;
}
Some(5) => amm_config.create_pool_fee = value, // 更新创建池子费用
Some(6) => amm_config.disable_create_pool = if value == 0 { false } else { true }, // 开关创建池子
Some(7) => update_creator_fee_rate(amm_config, value), // 更新创建者费率
_ => return err!(ErrorCode::InvalidInput),
}
Ok(())
}

2.3 辅助函数

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
// programs/cp-swap/src/instructions/admin/update_config.rs

/// 更新交易费率,确保与创建者费率之和不超过100%
fn update_trade_fee_rate(amm_config: &mut Account<AmmConfig>, trade_fee_rate: u64) {
assert!(trade_fee_rate + amm_config.creator_fee_rate <= FEE_RATE_DENOMINATOR_VALUE);
amm_config.trade_fee_rate = trade_fee_rate;
}

/// 更新协议费率,确保与基金费率之和不超过100%
fn update_protocol_fee_rate(amm_config: &mut Account<AmmConfig>, protocol_fee_rate: u64) {
assert!(protocol_fee_rate <= FEE_RATE_DENOMINATOR_VALUE);
assert!(protocol_fee_rate + amm_config.fund_fee_rate <= FEE_RATE_DENOMINATOR_VALUE);
amm_config.protocol_fee_rate = protocol_fee_rate;
}

/// 更新基金费率,确保与协议费率之和不超过100%
fn update_fund_fee_rate(amm_config: &mut Account<AmmConfig>, fund_fee_rate: u64) {
assert!(fund_fee_rate <= FEE_RATE_DENOMINATOR_VALUE);
assert!(fund_fee_rate + amm_config.protocol_fee_rate <= FEE_RATE_DENOMINATOR_VALUE);
amm_config.fund_fee_rate = fund_fee_rate;
}

/// 更新创建者费率,确保与交易费率之和不超过100%
fn update_creator_fee_rate(amm_config: &mut Account<AmmConfig>, creator_fee_rate: u64) {
assert!(creator_fee_rate + amm_config.trade_fee_rate <= FEE_RATE_DENOMINATOR_VALUE);
amm_config.creator_fee_rate = creator_fee_rate;
}

/// 设置新的协议费用所有者
fn set_new_protocol_owner(amm_config: &mut Account<AmmConfig>, new_owner: Pubkey) -> Result<()> {
require_keys_neq!(new_owner, Pubkey::default()); // 不能设置为默认地址
amm_config.protocol_owner = new_owner;
Ok(())
}

/// 设置新的基金费用所有者
fn set_new_fund_owner(amm_config: &mut Account<AmmConfig>, new_fund_owner: Pubkey) -> Result<()> {
require_keys_neq!(new_fund_owner, Pubkey::default()); // 不能设置为默认地址
amm_config.fund_owner = new_fund_owner;
Ok(())
}

2.4 参数对照表

param 功能 value 含义
0 更新交易费率 新的 trade_fee_rate
1 更新协议费率 新的 protocol_fee_rate
2 更新基金费率 新的 fund_fee_rate
3 更新协议所有者 忽略(从 remaining_accounts 读取)
4 更新基金所有者 忽略(从 remaining_accounts 读取)
5 更新创建池子费用 新的 create_pool_fee
6 禁用/启用创建池子 0=启用,其他=禁用
7 更新创建者费率 新的 creator_fee_rate

3. update_pool_status

用于更新池子的运行状态,可以控制存款、取款、交易等功能的启用/禁用。

3.1 账户约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// programs/cp-swap/src/instructions/admin/update_pool_status.rs
use crate::states::*;
use anchor_lang::prelude::*;

#[derive(Accounts)]
pub struct UpdatePoolStatus<'info> {
/// 调用者必须是管理员
#[account(address = crate::admin::ID)]
pub authority: Signer<'info>,

/// 要修改的池子状态账户
#[account(mut)]
pub pool_state: AccountLoader<'info, PoolState>,
}

3.2 指令逻辑

1
2
3
4
5
6
7
8
// programs/cp-swap/src/instructions/admin/update_pool_status.rs
pub fn update_pool_status(ctx: Context<UpdatePoolStatus>, status: u8) -> Result<()> {
require_gte!(255, status); // status 必须在 0-255 范围内
let mut pool_state = ctx.accounts.pool_state.load_mut()?;
pool_state.set_status(status); // 设置新状态
pool_state.recent_epoch = Clock::get()?.epoch; // 更新最近操作时间
Ok(())
}

3.3 状态位掩码说明

1
2
3
4
5
6
// programs/cp-swap/src/states/pool.rs
pub enum PoolStatusBitIndex {
Deposit, // bit 0: 存款控制
Withdraw, // bit 1: 取款控制
Swap, // bit 2: 交易控制
}

状态值示例

status 二进制 存款 取款 交易
0 000 启用 启用 启用
1 001 禁用 启用 启用
2 010 启用 禁用 启用
4 100 启用 启用 禁用
7 111 禁用 禁用 禁用

4. collect_protocol_fee

用于提取池子中累积的协议费用。

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
57
58
59
60
61
62
63
64
65
66
67
68
69
// programs/cp-swap/src/instructions/admin/collect_protocol_fee.rs
use crate::error::ErrorCode;
use crate::states::*;
use crate::utils::*;
use anchor_lang::prelude::*;
use anchor_spl::token::Token;
use anchor_spl::token_interface::{Mint, Token2022, TokenAccount};

#[derive(Accounts)]
pub struct CollectProtocolFee<'info> {
/// 调用者必须是协议所有者或管理员
#[account(
constraint = (owner.key() == amm_config.protocol_owner
|| owner.key() == crate::admin::ID) @ ErrorCode::InvalidOwner
)]
pub owner: Signer<'info>,

/// 池子权限账户(PDA),用于签名转账
/// Seeds: ["vault_and_lp_mint_auth_seed"]
#[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(mut)]
pub recipient_token_0_account: Box<InterfaceAccount<'info, TokenAccount>>,

/// 接收 token_1 协议费用的账户
#[account(mut)]
pub recipient_token_1_account: Box<InterfaceAccount<'info, TokenAccount>>,

/// SPL Token 程序
pub token_program: Program<'info, Token>,

/// SPL Token 2022 程序
pub token_program_2022: Program<'info, Token2022>,
}

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
57
58
59
60
61
62
63
64
65
// programs/cp-swap/src/instructions/admin/collect_protocol_fee.rs
pub fn collect_protocol_fee(
ctx: Context<CollectProtocolFee>,
amount_0_requested: u64, // 希望提取的 token_0 最大数量
amount_1_requested: u64, // 希望提取的 token_1 最大数量
) -> Result<()> {
let amount_0: u64;
let amount_1: u64;
let auth_bump: u8;

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

// 计算实际可提取数量(取请求量和累积量的较小值)
amount_0 = amount_0_requested.min(pool_state.protocol_fees_token_0);
amount_1 = amount_1_requested.min(pool_state.protocol_fees_token_1);

// 更新池子状态,减去已提取的费用
pool_state.protocol_fees_token_0 = pool_state
.protocol_fees_token_0
.checked_sub(amount_0)
.unwrap();
pool_state.protocol_fees_token_1 = pool_state
.protocol_fees_token_1
.checked_sub(amount_1)
.unwrap();

auth_bump = pool_state.auth_bump;
pool_state.recent_epoch = Clock::get()?.epoch; // 更新最近操作时间
}

// 从池子 vault 转账 token_0 到接收账户
transfer_from_pool_vault_to_user(
ctx.accounts.authority.to_account_info(),
ctx.accounts.token_0_vault.to_account_info(),
ctx.accounts.recipient_token_0_account.to_account_info(),
ctx.accounts.vault_0_mint.to_account_info(),
if ctx.accounts.vault_0_mint.to_account_info().owner == ctx.accounts.token_program.key {
ctx.accounts.token_program.to_account_info()
} else {
ctx.accounts.token_program_2022.to_account_info()
},
amount_0,
ctx.accounts.vault_0_mint.decimals,
&[&[crate::AUTH_SEED.as_bytes(), &[auth_bump]]], // PDA 签名
)?;

// 从池子 vault 转账 token_1 到接收账户
transfer_from_pool_vault_to_user(
ctx.accounts.authority.to_account_info(),
ctx.accounts.token_1_vault.to_account_info(),
ctx.accounts.recipient_token_1_account.to_account_info(),
ctx.accounts.vault_1_mint.to_account_info(),
if ctx.accounts.vault_1_mint.to_account_info().owner == ctx.accounts.token_program.key {
ctx.accounts.token_program.to_account_info()
} else {
ctx.accounts.token_program_2022.to_account_info()
},
amount_1,
ctx.accounts.vault_1_mint.decimals,
&[&[crate::AUTH_SEED.as_bytes(), &[auth_bump]]], // PDA 签名
)?;

Ok(())
}

5. collect_fund_fee

用于提取池子中累积的基金费用,逻辑与 collect_protocol_fee 几乎相同,仅权限验证和费用字段不同。

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
// programs/cp-swap/src/instructions/admin/collect_fund_fee.rs
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 CollectFundFee<'info> {
/// 调用者必须是基金所有者或管理员
#[account(
constraint = (owner.key() == amm_config.fund_owner
|| owner.key() == crate::admin::ID) @ ErrorCode::InvalidOwner
)]
pub owner: Signer<'info>,

/// 池子权限账户(PDA),用于签名转账
#[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(mut)]
pub recipient_token_0_account: Box<InterfaceAccount<'info, TokenAccount>>,

/// 接收 token_1 基金费用的账户
#[account(mut)]
pub recipient_token_1_account: Box<InterfaceAccount<'info, TokenAccount>>,

/// SPL Token 程序
pub token_program: Program<'info, Token>,

/// SPL Token 2022 程序
pub token_program_2022: Program<'info, Token2022>,
}

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
48
49
50
51
52
53
54
55
56
57
58
59
// programs/cp-swap/src/instructions/admin/collect_fund_fee.rs
pub fn collect_fund_fee(
ctx: Context<CollectFundFee>,
amount_0_requested: u64,
amount_1_requested: u64,
) -> Result<()> {
let amount_0: u64;
let amount_1: u64;
let auth_bump: u8;

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

// 计算实际可提取数量(注意这里是 fund_fees 字段)
amount_0 = amount_0_requested.min(pool_state.fund_fees_token_0);
amount_1 = amount_1_requested.min(pool_state.fund_fees_token_1);

// 更新池子状态,减去已提取的基金费用
pool_state.fund_fees_token_0 = pool_state.fund_fees_token_0.checked_sub(amount_0).unwrap();
pool_state.fund_fees_token_1 = pool_state.fund_fees_token_1.checked_sub(amount_1).unwrap();

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

// 转账 token_0(逻辑与 collect_protocol_fee 相同)
transfer_from_pool_vault_to_user(
ctx.accounts.authority.to_account_info(),
ctx.accounts.token_0_vault.to_account_info(),
ctx.accounts.recipient_token_0_account.to_account_info(),
ctx.accounts.vault_0_mint.to_account_info(),
if ctx.accounts.vault_0_mint.to_account_info().owner == ctx.accounts.token_program.key {
ctx.accounts.token_program.to_account_info()
} else {
ctx.accounts.token_program_2022.to_account_info()
},
amount_0,
ctx.accounts.vault_0_mint.decimals,
&[&[crate::AUTH_SEED.as_bytes(), &[auth_bump]]],
)?;

// 转账 token_1
transfer_from_pool_vault_to_user(
ctx.accounts.authority.to_account_info(),
ctx.accounts.token_1_vault.to_account_info(),
ctx.accounts.recipient_token_1_account.to_account_info(),
ctx.accounts.vault_1_mint.to_account_info(),
if ctx.accounts.vault_1_mint.to_account_info().owner == ctx.accounts.token_program.key {
ctx.accounts.token_program.to_account_info()
} else {
ctx.accounts.token_program_2022.to_account_info()
},
amount_1,
ctx.accounts.vault_1_mint.decimals,
&[&[crate::AUTH_SEED.as_bytes(), &[auth_bump]]],
)?;

Ok(())
}

6. create_permission_pda

用于创建权限账户,授权特定地址可以通过 initialize_with_permission 创建池子。

6.1 权限账户数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// programs/cp-swap/src/states/permission.rs
use anchor_lang::prelude::*;

pub const PERMISSION_SEED: &str = "permission";

#[account]
#[derive(Default, Debug)]
pub struct Permission {
pub authority: Pubkey, // 被授权的地址
pub padding: [u64; 30], // 预留空间
}

impl Permission {
pub const LEN: usize = 8 + 32 + 8 * 30;
}

6.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
// programs/cp-swap/src/instructions/admin/create_permission_pda.rs
use crate::error::ErrorCode;
use crate::states::*;
use anchor_lang::prelude::*;
use std::ops::DerefMut;

#[derive(Accounts)]
pub struct CreatePermissionPda<'info> {
/// 调用者必须是管理员
#[account(
mut,
address = crate::admin::ID @ ErrorCode::InvalidOwner
)]
pub owner: Signer<'info>,

/// 要被授权的地址(任意地址)
/// CHECK: permission account authority
pub permission_authority: UncheckedAccount<'info>,

/// 权限账户,使用 PDA 确保每个地址只能有一个权限账户
/// Seeds: ["permission", permission_authority]
#[account(
init,
seeds = [
PERMISSION_SEED.as_bytes(),
permission_authority.key().as_ref()
],
bump,
payer = owner,
space = Permission::LEN
)]
pub permission: Account<'info, Permission>,

pub system_program: Program<'info, System>,
}

6.3 指令逻辑

1
2
3
4
5
6
// programs/cp-swap/src/instructions/admin/create_permission_pda.rs
pub fn create_permission_pda(ctx: Context<CreatePermissionPda>) -> Result<()> {
let permission = ctx.accounts.permission.deref_mut();
permission.authority = ctx.accounts.permission_authority.key(); // 记录被授权地址
Ok(())
}

7. close_permission_pda

用于关闭权限账户,撤销特定地址的创建池子权限,并回收租金。

7.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
// programs/cp-swap/src/instructions/admin/close_permission_pda.rs
use crate::error::ErrorCode;
use crate::states::*;
use anchor_lang::prelude::*;

#[derive(Accounts)]
pub struct ClosePermissionPda<'info> {
/// 调用者必须是管理员,同时接收关闭账户的租金
#[account(
mut,
address = crate::admin::ID @ ErrorCode::InvalidOwner
)]
pub owner: Signer<'info>,

/// 要撤销权限的地址
/// CHECK: permission account authority
pub permission_authority: UncheckedAccount<'info>,

/// 要关闭的权限账户
/// Seeds: ["permission", permission_authority]
#[account(
mut,
seeds = [
PERMISSION_SEED.as_bytes(),
permission_authority.key().as_ref()
],
bump,
close = owner // 关闭账户,租金返还给 owner
)]
pub permission: Account<'info, Permission>,

pub system_program: Program<'info, System>,
}

7.2 指令逻辑

1
2
3
4
// programs/cp-swap/src/instructions/admin/close_permission_pda.rs
pub fn close_permission_pda(_ctx: Context<ClosePermissionPda>) -> Result<()> {
Ok(()) // 账户关闭由 Anchor 的 close 约束自动处理
}

8. 总结

8.1 指令列表

指令 功能 调用者
create_config 创建 AMM 全局配置 管理员
update_config 更新配置参数 管理员
update_pool_status 控制池子状态 管理员
collect_protocol_fee 提取协议费用 协议所有者/管理员
collect_fund_fee 提取基金费用 基金所有者/管理员
create_permission_pda 授权地址创建池子 管理员
close_permission_pda 撤销地址授权 管理员

8.2 调用流程

1
2
3
4
5
6
7
1. create_config          → 部署时创建全局配置
2. create_permission_pda → 授权特定地址(可选)
3. update_config → 按需调整费率/所有者
4. update_pool_status → 控制池子运行状态
5. collect_protocol_fee → 定期提取协议费用
collect_fund_fee → 定期提取基金费用
6. close_permission_pda → 撤销授权(可选)

8.3 费用分配机制

交易产生的手续费按以下方式分配:

  • trade_fee: 总交易手续费(从交易金额中扣除)
  • protocol_fee: 协议抽成 = trade_fee × protocol_fee_rate
  • fund_fee: 基金抽成 = trade_fee × fund_fee_rate
  • creator_fee: 创建者费用(独立计算,由池子创建者收取)