Skip to content

Commit

Permalink
Merge pull request #177 from blend-capital/blcr-015-016
Browse files Browse the repository at this point in the history
Fix Incorrect Validation Locations
  • Loading branch information
mootz12 authored Jan 2, 2024
2 parents a3a5072 + b6d243d commit b6b7e42
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 89 deletions.
67 changes: 63 additions & 4 deletions pool/src/auctions/auction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,6 @@ pub fn fill(
percent_filled: u64,
) {
let auction_data = storage::get_auction(e, &auction_type, user);
if percent_filled > 100 || percent_filled == 0 {
panic_with_error!(e, PoolError::BadRequest);
}

let (to_fill_auction, remaining_auction) = scale_auction(e, &auction_data, percent_filled);
match AuctionType::from_u32(auction_type) {
AuctionType::UserLiquidation => {
Expand Down Expand Up @@ -164,12 +160,19 @@ pub fn fill(
/// Returns the (Scaled Auction, Remaining Auction) such that:
/// - Scaled Auction is the auction data scaled
/// - Remaining Auction is the leftover auction data that will be stored in the ledger, or deleted if None
///
/// ### Panics
/// If the percent filled is greater than 100 or less than 0
#[allow(clippy::zero_prefixed_literal)]
fn scale_auction(
e: &Env,
auction_data: &AuctionData,
percent_filled: u64,
) -> (AuctionData, Option<AuctionData>) {
if percent_filled > 100 || percent_filled == 0 {
panic_with_error!(e, PoolError::BadRequest);
}

let mut to_fill_auction = AuctionData {
bid: map![e],
lot: map![e],
Expand Down Expand Up @@ -1577,4 +1580,60 @@ mod tests {
12_5000003
);
}

#[test]
#[should_panic(expected = "Error(Contract, #2)")]
fn test_scale_auction_fill_percentage_zero() {
let e = Env::default();
let underlying_0 = Address::generate(&e);
let underlying_1 = Address::generate(&e);

let base_auction_data = AuctionData {
bid: map![&e, (underlying_0.clone(), 25_0000005)],
lot: map![&e, (underlying_1.clone(), 25_0000005)],
block: 1000,
};

// 0 blocks
e.ledger().set(LedgerInfo {
timestamp: 12345,
protocol_version: 20,
sequence_number: 1000,
network_id: Default::default(),
base_reserve: 10,
min_temp_entry_ttl: 172800,
min_persistent_entry_ttl: 172800,
max_entry_ttl: 9999999,
});

let (_, _) = scale_auction(&e, &base_auction_data, 0);
}

#[test]
#[should_panic(expected = "Error(Contract, #2)")]
fn test_scale_auction_fill_percentage_over_100() {
let e = Env::default();
let underlying_0 = Address::generate(&e);
let underlying_1 = Address::generate(&e);

let base_auction_data = AuctionData {
bid: map![&e, (underlying_0.clone(), 25_0000005)],
lot: map![&e, (underlying_1.clone(), 25_0000005)],
block: 1000,
};

// 0 blocks
e.ledger().set(LedgerInfo {
timestamp: 12345,
protocol_version: 20,
sequence_number: 1000,
network_id: Default::default(),
base_reserve: 10,
min_temp_entry_ttl: 172800,
min_persistent_entry_ttl: 172800,
max_entry_ttl: 9999999,
});

let (_, _) = scale_auction(&e, &base_auction_data, 101);
}
}
94 changes: 94 additions & 0 deletions pool/src/auctions/backstop_interest_auction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ pub fn fill_interest_auction(
) {
// bid only contains the USDC token
let backstop = storage::get_backstop(e);
if filler.clone() == backstop {
panic_with_error!(e, PoolError::BadRequest);
}
let usdc_token = storage::get_usdc_token(e);
let usdc_bid_amount = auction_data.bid.get_unchecked(usdc_token);

Expand Down Expand Up @@ -587,4 +590,95 @@ mod tests {
assert_eq!(reserve_1_data.backstop_credit, 5_0000000);
});
}

#[test]
#[should_panic(expected = "Error(Contract, #2)")]
fn test_fill_interest_auction_with_backstop() {
let e = Env::default();
e.mock_all_auths_allowing_non_root_auth();
e.budget().reset_unlimited();

e.ledger().set(LedgerInfo {
timestamp: 12345,
protocol_version: 20,
sequence_number: 301,
network_id: Default::default(),
base_reserve: 10,
min_temp_entry_ttl: 10,
min_persistent_entry_ttl: 10,
max_entry_ttl: 2000000,
});

let bombadil = Address::generate(&e);
let samwise = Address::generate(&e);

let pool_address = create_pool(&e);

let (usdc_id, usdc_client) = testutils::create_usdc_token(&e, &pool_address, &bombadil);
let (backstop_address, _backstop_client) = testutils::create_backstop(&e);
testutils::setup_backstop(
&e,
&pool_address,
&backstop_address,
&Address::generate(&e),
&usdc_id,
&Address::generate(&e),
);

let (underlying_0, underlying_0_client) = testutils::create_token_contract(&e, &bombadil);
let (mut reserve_config_0, reserve_data_0) = testutils::default_reserve_meta();
reserve_config_0.index = 0;
testutils::create_reserve(
&e,
&pool_address,
&underlying_0,
&reserve_config_0,
&reserve_data_0,
);
underlying_0_client.mint(&pool_address, &1_000_0000000);

let (underlying_1, underlying_1_client) = testutils::create_token_contract(&e, &bombadil);
let (mut reserve_config_1, reserve_data_1) = testutils::default_reserve_meta();
reserve_config_1.index = 1;
testutils::create_reserve(
&e,
&pool_address,
&underlying_1,
&reserve_config_1,
&reserve_data_1,
);
underlying_1_client.mint(&pool_address, &1_000_0000000);

let pool_config = PoolConfig {
oracle: Address::generate(&e),
bstop_rate: 0_100_000_000,
status: 0,
max_positions: 4,
};
let mut auction_data = AuctionData {
bid: map![&e, (usdc_id.clone(), 95_0000000)],
lot: map![
&e,
(underlying_0.clone(), 100_0000000),
(underlying_1.clone(), 25_0000000)
],
block: 51,
};
usdc_client.mint(&samwise, &100_0000000);
e.as_contract(&pool_address, || {
e.mock_all_auths_allowing_non_root_auth();
storage::set_auction(
&e,
&(AuctionType::InterestAuction as u32),
&backstop_address,
&auction_data,
);
storage::set_pool_config(&e, &pool_config);
storage::set_backstop(&e, &backstop_address);
storage::set_usdc_token(&e, &usdc_id);

let mut pool = Pool::load(&e);
fill_interest_auction(&e, &mut pool, &mut auction_data, &backstop_address);
});
}
}
148 changes: 145 additions & 3 deletions pool/src/auctions/bad_debt_auction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ use crate::{
constants::SCALAR_7,
dependencies::BackstopClient,
errors::PoolError,
pool::{burn_backstop_bad_debt, calc_pool_backstop_threshold, Pool, User},
pool::{calc_pool_backstop_threshold, Pool, User},
storage,
};
use cast::i128;
use soroban_fixed_point_math::FixedPoint;
use soroban_sdk::{map, panic_with_error, unwrap::UnwrapOptimized, Address, Env};
use soroban_sdk::{map, panic_with_error, unwrap::UnwrapOptimized, Address, Env, Symbol};

use super::{AuctionData, AuctionType};

Expand Down Expand Up @@ -77,6 +77,9 @@ pub fn fill_bad_debt_auction(
filler_state: &mut User,
) {
let backstop_address = storage::get_backstop(e);
if filler_state.address == backstop_address {
panic_with_error!(e, PoolError::BadRequest);
}
let mut backstop_state = User::load(e, &backstop_address);

// bid only contains d_token asset amounts
Expand All @@ -99,7 +102,20 @@ pub fn fill_bad_debt_auction(
let threshold = calc_pool_backstop_threshold(&pool_backstop_data);
if threshold < 0_0000003 {
// ~5% of threshold
burn_backstop_bad_debt(e, &mut backstop_state, pool);
let reserve_list = storage::get_res_list(e);
let mut rm_liabilities = map![e];
for (reserve_index, liability_balance) in backstop_state.positions.liabilities.iter() {
let res_asset_address = reserve_list.get_unchecked(reserve_index);
rm_liabilities.set(res_asset_address.clone(), liability_balance);

e.events().publish(
(Symbol::new(e, "bad_debt"), backstop_address.clone()),
(res_asset_address, liability_balance),
);
}
// remove liability debtTokens from backstop resulting in a shared loss for
// token suppliers
backstop_state.rm_positions(e, pool, map![e], rm_liabilities);
}
}
backstop_state.store(e);
Expand Down Expand Up @@ -885,6 +901,8 @@ mod tests {
);
let backstop_positions = storage::get_user_positions(&e, &backstop_address);
assert_eq!(backstop_positions.liabilities.len(), 0);
assert_eq!(backstop_positions.collateral.len(), 0);
assert_eq!(backstop_positions.supply.len(), 0);
});
}

Expand Down Expand Up @@ -1050,4 +1068,128 @@ mod tests {
);
});
}

#[test]
#[should_panic(expected = "Error(Contract, #2)")]
fn test_fill_bad_debt_auction_with_backstop() {
let e = Env::default();
e.mock_all_auths_allowing_non_root_auth();
e.budget().reset_unlimited(); // setup exhausts budget

e.ledger().set(LedgerInfo {
timestamp: 12345,
protocol_version: 20,
sequence_number: 51,
network_id: Default::default(),
base_reserve: 10,
min_temp_entry_ttl: 10,
min_persistent_entry_ttl: 10,
max_entry_ttl: 2000000,
});

let bombadil = Address::generate(&e);
let samwise = Address::generate(&e);

let pool_address = create_pool(&e);

let (blnd, blnd_client) = testutils::create_blnd_token(&e, &pool_address, &bombadil);
let (usdc, usdc_client) = testutils::create_usdc_token(&e, &pool_address, &bombadil);
let (lp_token, lp_token_client) =
testutils::create_comet_lp_pool(&e, &bombadil, &blnd, &usdc);
let (backstop_address, backstop_client) = testutils::create_backstop(&e);
testutils::setup_backstop(
&e,
&pool_address,
&backstop_address,
&lp_token,
&usdc,
&blnd,
);
// mint lp tokens
blnd_client.mint(&samwise, &500_001_0000000);
blnd_client.approve(&samwise, &lp_token, &i128::MAX, &99999);
usdc_client.mint(&samwise, &12_501_0000000);
usdc_client.approve(&samwise, &lp_token, &i128::MAX, &99999);
lp_token_client.join_pool(
&50_000_0000000,
&vec![&e, 500_001_0000000, 12_501_0000000],
&samwise,
);
backstop_client.deposit(&samwise, &pool_address, &50_000_0000000);
backstop_client.update_tkn_val();

let (underlying_0, _) = testutils::create_token_contract(&e, &bombadil);
let (mut reserve_config_0, mut reserve_data_0) = testutils::default_reserve_meta();
reserve_data_0.d_rate = 1_100_000_000;
reserve_data_0.last_time = 12345;
reserve_config_0.index = 0;
testutils::create_reserve(
&e,
&pool_address,
&underlying_0,
&reserve_config_0,
&reserve_data_0,
);

let (underlying_1, _) = testutils::create_token_contract(&e, &bombadil);
let (mut reserve_config_1, mut reserve_data_1) = testutils::default_reserve_meta();
reserve_data_1.d_rate = 1_200_000_000;
reserve_data_1.last_time = 12345;
reserve_config_1.index = 1;
testutils::create_reserve(
&e,
&pool_address,
&underlying_1,
&reserve_config_1,
&reserve_data_1,
);

let (underlying_2, _) = testutils::create_token_contract(&e, &bombadil);
let (mut reserve_config_2, mut reserve_data_2) = testutils::default_reserve_meta();
reserve_data_2.b_rate = 1_100_000_000;
reserve_data_2.last_time = 12345;
reserve_config_2.index = 1;
testutils::create_reserve(
&e,
&pool_address,
&underlying_2,
&reserve_config_2,
&reserve_data_2,
);
let pool_config = PoolConfig {
oracle: Address::generate(&e),
bstop_rate: 0_100_000_000,
status: 0,
max_positions: 4,
};
let mut auction_data = AuctionData {
bid: map![&e, (underlying_0, 10_0000000), (underlying_1, 2_5000000)],
lot: map![&e, (lp_token.clone(), 47_6000000)],
block: 51,
};
let positions: Positions = Positions {
collateral: map![&e],
liabilities: map![
&e,
(reserve_config_0.index, 10_0000000),
(reserve_config_1.index, 2_5000000)
],
supply: map![&e],
};

e.as_contract(&pool_address, || {
storage::set_auction(
&e,
&(AuctionType::BadDebtAuction as u32),
&backstop_address,
&auction_data,
);
storage::set_pool_config(&e, &pool_config);
storage::set_user_positions(&e, &backstop_address, &positions);

let mut pool = Pool::load(&e);
let mut backstop_state = User::load(&e, &backstop_address);
fill_bad_debt_auction(&e, &mut pool, &mut auction_data, &mut backstop_state);
});
}
}
Loading

0 comments on commit b6b7e42

Please sign in to comment.