diff --git a/token-lending/program/src/instruction.rs b/token-lending/program/src/instruction.rs index 53b9929d4fe..8a475d93f53 100644 --- a/token-lending/program/src/instruction.rs +++ b/token-lending/program/src/instruction.rs @@ -368,13 +368,13 @@ pub enum LendingInstruction { /// /// Accounts expected by this instruction: /// - /// 1. `[writable]` Reserve account - refreshed - /// 2 `[]` Lending market account. - /// 3 `[]` Derived lending market authority. - /// 4 `[signer]` Lending market owner. - /// 5 `[]` Pyth product key. - /// 6 `[]` Pyth price key. - /// 7 `[]` Switchboard key. + /// 0. `[writable]` Reserve account - refreshed + /// 1. `[]` Lending market account. + /// 2. `[]` Derived lending market authority. + /// 3. `[signer]` Lending market owner. + /// 4. `[]` Pyth product key. + /// 5. `[]` Pyth price key. + /// 6. `[]` Switchboard key. UpdateReserveConfig { /// Reserve config to update to config: ReserveConfig, @@ -382,6 +382,16 @@ pub enum LendingInstruction { // 17 /// Claims unclaimed reserve protocol fees + /// + /// Accounts expected by this instruction: + /// + /// 0. `[writable]` Reserve account. + /// 1. `[]` Lending market account. + /// 2. `[]` Derived lending market authority. + /// 3. `[signer]` Lending market owner. + /// 4. `[writable]` Reserve liquidity supply SPL Token account. + /// 5. `[writable]` Borrow reserve liquidity fee receiver account. + /// 6. `[]` Token program id. ClaimReserveProtocolFees, } @@ -1253,6 +1263,7 @@ pub fn claim_protocol_fees( AccountMeta::new_readonly(lending_market_owner_pubkey, true), AccountMeta::new(reserve_liquidity_supply_pubkey, false), AccountMeta::new(reserve_liquidity_fee_receiver_pubkey, false), + AccountMeta::new_readonly(spl_token::id(), false), ]; Instruction { program_id, diff --git a/token-lending/program/src/processor.rs b/token-lending/program/src/processor.rs index 5941bf748f2..bf7b1b05f64 100644 --- a/token-lending/program/src/processor.rs +++ b/token-lending/program/src/processor.rs @@ -2146,6 +2146,10 @@ fn process_claim_reserve_protocol_fees( msg!("Reserve liquidity fee receiver does not match the reserve liquidity fee receiver provided"); return Err(LendingError::InvalidAccountInput.into()); } + if &reserve.liquidity.supply_pubkey != reserve_liquidity_supply_info.key { + msg!("Reserve liquidity supply does not match the reserve liquidity supply provided"); + return Err(LendingError::InvalidAccountInput.into()); + } // transfer accumulated fees to fee receiver let amount_to_transfer = reserve diff --git a/token-lending/program/tests/helpers/mod.rs b/token-lending/program/tests/helpers/mod.rs index 08998aae9e7..9e3c8365257 100644 --- a/token-lending/program/tests/helpers/mod.rs +++ b/token-lending/program/tests/helpers/mod.rs @@ -316,7 +316,7 @@ pub fn add_reserve( test.add_packable_account( config.fee_receiver, - u32::MAX as u64, + amount, &Token { mint: liquidity_mint_pubkey, owner: lending_market.owner.pubkey(), @@ -1353,3 +1353,12 @@ pub async fn get_token_balance(banks_client: &mut BanksClient, pubkey: Pubkey) - .unwrap() .amount } + +pub async fn get_token( + banks_client: &mut BanksClient, + pubkey: Pubkey, +) -> spl_token::state::Account { + let token: Account = banks_client.get_account(pubkey).await.unwrap().unwrap(); + + spl_token::state::Account::unpack(&token.data[..]).unwrap() +} diff --git a/token-lending/program/tests/liquidate_obligation.rs b/token-lending/program/tests/liquidate_obligation.rs index 5bf70906935..0d109275504 100644 --- a/token-lending/program/tests/liquidate_obligation.rs +++ b/token-lending/program/tests/liquidate_obligation.rs @@ -11,7 +11,7 @@ use solana_sdk::{ }; use spl_token::instruction::approve; use spl_token_lending::{ - instruction::{liquidate_obligation, refresh_obligation}, + instruction::{claim_protocol_fees, liquidate_obligation, refresh_obligation}, processor::process_instruction, state::INITIAL_COLLATERAL_RATIO, }; @@ -45,11 +45,11 @@ async fn test_success() { let user_transfer_authority = Keypair::new(); let lending_market = add_lending_market(&mut test); - let mut reserve_config = test_reserve_config(); - reserve_config.loan_to_value_ratio = 50; - reserve_config.liquidation_threshold = 80; - reserve_config.liquidation_bonus = 10; - reserve_config.protocol_liquidation_fee = 50; + let mut reserve_config_sol = test_reserve_config(); + reserve_config_sol.loan_to_value_ratio = 50; + reserve_config_sol.liquidation_threshold = 80; + reserve_config_sol.liquidation_bonus = 10; + reserve_config_sol.protocol_liquidation_fee = 50; let sol_oracle = add_sol_oracle(&mut test); let sol_test_reserve = add_reserve( @@ -62,12 +62,18 @@ async fn test_success() { liquidity_amount: SOL_RESERVE_COLLATERAL_LAMPORTS, liquidity_mint_pubkey: spl_token::native_mint::id(), liquidity_mint_decimals: 9, - config: reserve_config, + config: reserve_config_sol, mark_fresh: true, ..AddReserveArgs::default() }, ); + let mut reserve_config_usdc = test_reserve_config(); + reserve_config_usdc.loan_to_value_ratio = 50; + reserve_config_usdc.liquidation_threshold = 80; + reserve_config_usdc.liquidation_bonus = 10; + reserve_config_usdc.protocol_liquidation_fee = 50; + let usdc_mint = add_usdc_mint(&mut test); let usdc_oracle = add_usdc_oracle(&mut test); let usdc_test_reserve = add_reserve( @@ -81,7 +87,7 @@ async fn test_success() { liquidity_amount: USDC_RESERVE_LIQUIDITY_FRACTIONAL, liquidity_mint_pubkey: usdc_mint.pubkey, liquidity_mint_decimals: usdc_mint.decimals, - config: reserve_config, + config: reserve_config_usdc, mark_fresh: true, ..AddReserveArgs::default() }, @@ -163,7 +169,7 @@ async fn test_success() { initial_liquidity_supply_balance + USDC_LIQUIDATION_AMOUNT_FRACTIONAL ); - // the liquidation protocol stays in the reserve and is + // the liquidation protocol fee stays in the reserve custody let user_collateral_balance = get_token_balance(&mut banks_client, sol_test_reserve.user_collateral_pubkey).await; assert_eq!( @@ -198,4 +204,35 @@ async fn test_success() { .unwrap(), SOL_LIQUIDATION_PROTOCOL_FEE_AMOUNT_LAMPORTS ); + + let fee_reciever_pre_claim_balance = + get_token_balance(&mut banks_client, sol_test_reserve.config.fee_receiver).await; + assert_eq!(fee_reciever_pre_claim_balance, 0); + + let token_liquidity = + get_token(&mut banks_client, sol_test_reserve.liquidity_supply_pubkey).await; + let token_fee_reciever = + get_token(&mut banks_client, sol_test_reserve.config.fee_receiver).await; + assert_eq!(token_liquidity.mint, token_fee_reciever.mint); + + let mut transaction2 = Transaction::new_with_payer( + &[claim_protocol_fees( + spl_token_lending::id(), + sol_test_reserve.pubkey, + lending_market.pubkey, + lending_market.owner.pubkey(), + sol_test_reserve.liquidity_supply_pubkey, + sol_test_reserve.config.fee_receiver, + )], + Some(&payer.pubkey()), + ); + transaction2.sign(&[&payer, &lending_market.owner], recent_blockhash); + assert!(banks_client.process_transaction(transaction2).await.is_ok()); + + let fee_reciever_post_claim_balance = + get_token_balance(&mut banks_client, sol_test_reserve.config.fee_receiver).await; + assert_eq!( + fee_reciever_post_claim_balance, + SOL_LIQUIDATION_PROTOCOL_FEE_AMOUNT_LAMPORTS + ); }