diff --git a/token-lending/program/src/processor.rs b/token-lending/program/src/processor.rs index 14ab611aa1b..7855b0a7e20 100644 --- a/token-lending/program/src/processor.rs +++ b/token-lending/program/src/processor.rs @@ -1127,9 +1127,11 @@ fn update_borrow_attribution_values( return Err(LendingError::InvalidAccountInput.into()); } - deposit_reserve.attributed_borrow_value = deposit_reserve - .attributed_borrow_value - .saturating_sub(collateral.attributed_borrow_value); + if obligation.updated_borrow_attribution_after_upgrade { + deposit_reserve.attributed_borrow_value = deposit_reserve + .attributed_borrow_value + .saturating_sub(collateral.attributed_borrow_value); + } if obligation.deposited_value > Decimal::zero() { collateral.attributed_borrow_value = collateral @@ -1159,6 +1161,8 @@ fn update_borrow_attribution_values( Reserve::pack(deposit_reserve, &mut deposit_reserve_info.data.borrow_mut())?; } + obligation.updated_borrow_attribution_after_upgrade = true; + Ok(()) } diff --git a/token-lending/program/tests/attributed_borrows.rs b/token-lending/program/tests/attributed_borrows.rs index 197473f349f..2e2851d9e9a 100644 --- a/token-lending/program/tests/attributed_borrows.rs +++ b/token-lending/program/tests/attributed_borrows.rs @@ -1,8 +1,17 @@ #![cfg(feature = "test-bpf")] +use solana_sdk::compute_budget::ComputeBudgetInstruction; +use solend_sdk::instruction::refresh_obligation; + use crate::solend_program_test::custom_scenario; +use crate::solend_program_test::SolendProgramTest; use crate::solend_program_test::User; +use solana_sdk::pubkey::Pubkey; use solend_program::math::TryDiv; +use solend_program::processor::process_instruction; +use solend_sdk::state::ObligationCollateral; +use solend_sdk::state::ObligationLiquidity; +use solend_sdk::state::PROGRAM_VERSION; use solana_sdk::instruction::InstructionError; use solana_sdk::transaction::TransactionError; @@ -30,7 +39,7 @@ use solana_program_test::*; #[tokio::test] async fn test_refresh_obligation() { - let (mut test, lending_market, reserves, obligations, users, lending_market_owner) = + let (mut test, lending_market, reserves, obligations, _users, lending_market_owner) = custom_scenario( &[ ReserveArgs { @@ -851,3 +860,115 @@ async fn test_liquidate() { Decimal::zero() ); } + +#[tokio::test] +async fn test_calculation_on_program_upgrade() { + let mut test = ProgramTest::new( + "solend_program", + solend_program::id(), + processor!(process_instruction), + ); + + let reserve_1 = Reserve { + version: PROGRAM_VERSION, + last_update: LastUpdate { + slot: 1, + stale: false, + }, + attributed_borrow_value: Decimal::from(10u64), + liquidity: ReserveLiquidity { + market_price: Decimal::from(10u64), + mint_decimals: 0, + ..ReserveLiquidity::default() + }, + ..Reserve::default() + }; + let reserve_1_pubkey = Pubkey::new_unique(); + + test.add_packable_account( + reserve_1_pubkey, + u32::MAX as u64, + &reserve_1, + &solend_program::id(), + ); + + let reserve_2 = Reserve { + version: PROGRAM_VERSION, + last_update: LastUpdate { + slot: 1, + stale: false, + }, + liquidity: ReserveLiquidity { + market_price: Decimal::from(10u64), + mint_decimals: 0, + ..ReserveLiquidity::default() + }, + ..Reserve::default() + }; + let reserve_2_pubkey = Pubkey::new_unique(); + test.add_packable_account( + reserve_2_pubkey, + u32::MAX as u64, + &reserve_2, + &solend_program::id(), + ); + + let obligation_pubkey = Pubkey::new_unique(); + let obligation = Obligation { + version: PROGRAM_VERSION, + deposits: vec![ObligationCollateral { + deposit_reserve: reserve_1_pubkey, + deposited_amount: 2u64, + market_value: Decimal::from(20u64), + attributed_borrow_value: Decimal::from(10u64), + }], + borrows: vec![ObligationLiquidity { + borrow_reserve: reserve_2_pubkey, + borrowed_amount_wads: Decimal::from(1u64), + ..ObligationLiquidity::default() + }], + updated_borrow_attribution_after_upgrade: false, + ..Obligation::default() + }; + + test.add_packable_account( + obligation_pubkey, + u32::MAX as u64, + &obligation, + &solend_program::id(), + ); + + let mut test = SolendProgramTest::start_with_test(test).await; + + let ix = [refresh_obligation( + solend_program::id(), + obligation_pubkey, + vec![reserve_1_pubkey, reserve_2_pubkey], + )]; + + test.process_transaction(&ix, None).await.unwrap(); + + let reserve_1 = test.load_account::(reserve_1_pubkey).await; + assert_eq!( + reserve_1.account.attributed_borrow_value, + Decimal::from(20u64) + ); + + // run it again, this time make sure the borrow attribution value gets correctly subtracted + let ix = [ + ComputeBudgetInstruction::set_compute_unit_price(1), + refresh_obligation( + solend_program::id(), + obligation_pubkey, + vec![reserve_1_pubkey, reserve_2_pubkey], + ), + ]; + + test.process_transaction(&ix, None).await.unwrap(); + + let reserve_1 = test.load_account::(reserve_1_pubkey).await; + assert_eq!( + reserve_1.account.attributed_borrow_value, + Decimal::from(20u64) + ); +} diff --git a/token-lending/program/tests/init_obligation.rs b/token-lending/program/tests/init_obligation.rs index 8788899e6a1..bc802bc3954 100644 --- a/token-lending/program/tests/init_obligation.rs +++ b/token-lending/program/tests/init_obligation.rs @@ -50,7 +50,8 @@ async fn test_success() { allowed_borrow_value: Decimal::zero(), unhealthy_borrow_value: Decimal::zero(), super_unhealthy_borrow_value: Decimal::zero(), - borrowing_isolated_asset: false + borrowing_isolated_asset: false, + updated_borrow_attribution_after_upgrade: false } ); } diff --git a/token-lending/sdk/src/state/obligation.rs b/token-lending/sdk/src/state/obligation.rs index 6effe3f2035..6fea396778c 100644 --- a/token-lending/sdk/src/state/obligation.rs +++ b/token-lending/sdk/src/state/obligation.rs @@ -61,6 +61,8 @@ pub struct Obligation { pub super_unhealthy_borrow_value: Decimal, /// True if the obligation is currently borrowing an isolated tier asset pub borrowing_isolated_asset: bool, + /// Updated borrow attribution after upgrade. initially false when upgrading to v2.0.3 + pub updated_borrow_attribution_after_upgrade: bool, } impl Obligation { @@ -436,7 +438,8 @@ impl Pack for Obligation { borrowed_value_upper_bound, borrowing_isolated_asset, super_unhealthy_borrow_value, - true_borrowed_value, + unweighted_borrowed_value, + updated_borrow_attribution_after_upgrade, _padding, deposits_len, borrows_len, @@ -456,7 +459,8 @@ impl Pack for Obligation { 1, 16, 16, - 15, + 1, + 14, 1, 1, OBLIGATION_COLLATERAL_LEN + (OBLIGATION_LIQUIDITY_LEN * (MAX_OBLIGATION_RESERVES - 1)) @@ -478,7 +482,11 @@ impl Pack for Obligation { self.super_unhealthy_borrow_value, super_unhealthy_borrow_value, ); - pack_decimal(self.unweighted_borrowed_value, true_borrowed_value); + pack_decimal(self.unweighted_borrowed_value, unweighted_borrowed_value); + pack_bool( + self.updated_borrow_attribution_after_upgrade, + updated_borrow_attribution_after_upgrade, + ); *deposits_len = u8::try_from(self.deposits.len()).unwrap().to_le_bytes(); *borrows_len = u8::try_from(self.borrows.len()).unwrap().to_le_bytes(); @@ -542,7 +550,8 @@ impl Pack for Obligation { borrowed_value_upper_bound, borrowing_isolated_asset, super_unhealthy_borrow_value, - true_borrowed_value, + unweighted_borrowed_value, + updated_borrow_attribution_after_upgrade, _padding, deposits_len, borrows_len, @@ -562,7 +571,8 @@ impl Pack for Obligation { 1, 16, 16, - 15, + 1, + 14, 1, 1, OBLIGATION_COLLATERAL_LEN + (OBLIGATION_LIQUIDITY_LEN * (MAX_OBLIGATION_RESERVES - 1)) @@ -629,12 +639,15 @@ impl Pack for Obligation { borrows, deposited_value: unpack_decimal(deposited_value), borrowed_value: unpack_decimal(borrowed_value), - unweighted_borrowed_value: unpack_decimal(true_borrowed_value), + unweighted_borrowed_value: unpack_decimal(unweighted_borrowed_value), borrowed_value_upper_bound: unpack_decimal(borrowed_value_upper_bound), allowed_borrow_value: unpack_decimal(allowed_borrow_value), unhealthy_borrow_value: unpack_decimal(unhealthy_borrow_value), super_unhealthy_borrow_value: unpack_decimal(super_unhealthy_borrow_value), borrowing_isolated_asset: unpack_bool(borrowing_isolated_asset)?, + updated_borrow_attribution_after_upgrade: unpack_bool( + updated_borrow_attribution_after_upgrade, + )?, }) } } @@ -685,6 +698,7 @@ mod test { unhealthy_borrow_value: rand_decimal(), super_unhealthy_borrow_value: rand_decimal(), borrowing_isolated_asset: rng.gen(), + updated_borrow_attribution_after_upgrade: rng.gen(), }; let mut packed = [0u8; OBLIGATION_LEN];