Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

oracleless repay #64

Merged
merged 4 commits into from
Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions token-lending/program/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1466,6 +1466,7 @@ fn process_repay_obligation_liquidity(
return Err(LendingError::InvalidTokenProgram.into());
}

_refresh_reserve_interest(program_id, repay_reserve_info, clock)?;
let mut repay_reserve = Reserve::unpack(&repay_reserve_info.data.borrow())?;
if repay_reserve_info.owner != program_id {
msg!("Repay reserve provided is not owned by the lending program");
Expand Down Expand Up @@ -1497,18 +1498,17 @@ fn process_repay_obligation_liquidity(
msg!("Obligation lending market does not match the lending market provided");
return Err(LendingError::InvalidAccountInput.into());
}
if obligation.last_update.is_stale(clock.slot)? {
msg!("Obligation is stale and must be refreshed in the current slot");
return Err(LendingError::ObligationStale.into());
}

let (liquidity, liquidity_index) =
obligation.find_liquidity_in_borrows(*repay_reserve_info.key)?;
obligation.find_liquidity_in_borrows_mut(*repay_reserve_info.key)?;
if liquidity.borrowed_amount_wads == Decimal::zero() {
msg!("Liquidity borrowed amount is zero");
return Err(LendingError::ObligationLiquidityEmpty.into());
}

// refreshing specific borrow instead of checking obligation stale
liquidity.accrue_interest(repay_reserve.liquidity.cumulative_borrow_rate_wads)?;

let CalculateRepayResult {
settle_amount,
repay_amount,
Expand Down
15 changes: 15 additions & 0 deletions token-lending/program/src/state/obligation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,21 @@ impl Obligation {
Ok((&self.borrows[liquidity_index], liquidity_index))
}

/// Find liquidity by borrow reserve mut
pub fn find_liquidity_in_borrows_mut(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so only difference with non-mut version is this returns a mutable ObligationLiquidity so it can be updated with accrue_interest?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason why we don't just make the original one return a mutable variable?

I suppose theres a small risk of us wanting to change logic here and then only fixing it one of the places or something

&mut self,
borrow_reserve: Pubkey,
) -> Result<(&mut ObligationLiquidity, usize), ProgramError> {
if self.borrows.is_empty() {
msg!("Obligation has no borrows");
return Err(LendingError::ObligationBorrowsEmpty.into());
}
let liquidity_index = self
._find_liquidity_index_in_borrows(borrow_reserve)
.ok_or(LendingError::InvalidObligationLiquidity)?;
Ok((&mut self.borrows[liquidity_index], liquidity_index))
}

/// Find or add liquidity by borrow reserve
pub fn find_or_add_liquidity_to_borrows(
&mut self,
Expand Down
28 changes: 20 additions & 8 deletions token-lending/program/tests/repay_obligation_liquidity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ use solana_sdk::{
transaction::Transaction,
};
use spl_token::instruction::approve;
use spl_token_lending::instruction::refresh_obligation;
use spl_token_lending::{
instruction::repay_obligation_liquidity, processor::process_instruction,
instruction::repay_obligation_liquidity,
math::{Decimal, Rate, TryAdd, TryMul, TrySub},
processor::process_instruction,
state::INITIAL_COLLATERAL_RATIO,
};

Expand All @@ -31,6 +32,7 @@ async fn test_success() {
const USDC_BORROW_AMOUNT_FRACTIONAL: u64 = 1_000 * FRACTIONAL_TO_USDC;
const SOL_RESERVE_COLLATERAL_LAMPORTS: u64 = 2 * SOL_DEPOSIT_AMOUNT_LAMPORTS;
const USDC_RESERVE_LIQUIDITY_FRACTIONAL: u64 = 2 * USDC_BORROW_AMOUNT_FRACTIONAL;
const USDC_RESERVE_BORROW_RATE: u8 = 110;

let user_accounts_owner = Keypair::new();
let user_transfer_authority = Keypair::new();
Expand Down Expand Up @@ -68,6 +70,7 @@ async fn test_success() {
liquidity_amount: USDC_RESERVE_LIQUIDITY_FRACTIONAL,
liquidity_mint_pubkey: usdc_mint.pubkey,
liquidity_mint_decimals: usdc_mint.decimals,
initial_borrow_rate: USDC_RESERVE_BORROW_RATE,
config: reserve_config,
mark_fresh: true,
..AddReserveArgs::default()
Expand Down Expand Up @@ -103,11 +106,6 @@ async fn test_success() {
USDC_BORROW_AMOUNT_FRACTIONAL,
)
.unwrap(),
refresh_obligation(
spl_token_lending::id(),
test_obligation.pubkey,
vec![sol_test_reserve.pubkey, usdc_test_reserve.pubkey],
),
repay_obligation_liquidity(
spl_token_lending::id(),
USDC_BORROW_AMOUNT_FRACTIONAL,
Expand Down Expand Up @@ -143,5 +141,19 @@ async fn test_success() {
);

let obligation = test_obligation.get_state(&mut banks_client).await;
assert_eq!(obligation.borrows.len(), 0);
assert_eq!(obligation.borrows.len(), 1);
let new_rate = Rate::one()
.try_add(Rate::from_percent(USDC_RESERVE_BORROW_RATE))
.unwrap();
let new_rate_decmial = Decimal::one().try_mul(new_rate).unwrap();
let balance_due = new_rate_decmial
.try_mul(USDC_BORROW_AMOUNT_FRACTIONAL)
.unwrap();
let expected_balance_after_repay = balance_due
.try_sub(Decimal::from(USDC_BORROW_AMOUNT_FRACTIONAL))
.unwrap();
assert_eq!(
obligation.borrows[0].borrowed_amount_wads,
expected_balance_after_repay
);
}