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 use anchor_lang::prelude::*;pub const AMM_CONFIG_SEED: &str = "amm_config" ; #[account] #[derive(Default, Debug)] pub struct AmmConfig { pub bump: u8 , pub disable_create_pool: bool , pub index: u16 , pub trade_fee_rate: u64 , pub protocol_fee_rate: u64 , pub fund_fee_rate: u64 , pub create_pool_fee: u64 , pub protocol_owner: Pubkey, pub fund_owner: Pubkey, pub creator_fee_rate: u64 , 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 use crate::error::ErrorCode;use crate::states::*;use anchor_lang::prelude::*;use std::ops::DerefMut;#[derive(Accounts)] #[instruction(index: u16)] pub struct CreateAmmConfig <'info > { #[account( mut, address = crate::admin::ID @ ErrorCode::InvalidOwner // 验证必须是管理员 )] pub owner: Signer<'info >, #[account( init, // 初始化新账户 seeds = [ AMM_CONFIG_SEED.as_bytes(), // "amm_config" &index.to_be_bytes() // 配置索引(大端序) ] , bump, payer = owner, space = AmmConfig::LEN )] pub amm_config: Account<'info , AmmConfig>, pub system_program: Program<'info , System>, }
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 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; 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 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 <()> { assert! (trade_fee_rate + creator_fee_rate < FEE_RATE_DENOMINATOR_VALUE); assert! (protocol_fee_rate <= FEE_RATE_DENOMINATOR_VALUE); assert! (fund_fee_rate <= FEE_RATE_DENOMINATOR_VALUE); 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 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 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 ) => { let new_procotol_owner = *ctx.remaining_accounts.iter ().next ().unwrap ().key; set_new_protocol_owner (amm_config, new_procotol_owner)?; } Some (4 ) => { 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 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; } 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; } 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; } 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 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 pub fn update_pool_status (ctx: Context<UpdatePoolStatus>, status: u8 ) -> Result <()> { require_gte!(255 , status); 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 pub enum PoolStatusBitIndex { Deposit, Withdraw, Swap, }
状态值示例 :
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 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 >, #[account( seeds = [crate::AUTH_SEED.as_bytes()] , bump, )] pub authority: UncheckedAccount<'info >, #[account(mut)] pub pool_state: AccountLoader<'info , PoolState>, #[account(address = pool_state.load()?.amm_config)] pub amm_config: Account<'info , AmmConfig>, #[account( mut, constraint = token_0_vault.key() == pool_state.load()?.token_0_vault )] pub token_0_vault: Box <InterfaceAccount<'info , TokenAccount>>, #[account( mut, constraint = token_1_vault.key() == pool_state.load()?.token_1_vault )] pub token_1_vault: Box <InterfaceAccount<'info , TokenAccount>>, #[account(address = token_0_vault.mint)] pub vault_0_mint: Box <InterfaceAccount<'info , Mint>>, #[account(address = token_1_vault.mint)] pub vault_1_mint: Box <InterfaceAccount<'info , Mint>>, #[account(mut)] pub recipient_token_0_account: Box <InterfaceAccount<'info , TokenAccount>>, #[account(mut)] pub recipient_token_1_account: Box <InterfaceAccount<'info , TokenAccount>>, pub token_program: Program<'info , Token>, 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 pub fn collect_protocol_fee ( ctx: Context<CollectProtocolFee>, 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 ()?; 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; } 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]]], )?; 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 (()) }
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 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 >, #[account( seeds = [crate::AUTH_SEED.as_bytes()] , bump, )] pub authority: UncheckedAccount<'info >, #[account(mut)] pub pool_state: AccountLoader<'info , PoolState>, #[account(address = pool_state.load()?.amm_config)] pub amm_config: Account<'info , AmmConfig>, #[account( mut, constraint = token_0_vault.key() == pool_state.load()?.token_0_vault )] pub token_0_vault: Box <InterfaceAccount<'info , TokenAccount>>, #[account( mut, constraint = token_1_vault.key() == pool_state.load()?.token_1_vault )] pub token_1_vault: Box <InterfaceAccount<'info , TokenAccount>>, #[account(address = token_0_vault.mint)] pub vault_0_mint: Box <InterfaceAccount<'info , Mint>>, #[account(address = token_1_vault.mint)] pub vault_1_mint: Box <InterfaceAccount<'info , Mint>>, #[account(mut)] pub recipient_token_0_account: Box <InterfaceAccount<'info , TokenAccount>>, #[account(mut)] pub recipient_token_1_account: Box <InterfaceAccount<'info , TokenAccount>>, pub token_program: Program<'info , Token>, 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 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 ()?; 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; } 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]]], )?; 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 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 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 >, pub permission_authority: UncheckedAccount<'info >, #[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 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 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 >, pub permission_authority: UncheckedAccount<'info >, #[account( mut, seeds = [ PERMISSION_SEED.as_bytes(), permission_authority.key().as_ref() ] , bump, close = owner )] pub permission: Account<'info , Permission>, pub system_program: Program<'info , System>, }
7.2 指令逻辑 1 2 3 4 pub fn close_permission_pda (_ctx: Context<ClosePermissionPda>) -> Result <()> { Ok (()) }
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 : 创建者费用(独立计算,由池子创建者收取)