diff --git a/programs/dynamic-fee-sharing/src/error.rs b/programs/dynamic-fee-sharing/src/error.rs index 6908be3..077a3e4 100644 --- a/programs/dynamic-fee-sharing/src/error.rs +++ b/programs/dynamic-fee-sharing/src/error.rs @@ -34,4 +34,7 @@ pub enum FeeVaultError { #[msg("Invalid dammv2 pool")] InvalidDbcPool, + + #[msg("Invalid signer")] + InvalidSigner, } diff --git a/programs/dynamic-fee-sharing/src/instructions/funding_fee.rs b/programs/dynamic-fee-sharing/src/instructions/funding_fee.rs new file mode 100644 index 0000000..bdbae2c --- /dev/null +++ b/programs/dynamic-fee-sharing/src/instructions/funding_fee.rs @@ -0,0 +1,69 @@ +use anchor_lang::prelude::*; +use anchor_spl::token_interface::TokenAccount; + +use crate::{ + error::FeeVaultError, + event::EvtFundFee, + math::SafeMath, + state::{FeeVault, FundingType}, +}; +pub fn handle_funding_fee<'info, F: Fn(&[&[u8]; 4]) -> Result<()>>( + signer: &Pubkey, + fee_vault_account: &AccountLoader<'_, FeeVault>, + token_b_account: &mut Box>, + token_b_mint: &Pubkey, + funder: Pubkey, + funding_type: FundingType, + op: F, +) -> Result<()> { + let fee_vault = fee_vault_account.load()?; + + require!( + fee_vault.is_share_holder(signer), + FeeVaultError::InvalidSigner + ); + + require!( + fee_vault.token_vault.eq(&token_b_account.key()) && fee_vault.token_mint.eq(&token_b_mint), + FeeVaultError::InvalidFeeVault + ); + + // support fee vault type is pda account + require!( + fee_vault.fee_vault_type == 1, + FeeVaultError::InvalidFeeVault + ); + + let signer_seeds = fee_vault_seeds!( + fee_vault.base, + fee_vault.token_mint, + fee_vault.fee_vault_bump + ); + + let before_token_vault_balance = token_b_account.amount; + + op(signer_seeds)?; + + token_b_account.reload()?; + + let after_token_vault_balance = token_b_account.amount; + + let claimed_amount = after_token_vault_balance.safe_sub(before_token_vault_balance)?; + + if claimed_amount > 0 { + drop(fee_vault); + + let mut fee_vault = fee_vault_account.load_mut()?; + fee_vault.fund_fee(claimed_amount)?; + + emit!(EvtFundFee { + funding_type, + fee_vault: fee_vault_account.key(), + funder, + funded_amount: claimed_amount, + fee_per_share: fee_vault.fee_per_share, + }); + } + + Ok(()) +} diff --git a/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_damm_v2.rs b/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_damm_v2.rs index 0cb5024..4381b99 100644 --- a/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_damm_v2.rs +++ b/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_damm_v2.rs @@ -4,12 +4,10 @@ use damm_v2::accounts::Pool; use crate::{ error::FeeVaultError, - event::EvtFundFee, - math::SafeMath, + handle_funding_fee, state::{FeeVault, FundingType}, }; -#[event_cpi] #[derive(Accounts)] pub struct FundingByClaimDammv2FeeCtx<'info> { #[account(mut)] @@ -65,6 +63,9 @@ pub struct FundingByClaimDammv2FeeCtx<'info> { pub dammv2_program: UncheckedAccount<'info>, /// CHECK: dammv2 authority pub dammv2_event_authority: UncheckedAccount<'info>, + + /// signer + pub signer: Signer<'info>, } pub fn handle_funding_by_claim_dammv2_fee(ctx: Context) -> Result<()> { @@ -72,72 +73,39 @@ pub fn handle_funding_by_claim_dammv2_fee(ctx: Context 0 { - let mut fee_vault = ctx.accounts.fee_vault.load_mut()?; - fee_vault.fund_fee(claimed_amount)?; - - emit_cpi!(EvtFundFee { - funding_type: FundingType::ClaimDammV2, - fee_vault: ctx.accounts.fee_vault.key(), - funder: ctx.accounts.pool.key(), - funded_amount: claimed_amount, - fee_per_share: fee_vault.fee_per_share, - }); - } + )?; Ok(()) } diff --git a/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_creator_surplus.rs b/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_creator_surplus.rs index b2f81df..3104009 100644 --- a/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_creator_surplus.rs +++ b/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_creator_surplus.rs @@ -3,13 +3,10 @@ use anchor_spl::token_interface::TokenAccount; use dynamic_bonding_curve::accounts::VirtualPool; use crate::{ - error::FeeVaultError, - event::EvtFundFee, - math::SafeMath, + handle_funding_fee, state::{FeeVault, FundingType}, }; -#[event_cpi] #[derive(Accounts)] pub struct FundingByClaimDbcCreatorSurplusCtx<'info> { #[account(mut)] @@ -33,9 +30,6 @@ pub struct FundingByClaimDbcCreatorSurplusCtx<'info> { /// CHECK: The mint of token base pub quote_mint: UncheckedAccount<'info>, - /// CHECK: Token base program - pub token_base_program: UncheckedAccount<'info>, - /// CHECK: Token quote program pub token_quote_program: UncheckedAccount<'info>, @@ -47,6 +41,9 @@ pub struct FundingByClaimDbcCreatorSurplusCtx<'info> { pub dbc_program: UncheckedAccount<'info>, /// CHECK: dbc event authority pub dbc_event_authority: UncheckedAccount<'info>, + + /// signer + pub signer: Signer<'info>, } pub fn handle_funding_by_claim_dbc_creator_surplus( @@ -60,66 +57,33 @@ pub fn handle_funding_by_claim_dbc_creator_surplus( drop(virtual_pool); - let fee_vault = ctx.accounts.fee_vault.load()?; - - require!( - fee_vault - .token_vault - .eq(&ctx.accounts.token_quote_account.key()) - && fee_vault.token_mint.eq(&ctx.accounts.quote_mint.key()), - FeeVaultError::InvalidFeeVault - ); - - // support fee vault type is pda account - require!( - fee_vault.fee_vault_type == 1, - FeeVaultError::InvalidFeeVault - ); - - let before_token_vault_balance = ctx.accounts.token_quote_account.amount; - - let signer_seeds = fee_vault_seeds!( - fee_vault.base, - fee_vault.token_mint, - fee_vault.fee_vault_bump - ); - - dynamic_bonding_curve::cpi::creator_withdraw_surplus(CpiContext::new_with_signer( - ctx.accounts.dbc_program.to_account_info(), - dynamic_bonding_curve::cpi::accounts::CreatorWithdrawSurplus { - pool_authority: ctx.accounts.dbc_pool_authority.to_account_info(), - config: ctx.accounts.config.to_account_info(), - virtual_pool: ctx.accounts.pool.to_account_info(), - token_quote_account: ctx.accounts.token_quote_account.to_account_info(), - quote_vault: ctx.accounts.quote_vault.to_account_info(), - quote_mint: ctx.accounts.quote_mint.to_account_info(), - creator: ctx.accounts.fee_vault.to_account_info(), - token_quote_program: ctx.accounts.token_quote_program.to_account_info(), - event_authority: ctx.accounts.dbc_event_authority.to_account_info(), - program: ctx.accounts.dbc_program.to_account_info(), + handle_funding_fee( + ctx.accounts.signer.key, + &ctx.accounts.fee_vault, + &mut ctx.accounts.token_quote_account.clone(), + ctx.accounts.quote_mint.key, + ctx.accounts.pool.key(), + FundingType::ClaimDbcCreatorSurplus, + |signer_seeds| { + dynamic_bonding_curve::cpi::creator_withdraw_surplus(CpiContext::new_with_signer( + ctx.accounts.dbc_program.to_account_info(), + dynamic_bonding_curve::cpi::accounts::CreatorWithdrawSurplus { + pool_authority: ctx.accounts.dbc_pool_authority.to_account_info(), + config: ctx.accounts.config.to_account_info(), + virtual_pool: ctx.accounts.pool.to_account_info(), + token_quote_account: ctx.accounts.token_quote_account.to_account_info(), + quote_vault: ctx.accounts.quote_vault.to_account_info(), + quote_mint: ctx.accounts.quote_mint.to_account_info(), + creator: ctx.accounts.fee_vault.to_account_info(), + token_quote_program: ctx.accounts.token_quote_program.to_account_info(), + event_authority: ctx.accounts.dbc_event_authority.to_account_info(), + program: ctx.accounts.dbc_program.to_account_info(), + }, + &[signer_seeds], + ))?; + Ok(()) }, - &[signer_seeds], - ))?; - ctx.accounts.token_quote_account.reload()?; - - let after_token_vault_balance = ctx.accounts.token_quote_account.amount; - - let claimed_amount = after_token_vault_balance.safe_sub(before_token_vault_balance)?; - - drop(fee_vault); - - if claimed_amount > 0 { - let mut fee_vault = ctx.accounts.fee_vault.load_mut()?; - fee_vault.fund_fee(claimed_amount)?; - - emit_cpi!(EvtFundFee { - funding_type: FundingType::ClaimDbcCreatorSurplus, - fee_vault: ctx.accounts.fee_vault.key(), - funder: ctx.accounts.pool.key(), - funded_amount: claimed_amount, - fee_per_share: fee_vault.fee_per_share, - }); - } + )?; Ok(()) } diff --git a/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_creator_trading_fee.rs b/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_creator_trading_fee.rs index d716a89..06bb3a5 100644 --- a/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_creator_trading_fee.rs +++ b/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_creator_trading_fee.rs @@ -4,12 +4,10 @@ use dynamic_bonding_curve::accounts::PoolConfig; use crate::{ error::FeeVaultError, - event::EvtFundFee, - math::SafeMath, + handle_funding_fee, state::{FeeVault, FundingType}, }; -#[event_cpi] #[derive(Accounts)] pub struct FundingByClaimDbcCreatorTradingFeeCtx<'info> { #[account(mut)] @@ -58,79 +56,52 @@ pub struct FundingByClaimDbcCreatorTradingFeeCtx<'info> { pub dbc_program: UncheckedAccount<'info>, /// CHECK: dbc event authority pub dbc_event_authority: UncheckedAccount<'info>, + + /// signer + pub signer: Signer<'info>, } pub fn handle_funding_by_claim_dbc_creator_trading_fee( ctx: Context, ) -> Result<()> { let config = ctx.accounts.config.load()?; - let fee_vault = ctx.accounts.fee_vault.load()?; // support collect fee mode is 0 (only quote token) require!(config.collect_fee_mode == 0, FeeVaultError::InvalidDbcPool); - require!( - fee_vault - .token_vault - .eq(&ctx.accounts.token_b_account.key()) - && fee_vault.token_mint.eq(&ctx.accounts.quote_mint.key()), - FeeVaultError::InvalidFeeVault - ); - - // support fee vault type is pda account - require!( - fee_vault.fee_vault_type == 1, - FeeVaultError::InvalidFeeVault - ); - - let before_token_vault_balance = ctx.accounts.token_b_account.amount; - let signer_seeds = fee_vault_seeds!( - fee_vault.base, - fee_vault.token_mint, - fee_vault.fee_vault_bump - ); - dynamic_bonding_curve::cpi::claim_creator_trading_fee( - CpiContext::new_with_signer( - ctx.accounts.dbc_program.to_account_info(), - dynamic_bonding_curve::cpi::accounts::ClaimCreatorTradingFee { - pool_authority: ctx.accounts.dbc_pool_authority.to_account_info(), - pool: ctx.accounts.pool.to_account_info(), - token_a_account: ctx.accounts.token_a_account.to_account_info(), - token_b_account: ctx.accounts.token_b_account.to_account_info(), - base_vault: ctx.accounts.base_vault.to_account_info(), - quote_vault: ctx.accounts.quote_vault.to_account_info(), - base_mint: ctx.accounts.base_mint.to_account_info(), - quote_mint: ctx.accounts.quote_mint.to_account_info(), - creator: ctx.accounts.fee_vault.to_account_info(), - token_base_program: ctx.accounts.token_base_program.to_account_info(), - token_quote_program: ctx.accounts.token_quote_program.to_account_info(), - event_authority: ctx.accounts.dbc_event_authority.to_account_info(), - program: ctx.accounts.dbc_program.to_account_info(), - }, - &[signer_seeds], - ), - 0, // max base amount, - u64::MAX, // max quote amount, + handle_funding_fee( + ctx.accounts.signer.key, + &ctx.accounts.fee_vault, + &mut ctx.accounts.token_b_account.clone(), + ctx.accounts.quote_mint.key, + ctx.accounts.pool.key(), + FundingType::ClaimDbcCreatorTradingFee, + |signer_seeds| { + dynamic_bonding_curve::cpi::claim_creator_trading_fee( + CpiContext::new_with_signer( + ctx.accounts.dbc_program.to_account_info(), + dynamic_bonding_curve::cpi::accounts::ClaimCreatorTradingFee { + pool_authority: ctx.accounts.dbc_pool_authority.to_account_info(), + pool: ctx.accounts.pool.to_account_info(), + token_a_account: ctx.accounts.token_a_account.to_account_info(), + token_b_account: ctx.accounts.token_b_account.to_account_info(), + base_vault: ctx.accounts.base_vault.to_account_info(), + quote_vault: ctx.accounts.quote_vault.to_account_info(), + base_mint: ctx.accounts.base_mint.to_account_info(), + quote_mint: ctx.accounts.quote_mint.to_account_info(), + creator: ctx.accounts.fee_vault.to_account_info(), + token_base_program: ctx.accounts.token_base_program.to_account_info(), + token_quote_program: ctx.accounts.token_quote_program.to_account_info(), + event_authority: ctx.accounts.dbc_event_authority.to_account_info(), + program: ctx.accounts.dbc_program.to_account_info(), + }, + &[signer_seeds], + ), + 0, // max base amount, + u64::MAX, // max quote amount, + )?; + Ok(()) + }, )?; - ctx.accounts.token_b_account.reload()?; - - let after_token_vault_balance = ctx.accounts.token_b_account.amount; - - let claimed_amount = after_token_vault_balance.safe_sub(before_token_vault_balance)?; - - drop(fee_vault); - - if claimed_amount > 0 { - let mut fee_vault = ctx.accounts.fee_vault.load_mut()?; - fee_vault.fund_fee(claimed_amount)?; - - emit_cpi!(EvtFundFee { - funding_type: FundingType::ClaimDbcCreatorTradingFee, - fee_vault: ctx.accounts.fee_vault.key(), - funder: ctx.accounts.pool.key(), - funded_amount: claimed_amount, - fee_per_share: fee_vault.fee_per_share, - }); - } Ok(()) } diff --git a/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_partner_surplus.rs b/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_partner_surplus.rs index 4465835..65d52e2 100644 --- a/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_partner_surplus.rs +++ b/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_partner_surplus.rs @@ -3,13 +3,10 @@ use anchor_spl::token_interface::TokenAccount; use dynamic_bonding_curve::accounts::VirtualPool; use crate::{ - error::FeeVaultError, - event::EvtFundFee, - math::SafeMath, + handle_funding_fee, state::{FeeVault, FundingType}, }; -#[event_cpi] #[derive(Accounts)] pub struct FundingByClaimDbcPartnerSurplusCtx<'info> { #[account(mut)] @@ -33,9 +30,6 @@ pub struct FundingByClaimDbcPartnerSurplusCtx<'info> { /// CHECK: The mint of token base pub quote_mint: UncheckedAccount<'info>, - /// CHECK: Token base program - pub token_base_program: UncheckedAccount<'info>, - /// CHECK: Token quote program pub token_quote_program: UncheckedAccount<'info>, @@ -47,6 +41,9 @@ pub struct FundingByClaimDbcPartnerSurplusCtx<'info> { pub dbc_program: UncheckedAccount<'info>, /// CHECK: dbc event authority pub dbc_event_authority: UncheckedAccount<'info>, + + /// signer + pub signer: Signer<'info>, } pub fn handle_funding_by_claim_dbc_partner_surplus( @@ -60,66 +57,33 @@ pub fn handle_funding_by_claim_dbc_partner_surplus( drop(virtual_pool); - let fee_vault = ctx.accounts.fee_vault.load()?; - - require!( - fee_vault - .token_vault - .eq(&ctx.accounts.token_quote_account.key()) - && fee_vault.token_mint.eq(&ctx.accounts.quote_mint.key()), - FeeVaultError::InvalidFeeVault - ); - - // support fee vault type is pda account - require!( - fee_vault.fee_vault_type == 1, - FeeVaultError::InvalidFeeVault - ); - - let before_token_vault_balance = ctx.accounts.token_quote_account.amount; - - let signer_seeds = fee_vault_seeds!( - fee_vault.base, - fee_vault.token_mint, - fee_vault.fee_vault_bump - ); - - dynamic_bonding_curve::cpi::partner_withdraw_surplus(CpiContext::new_with_signer( - ctx.accounts.dbc_program.to_account_info(), - dynamic_bonding_curve::cpi::accounts::PartnerWithdrawSurplus { - pool_authority: ctx.accounts.dbc_pool_authority.to_account_info(), - config: ctx.accounts.config.to_account_info(), - virtual_pool: ctx.accounts.pool.to_account_info(), - token_quote_account: ctx.accounts.token_quote_account.to_account_info(), - quote_vault: ctx.accounts.quote_vault.to_account_info(), - quote_mint: ctx.accounts.quote_mint.to_account_info(), - fee_claimer: ctx.accounts.fee_vault.to_account_info(), - token_quote_program: ctx.accounts.token_quote_program.to_account_info(), - event_authority: ctx.accounts.dbc_event_authority.to_account_info(), - program: ctx.accounts.dbc_program.to_account_info(), + handle_funding_fee( + ctx.accounts.signer.key, + &ctx.accounts.fee_vault, + &mut ctx.accounts.token_quote_account.clone(), + ctx.accounts.quote_mint.key, + ctx.accounts.pool.key(), + FundingType::ClaimDbcPartnerSurplus, + |signer_seeds| { + dynamic_bonding_curve::cpi::partner_withdraw_surplus(CpiContext::new_with_signer( + ctx.accounts.dbc_program.to_account_info(), + dynamic_bonding_curve::cpi::accounts::PartnerWithdrawSurplus { + pool_authority: ctx.accounts.dbc_pool_authority.to_account_info(), + config: ctx.accounts.config.to_account_info(), + virtual_pool: ctx.accounts.pool.to_account_info(), + token_quote_account: ctx.accounts.token_quote_account.to_account_info(), + quote_vault: ctx.accounts.quote_vault.to_account_info(), + quote_mint: ctx.accounts.quote_mint.to_account_info(), + fee_claimer: ctx.accounts.fee_vault.to_account_info(), + token_quote_program: ctx.accounts.token_quote_program.to_account_info(), + event_authority: ctx.accounts.dbc_event_authority.to_account_info(), + program: ctx.accounts.dbc_program.to_account_info(), + }, + &[signer_seeds], + ))?; + Ok(()) }, - &[signer_seeds], - ))?; - ctx.accounts.token_quote_account.reload()?; - - let after_token_vault_balance = ctx.accounts.token_quote_account.amount; - - let claimed_amount = after_token_vault_balance.safe_sub(before_token_vault_balance)?; - - drop(fee_vault); - - if claimed_amount > 0 { - let mut fee_vault = ctx.accounts.fee_vault.load_mut()?; - fee_vault.fund_fee(claimed_amount)?; - - emit_cpi!(EvtFundFee { - funding_type: FundingType::ClaimDbcPartnerSurplus, - fee_vault: ctx.accounts.fee_vault.key(), - funder: ctx.accounts.pool.key(), - funded_amount: claimed_amount, - fee_per_share: fee_vault.fee_per_share, - }); - } + )?; Ok(()) } diff --git a/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_partner_trading_fee.rs b/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_partner_trading_fee.rs index ac700d4..b6b9fef 100644 --- a/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_partner_trading_fee.rs +++ b/programs/dynamic-fee-sharing/src/instructions/ix_funding_by_claim_dbc_partner_trading_fee.rs @@ -4,12 +4,10 @@ use dynamic_bonding_curve::accounts::PoolConfig; use crate::{ error::FeeVaultError, - event::EvtFundFee, - math::SafeMath, + handle_funding_fee, state::{FeeVault, FundingType}, }; -#[event_cpi] #[derive(Accounts)] pub struct FundingByClaimDbcTradingFeeCtx<'info> { #[account(mut)] @@ -58,82 +56,54 @@ pub struct FundingByClaimDbcTradingFeeCtx<'info> { pub dbc_program: UncheckedAccount<'info>, /// CHECK: dbc authority pub dbc_event_authority: UncheckedAccount<'info>, + + /// signer + pub signer: Signer<'info>, } pub fn handle_funding_by_claim_dbc_partner_trading_fee( ctx: Context, ) -> Result<()> { let config = ctx.accounts.config.load()?; - let fee_vault = ctx.accounts.fee_vault.load()?; // support collect fee mode is 0 (only quote token) require!(config.collect_fee_mode == 0, FeeVaultError::InvalidDbcPool); - require!( - fee_vault - .token_vault - .eq(&ctx.accounts.token_b_account.key()) - && fee_vault.token_mint.eq(&ctx.accounts.quote_mint.key()), - FeeVaultError::InvalidFeeVault - ); - - // support fee vault type is pda account - require!( - fee_vault.fee_vault_type == 1, - FeeVaultError::InvalidFeeVault - ); - - let before_token_vault_balance = ctx.accounts.token_b_account.amount; - - let signer_seeds = fee_vault_seeds!( - fee_vault.base, - fee_vault.token_mint, - fee_vault.fee_vault_bump - ); - - dynamic_bonding_curve::cpi::claim_trading_fee( - CpiContext::new_with_signer( - ctx.accounts.dbc_program.to_account_info(), - dynamic_bonding_curve::cpi::accounts::ClaimTradingFee { - pool_authority: ctx.accounts.dbc_pool_authority.to_account_info(), - config: ctx.accounts.config.to_account_info(), - pool: ctx.accounts.pool.to_account_info(), - token_a_account: ctx.accounts.token_a_account.to_account_info(), - token_b_account: ctx.accounts.token_b_account.to_account_info(), - base_vault: ctx.accounts.base_vault.to_account_info(), - quote_vault: ctx.accounts.quote_vault.to_account_info(), - base_mint: ctx.accounts.base_mint.to_account_info(), - quote_mint: ctx.accounts.quote_mint.to_account_info(), - fee_claimer: ctx.accounts.fee_vault.to_account_info(), - token_base_program: ctx.accounts.token_base_program.to_account_info(), - token_quote_program: ctx.accounts.token_quote_program.to_account_info(), - event_authority: ctx.accounts.dbc_event_authority.to_account_info(), - program: ctx.accounts.dbc_program.to_account_info(), - }, - &[signer_seeds], - ), - 0, // max_amount_a, fee only token b - u64::MAX, // max_amount_b, + handle_funding_fee( + ctx.accounts.signer.key, + &ctx.accounts.fee_vault, + &mut ctx.accounts.token_b_account.clone(), + ctx.accounts.quote_mint.key, + ctx.accounts.pool.key(), + FundingType::ClaimDbcPartnerTradingFee, + |signer_seeds| { + dynamic_bonding_curve::cpi::claim_trading_fee( + CpiContext::new_with_signer( + ctx.accounts.dbc_program.to_account_info(), + dynamic_bonding_curve::cpi::accounts::ClaimTradingFee { + pool_authority: ctx.accounts.dbc_pool_authority.to_account_info(), + config: ctx.accounts.config.to_account_info(), + pool: ctx.accounts.pool.to_account_info(), + token_a_account: ctx.accounts.token_a_account.to_account_info(), + token_b_account: ctx.accounts.token_b_account.to_account_info(), + base_vault: ctx.accounts.base_vault.to_account_info(), + quote_vault: ctx.accounts.quote_vault.to_account_info(), + base_mint: ctx.accounts.base_mint.to_account_info(), + quote_mint: ctx.accounts.quote_mint.to_account_info(), + fee_claimer: ctx.accounts.fee_vault.to_account_info(), + token_base_program: ctx.accounts.token_base_program.to_account_info(), + token_quote_program: ctx.accounts.token_quote_program.to_account_info(), + event_authority: ctx.accounts.dbc_event_authority.to_account_info(), + program: ctx.accounts.dbc_program.to_account_info(), + }, + &[signer_seeds], + ), + 0, // max_amount_a, fee only token b + u64::MAX, // max_amount_b, + )?; + + Ok(()) + }, )?; - ctx.accounts.token_b_account.reload()?; - - let after_token_vault_balance = ctx.accounts.token_b_account.amount; - - let claimed_amount = after_token_vault_balance.safe_sub(before_token_vault_balance)?; - - drop(fee_vault); - - if claimed_amount > 0 { - let mut fee_vault = ctx.accounts.fee_vault.load_mut()?; - fee_vault.fund_fee(claimed_amount)?; - - emit_cpi!(EvtFundFee { - funding_type: FundingType::ClaimDbcPartnerTradingFee, - fee_vault: ctx.accounts.fee_vault.key(), - funder: ctx.accounts.pool.key(), - funded_amount: claimed_amount, - fee_per_share: fee_vault.fee_per_share, - }); - } Ok(()) } diff --git a/programs/dynamic-fee-sharing/src/instructions/mod.rs b/programs/dynamic-fee-sharing/src/instructions/mod.rs index ab0a0ac..58bad2c 100644 --- a/programs/dynamic-fee-sharing/src/instructions/mod.rs +++ b/programs/dynamic-fee-sharing/src/instructions/mod.rs @@ -16,3 +16,5 @@ pub mod ix_funding_by_claim_dbc_creator_surplus; pub use ix_funding_by_claim_dbc_creator_surplus::*; pub mod ix_funding_by_claim_dbc_partner_surplus; pub use ix_funding_by_claim_dbc_partner_surplus::*; +pub mod funding_fee; +pub use funding_fee::*; diff --git a/programs/dynamic-fee-sharing/src/state/fee_vault.rs b/programs/dynamic-fee-sharing/src/state/fee_vault.rs index f72ad0d..a037227 100644 --- a/programs/dynamic-fee-sharing/src/state/fee_vault.rs +++ b/programs/dynamic-fee-sharing/src/state/fee_vault.rs @@ -130,4 +130,10 @@ impl FeeVault { Ok(fee_being_claimed) } + + pub fn is_share_holder(&self, signer: &Pubkey) -> bool { + self.users + .iter() + .any(|share_holder| share_holder.address.eq(signer)) + } } diff --git a/tests/claim_damm_v2.test.ts b/tests/claim_damm_v2.test.ts index c403e07..dd3dc83 100644 --- a/tests/claim_damm_v2.test.ts +++ b/tests/claim_damm_v2.test.ts @@ -9,11 +9,13 @@ import { import { createToken, deriveFeeVaultAuthorityAddress, + expectThrowsErrorCode, getFeeVault, + getProgramErrorCodeHexString, mintToken, } from "./common"; import { createDammV2Pool, dammV2Swap } from "./common/damm_v2"; -import { claimDammV2Fee, createFeeVaultPda } from "./common/dfs"; +import { claimDammV2Fee, claimDammV2FeeExpectThrowError, createFeeVaultPda } from "./common/dfs"; import { BN } from "bn.js"; import { expect } from "chai"; import { @@ -32,10 +34,11 @@ describe("Claim damm v2 fee", () => { let dammV2Pool: PublicKey; let positionNftAccount: PublicKey; let position: PublicKey; + let shareHolder: Keypair; beforeEach(async () => { svm = startSvm(); - [admin, creator, vaultOwner] = generateUsers(svm, 7); + [admin, creator, vaultOwner, shareHolder] = generateUsers(svm, 4); tokenAMint = createToken(svm, admin, admin.publicKey, null); tokenBMint = createToken(svm, admin, admin.publicKey, null); @@ -64,7 +67,7 @@ describe("Claim damm v2 fee", () => { padding: [], users: [ { - address: PublicKey.unique(), + address: shareHolder.publicKey, share: 100, }, { @@ -106,8 +109,21 @@ describe("Claim damm v2 fee", () => { minimumAmountOut: new BN(0), }); + + await claimDammV2FeeExpectThrowError( + svm, + creator, + creator, + feeVault, + tokenVault, + dammV2Pool, + position, + positionNftAccount + ) + await claimDammV2Fee( svm, + shareHolder, creator, feeVault, tokenVault, diff --git a/tests/claim_dbc_creator_trading_fee.test.ts b/tests/claim_dbc_creator_trading_fee.test.ts index fdf975d..e74a2d6 100644 --- a/tests/claim_dbc_creator_trading_fee.test.ts +++ b/tests/claim_dbc_creator_trading_fee.test.ts @@ -36,6 +36,7 @@ describe("Claim fee and withdraw dbc surplus", () => { let poolCreator: Keypair; let vaultOwner: Keypair; let quoteMint: PublicKey; + let shareHolder: Keypair; beforeEach(async () => { svm = startSvm(); @@ -43,7 +44,7 @@ describe("Claim fee and withdraw dbc surplus", () => { payer = Keypair.generate(); user = Keypair.generate(); poolCreator = Keypair.generate(); - [admin, payer, user, poolCreator, vaultOwner] = generateUsers(svm, 5); + [admin, payer, user, poolCreator, vaultOwner, shareHolder] = generateUsers(svm, 6); quoteMint = createToken(svm, admin, admin.publicKey, null); }); @@ -57,7 +58,7 @@ describe("Claim fee and withdraw dbc surplus", () => { padding: [], users: [ { - address: PublicKey.unique(), + address: shareHolder.publicKey, share: 100, }, { @@ -87,6 +88,7 @@ describe("Claim fee and withdraw dbc surplus", () => { await claimDbcCreatorTradingFee( svm, + shareHolder, poolCreator, feeVault, tokenVault, @@ -116,7 +118,7 @@ describe("Claim fee and withdraw dbc surplus", () => { padding: [], users: [ { - address: PublicKey.unique(), + address: shareHolder.publicKey, share: 100, }, { @@ -146,6 +148,7 @@ describe("Claim fee and withdraw dbc surplus", () => { await claimDbcTradingFee( svm, + shareHolder, payer, feeVault, tokenVault, @@ -175,7 +178,7 @@ describe("Claim fee and withdraw dbc surplus", () => { padding: [], users: [ { - address: PublicKey.unique(), + address: shareHolder.publicKey, share: 100, }, { @@ -205,7 +208,7 @@ describe("Claim fee and withdraw dbc surplus", () => { await withdrawDbcCreatorSurplus( svm, - poolCreator, + shareHolder, feeVault, tokenVault, virtualPoolConfig, @@ -234,7 +237,7 @@ describe("Claim fee and withdraw dbc surplus", () => { padding: [], users: [ { - address: PublicKey.unique(), + address: shareHolder.publicKey, share: 100, }, { @@ -264,7 +267,7 @@ describe("Claim fee and withdraw dbc surplus", () => { await withdrawDbcPartnerSurplus( svm, - payer, + shareHolder, feeVault, tokenVault, virtualPoolConfig, diff --git a/tests/common/dfs.ts b/tests/common/dfs.ts index 422ae51..4df7e6c 100644 --- a/tests/common/dfs.ts +++ b/tests/common/dfs.ts @@ -5,6 +5,7 @@ import { deriveFeeVaultPdaAddress, deriveTokenVaultAddress, getOrCreateAtA, + getProgramErrorCodeHexString, InitializeFeeVaultParameters, } from "."; import { LiteSVM } from "litesvm"; @@ -66,14 +67,65 @@ export async function createFeeVaultPda( return { feeVault, tokenVault }; } +export async function claimDammV2FeeExpectThrowError( + svm: LiteSVM, + signer: Keypair, + owner: Keypair, + feeVault: PublicKey, + tokenVault: PublicKey, + dammv2Pool: PublicKey, + position: PublicKey, + positionNftAccount: PublicKey, +) { + const program = createProgram(); + const dammV2PoolState = getDammV2PoolState(svm, dammv2Pool); + + const tokenAAccount = getAssociatedTokenAddressSync( + dammV2PoolState.tokenAMint, + owner.publicKey, + true, + getProgramFromFlagDammV2(dammV2PoolState.tokenAFlag) + ); + + const tx = await program.methods + .fundingByClaimDammv2Fee() + .accountsPartial({ + feeVault, + pool: dammv2Pool, + position, + positionNftAccount, + tokenAAccount, + tokenBAccount: tokenVault, + tokenAVault: dammV2PoolState.tokenAVault, + tokenBVault: dammV2PoolState.tokenBVault, + tokenAMint: dammV2PoolState.tokenAMint, + tokenBMint: dammV2PoolState.tokenBMint, + tokenAProgram: getProgramFromFlagDammV2(dammV2PoolState.tokenAFlag), + tokenBProgram: getProgramFromFlagDammV2(dammV2PoolState.tokenBFlag), + dammv2EventAuthority: deriveDammV2EventAuthority(), + dammv2PoolAuthority: deriveDammV2PoolAuthority(), + dammv2Program: DAMM_V2_PROGRAM_ID, + signer: signer.publicKey + }) + .transaction(); + + tx.recentBlockhash = svm.latestBlockhash(); + tx.sign(signer); + + const errorCode = getProgramErrorCodeHexString("InvalidSigner"); + + sendTransactionOrExpectThrowError(svm, tx, true, errorCode); +} + export async function claimDammV2Fee( svm: LiteSVM, + signer: Keypair, owner: Keypair, feeVault: PublicKey, tokenVault: PublicKey, dammv2Pool: PublicKey, position: PublicKey, - positionNftAccount: PublicKey + positionNftAccount: PublicKey, ) { const program = createProgram(); const dammV2PoolState = getDammV2PoolState(svm, dammv2Pool); @@ -103,17 +155,21 @@ export async function claimDammV2Fee( dammv2EventAuthority: deriveDammV2EventAuthority(), dammv2PoolAuthority: deriveDammV2PoolAuthority(), dammv2Program: DAMM_V2_PROGRAM_ID, + signer: signer.publicKey }) .transaction(); tx.recentBlockhash = svm.latestBlockhash(); - tx.sign(owner); + tx.sign(signer); - sendTransactionOrExpectThrowError(svm, tx, true); + const result = sendTransactionOrExpectThrowError(svm, tx, true); + + return result } export async function claimDbcCreatorTradingFee( svm: LiteSVM, + signer: Keypair, creator: Keypair, feeVault: PublicKey, tokenVault: PublicKey, @@ -149,17 +205,19 @@ export async function claimDbcCreatorTradingFee( dbcEventAuthority: deriveDbcEventAuthority(), dbcPoolAuthority: deriveDbcPoolAuthority(), dbcProgram: DBC_PROGRAM_ID, + signer: signer.publicKey }) .transaction(); tx.recentBlockhash = svm.latestBlockhash(); - tx.sign(creator); + tx.sign(signer); sendTransactionOrExpectThrowError(svm, tx); } export async function claimDbcTradingFee( svm: LiteSVM, + signer: Keypair, feeClaimer: Keypair, feeVault: PublicKey, tokenVault: PublicKey, @@ -195,18 +253,19 @@ export async function claimDbcTradingFee( dbcEventAuthority: deriveDbcEventAuthority(), dbcPoolAuthority: deriveDbcPoolAuthority(), dbcProgram: DBC_PROGRAM_ID, + signer: signer.publicKey }) .transaction(); tx.recentBlockhash = svm.latestBlockhash(); - tx.sign(feeClaimer); + tx.sign(signer); sendTransactionOrExpectThrowError(svm, tx); } export async function withdrawDbcCreatorSurplus( svm: LiteSVM, - creator: Keypair, + signer: Keypair, feeVault: PublicKey, tokenVault: PublicKey, poolConfig: PublicKey, @@ -225,23 +284,23 @@ export async function withdrawDbcCreatorSurplus( tokenQuoteAccount: tokenVault, quoteVault: virtualPoolState.quoteVault, quoteMint: poolConfigState.quoteMint, - tokenBaseProgram: TOKEN_2022_PROGRAM_ID, tokenQuoteProgram: TOKEN_PROGRAM_ID, dbcEventAuthority: deriveDbcEventAuthority(), dbcPoolAuthority: deriveDbcPoolAuthority(), dbcProgram: DBC_PROGRAM_ID, + signer: signer.publicKey }) .transaction(); tx.recentBlockhash = svm.latestBlockhash(); - tx.sign(creator); + tx.sign(signer); sendTransactionOrExpectThrowError(svm, tx, true); } export async function withdrawDbcPartnerSurplus( svm: LiteSVM, - feeClaimer: Keypair, + signer: Keypair, feeVault: PublicKey, tokenVault: PublicKey, poolConfig: PublicKey, @@ -260,16 +319,16 @@ export async function withdrawDbcPartnerSurplus( tokenQuoteAccount: tokenVault, quoteVault: virtualPoolState.quoteVault, quoteMint: poolConfigState.quoteMint, - tokenBaseProgram: TOKEN_2022_PROGRAM_ID, tokenQuoteProgram: TOKEN_PROGRAM_ID, dbcEventAuthority: deriveDbcEventAuthority(), dbcPoolAuthority: deriveDbcPoolAuthority(), dbcProgram: DBC_PROGRAM_ID, + signer: signer.publicKey }) .transaction(); tx.recentBlockhash = svm.latestBlockhash(); - tx.sign(feeClaimer); + tx.sign(signer); sendTransactionOrExpectThrowError(svm, tx, true); } diff --git a/tests/common/svm.ts b/tests/common/svm.ts index 6d48139..fb5152c 100644 --- a/tests/common/svm.ts +++ b/tests/common/svm.ts @@ -56,7 +56,7 @@ export function sendTransactionOrExpectThrowError( transaction: Transaction, logging = false, errorCode?: number -): void { +) { const result = svm.sendTransaction(transaction); if (logging) { if (result instanceof TransactionMetadata) { @@ -70,6 +70,8 @@ export function sendTransactionOrExpectThrowError( } else { expect(result).instanceOf(TransactionMetadata); } + + return result } export function generateUsers(svm: LiteSVM, numberOfUsers: number): Keypair[] {