From 18b2b3a89ce738f1ad1dd7dee4a5fe9e2520b458 Mon Sep 17 00:00:00 2001 From: Jon Gurary Date: Tue, 27 Aug 2024 17:07:53 -0400 Subject: [PATCH] Add fee state to pool generation args, basic setup of text fixture, WIP updating tests to include the fee --- .../instructions/marginfi_group/add_pool.rs | 206 ++++------------- .../marginfi_group/add_pool_with_seed.rs | 213 ++++++++++++++++++ .../marginfi_group/edit_global_fee.rs | 34 +++ .../marginfi_group/init_global_fee_state.rs | 43 ++++ .../src/instructions/marginfi_group/mod.rs | 6 + programs/marginfi/src/lib.rs | 20 ++ test-utils/src/marginfi_group.rs | 70 ++++++ 7 files changed, 429 insertions(+), 163 deletions(-) create mode 100644 programs/marginfi/src/instructions/marginfi_group/add_pool_with_seed.rs create mode 100644 programs/marginfi/src/instructions/marginfi_group/edit_global_fee.rs create mode 100644 programs/marginfi/src/instructions/marginfi_group/init_global_fee_state.rs diff --git a/programs/marginfi/src/instructions/marginfi_group/add_pool.rs b/programs/marginfi/src/instructions/marginfi_group/add_pool.rs index 35895a1f2..b9de03eab 100644 --- a/programs/marginfi/src/instructions/marginfi_group/add_pool.rs +++ b/programs/marginfi/src/instructions/marginfi_group/add_pool.rs @@ -1,10 +1,9 @@ use crate::{ constants::{ - FEE_VAULT_AUTHORITY_SEED, FEE_VAULT_SEED, INSURANCE_VAULT_AUTHORITY_SEED, - INSURANCE_VAULT_SEED, LIQUIDITY_VAULT_AUTHORITY_SEED, LIQUIDITY_VAULT_SEED, + FEE_STATE_SEED, FEE_VAULT_AUTHORITY_SEED, FEE_VAULT_SEED, INSURANCE_VAULT_AUTHORITY_SEED, INSURANCE_VAULT_SEED, LIQUIDITY_VAULT_AUTHORITY_SEED, LIQUIDITY_VAULT_SEED }, events::{GroupEventHeader, LendingPoolBankCreateEvent}, - state::marginfi_group::{Bank, BankConfig, BankConfigCompact, MarginfiGroup}, + state::{fee_state::FeeState, marginfi_group::{Bank, BankConfig, BankConfigCompact, MarginfiGroup}}, MarginfiResult, }; use anchor_lang::prelude::*; @@ -19,6 +18,16 @@ pub fn lending_pool_add_bank( ctx: Context, bank_config: BankConfig, ) -> MarginfiResult { + // Transfer the flat sol init fee to the global fee wallet + let fee_state = ctx.accounts.fee_state.load()?; + let bank_init_flat_sol_fee = fee_state.bank_init_flat_sol_fee; + if bank_init_flat_sol_fee > 0 { + anchor_lang::system_program::transfer( + ctx.accounts.transfer_flat_fee(), + bank_init_flat_sol_fee as u64, + )?; + } + let LendingPoolAddBank { bank_mint, liquidity_vault, @@ -69,6 +78,10 @@ pub fn lending_pool_add_bank( Ok(()) } +/* +. Aligns line spacing for easier comparison against with_seed +. +*/ #[derive(Accounts)] #[instruction(bank_config: BankConfigCompact)] pub struct LendingPoolAddBank<'info> { @@ -80,168 +93,21 @@ pub struct LendingPoolAddBank<'info> { )] pub admin: Signer<'info>, + /// Pays to init accounts and pays `fee_state.bank_init_flat_sol_fee` lamports to the protocol #[account(mut)] pub fee_payer: Signer<'info>, - pub bank_mint: Box>, - + // Note: there is just one FeeState per program, so no further check is required. #[account( - init, - space = 8 + std::mem::size_of::(), - payer = fee_payer, - )] - pub bank: AccountLoader<'info, Bank>, - - /// CHECK: ⋐ ͡⋄ ω ͡⋄ ⋑ - #[account( - seeds = [ - LIQUIDITY_VAULT_AUTHORITY_SEED.as_bytes(), - bank.key().as_ref(), - ], - bump - )] - pub liquidity_vault_authority: AccountInfo<'info>, - - #[account( - init, - payer = fee_payer, - token::mint = bank_mint, - token::authority = liquidity_vault_authority, - seeds = [ - LIQUIDITY_VAULT_SEED.as_bytes(), - bank.key().as_ref(), - ], - bump, - )] - pub liquidity_vault: Box>, - - /// CHECK: ⋐ ͡⋄ ω ͡⋄ ⋑ - #[account( - seeds = [ - INSURANCE_VAULT_AUTHORITY_SEED.as_bytes(), - bank.key().as_ref(), - ], - bump - )] - pub insurance_vault_authority: AccountInfo<'info>, - - #[account( - init, - payer = fee_payer, - token::mint = bank_mint, - token::authority = insurance_vault_authority, - seeds = [ - INSURANCE_VAULT_SEED.as_bytes(), - bank.key().as_ref(), - ], - bump, - )] - pub insurance_vault: Box>, - - /// CHECK: ⋐ ͡⋄ ω ͡⋄ ⋑ - #[account( - seeds = [ - FEE_VAULT_AUTHORITY_SEED.as_bytes(), - bank.key().as_ref(), - ], - bump - )] - pub fee_vault_authority: AccountInfo<'info>, - - #[account( - init, - payer = fee_payer, - token::mint = bank_mint, - token::authority = fee_vault_authority, - seeds = [ - FEE_VAULT_SEED.as_bytes(), - bank.key().as_ref(), - ], + seeds = [FEE_STATE_SEED.as_bytes()], bump, + has_one = global_fee_wallet )] - pub fee_vault: Box>, - - pub rent: Sysvar<'info, Rent>, - pub token_program: Interface<'info, TokenInterface>, - pub system_program: Program<'info, System>, -} - -/// A copy of lending_pool_add_bank but with an additional bank seed provided. -/// This seed is used by the LendingPoolAddBankWithSeed.bank to generate a -/// PDA account to sign for newly added bank transactions securely. -/// The previous lending_pool_add_bank is preserved for backwards-compatibility. -pub fn lending_pool_add_bank_with_seed( - ctx: Context, - bank_config: BankConfig, - _bank_seed: u64, -) -> MarginfiResult { - let LendingPoolAddBankWithSeed { - bank_mint, - liquidity_vault, - insurance_vault, - fee_vault, - bank: bank_loader, - .. - } = ctx.accounts; - - let mut bank = bank_loader.load_init()?; - - let liquidity_vault_bump = ctx.bumps.liquidity_vault; - let liquidity_vault_authority_bump = ctx.bumps.liquidity_vault_authority; - let insurance_vault_bump = ctx.bumps.insurance_vault; - let insurance_vault_authority_bump = ctx.bumps.insurance_vault_authority; - let fee_vault_bump = ctx.bumps.fee_vault; - let fee_vault_authority_bump = ctx.bumps.fee_vault_authority; - - *bank = Bank::new( - ctx.accounts.marginfi_group.key(), - bank_config, - bank_mint.key(), - bank_mint.decimals, - liquidity_vault.key(), - insurance_vault.key(), - fee_vault.key(), - Clock::get().unwrap().unix_timestamp, - liquidity_vault_bump, - liquidity_vault_authority_bump, - insurance_vault_bump, - insurance_vault_authority_bump, - fee_vault_bump, - fee_vault_authority_bump, - ); - - bank.config.validate()?; - bank.config.validate_oracle_setup(ctx.remaining_accounts)?; - - emit!(LendingPoolBankCreateEvent { - header: GroupEventHeader { - marginfi_group: ctx.accounts.marginfi_group.key(), - signer: Some(*ctx.accounts.admin.key) - }, - bank: bank_loader.key(), - mint: bank_mint.key(), - }); - - Ok(()) -} - -/// A copy of LendingPoolAddBank but with an additional bank seed provided. -/// This seed is used by the LendingPoolAddBankWithSeed.bank to generate a -/// PDA account to sign for newly added bank transactions securely. -/// The previous LendingPoolAddBank is preserved for backwards-compatibility. -#[derive(Accounts)] -#[instruction(bank_config: BankConfigCompact, bank_seed: u64)] -pub struct LendingPoolAddBankWithSeed<'info> { - pub marginfi_group: AccountLoader<'info, MarginfiGroup>, - - #[account( - mut, - address = marginfi_group.load()?.admin, - )] - pub admin: Signer<'info>, + pub fee_state: AccountLoader<'info, FeeState>, + /// CHECK: The fee admin's native SOL wallet, validated against fee state #[account(mut)] - pub fee_payer: Signer<'info>, + pub global_fee_wallet: AccountInfo<'info>, pub bank_mint: Box>, @@ -249,12 +115,12 @@ pub struct LendingPoolAddBankWithSeed<'info> { init, space = 8 + std::mem::size_of::(), payer = fee_payer, - seeds = [ - marginfi_group.key().as_ref(), - bank_mint.key().as_ref(), - &bank_seed.to_le_bytes(), - ], - bump, + /* + In the "with seed" version of this ix, the seed is defined here + . + . + . + */ )] pub bank: AccountLoader<'info, Bank>, @@ -331,3 +197,17 @@ pub struct LendingPoolAddBankWithSeed<'info> { pub token_program: Interface<'info, TokenInterface>, pub system_program: Program<'info, System>, } + +impl<'info> LendingPoolAddBank<'info> { + fn transfer_flat_fee( + &self, + ) -> CpiContext<'_, '_, '_, 'info, anchor_lang::system_program::Transfer<'info>> { + CpiContext::new( + self.system_program.to_account_info(), + anchor_lang::system_program::Transfer { + from: self.fee_payer.to_account_info(), + to: self.global_fee_wallet.to_account_info(), + }, + ) + } +} diff --git a/programs/marginfi/src/instructions/marginfi_group/add_pool_with_seed.rs b/programs/marginfi/src/instructions/marginfi_group/add_pool_with_seed.rs new file mode 100644 index 000000000..97199c2cc --- /dev/null +++ b/programs/marginfi/src/instructions/marginfi_group/add_pool_with_seed.rs @@ -0,0 +1,213 @@ +use crate::{ + constants::{ + FEE_STATE_SEED, FEE_VAULT_AUTHORITY_SEED, FEE_VAULT_SEED, INSURANCE_VAULT_AUTHORITY_SEED, INSURANCE_VAULT_SEED, LIQUIDITY_VAULT_AUTHORITY_SEED, LIQUIDITY_VAULT_SEED + }, + events::{GroupEventHeader, LendingPoolBankCreateEvent}, + state::{fee_state::FeeState, marginfi_group::{Bank, BankConfig, BankConfigCompact, MarginfiGroup}}, + MarginfiResult, +}; +use anchor_lang::prelude::*; +use anchor_spl::token_interface::*; + +/// A copy of lending_pool_add_bank but with an additional bank seed provided. +/// This seed is used by the LendingPoolAddBankWithSeed.bank to generate a +/// PDA account to sign for newly added bank transactions securely. +/// The previous lending_pool_add_bank is preserved for backwards-compatibility. +pub fn lending_pool_add_bank_with_seed( + ctx: Context, + bank_config: BankConfig, + _bank_seed: u64, +) -> MarginfiResult { + // Transfer the flat sol init fee to the global fee wallet + let fee_state = ctx.accounts.fee_state.load()?; + let bank_init_flat_sol_fee = fee_state.bank_init_flat_sol_fee; + if bank_init_flat_sol_fee > 0 { + anchor_lang::system_program::transfer( + ctx.accounts.transfer_flat_fee(), + bank_init_flat_sol_fee as u64, + )?; + } + + let LendingPoolAddBankWithSeed { + bank_mint, + liquidity_vault, + insurance_vault, + fee_vault, + bank: bank_loader, + .. + } = ctx.accounts; + + let mut bank = bank_loader.load_init()?; + + let liquidity_vault_bump = ctx.bumps.liquidity_vault; + let liquidity_vault_authority_bump = ctx.bumps.liquidity_vault_authority; + let insurance_vault_bump = ctx.bumps.insurance_vault; + let insurance_vault_authority_bump = ctx.bumps.insurance_vault_authority; + let fee_vault_bump = ctx.bumps.fee_vault; + let fee_vault_authority_bump = ctx.bumps.fee_vault_authority; + + *bank = Bank::new( + ctx.accounts.marginfi_group.key(), + bank_config, + bank_mint.key(), + bank_mint.decimals, + liquidity_vault.key(), + insurance_vault.key(), + fee_vault.key(), + Clock::get().unwrap().unix_timestamp, + liquidity_vault_bump, + liquidity_vault_authority_bump, + insurance_vault_bump, + insurance_vault_authority_bump, + fee_vault_bump, + fee_vault_authority_bump, + ); + + bank.config.validate()?; + bank.config.validate_oracle_setup(ctx.remaining_accounts)?; + + emit!(LendingPoolBankCreateEvent { + header: GroupEventHeader { + marginfi_group: ctx.accounts.marginfi_group.key(), + signer: Some(*ctx.accounts.admin.key) + }, + bank: bank_loader.key(), + mint: bank_mint.key(), + }); + + Ok(()) +} + +/// A copy of LendingPoolAddBank but with an additional bank seed provided. +/// This seed is used by the LendingPoolAddBankWithSeed.bank to generate a +/// PDA account to sign for newly added bank transactions securely. +/// The previous LendingPoolAddBank is preserved for backwards-compatibility. +#[derive(Accounts)] +#[instruction(bank_config: BankConfigCompact, bank_seed: u64)] +pub struct LendingPoolAddBankWithSeed<'info> { + pub marginfi_group: AccountLoader<'info, MarginfiGroup>, + + #[account( + mut, + address = marginfi_group.load()?.admin, + )] + pub admin: Signer<'info>, + + /// Pays to init accounts and pays `fee_state.bank_init_flat_sol_fee` lamports to the protocol + #[account(mut)] + pub fee_payer: Signer<'info>, + + // Note: there is just one FeeState per program, so no further check is required. + #[account( + seeds = [FEE_STATE_SEED.as_bytes()], + bump, + has_one = global_fee_wallet + )] + pub fee_state: AccountLoader<'info, FeeState>, + + /// CHECK: The fee admin's native SOL wallet, validated against fee state + #[account(mut)] + pub global_fee_wallet: AccountInfo<'info>, + + pub bank_mint: Box>, + + #[account( + init, + space = 8 + std::mem::size_of::(), + payer = fee_payer, + seeds = [ + marginfi_group.key().as_ref(), + bank_mint.key().as_ref(), + &bank_seed.to_le_bytes(), + ], + bump, + )] + pub bank: AccountLoader<'info, Bank>, + + /// CHECK: ⋐ ͡⋄ ω ͡⋄ ⋑ + #[account( + seeds = [ + LIQUIDITY_VAULT_AUTHORITY_SEED.as_bytes(), + bank.key().as_ref(), + ], + bump + )] + pub liquidity_vault_authority: AccountInfo<'info>, + + #[account( + init, + payer = fee_payer, + token::mint = bank_mint, + token::authority = liquidity_vault_authority, + seeds = [ + LIQUIDITY_VAULT_SEED.as_bytes(), + bank.key().as_ref(), + ], + bump, + )] + pub liquidity_vault: Box>, + + /// CHECK: ⋐ ͡⋄ ω ͡⋄ ⋑ + #[account( + seeds = [ + INSURANCE_VAULT_AUTHORITY_SEED.as_bytes(), + bank.key().as_ref(), + ], + bump + )] + pub insurance_vault_authority: AccountInfo<'info>, + + #[account( + init, + payer = fee_payer, + token::mint = bank_mint, + token::authority = insurance_vault_authority, + seeds = [ + INSURANCE_VAULT_SEED.as_bytes(), + bank.key().as_ref(), + ], + bump, + )] + pub insurance_vault: Box>, + + /// CHECK: ⋐ ͡⋄ ω ͡⋄ ⋑ + #[account( + seeds = [ + FEE_VAULT_AUTHORITY_SEED.as_bytes(), + bank.key().as_ref(), + ], + bump + )] + pub fee_vault_authority: AccountInfo<'info>, + + #[account( + init, + payer = fee_payer, + token::mint = bank_mint, + token::authority = fee_vault_authority, + seeds = [ + FEE_VAULT_SEED.as_bytes(), + bank.key().as_ref(), + ], + bump, + )] + pub fee_vault: Box>, + + pub rent: Sysvar<'info, Rent>, + pub token_program: Interface<'info, TokenInterface>, + pub system_program: Program<'info, System>, +} + +impl<'info> LendingPoolAddBankWithSeed<'info> { + fn transfer_flat_fee( + &self, + ) -> CpiContext<'_, '_, '_, 'info, anchor_lang::system_program::Transfer<'info>> { + CpiContext::new( + self.system_program.to_account_info(), + anchor_lang::system_program::Transfer { + from: self.fee_payer.to_account_info(), + to: self.global_fee_wallet.to_account_info(), + }, + ) + } +} diff --git a/programs/marginfi/src/instructions/marginfi_group/edit_global_fee.rs b/programs/marginfi/src/instructions/marginfi_group/edit_global_fee.rs new file mode 100644 index 000000000..e05138f5a --- /dev/null +++ b/programs/marginfi/src/instructions/marginfi_group/edit_global_fee.rs @@ -0,0 +1,34 @@ +// Global fee admin calls this to edit the fee rate or the fee wallet. + +use crate::state::fee_state; +use crate::constants::FEE_STATE_SEED; +use anchor_lang::prelude::*; +use fee_state::FeeState; + +pub fn edit_fee_state( + ctx: Context, + fee_wallet: Pubkey, + bank_init_flat_sol_fee: u32, +) -> Result<()> { + let mut fee_state = ctx.accounts.fee_state.load_mut()?; + fee_state.global_fee_wallet = fee_wallet; + fee_state.bank_init_flat_sol_fee = bank_init_flat_sol_fee; + + Ok(()) +} + +#[derive(Accounts)] +pub struct EditFeeState<'info> { + /// Admin of the global FeeState + #[account(mut)] + pub global_fee_admin: Signer<'info>, + + // Note: there is just one FeeState per program, so no further check is required. + #[account( + mut, + seeds = [FEE_STATE_SEED.as_bytes()], + bump, + has_one = global_fee_admin + )] + pub fee_state: AccountLoader<'info, FeeState>, +} diff --git a/programs/marginfi/src/instructions/marginfi_group/init_global_fee_state.rs b/programs/marginfi/src/instructions/marginfi_group/init_global_fee_state.rs new file mode 100644 index 000000000..f8fd51b72 --- /dev/null +++ b/programs/marginfi/src/instructions/marginfi_group/init_global_fee_state.rs @@ -0,0 +1,43 @@ +// Runs once per program to init the global fee state. + +use crate::constants::FEE_STATE_SEED; +use crate::state::fee_state; +use anchor_lang::prelude::*; +use fee_state::FeeState; + +pub fn initialize_fee_state( + ctx: Context, + admin_key: Pubkey, + fee_wallet: Pubkey, + bank_init_flat_sol_fee: u32, +) -> Result<()> { + let mut fee_state = ctx.accounts.fee_state.load_init()?; + fee_state.global_fee_admin = admin_key; + fee_state.global_fee_wallet = fee_wallet; + fee_state.key = ctx.accounts.fee_state.key(); + fee_state.bank_init_flat_sol_fee = bank_init_flat_sol_fee; + fee_state.bump_seed = ctx.bumps.fee_state; + + Ok(()) +} + +#[derive(Accounts)] +pub struct InitFeeState<'info> { + /// Pays the init fee + #[account(mut)] + pub payer: Signer<'info>, + + #[account( + init, + seeds = [ + FEE_STATE_SEED.as_bytes() + ], + bump, + payer = payer, + space = 8 + FeeState::LEN, + )] + pub fee_state: AccountLoader<'info, FeeState>, + + pub rent: Sysvar<'info, Rent>, + pub system_program: Program<'info, System>, +} diff --git a/programs/marginfi/src/instructions/marginfi_group/mod.rs b/programs/marginfi/src/instructions/marginfi_group/mod.rs index 33bc6a914..5ea883675 100644 --- a/programs/marginfi/src/instructions/marginfi_group/mod.rs +++ b/programs/marginfi/src/instructions/marginfi_group/mod.rs @@ -1,15 +1,21 @@ mod accrue_bank_interest; mod add_pool; +mod add_pool_with_seed; mod collect_bank_fees; mod configure; mod configure_bank; +mod edit_global_fee; mod handle_bankruptcy; +mod init_global_fee_state; mod initialize; pub use accrue_bank_interest::*; pub use add_pool::*; +pub use add_pool_with_seed::*; pub use collect_bank_fees::*; pub use configure::*; pub use configure_bank::*; +pub use edit_global_fee::*; pub use handle_bankruptcy::*; +pub use init_global_fee_state::*; pub use initialize::*; diff --git a/programs/marginfi/src/lib.rs b/programs/marginfi/src/lib.rs index d9dbd9f70..8ea7342d5 100644 --- a/programs/marginfi/src/lib.rs +++ b/programs/marginfi/src/lib.rs @@ -214,6 +214,26 @@ pub mod marginfi { pub fn marginfi_account_close(ctx: Context) -> MarginfiResult { marginfi_account::close_account(ctx) } + + /// (Runs once per program) Configures the fee state account, where the global admin sets fees + /// that are assessed to the protocol + pub fn init_global_fee_state( + ctx: Context, + admin: Pubkey, + fee_wallet: Pubkey, + bank_init_flat_sol_fee: u32, + ) -> MarginfiResult { + marginfi_group::initialize_fee_state(ctx, admin, fee_wallet, bank_init_flat_sol_fee) + } + + /// (global fee admin only) Adjust fees or the destination wallet + pub fn edit_global_fee_state( + ctx: Context, + fee_wallet: Pubkey, + bank_init_flat_sol_fee: u32, + ) -> MarginfiResult { + marginfi_group::edit_fee_state(ctx, fee_wallet, bank_init_flat_sol_fee) + } } #[cfg(not(feature = "no-entrypoint"))] diff --git a/test-utils/src/marginfi_group.rs b/test-utils/src/marginfi_group.rs index 583141d0d..bcc54384d 100644 --- a/test-utils/src/marginfi_group.rs +++ b/test-utils/src/marginfi_group.rs @@ -4,6 +4,8 @@ use crate::utils::*; use anchor_lang::{prelude::*, solana_program::system_program, InstructionData}; use anyhow::Result; +use marginfi::constants::FEE_STATE_SEED; +use marginfi::state::fee_state::FeeState; use marginfi::{ prelude::MarginfiGroup, state::marginfi_group::{BankConfig, BankConfigOpt, BankVaultType, GroupConfig}, @@ -16,9 +18,72 @@ use solana_sdk::{ }; use std::{cell::RefCell, mem, rc::Rc}; +pub struct FeeStateFixture { + pub fee_state: Pubkey, + pub fee_wallet: Pubkey, +} + +impl FeeStateFixture { + pub async fn new(ctx: Rc>) -> FeeStateFixture { + let (fee_state_key, _bump) = + Pubkey::find_program_address(&[FEE_STATE_SEED.as_bytes()], &marginfi::id()); + + { + let mut ctx = ctx.borrow_mut(); + + // Skip setup if the fee state was already initialized + let fee_state_account = ctx.banks_client.get_account(fee_state_key).await.unwrap(); + + if let Some(account) = fee_state_account { + if !account.data.is_empty() { + let fee_state_data: FeeState = + FeeState::try_deserialize(&mut &account.data[..]).unwrap(); + return FeeStateFixture { + fee_state: fee_state_key, + fee_wallet: fee_state_data.global_fee_wallet, + }; + } + } + + let fee_wallet = Keypair::new(); + + let init_fee_state_ix = Instruction { + program_id: marginfi::id(), + accounts: marginfi::accounts::InitFeeState { + payer: ctx.payer.pubkey(), + fee_state: fee_state_key, + rent: sysvar::rent::id(), + system_program: system_program::id(), + } + .to_account_metas(Some(true)), + data: marginfi::instruction::InitGlobalFeeState { + admin: ctx.payer.pubkey(), + fee_wallet: fee_wallet.pubkey(), + bank_init_flat_sol_fee: 10000, + } + .data(), + }; + + let tx = Transaction::new_signed_with_payer( + &[init_fee_state_ix], + Some(&ctx.payer.pubkey().clone()), + &[&ctx.payer], + ctx.last_blockhash, + ); + ctx.banks_client.process_transaction(tx).await.unwrap(); + + FeeStateFixture { + fee_state: fee_state_key, + fee_wallet: fee_wallet.pubkey(), + } + } + } +} + pub struct MarginfiGroupFixture { ctx: Rc>, pub key: Pubkey, + pub fee_state_fixture: FeeStateFixture } impl MarginfiGroupFixture { @@ -66,6 +131,7 @@ impl MarginfiGroupFixture { MarginfiGroupFixture { ctx: ctx_ref.clone(), key: group_key.pubkey(), + fee_state_fixture: FeeStateFixture::new(ctx).await } } @@ -83,6 +149,8 @@ impl MarginfiGroupFixture { marginfi_group: self.key, admin: self.ctx.borrow().payer.pubkey(), fee_payer: self.ctx.borrow().payer.pubkey(), + fee_state: self.fee_state_fixture.fee_state, + global_fee_wallet: self.fee_state_fixture.fee_wallet, bank_mint, bank: bank_key.pubkey(), liquidity_vault_authority: bank_fixture.get_vault_authority(BankVaultType::Liquidity).0, @@ -160,6 +228,8 @@ impl MarginfiGroupFixture { marginfi_group: self.key, admin: self.ctx.borrow().payer.pubkey(), fee_payer: self.ctx.borrow().payer.pubkey(), + fee_state: self.fee_state_fixture.fee_state, + global_fee_wallet: self.fee_state_fixture.fee_wallet, bank_mint, bank: pda, liquidity_vault_authority: bank_fixture.get_vault_authority(BankVaultType::Liquidity).0,