Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
8 changes: 4 additions & 4 deletions .github/workflows/pull-request-token-lending.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ name: Token Lending Pull Request
on:
pull_request:
paths:
- 'token-lending/**'
- 'token/**'
- "token-lending/**"
- "token/**"
push:
branches: [master]
paths:
- 'token-lending/**'
- 'token/**'
- "token-lending/**"
- "token/**"

jobs:
cargo-test-bpf:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ name: Pull Request
on:
pull_request:
paths-ignore:
- 'docs/**'
- "docs/**"
push:
branches: [master, upcoming]
paths-ignore:
- 'docs/**'
- "docs/**"

jobs:
all_github_action_checks:
Expand Down
24 changes: 12 additions & 12 deletions token-lending/program/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1535,23 +1535,22 @@ fn _withdraw_obligation_collateral<'a>(
// account for lending market and reserve rate limiter when withdrawing. this is needed to
// support max withdraws.
let max_outflow_collateral_amount = if account_for_rate_limiter {
let max_reserve_outflow_liquidity_amount = withdraw_reserve
.rate_limiter
.clone()
.remaining_outflow(clock.slot)?;

let max_outflow_usd = lending_market
.rate_limiter
.clone() // remaining_outflow is a mutable call, but we don't have mutable access here
.remaining_outflow(clock.slot)?;

// Because the max_outflow_usd can be an large number we can get an overflow error.
// However, we don't actually care about large values because we're taking `min`.
// So just default to `max_reserve_outflow_liquidity_amount` if we overflow.
let max_lending_market_outflow_liquidity_amount = withdraw_reserve
.usd_to_liquidity_amount_lower_bound(min(
max_outflow_usd,
// min here bc this function can overflow if max_outflow_usd is u64::MAX
// the actual value doesn't matter too much as long as its sensible
obligation.deposited_value.try_mul(2)?,
Copy link
Author

Choose a reason for hiding this comment

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

or use a static value here that doesn't overflow

))?;

let max_reserve_outflow_liquidity_amount = withdraw_reserve
.rate_limiter
.clone()
.remaining_outflow(clock.slot)?;
.usd_to_liquidity_amount_lower_bound(max_outflow_usd)
.unwrap_or(max_reserve_outflow_liquidity_amount);

let max_outflow_liquidity_amount = min(
max_lending_market_outflow_liquidity_amount,
Expand All @@ -1561,7 +1560,8 @@ fn _withdraw_obligation_collateral<'a>(
withdraw_reserve
.collateral_exchange_rate()?
.decimal_liquidity_to_collateral(max_outflow_liquidity_amount)?
.try_floor_u64()?
.try_floor_u64()
.unwrap_or(u64::MAX)
} else {
u64::MAX
};
Expand Down
6 changes: 6 additions & 0 deletions token-lending/program/tests/attributed_borrows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ async fn test_refresh_obligation() {
(usdc_mint::id(), 10 * FRACTIONAL_TO_USDC),
(wsol_mint::id(), LAMPORTS_PER_SOL),
],
..Default::default()
},
ObligationArgs {
deposits: vec![
Expand All @@ -102,6 +103,7 @@ async fn test_refresh_obligation() {
(usdc_mint::id(), 100 * FRACTIONAL_TO_USDC),
(wsol_mint::id(), 2 * LAMPORTS_PER_SOL),
],
..Default::default()
},
],
)
Expand Down Expand Up @@ -230,6 +232,7 @@ async fn test_calculations() {
(usdc_mint::id(), 10 * FRACTIONAL_TO_USDC),
(wsol_mint::id(), LAMPORTS_PER_SOL),
],
..Default::default()
},
ObligationArgs {
deposits: vec![
Expand All @@ -240,6 +243,7 @@ async fn test_calculations() {
(usdc_mint::id(), 100 * FRACTIONAL_TO_USDC),
(wsol_mint::id(), 2 * LAMPORTS_PER_SOL),
],
..Default::default()
},
],
)
Expand Down Expand Up @@ -599,6 +603,7 @@ async fn test_withdraw() {
(wsol_mint::id(), 2 * LAMPORTS_PER_SOL),
],
borrows: vec![(usdc_mint::id(), 10 * FRACTIONAL_TO_USDC)],
..Default::default()
}],
)
.await;
Expand Down Expand Up @@ -796,6 +801,7 @@ async fn test_liquidate() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), FRACTIONAL_TO_USDC / 2)],
borrows: vec![(wsol_mint::id(), LAMPORTS_PER_SOL / 40)],
..Default::default()
}],
)
.await;
Expand Down
1 change: 1 addition & 0 deletions token-lending/program/tests/borrow_obligation_liquidity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ async fn test_borrow_max_rate_limiter() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 100 * FRACTIONAL_TO_USDC)],
borrows: vec![],
..Default::default()
}],
)
.await;
Expand Down
4 changes: 4 additions & 0 deletions token-lending/program/tests/forgive_debt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,12 @@ async fn test_forgive_debt_success_easy() {
ObligationArgs {
deposits: vec![(usdc_mint::id(), 20 * FRACTIONAL_TO_USDC)],
borrows: vec![(wsol_mint::id(), LAMPORTS_PER_SOL)],
..Default::default()
},
ObligationArgs {
deposits: vec![(wsol_mint::id(), LAMPORTS_PER_SOL)],
borrows: vec![],
..Default::default()
},
],
)
Expand Down Expand Up @@ -272,6 +274,7 @@ async fn test_forgive_debt_fail_invalid_signer() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 200 * FRACTIONAL_TO_USDC)],
borrows: vec![(wsol_mint::id(), 10 * LAMPORTS_PER_SOL)],
..Default::default()
}],
)
.await;
Expand Down Expand Up @@ -373,6 +376,7 @@ async fn test_forgive_debt_fail_no_signer() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 200 * FRACTIONAL_TO_USDC)],
borrows: vec![(wsol_mint::id(), 10 * LAMPORTS_PER_SOL)],
..Default::default()
}],
)
.await;
Expand Down
33 changes: 25 additions & 8 deletions token-lending/program/tests/helpers/solend_program_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1875,6 +1875,17 @@ pub struct ReserveArgs {
pub struct ObligationArgs {
pub deposits: Vec<(Pubkey, u64)>,
pub borrows: Vec<(Pubkey, u64)>,
pub should_refresh: bool,
}

impl Default for ObligationArgs {
fn default() -> Self {
ObligationArgs {
deposits: vec![],
borrows: vec![],
should_refresh: true,
}
}
}

pub async fn custom_scenario(
Expand Down Expand Up @@ -1987,17 +1998,19 @@ pub async fn custom_scenario(
}
}

for (i, obligation_arg) in obligation_args.iter().enumerate() {
for ((obligation, obligation_owner), obligation_arg) in obligations
.iter_mut()
.zip(obligation_owners.iter_mut())
.zip(obligation_args.iter())
{
for (mint, amount) in obligation_arg.borrows.iter() {
let reserve = reserves
.iter()
.find(|reserve| reserve.account.liquidity.mint_pubkey == *mint)
.unwrap();

obligation_owners[i]
.create_token_account(mint, &mut test)
.await;
obligation_owners[i]
obligation_owner.create_token_account(mint, &mut test).await;
obligation_owner
.create_token_account(&reserve.account.collateral.mint_pubkey, &mut test)
.await;

Expand All @@ -2007,8 +2020,8 @@ pub async fn custom_scenario(
.borrow_obligation_liquidity(
&mut test,
reserve,
&obligations[i],
&obligation_owners[i],
obligation,
obligation_owner,
fee_receiver.get_account(mint),
*amount,
)
Expand All @@ -2017,7 +2030,11 @@ pub async fn custom_scenario(
}
}

for obligation in obligations.iter_mut() {
for obligation in obligations
.iter_mut()
.zip(obligation_args.iter())
.filter_map(|(obligation, arg)| arg.should_refresh.then_some(obligation))
{
lending_market
.refresh_obligation(&mut test, obligation)
.await
Expand Down
5 changes: 5 additions & 0 deletions token-lending/program/tests/isolated_tier_assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ async fn test_refresh_obligation() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 100 * FRACTIONAL_TO_USDC)],
borrows: vec![],
..Default::default()
}],
)
.await;
Expand Down Expand Up @@ -173,6 +174,7 @@ async fn borrow_isolated_asset() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 100 * FRACTIONAL_TO_USDC)],
borrows: vec![],
..Default::default()
}],
)
.await;
Expand Down Expand Up @@ -269,6 +271,7 @@ async fn borrow_isolated_asset_invalid() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 100 * FRACTIONAL_TO_USDC)],
borrows: vec![(wsol_mint::id(), 1)],
..Default::default()
}],
)
.await;
Expand Down Expand Up @@ -354,6 +357,7 @@ async fn borrow_regular_asset_invalid() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 100 * FRACTIONAL_TO_USDC)],
borrows: vec![(bonk_mint::id(), 1)],
..Default::default()
}],
)
.await;
Expand Down Expand Up @@ -449,6 +453,7 @@ async fn invalid_borrow_due_to_reserve_config_change() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 100 * FRACTIONAL_TO_USDC)],
borrows: vec![(bonk_mint::id(), 1), (wsol_mint::id(), LAMPORTS_PER_SOL)],
..Default::default()
}],
)
.await;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,7 @@ async fn test_liquidity_ordering() {
(wsol_mint::id(), LAMPORTS_PER_SOL),
(usdc_mint::id(), FRACTIONAL_TO_USDC),
],
..Default::default()
}],
)
.await;
Expand Down Expand Up @@ -705,6 +706,7 @@ async fn test_liquidate_closeable_obligation() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 20 * FRACTIONAL_TO_USDC)],
borrows: vec![(wsol_mint::id(), LAMPORTS_PER_SOL)],
..Default::default()
}],
)
.await;
Expand Down
2 changes: 2 additions & 0 deletions token-lending/program/tests/mark_obligation_as_closeable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ async fn test_mark_obligation_as_closeable_success() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 20 * FRACTIONAL_TO_USDC)],
borrows: vec![(wsol_mint::id(), LAMPORTS_PER_SOL)],
..Default::default()
}],
)
.await;
Expand Down Expand Up @@ -176,6 +177,7 @@ async fn invalid_signer() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 20 * FRACTIONAL_TO_USDC)],
borrows: vec![(wsol_mint::id(), LAMPORTS_PER_SOL)],
..Default::default()
}],
)
.await;
Expand Down
1 change: 1 addition & 0 deletions token-lending/program/tests/refresh_obligation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ async fn test_obligation_liquidity_ordering() {
(usdc_mint::id(), 1),
(bonk_mint::id(), 1),
],
..Default::default()
}],
)
.await;
Expand Down
3 changes: 3 additions & 0 deletions token-lending/program/tests/two_prices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ async fn test_borrow() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 100 * FRACTIONAL_TO_USDC)],
borrows: vec![(wsol_mint::id(), LAMPORTS_PER_SOL)],
..Default::default()
}],
)
.await;
Expand Down Expand Up @@ -204,6 +205,7 @@ async fn test_withdraw() {
(usdt_mint::id(), 20 * FRACTIONAL_TO_USDC),
],
borrows: vec![(wsol_mint::id(), LAMPORTS_PER_SOL)],
..Default::default()
}],
)
.await;
Expand Down Expand Up @@ -340,6 +342,7 @@ async fn test_liquidation_doesnt_use_smoothed_price() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 100 * FRACTIONAL_TO_USDC)],
borrows: vec![(wsol_mint::id(), LAMPORTS_PER_SOL)],
..Default::default()
}],
)
.await;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ async fn test_withdraw_max_rate_limiter() {
&[ObligationArgs {
deposits: vec![(wsol_mint::id(), 50 * LAMPORTS_PER_SOL)],
borrows: vec![],
..Default::default()
}],
)
.await;
Expand Down Expand Up @@ -299,11 +300,50 @@ async fn test_withdraw_no_borrows() {
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 100_000 * FRACTIONAL_TO_USDC)],
borrows: vec![],
..Default::default()
}],
)
.await;

test.advance_clock_by_slots(1).await;
lending_market
.withdraw_obligation_collateral_and_redeem_reserve_collateral(
&mut test,
&reserves[0],
&obligations[0],
&users[0],
100_000 * FRACTIONAL_TO_USDC,
)
.await
.unwrap();
}

/// If someone creates an obligation, deposits collateral, then they should be
/// able to withdraw that collateral without ever refreshing the obligation.
#[tokio::test]
async fn test_withdraw_no_borrows_no_refresh() {
let (mut test, lending_market, reserves, obligations, users, _) = custom_scenario(
&[ReserveArgs {
mint: usdc_mint::id(),
config: test_reserve_config(),
liquidity_amount: 100_000 * FRACTIONAL_TO_USDC,
price: PriceArgs {
price: 10,
conf: 0,
expo: -1,
ema_price: 10,
ema_conf: 1,
},
}],
&[ObligationArgs {
deposits: vec![(usdc_mint::id(), 100_000 * FRACTIONAL_TO_USDC)],
borrows: vec![],
should_refresh: false,
}],
)
.await;

test.advance_clock_by_slots(100).await;

lending_market
.withdraw_obligation_collateral_and_redeem_reserve_collateral(
Expand Down
Loading