diff --git a/token-lending/program/src/instruction.rs b/token-lending/program/src/instruction.rs index a75ad47dffd..82c5a7c0531 100644 --- a/token-lending/program/src/instruction.rs +++ b/token-lending/program/src/instruction.rs @@ -444,7 +444,8 @@ impl LendingInstruction { let (host_fee_percentage, rest) = Self::unpack_u8(rest)?; let (deposit_limit, rest) = Self::unpack_u64(rest)?; let (borrow_limit, rest) = Self::unpack_u64(rest)?; - let (fee_receiver, _) = Self::unpack_pubkey(rest)?; + let (fee_receiver, rest) = Self::unpack_pubkey(rest)?; + let (protocol_liquidation_fee, _rest) = Self::unpack_u8(rest)?; Self::InitReserve { liquidity_amount, config: ReserveConfig { @@ -463,6 +464,7 @@ impl LendingInstruction { deposit_limit, borrow_limit, fee_receiver, + protocol_liquidation_fee, }, } } @@ -510,20 +512,20 @@ impl LendingInstruction { Self::WithdrawObligationCollateralAndRedeemReserveCollateral { collateral_amount } } 16 => { - let (optimal_utilization_rate, _rest) = Self::unpack_u8(rest)?; - let (loan_to_value_ratio, _rest) = Self::unpack_u8(_rest)?; - let (liquidation_bonus, _rest) = Self::unpack_u8(_rest)?; - let (liquidation_threshold, _rest) = Self::unpack_u8(_rest)?; - let (min_borrow_rate, _rest) = Self::unpack_u8(_rest)?; - let (optimal_borrow_rate, _rest) = Self::unpack_u8(_rest)?; - let (max_borrow_rate, _rest) = Self::unpack_u8(_rest)?; - let (borrow_fee_wad, _rest) = Self::unpack_u64(_rest)?; - let (flash_loan_fee_wad, _rest) = Self::unpack_u64(_rest)?; - let (host_fee_percentage, _rest) = Self::unpack_u8(_rest)?; - let (deposit_limit, _rest) = Self::unpack_u64(_rest)?; - let (borrow_limit, _rest) = Self::unpack_u64(_rest)?; - let (fee_receiver, _) = Self::unpack_pubkey(_rest)?; - + let (optimal_utilization_rate, rest) = Self::unpack_u8(rest)?; + let (loan_to_value_ratio, rest) = Self::unpack_u8(rest)?; + let (liquidation_bonus, rest) = Self::unpack_u8(rest)?; + let (liquidation_threshold, rest) = Self::unpack_u8(rest)?; + let (min_borrow_rate, rest) = Self::unpack_u8(rest)?; + let (optimal_borrow_rate, rest) = Self::unpack_u8(rest)?; + let (max_borrow_rate, rest) = Self::unpack_u8(rest)?; + let (borrow_fee_wad, rest) = Self::unpack_u64(rest)?; + let (flash_loan_fee_wad, rest) = Self::unpack_u64(rest)?; + let (host_fee_percentage, rest) = Self::unpack_u8(rest)?; + let (deposit_limit, rest) = Self::unpack_u64(rest)?; + let (borrow_limit, rest) = Self::unpack_u64(rest)?; + let (fee_receiver, rest) = Self::unpack_pubkey(rest)?; + let (protocol_liquidation_fee, _rest) = Self::unpack_u8(rest)?; Self::UpdateReserveConfig { config: ReserveConfig { optimal_utilization_rate, @@ -541,6 +543,7 @@ impl LendingInstruction { deposit_limit, borrow_limit, fee_receiver, + protocol_liquidation_fee }, } } @@ -643,6 +646,7 @@ impl LendingInstruction { deposit_limit, borrow_limit, fee_receiver, + protocol_liquidation_fee, }, } => { buf.push(2); @@ -660,6 +664,7 @@ impl LendingInstruction { buf.extend_from_slice(&deposit_limit.to_le_bytes()); buf.extend_from_slice(&borrow_limit.to_le_bytes()); buf.extend_from_slice(&fee_receiver.to_bytes()); + buf.extend_from_slice(&protocol_liquidation_fee.to_le_bytes()); } Self::RefreshReserve => { buf.push(3); @@ -725,6 +730,7 @@ impl LendingInstruction { buf.extend_from_slice(&config.deposit_limit.to_le_bytes()); buf.extend_from_slice(&config.borrow_limit.to_le_bytes()); buf.extend_from_slice(&config.fee_receiver.to_bytes()); + buf.extend_from_slice(&config.protocol_liquidation_fee.to_le_bytes()); } Self::LiquidateObligationAndRedeemReserveCollateral { liquidity_amount } => { buf.push(17); diff --git a/token-lending/program/src/processor.rs b/token-lending/program/src/processor.rs index 82dc22361ba..e716236d0d4 100644 --- a/token-lending/program/src/processor.rs +++ b/token-lending/program/src/processor.rs @@ -2556,6 +2556,10 @@ fn validate_reserve_config(config: ReserveConfig) -> ProgramResult { msg!("Host fee percentage must be in range [0, 100]"); return Err(LendingError::InvalidConfig.into()); } + if config.protocol_liquidation_fee >= 100 { + msg!("Protocol liquidation fee must be in range [0, 100)"); + return Err(LendingError::InvalidConfig.into()); + } Ok(()) } diff --git a/token-lending/program/src/state/reserve.rs b/token-lending/program/src/state/reserve.rs index d334f85b1ac..1319075941d 100644 --- a/token-lending/program/src/state/reserve.rs +++ b/token-lending/program/src/state/reserve.rs @@ -307,6 +307,8 @@ impl Reserve { let bonus = amount_liquidated_wads.try_sub(amount_liquidated_wads.try_div(bonus_rate)?)?; + // After deploying must update all reserves to set liquidation fee then redeploy with this line instead of hardcode + // let protocol_fee = bonus.try_mul(Rate::from_percent(self.config.protocol_liquidation_fee))?.try_ceil_u64()?; let protocol_fee = bonus.try_mul(Rate::from_percent(30))?.try_ceil_u64()?; Ok(protocol_fee) } @@ -629,6 +631,8 @@ pub struct ReserveConfig { pub borrow_limit: u64, /// Reserve liquidity fee receiver address pub fee_receiver: Pubkey, + /// Cut of the liquidation bonus that the protocol receives, as a percentage + pub protocol_liquidation_fee: u8, } /// Additional fee information on a reserve @@ -739,7 +743,7 @@ impl IsInitialized for Reserve { } } -const RESERVE_LEN: usize = 619; // 1 + 8 + 1 + 32 + 32 + 1 + 32 + 32 + 32 + 8 + 16 + 16 + 16 + 32 + 8 + 32 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 8 + 8 + 1 + 8 + 8 + 32 + 248 +const RESERVE_LEN: usize = 619; // 1 + 8 + 1 + 32 + 32 + 1 + 32 + 32 + 32 + 8 + 16 + 16 + 16 + 32 + 8 + 32 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 8 + 8 + 1 + 8 + 8 + 32 + 1 + 247 impl Pack for Reserve { const LEN: usize = RESERVE_LEN; @@ -777,6 +781,7 @@ impl Pack for Reserve { config_deposit_limit, config_borrow_limit, config_fee_receiver, + config_protocol_liquidation_fee, _padding, ) = mut_array_refs![ output, @@ -809,7 +814,8 @@ impl Pack for Reserve { 8, 8, PUBKEY_BYTES, - 248 + 1, + 247 ]; // reserve @@ -855,6 +861,7 @@ impl Pack for Reserve { *config_deposit_limit = self.config.deposit_limit.to_le_bytes(); *config_borrow_limit = self.config.borrow_limit.to_le_bytes(); config_fee_receiver.copy_from_slice(self.config.fee_receiver.as_ref()); + *config_protocol_liquidation_fee = self.config.protocol_liquidation_fee.to_le_bytes(); } /// Unpacks a byte buffer into a [ReserveInfo](struct.ReserveInfo.html). @@ -891,6 +898,7 @@ impl Pack for Reserve { config_deposit_limit, config_borrow_limit, config_fee_receiver, + config_protocol_liquidation_fee, _padding, ) = array_refs![ input, @@ -923,7 +931,8 @@ impl Pack for Reserve { 8, 8, PUBKEY_BYTES, - 248 + 1, + 247 ]; let version = u8::from_le_bytes(*version); @@ -973,6 +982,7 @@ impl Pack for Reserve { deposit_limit: u64::from_le_bytes(*config_deposit_limit), borrow_limit: u64::from_le_bytes(*config_borrow_limit), fee_receiver: Pubkey::new_from_array(*config_fee_receiver), + protocol_liquidation_fee: u8::from_le_bytes(*config_protocol_liquidation_fee), }, }) } diff --git a/token-lending/program/tests/helpers/mod.rs b/token-lending/program/tests/helpers/mod.rs index d81cc399221..2b5db31ae6b 100644 --- a/token-lending/program/tests/helpers/mod.rs +++ b/token-lending/program/tests/helpers/mod.rs @@ -56,6 +56,7 @@ pub fn test_reserve_config() -> ReserveConfig { deposit_limit: 100_000_000_000, borrow_limit: u64::MAX, fee_receiver: Keypair::new().pubkey(), + protocol_liquidation_fee: 30, } } diff --git a/token-lending/program/tests/init_reserve.rs b/token-lending/program/tests/init_reserve.rs index 39a9f8618fa..b80c2a93e3e 100644 --- a/token-lending/program/tests/init_reserve.rs +++ b/token-lending/program/tests/init_reserve.rs @@ -418,6 +418,7 @@ async fn test_update_reserve_config() { deposit_limit: 1_000_000, borrow_limit: 300_000, fee_receiver: Keypair::new().pubkey(), + protocol_liquidation_fee: 30, }; let (mut banks_client, payer, recent_blockhash) = test.start().await; diff --git a/token-lending/program/tests/liquidate_obligation_and_redeem_collateral.rs b/token-lending/program/tests/liquidate_obligation_and_redeem_collateral.rs index 56d74bb5a94..0e87edbc2e8 100644 --- a/token-lending/program/tests/liquidate_obligation_and_redeem_collateral.rs +++ b/token-lending/program/tests/liquidate_obligation_and_redeem_collateral.rs @@ -174,7 +174,7 @@ async fn test_success() { ); assert_eq!( - // 30% of the bonus + // 30% of the bonus SOL_LIQUIDATION_AMOUNT_LAMPORTS * 3 / 10 / 11, (fee_reciever_withdraw_liquidity_balance - initial_fee_reciever_withdraw_liquidity_balance) );