use crate::error::ErrorCode; use crate::states::*; use crate::util::*; use anchor_lang::prelude::*; use anchor_spl::token::Token; use anchor_spl::token_interface::{Mint, Token2022, TokenAccount}; #[derive(Accounts)] pub struct CollectProtocolFee<'info> { /// Only admin or config owner can collect fee now #[account(constraint = (owner.key() == amm_config.owner || owner.key() == crate::admin::ID) @ ErrorCode::NotApproved)] pub owner: Signer<'info>, /// Pool state stores accumulated protocol fee amount #[account(mut)] pub pool_state: AccountLoader<'info, PoolState>, /// Amm config account stores owner #[account( address = pool_state.load()?.amm_config )] pub amm_config: Account<'info, AmmConfig>, /// The address that holds pool tokens for token_0 #[account( mut, constraint = token_vault_0.key() == pool_state.load()?.token_vault_0 )] pub token_vault_0: Box>, /// The address that holds pool tokens for token_1 #[account( mut, constraint = token_vault_1.key() == pool_state.load()?.token_vault_1 )] pub token_vault_1: Box>, /// The mint of token vault 0 #[account( address = token_vault_0.mint )] pub vault_0_mint: Box>, /// The mint of token vault 1 #[account( address = token_vault_1.mint )] pub vault_1_mint: Box>, /// The address that receives the collected token_0 protocol fees #[account(mut)] pub recipient_token_account_0: Box>, /// The address that receives the collected token_1 protocol fees #[account(mut)] pub recipient_token_account_1: Box>, /// The SPL program to perform token transfers pub token_program: Program<'info, Token>, /// The SPL program 2022 to perform token transfers pub token_program_2022: Program<'info, Token2022>, } pub fn collect_protocol_fee( ctx: Context, amount_0_requested: u64, amount_1_requested: u64, ) -> Result<()> { let amount_0: u64; let amount_1: u64; { 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) .ok_or(ErrorCode::CalculateOverflow)?; pool_state.protocol_fees_token_1 = pool_state .protocol_fees_token_1 .checked_sub(amount_1) .ok_or(ErrorCode::CalculateOverflow)?; } transfer_from_pool_vault_to_user( &ctx.accounts.pool_state, &ctx.accounts.token_vault_0.to_account_info(), &ctx.accounts.recipient_token_account_0.to_account_info(), Some(ctx.accounts.vault_0_mint.clone()), &ctx.accounts.token_program, Some(ctx.accounts.token_program_2022.to_account_info()), amount_0, )?; transfer_from_pool_vault_to_user( &ctx.accounts.pool_state, &ctx.accounts.token_vault_1.to_account_info(), &ctx.accounts.recipient_token_account_1.to_account_info(), Some(ctx.accounts.vault_1_mint.clone()), &ctx.accounts.token_program, Some(ctx.accounts.token_program_2022.to_account_info()), amount_1, )?; emit!(CollectProtocolFeeEvent { pool_state: ctx.accounts.pool_state.key(), recipient_token_account_0: ctx.accounts.recipient_token_account_0.key(), recipient_token_account_1: ctx.accounts.recipient_token_account_1.key(), amount_0, amount_1, }); Ok(()) }