lending: Split interest accrual into new instruction (#1115)

This commit is contained in:
Justin Starry 2021-01-22 08:40:44 +08:00 committed by GitHub
parent 14daf0d370
commit 62e6a67eb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 220 additions and 91 deletions

View File

@ -86,6 +86,9 @@ pub enum LendingError {
/// Borrow amount too small
#[error("Borrow amount too small")]
BorrowTooSmall,
/// Reserve state stale
#[error("Reserve state needs to be updated for the current slot")]
ReserveStale,
/// Trade simulation error
#[error("Trade simulation error")]

View File

@ -66,7 +66,7 @@ pub enum LendingInstruction {
/// Initializes a new loan obligation.
///
/// 0. `[]` Deposit reserve account.
/// 1. `[writable]` Borrow reserve account.
/// 1. `[]` Borrow reserve account.
/// 2. `[writable]` Obligation
/// 3. `[writable]` Obligation token mint
/// 4. `[writable]` Obligation token output
@ -119,7 +119,7 @@ pub enum LendingInstruction {
/// 0. `[writable]` Source collateral token account, minted by deposit reserve collateral mint,
/// $authority can transfer $collateral_amount
/// 1. `[writable]` Destination liquidity token account, minted by borrow reserve liquidity mint
/// 2. `[writable]` Deposit reserve account.
/// 2. `[]` Deposit reserve account.
/// 3. `[writable]` Deposit reserve collateral supply SPL Token account
/// 4. `[writable]` Deposit reserve collateral fee receiver account.
/// Must be the fee account specified at InitReserve.
@ -175,7 +175,7 @@ pub enum LendingInstruction {
/// 1. `[writable]` Destination collateral token account, minted by withdraw reserve collateral mint
/// 2. `[writable]` Repay reserve account.
/// 3. `[writable]` Repay reserve liquidity supply SPL Token account
/// 4. `[writable]` Withdraw reserve account.
/// 4. `[]` Withdraw reserve account.
/// 5. `[writable]` Withdraw reserve collateral supply SPL Token account
/// 6. `[writable]` Obligation - initialized
/// 7. `[]` Lending market account.
@ -190,6 +190,13 @@ pub enum LendingInstruction {
/// Amount of loan to repay
liquidity_amount: u64,
},
/// Accrue interest on reserves
///
/// 0. `[]` Clock sysvar
/// 1. `[writable]` Reserve account.
/// .. `[writable]` Additional reserve accounts.
AccrueReserveInterest,
}
impl LendingInstruction {
@ -258,6 +265,7 @@ impl LendingInstruction {
let (liquidity_amount, _rest) = Self::unpack_u64(rest)?;
Self::LiquidateObligation { liquidity_amount }
}
8 => Self::AccrueReserveInterest,
_ => return Err(LendingError::InstructionUnpackError.into()),
})
}
@ -365,6 +373,9 @@ impl LendingInstruction {
buf.push(7);
buf.extend_from_slice(&liquidity_amount.to_le_bytes());
}
Self::AccrueReserveInterest => {
buf.push(8);
}
}
buf
}
@ -462,7 +473,7 @@ pub fn init_obligation(
Pubkey::find_program_address(&[&lending_market_pubkey.to_bytes()[..32]], &program_id);
let accounts = vec![
AccountMeta::new_readonly(deposit_reserve_pubkey, false),
AccountMeta::new(borrow_reserve_pubkey, false),
AccountMeta::new_readonly(borrow_reserve_pubkey, false),
AccountMeta::new(obligation_pubkey, false),
AccountMeta::new(obligation_token_mint_pubkey, false),
AccountMeta::new(obligation_token_output_pubkey, false),
@ -571,7 +582,7 @@ pub fn borrow_reserve_liquidity(
let mut accounts = vec![
AccountMeta::new(source_collateral_pubkey, false),
AccountMeta::new(destination_liquidity_pubkey, false),
AccountMeta::new(deposit_reserve_pubkey, false),
AccountMeta::new_readonly(deposit_reserve_pubkey, false),
AccountMeta::new(deposit_reserve_collateral_supply_pubkey, false),
AccountMeta::new(deposit_reserve_collateral_fees_receiver_pubkey, false),
AccountMeta::new(borrow_reserve_pubkey, false),
@ -671,7 +682,7 @@ pub fn liquidate_obligation(
AccountMeta::new(destination_collateral_pubkey, false),
AccountMeta::new(repay_reserve_pubkey, false),
AccountMeta::new(repay_reserve_liquidity_supply_pubkey, false),
AccountMeta::new(withdraw_reserve_pubkey, false),
AccountMeta::new_readonly(withdraw_reserve_pubkey, false),
AccountMeta::new(withdraw_reserve_collateral_supply_pubkey, false),
AccountMeta::new(obligation_pubkey, false),
AccountMeta::new_readonly(lending_market_pubkey, false),
@ -686,3 +697,18 @@ pub fn liquidate_obligation(
data: LendingInstruction::LiquidateObligation { liquidity_amount }.pack(),
}
}
/// Creates an `AccrueReserveInterest` instruction
pub fn accrue_reserve_interest(program_id: Pubkey, reserve_pubkeys: Vec<Pubkey>) -> Instruction {
let mut accounts = vec![AccountMeta::new_readonly(sysvar::clock::id(), false)];
accounts.extend(
reserve_pubkeys
.into_iter()
.map(|reserve_pubkey| AccountMeta::new(reserve_pubkey, false)),
);
Instruction {
program_id,
accounts,
data: LendingInstruction::AccrueReserveInterest.pack(),
}
}

View File

@ -13,6 +13,7 @@ use crate::{
use num_traits::FromPrimitive;
use solana_program::{
account_info::{next_account_info, AccountInfo},
clock::Slot,
decode_error::DecodeError,
entrypoint::ProgramResult,
msg,
@ -71,6 +72,10 @@ pub fn process_instruction(
msg!("Instruction: Liquidate");
process_liquidate(program_id, liquidity_amount, accounts)
}
LendingInstruction::AccrueReserveInterest => {
msg!("Instruction: Accrue Interest");
process_accrue_interest(program_id, accounts)
}
}
}
@ -339,7 +344,7 @@ fn process_init_obligation(program_id: &Pubkey, accounts: &[AccountInfo]) -> Pro
return Err(LendingError::InvalidAccountInput.into());
}
let mut borrow_reserve = Reserve::unpack(&borrow_reserve_info.data.borrow())?;
let borrow_reserve = Reserve::unpack(&borrow_reserve_info.data.borrow())?;
if borrow_reserve_info.owner != program_id {
return Err(LendingError::InvalidAccountOwner.into());
}
@ -359,12 +364,9 @@ fn process_init_obligation(program_id: &Pubkey, accounts: &[AccountInfo]) -> Pro
assert_rent_exempt(rent, obligation_info)?;
assert_uninitialized::<Obligation>(obligation_info)?;
assert_last_update_slot(&borrow_reserve, clock.slot)?;
// accrue interest and update rates
borrow_reserve.accrue_interest(clock.slot)?;
let cumulative_borrow_rate = borrow_reserve.cumulative_borrow_rate_wads;
Reserve::pack(borrow_reserve, &mut borrow_reserve_info.data.borrow_mut())?;
let obligation_mint_decimals = deposit_reserve.liquidity.mint_decimals;
let obligation = Obligation::new(NewObligationParams {
collateral_reserve: *deposit_reserve_info.key,
@ -459,7 +461,7 @@ fn process_deposit(
return Err(LendingError::InvalidAccountInput.into());
}
reserve.accrue_interest(clock.slot)?;
assert_last_update_slot(&reserve, clock.slot)?;
let collateral_amount = reserve.deposit_liquidity(liquidity_amount)?;
Reserve::pack(reserve, &mut reserve_info.data.borrow_mut())?;
@ -548,7 +550,7 @@ fn process_withdraw(
return Err(LendingError::InvalidAccountInput.into());
}
reserve.accrue_interest(clock.slot)?;
assert_last_update_slot(&reserve, clock.slot)?;
let liquidity_withdraw_amount = reserve.redeem_collateral(collateral_amount)?;
Reserve::pack(reserve, &mut reserve_info.data.borrow_mut())?;
@ -627,7 +629,7 @@ fn process_borrow(
return Err(LendingError::InvalidTokenProgram.into());
}
let mut deposit_reserve = Reserve::unpack(&deposit_reserve_info.data.borrow())?;
let deposit_reserve = Reserve::unpack(&deposit_reserve_info.data.borrow())?;
if deposit_reserve_info.owner != program_id {
return Err(LendingError::InvalidAccountOwner.into());
}
@ -721,9 +723,8 @@ fn process_borrow(
return Err(LendingError::InvalidTokenMint.into());
}
// accrue interest and update rates
borrow_reserve.accrue_interest(clock.slot)?;
deposit_reserve.accrue_interest(clock.slot)?;
assert_last_update_slot(&borrow_reserve, clock.slot)?;
assert_last_update_slot(&deposit_reserve, clock.slot)?;
obligation.accrue_interest(borrow_reserve.cumulative_borrow_rate_wads)?;
let mut trade_simulator = TradeSimulator::new(
@ -756,7 +757,6 @@ fn process_borrow(
Obligation::pack(obligation, &mut obligation_info.data.borrow_mut())?;
Reserve::pack(borrow_reserve, &mut borrow_reserve_info.data.borrow_mut())?;
Reserve::pack(deposit_reserve, &mut deposit_reserve_info.data.borrow_mut())?;
let authority_signer_seeds = &[
lending_market_info.key.as_ref(),
@ -922,7 +922,7 @@ fn process_repay(
}
// accrue interest and update rates
repay_reserve.accrue_interest(clock.slot)?;
assert_last_update_slot(&repay_reserve, clock.slot)?;
obligation.accrue_interest(repay_reserve.cumulative_borrow_rate_wads)?;
let RepayResult {
@ -1043,7 +1043,7 @@ fn process_liquidate(
return Err(LendingError::InvalidAccountInput.into());
}
let mut withdraw_reserve = Reserve::unpack(&withdraw_reserve_info.data.borrow())?;
let withdraw_reserve = Reserve::unpack(&withdraw_reserve_info.data.borrow())?;
if withdraw_reserve_info.owner != program_id {
return Err(LendingError::InvalidAccountOwner.into());
}
@ -1093,8 +1093,8 @@ fn process_liquidate(
}
// accrue interest and update rates
repay_reserve.accrue_interest(clock.slot)?;
withdraw_reserve.accrue_interest(clock.slot)?;
assert_last_update_slot(&repay_reserve, clock.slot)?;
assert_last_update_slot(&withdraw_reserve, clock.slot)?;
obligation.accrue_interest(repay_reserve.cumulative_borrow_rate_wads)?;
let mut trade_simulator = TradeSimulator::new(
@ -1152,10 +1152,6 @@ fn process_liquidate(
.min(withdraw_amount_as_collateral + liquidation_bonus_amount);
Reserve::pack(repay_reserve, &mut repay_reserve_info.data.borrow_mut())?;
Reserve::pack(
withdraw_reserve,
&mut withdraw_reserve_info.data.borrow_mut(),
)?;
obligation.borrowed_liquidity_wads = obligation
.borrowed_liquidity_wads
@ -1196,6 +1192,23 @@ fn process_liquidate(
Ok(())
}
#[inline(never)] // avoid stack frame limit
fn process_accrue_interest(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let clock = &Clock::from_account_info(next_account_info(account_info_iter)?)?;
for reserve_info in account_info_iter {
let mut reserve = Reserve::unpack(&reserve_info.data.borrow())?;
if reserve_info.owner != program_id {
return Err(LendingError::InvalidAccountOwner.into());
}
reserve.accrue_interest(clock.slot)?;
Reserve::pack(reserve, &mut reserve_info.data.borrow_mut())?;
}
Ok(())
}
fn assert_rent_exempt(rent: &Rent, account_info: &AccountInfo) -> ProgramResult {
if !rent.is_exempt(account_info.lamports(), account_info.data_len()) {
msg!(&rent.minimum_balance(account_info.data_len()).to_string());
@ -1205,6 +1218,14 @@ fn assert_rent_exempt(rent: &Rent, account_info: &AccountInfo) -> ProgramResult
}
}
fn assert_last_update_slot(reserve: &Reserve, slot: Slot) -> ProgramResult {
if !reserve.last_update_slot == slot {
Err(LendingError::ReserveStale.into())
} else {
Ok(())
}
}
fn assert_uninitialized<T: Pack + IsInitialized>(
account_info: &AccountInfo,
) -> Result<T, ProgramError> {

View File

@ -0,0 +1,108 @@
#![cfg(feature = "test-bpf")]
mod helpers;
use helpers::*;
use solana_program_test::*;
use solana_sdk::{
pubkey::Pubkey,
signature::{Keypair, Signer},
transaction::Transaction,
};
use spl_token_lending::{
instruction::accrue_reserve_interest,
math::{Decimal, Rate, TryMul},
processor::process_instruction,
state::SLOTS_PER_YEAR,
};
const LAMPORTS_TO_SOL: u64 = 1_000_000_000;
const FRACTIONAL_TO_USDC: u64 = 1_000_000;
const INITIAL_SOL_RESERVE_SUPPLY_LAMPORTS: u64 = 100 * LAMPORTS_TO_SOL;
const INITIAL_USDC_RESERVE_SUPPLY_FRACTIONAL: u64 = 100 * FRACTIONAL_TO_USDC;
#[tokio::test]
async fn test_success() {
let mut test = ProgramTest::new(
"spl_token_lending",
spl_token_lending::id(),
processor!(process_instruction),
);
// limit to track compute unit increase
test.set_bpf_compute_max_units(80_000);
let user_accounts_owner = Keypair::new();
let usdc_mint = add_usdc_mint(&mut test);
let lending_market = add_lending_market(&mut test, usdc_mint.pubkey);
let mut reserve_config = TEST_RESERVE_CONFIG;
reserve_config.loan_to_value_ratio = 80;
// Configure reserve to a fixed borrow rate of 1%
const BORROW_RATE: u8 = 1;
reserve_config.min_borrow_rate = BORROW_RATE;
reserve_config.optimal_borrow_rate = BORROW_RATE;
reserve_config.optimal_utilization_rate = 100;
let usdc_reserve = add_reserve(
&mut test,
&user_accounts_owner,
&lending_market,
AddReserveArgs {
borrow_amount: 100,
liquidity_amount: INITIAL_USDC_RESERVE_SUPPLY_FRACTIONAL,
liquidity_mint_decimals: usdc_mint.decimals,
liquidity_mint_pubkey: usdc_mint.pubkey,
slots_elapsed: SLOTS_PER_YEAR,
config: reserve_config,
..AddReserveArgs::default()
},
);
let sol_reserve = add_reserve(
&mut test,
&user_accounts_owner,
&lending_market,
AddReserveArgs {
borrow_amount: 100,
liquidity_amount: INITIAL_SOL_RESERVE_SUPPLY_LAMPORTS,
liquidity_mint_decimals: 9,
liquidity_mint_pubkey: spl_token::native_mint::id(),
slots_elapsed: SLOTS_PER_YEAR,
config: reserve_config,
..AddReserveArgs::default()
},
);
let (mut banks_client, payer, recent_blockhash) = test.start().await;
let mut transaction = Transaction::new_with_payer(
&[accrue_reserve_interest(
spl_token_lending::id(),
vec![usdc_reserve.pubkey, sol_reserve.pubkey],
)],
Some(&payer.pubkey()),
);
transaction.sign(&[&payer], recent_blockhash);
assert!(banks_client.process_transaction(transaction).await.is_ok());
let sol_reserve = sol_reserve.get_state(&mut banks_client).await;
let usdc_reserve = usdc_reserve.get_state(&mut banks_client).await;
let borrow_rate = Rate::from_percent(100u8 + BORROW_RATE);
assert!(sol_reserve.cumulative_borrow_rate_wads > borrow_rate.into());
assert_eq!(
sol_reserve.cumulative_borrow_rate_wads,
usdc_reserve.cumulative_borrow_rate_wads
);
assert!(
sol_reserve.liquidity.borrowed_amount_wads
> Decimal::from(100u64).try_mul(borrow_rate).unwrap()
);
assert_eq!(
sol_reserve.liquidity.borrowed_amount_wads,
usdc_reserve.liquidity.borrowed_amount_wads
);
}

View File

@ -6,10 +6,8 @@ use helpers::*;
use solana_program_test::*;
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
use spl_token_lending::{
instruction::BorrowAmountType,
math::Decimal,
processor::process_instruction,
state::{INITIAL_COLLATERAL_RATIO, SLOTS_PER_YEAR},
instruction::BorrowAmountType, math::Decimal, processor::process_instruction,
state::INITIAL_COLLATERAL_RATIO,
};
const LAMPORTS_TO_SOL: u64 = 1_000_000_000;
@ -36,7 +34,7 @@ async fn test_borrow_quote_currency() {
);
// limit to track compute unit increase
test.set_bpf_compute_max_units(185_000);
test.set_bpf_compute_max_units(118_000);
let user_accounts_owner = Keypair::new();
let sol_usdc_dex_market = TestDexMarket::setup(&mut test, TestDexMarketPair::SOL_USDC);
@ -46,17 +44,11 @@ async fn test_borrow_quote_currency() {
let mut reserve_config = TEST_RESERVE_CONFIG;
reserve_config.loan_to_value_ratio = 80;
// Configure reserve to a fixed borrow rate of 1%
reserve_config.min_borrow_rate = 1;
reserve_config.optimal_borrow_rate = 1;
reserve_config.optimal_utilization_rate = 100;
let usdc_reserve = add_reserve(
&mut test,
&user_accounts_owner,
&lending_market,
AddReserveArgs {
slots_elapsed: SLOTS_PER_YEAR,
liquidity_amount: INITIAL_USDC_RESERVE_SUPPLY_FRACTIONAL,
liquidity_mint_pubkey: usdc_mint.pubkey,
liquidity_mint_decimals: usdc_mint.decimals,
@ -70,8 +62,6 @@ async fn test_borrow_quote_currency() {
&user_accounts_owner,
&lending_market,
AddReserveArgs {
slots_elapsed: SLOTS_PER_YEAR,
borrow_amount: 20, // slightly increase collateral value w/ interest accrual
dex_market_pubkey: Some(sol_usdc_dex_market.pubkey),
liquidity_amount: INITIAL_SOL_RESERVE_SUPPLY_LAMPORTS,
liquidity_mint_pubkey: spl_token::native_mint::id(),
@ -154,14 +144,11 @@ async fn test_borrow_quote_currency() {
get_token_balance(&mut banks_client, usdc_reserve.user_liquidity_account).await;
assert_eq!(borrow_amount, 2 * USDC_BORROW_AMOUNT_FRACTIONAL);
// The SOL reserve accumulates interest which slightly increases the value
// of collateral, resulting in slightly less collateral required for new loans
const COLLATERAL_EPSILON: u64 = 1;
let user_collateral_balance =
get_token_balance(&mut banks_client, sol_reserve.user_collateral_account).await;
assert_eq!(user_collateral_balance, COLLATERAL_EPSILON);
assert_eq!(user_collateral_balance, 0);
let collateral_deposited = 2 * collateral_deposit_amount - COLLATERAL_EPSILON;
let collateral_deposited = 2 * collateral_deposit_amount;
let (total_fee, host_fee) = TEST_RESERVE_CONFIG
.fees
.calculate_borrow_fees(collateral_deposited)
@ -196,10 +183,6 @@ async fn test_borrow_base_currency() {
const INITIAL_SOL_RESERVE_SUPPLY_LAMPORTS: u64 = 5000 * LAMPORTS_TO_SOL;
const INITIAL_USDC_RESERVE_SUPPLY_FRACTIONAL: u64 = 2 * USDC_COLLATERAL_LAMPORTS;
// The USDC collateral reserve accumulates interest which slightly increases
// the value of collateral, resulting in additional borrow power
const INTEREST_EPSILON: u64 = 2;
let mut test = ProgramTest::new(
"spl_token_lending",
spl_token_lending::id(),
@ -207,7 +190,7 @@ async fn test_borrow_base_currency() {
);
// limit to track compute unit increase
test.set_bpf_compute_max_units(188_000);
test.set_bpf_compute_max_units(118_000);
let user_accounts_owner = Keypair::new();
let sol_usdc_dex_market = TestDexMarket::setup(&mut test, TestDexMarketPair::SOL_USDC);
@ -217,18 +200,11 @@ async fn test_borrow_base_currency() {
let mut reserve_config = TEST_RESERVE_CONFIG;
reserve_config.loan_to_value_ratio = 100;
// Configure reserve to a fixed borrow rate of 1%
reserve_config.min_borrow_rate = 1;
reserve_config.optimal_borrow_rate = 1;
reserve_config.optimal_utilization_rate = 100;
let usdc_reserve = add_reserve(
&mut test,
&user_accounts_owner,
&lending_market,
AddReserveArgs {
slots_elapsed: SLOTS_PER_YEAR,
borrow_amount: 1, // slightly increase collateral value w/ interest accrual
liquidity_amount: INITIAL_USDC_RESERVE_SUPPLY_FRACTIONAL,
liquidity_mint_pubkey: usdc_mint.pubkey,
liquidity_mint_decimals: usdc_mint.decimals,
@ -242,7 +218,6 @@ async fn test_borrow_base_currency() {
&user_accounts_owner,
&lending_market,
AddReserveArgs {
slots_elapsed: SLOTS_PER_YEAR,
dex_market_pubkey: Some(sol_usdc_dex_market.pubkey),
liquidity_amount: INITIAL_SOL_RESERVE_SUPPLY_LAMPORTS,
liquidity_mint_pubkey: spl_token::native_mint::id(),
@ -293,7 +268,7 @@ async fn test_borrow_base_currency() {
let borrow_amount =
get_token_balance(&mut banks_client, sol_reserve.user_liquidity_account).await;
assert_eq!(borrow_amount, SOL_BORROW_AMOUNT_LAMPORTS + INTEREST_EPSILON);
assert_eq!(borrow_amount, SOL_BORROW_AMOUNT_LAMPORTS);
let borrow_fees = TEST_RESERVE_CONFIG
.fees
@ -314,7 +289,7 @@ async fn test_borrow_base_currency() {
borrow_reserve: &sol_reserve,
dex_market: &sol_usdc_dex_market,
borrow_amount_type: BorrowAmountType::LiquidityBorrowAmount,
amount: borrow_amount - INTEREST_EPSILON,
amount: borrow_amount,
user_accounts_owner: &user_accounts_owner,
obligation: &sol_obligation,
},
@ -323,10 +298,7 @@ async fn test_borrow_base_currency() {
let borrow_amount =
get_token_balance(&mut banks_client, sol_reserve.user_liquidity_account).await;
assert_eq!(
borrow_amount,
2 * SOL_BORROW_AMOUNT_LAMPORTS + INTEREST_EPSILON
);
assert_eq!(borrow_amount, 2 * SOL_BORROW_AMOUNT_LAMPORTS);
let (mut total_fee, mut host_fee) = TEST_RESERVE_CONFIG
.fees

View File

@ -5,7 +5,7 @@ mod helpers;
use helpers::*;
use solana_program_test::*;
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
use spl_token_lending::{processor::process_instruction, state::SLOTS_PER_YEAR};
use spl_token_lending::processor::process_instruction;
const FRACTIONAL_TO_USDC: u64 = 1_000_000;
@ -18,7 +18,7 @@ async fn test_success() {
);
// limit to track compute unit increase
test.set_bpf_compute_max_units(65_000);
test.set_bpf_compute_max_units(31_000);
let user_accounts_owner = Keypair::new();
let usdc_mint = add_usdc_mint(&mut test);
@ -29,7 +29,6 @@ async fn test_success() {
&user_accounts_owner,
&lending_market,
AddReserveArgs {
slots_elapsed: SLOTS_PER_YEAR,
user_liquidity_amount: 100 * FRACTIONAL_TO_USDC,
liquidity_amount: 10_000 * FRACTIONAL_TO_USDC,
liquidity_mint_decimals: usdc_mint.decimals,

View File

@ -18,7 +18,7 @@ use spl_token_lending::{
borrow_reserve_liquidity, deposit_reserve_liquidity, init_lending_market, init_obligation,
init_reserve, liquidate_obligation, BorrowAmountType,
},
math::Decimal,
math::{Decimal, Rate, TryAdd, TryMul},
processor::process_instruction,
state::{
LendingMarket, NewReserveParams, Obligation, Reserve, ReserveCollateral, ReserveConfig,
@ -227,6 +227,7 @@ pub struct AddReserveArgs {
pub liquidity_mint_decimals: u8,
pub user_liquidity_amount: u64,
pub borrow_amount: u64,
pub initial_borrow_rate: u8,
pub collateral_amount: u64,
pub fees_amount: u64,
pub dex_market_pubkey: Option<Pubkey>,
@ -247,6 +248,7 @@ pub fn add_reserve(
liquidity_mint_decimals,
user_liquidity_amount,
borrow_amount,
initial_borrow_rate,
collateral_amount,
fees_amount,
dex_market_pubkey,
@ -358,6 +360,10 @@ pub fn add_reserve(
});
reserve.deposit_liquidity(liquidity_amount).unwrap();
reserve.liquidity.borrow(borrow_amount).unwrap();
let borrow_rate_multiplier = Rate::one()
.try_add(Rate::from_percent(initial_borrow_rate))
.unwrap();
reserve.cumulative_borrow_rate_wads = Decimal::one().try_mul(borrow_rate_multiplier).unwrap();
test.add_packable_account(
reserve_pubkey,
u32::MAX as u64,

View File

@ -12,7 +12,7 @@ use solana_sdk::{
};
use spl_token_lending::{
error::LendingError, instruction::init_obligation, math::Decimal,
processor::process_instruction, state::SLOTS_PER_YEAR,
processor::process_instruction,
};
#[tokio::test]
@ -24,7 +24,7 @@ async fn test_success() {
);
// limit to track compute unit increase
test.set_bpf_compute_max_units(60_000);
test.set_bpf_compute_max_units(28_000);
let user_accounts_owner = Keypair::new();
let sol_usdc_dex_market = TestDexMarket::setup(&mut test, TestDexMarketPair::SOL_USDC);
@ -36,7 +36,6 @@ async fn test_success() {
&user_accounts_owner,
&lending_market,
AddReserveArgs {
slots_elapsed: SLOTS_PER_YEAR,
liquidity_mint_pubkey: usdc_mint.pubkey,
liquidity_mint_decimals: usdc_mint.decimals,
config: TEST_RESERVE_CONFIG,
@ -84,7 +83,7 @@ async fn test_already_initialized() {
);
// limit to track compute unit increase
test.set_bpf_compute_max_units(14_000);
test.set_bpf_compute_max_units(13_000);
let user_accounts_owner = Keypair::new();
let sol_usdc_dex_market = TestDexMarket::setup(&mut test, TestDexMarketPair::SOL_USDC);
@ -96,7 +95,6 @@ async fn test_already_initialized() {
&user_accounts_owner,
&lending_market,
AddReserveArgs {
slots_elapsed: SLOTS_PER_YEAR,
liquidity_mint_pubkey: usdc_mint.pubkey,
liquidity_mint_decimals: usdc_mint.decimals,
config: TEST_RESERVE_CONFIG,

View File

@ -6,9 +6,7 @@ use helpers::*;
use solana_program_test::*;
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
use spl_token_lending::{
math::Decimal,
processor::process_instruction,
state::{INITIAL_COLLATERAL_RATIO, SLOTS_PER_YEAR},
math::Decimal, processor::process_instruction, state::INITIAL_COLLATERAL_RATIO,
};
const LAMPORTS_TO_SOL: u64 = 1_000_000_000;
@ -26,7 +24,7 @@ async fn test_success() {
);
// limit to track compute unit increase
test.set_bpf_compute_max_units(163_000);
test.set_bpf_compute_max_units(90_000);
// set loan values to about 90% of collateral value so that it gets liquidated
const USDC_LOAN: u64 = 2 * FRACTIONAL_TO_USDC;
@ -50,11 +48,11 @@ async fn test_success() {
&lending_market,
AddReserveArgs {
config: reserve_config,
slots_elapsed: SLOTS_PER_YEAR,
initial_borrow_rate: 1,
liquidity_amount: INITIAL_USDC_RESERVE_SUPPLY_FRACTIONAL,
liquidity_mint_pubkey: usdc_mint.pubkey,
liquidity_mint_decimals: usdc_mint.decimals,
borrow_amount: USDC_LOAN,
borrow_amount: USDC_LOAN * 101 / 100,
user_liquidity_amount: USDC_LOAN,
collateral_amount: SOL_LOAN_USDC_COLLATERAL,
..AddReserveArgs::default()
@ -67,13 +65,13 @@ async fn test_success() {
&lending_market,
AddReserveArgs {
config: reserve_config,
slots_elapsed: SLOTS_PER_YEAR,
initial_borrow_rate: 1,
liquidity_amount: INITIAL_SOL_RESERVE_SUPPLY_LAMPORTS,
liquidity_mint_decimals: 9,
liquidity_mint_pubkey: spl_token::native_mint::id(),
dex_market_pubkey: Some(sol_usdc_dex_market.pubkey),
collateral_amount: USDC_LOAN_SOL_COLLATERAL,
borrow_amount: SOL_LOAN,
borrow_amount: SOL_LOAN * 101 / 100,
user_liquidity_amount: SOL_LOAN,
..AddReserveArgs::default()
},

View File

@ -29,7 +29,7 @@ async fn test_success() {
);
// limit to track compute unit increase
test.set_bpf_compute_max_units(87_000);
test.set_bpf_compute_max_units(51_000);
const INITIAL_SOL_RESERVE_SUPPLY_LAMPORTS: u64 = 100 * LAMPORTS_TO_SOL;
const INITIAL_USDC_RESERVE_SUPPLY_FRACTIONAL: u64 = 100 * FRACTIONAL_TO_USDC;
@ -49,11 +49,11 @@ async fn test_success() {
&lending_market,
AddReserveArgs {
config: TEST_RESERVE_CONFIG,
slots_elapsed: SLOTS_PER_YEAR,
initial_borrow_rate: 1,
liquidity_amount: INITIAL_USDC_RESERVE_SUPPLY_FRACTIONAL,
liquidity_mint_pubkey: usdc_mint.pubkey,
liquidity_mint_decimals: usdc_mint.decimals,
borrow_amount: OBLIGATION_LOAN,
borrow_amount: OBLIGATION_LOAN * 101 / 100,
user_liquidity_amount: OBLIGATION_LOAN,
..AddReserveArgs::default()
},

View File

@ -11,13 +11,12 @@ use solana_sdk::{
};
use spl_token::instruction::approve;
use spl_token_lending::{
instruction::withdraw_reserve_liquidity,
processor::process_instruction,
state::{INITIAL_COLLATERAL_RATIO, SLOTS_PER_YEAR},
instruction::withdraw_reserve_liquidity, processor::process_instruction,
state::INITIAL_COLLATERAL_RATIO,
};
const FRACTIONAL_TO_USDC: u64 = 1_000_000;
const INITIAL_USDC_RESERVE_SUPPLY_LAMPORTS: u64 = 10 * FRACTIONAL_TO_USDC;
const INITIAL_USDC_RESERVE_SUPPLY_FRACTIONAL: u64 = 10 * FRACTIONAL_TO_USDC;
#[tokio::test]
async fn test_success() {
@ -28,22 +27,21 @@ async fn test_success() {
);
// limit to track compute unit increase
test.set_bpf_compute_max_units(66_000);
test.set_bpf_compute_max_units(33_000);
let user_accounts_owner = Keypair::new();
let usdc_mint = add_usdc_mint(&mut test);
let lending_market = add_lending_market(&mut test, usdc_mint.pubkey);
const WITHDRAW_COLLATERAL_AMOUNT: u64 =
INITIAL_COLLATERAL_RATIO * INITIAL_USDC_RESERVE_SUPPLY_LAMPORTS;
INITIAL_COLLATERAL_RATIO * INITIAL_USDC_RESERVE_SUPPLY_FRACTIONAL;
let usdc_reserve = add_reserve(
&mut test,
&user_accounts_owner,
&lending_market,
AddReserveArgs {
slots_elapsed: SLOTS_PER_YEAR,
liquidity_amount: INITIAL_USDC_RESERVE_SUPPLY_LAMPORTS,
liquidity_amount: INITIAL_USDC_RESERVE_SUPPLY_FRACTIONAL,
liquidity_mint_decimals: usdc_mint.decimals,
liquidity_mint_pubkey: usdc_mint.pubkey,
collateral_amount: WITHDRAW_COLLATERAL_AMOUNT,