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

Safely Delete Auctions after Restoring Position #149

Merged
merged 1 commit into from
Nov 22, 2023
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
144 changes: 8 additions & 136 deletions pool/src/auctions/auction.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
constants::SCALAR_7,
errors::PoolError,
pool::{Pool, PositionData, User},
pool::{Pool, User},
storage,
};
use cast::i128;
Expand Down Expand Up @@ -90,22 +90,19 @@ pub fn create_liquidation(e: &Env, user: &Address, percent_liquidated: u64) -> A
auction_data
}

/// Delete a liquidation auction if the user being liquidated is no longer eligible for liquidation.
/// Delete a liquidation auction if the user being liquidated
///
/// NOTE: Does not verify if the user's positions are healthy. This must be done before calling.
///
/// ### Arguments
/// * `auction_type` - The type of auction being created
///
/// ### Panics
/// If no auction exists for the user or if the user is still eligible for liquidation.
/// If no auction exists for the user
pub fn delete_liquidation(e: &Env, user: &Address) {
if !storage::has_auction(e, &(AuctionType::UserLiquidation as u32), user) {
panic_with_error!(e, PoolError::BadRequest);
}

let mut pool = Pool::load(e);
let positions = storage::get_user_positions(e, user);
let position_data = PositionData::calculate_from_positions(e, &mut pool, &positions);
position_data.require_healthy(e);
storage::del_auction(e, &(AuctionType::UserLiquidation as u32), user);
}

Expand Down Expand Up @@ -629,66 +626,16 @@ mod tests {
fn test_delete_user_liquidation() {
let e = Env::default();
e.mock_all_auths();
let pool_id = create_pool(&e);

let bombadil = Address::random(&e);
let pool_id = create_pool(&e);
let samwise = Address::random(&e);
let (underlying_0, _) = testutils::create_token_contract(&e, &bombadil);
let (reserve_config_0, reserve_data_0) = testutils::default_reserve_meta();
testutils::create_reserve(
&e,
&pool_id,
&underlying_0,
&reserve_config_0,
&reserve_data_0,
);

let (underlying_1, _) = 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_id,
&underlying_1,
&reserve_config_1,
&reserve_data_1,
);

let (oracle_id, oracle_client) = testutils::create_mock_oracle(&e);
oracle_client.set_data(
&bombadil,
&Asset::Other(Symbol::new(&e, "USD")),
&vec![
&e,
Asset::Stellar(underlying_0),
Asset::Stellar(underlying_1),
],
&7,
&300,
);
oracle_client.set_price_stable(&vec![&e, 10_0000000, 5_0000000]);

// setup user (collateralize reserve 0 and borrow reserve 1)
let collateral_amount = 17_8000000;
let liability_amount = 20_0000000;
let positions: Positions = Positions {
collateral: map![&e, (reserve_config_0.index, collateral_amount)],
liabilities: map![&e, (reserve_config_1.index, liability_amount)],
supply: map![&e],
};
let auction_data = AuctionData {
bid: map![&e],
lot: map![&e],
block: 100,
};
let pool_config = PoolConfig {
oracle: oracle_id,
bstop_rate: 0_100_000_000,
status: 0,
};
e.as_contract(&pool_id, || {
storage::set_pool_config(&e, &pool_config);
storage::set_user_positions(&e, &samwise, &positions);
storage::set_auction(
&e,
&(AuctionType::UserLiquidation as u32),
Expand All @@ -706,91 +653,16 @@ mod tests {
}

#[test]
#[should_panic(expected = "Error(Contract, #10)")]
fn test_delete_user_liquidation_invalid_hf() {
#[should_panic(expected = "Error(Contract, #2)")]
fn test_delete_user_liquidation_does_not_exist() {
let e = Env::default();
e.mock_all_auths();
let pool_id = create_pool(&e);

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

let (underlying_0, _) = testutils::create_token_contract(&e, &bombadil);
let (reserve_config_0, reserve_data_0) = testutils::default_reserve_meta();
testutils::create_reserve(
&e,
&pool_id,
&underlying_0,
&reserve_config_0,
&reserve_data_0,
);

let (underlying_1, _) = 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_id,
&underlying_1,
&reserve_config_1,
&reserve_data_1,
);

let (oracle_id, oracle_client) = testutils::create_mock_oracle(&e);
oracle_client.set_data(
&bombadil,
&Asset::Other(Symbol::new(&e, "USD")),
&vec![
&e,
Asset::Stellar(underlying_0),
Asset::Stellar(underlying_1),
],
&7,
&300,
);
oracle_client.set_price_stable(&vec![&e, 10_0000000, 5_0000000]);

// setup user (collateralize reserve 0 and borrow reserve 1)
let collateral_amount = 15_0000000;
let liability_amount = 20_0000000;
let positions: Positions = Positions {
collateral: map![&e, (reserve_config_0.index, collateral_amount)],
liabilities: map![&e, (reserve_config_1.index, liability_amount)],
supply: map![&e],
};
let auction_data = AuctionData {
bid: map![&e],
lot: map![&e],
block: 100,
};
let pool_config = PoolConfig {
oracle: oracle_id,
bstop_rate: 0_100_000_000,
status: 0,
};
e.as_contract(&pool_id, || {
storage::set_pool_config(&e, &pool_config);
storage::set_user_positions(&e, &samwise, &positions);

storage::set_auction(
&e,
&(AuctionType::UserLiquidation as u32),
&samwise,
&auction_data,
);
storage::set_auction(
&e,
&(AuctionType::UserLiquidation as u32),
&samwise,
&auction_data,
);

delete_liquidation(&e, &samwise);
assert!(storage::has_auction(
&e,
&(AuctionType::UserLiquidation as u32),
&samwise
));
});
}

Expand Down
16 changes: 0 additions & 16 deletions pool/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,15 +186,6 @@ pub trait Pool {
/// If the user liquidation auction was unable to be created
fn new_liquidation_auction(e: Env, user: Address, percent_liquidated: u64) -> AuctionData;

/// Delete a user liquidation auction if the user is no longer eligible to be liquidated.
///
/// ### Arguments
/// * `user` - The user getting liquidated through the auction
///
/// ### Panics
/// If the user is still eligible to be liquidated state or the auction doesn't exist
fn del_liquidation_auction(e: Env, user: Address);

/// Fetch an auction from the ledger. Returns a quote based on the current block.
///
/// ### Arguments
Expand Down Expand Up @@ -375,13 +366,6 @@ impl Pool for PoolContract {
auction_data
}

fn del_liquidation_auction(e: Env, user: Address) {
auctions::delete_liquidation(&e, &user);

e.events()
.publish((Symbol::new(&e, "delete_liquidation_auction"), user), ());
}

fn get_auction(e: Env, auction_type: u32, user: Address) -> AuctionData {
storage::get_auction(&e, &auction_type, &user)
}
Expand Down
83 changes: 83 additions & 0 deletions pool/src/pool/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,16 @@ pub fn build_actions_from_request(
(from.clone(), request.amount),
);
}
9 => {
// delete liquidation auction
// Note: request object is ignored besides type
auctions::delete_liquidation(e, &from);
check_health = true;
e.events().publish(
(Symbol::new(&e, "delete_liquidation_auction"), from.clone()),
(),
);
}
_ => panic_with_error!(e, PoolError::BadRequest),
}
}
Expand Down Expand Up @@ -1416,4 +1426,77 @@ mod tests {
assert_eq!(actions.spender_transfer.len(), 0);
});
}

/***** delete liquidation auction *****/

#[test]
fn test_delete_liquidation_auction() {
let e = Env::default();
e.budget().reset_unlimited();
e.mock_all_auths_allowing_non_root_auth();

e.ledger().set(LedgerInfo {
timestamp: 12345,
protocol_version: 20,
sequence_number: 51 + 200,
network_id: Default::default(),
base_reserve: 10,
min_temp_entry_expiration: 10,
min_persistent_entry_expiration: 10,
max_entry_expiration: 2000000,
});

let samwise = Address::random(&e);
let underlying_0 = Address::random(&e);
let underlying_1 = Address::random(&e);

let pool_address = create_pool(&e);

let pool_config = PoolConfig {
oracle: Address::random(&e),
bstop_rate: 0_100_000_000,
status: 0,
};
let auction_data = AuctionData {
bid: map![&e, (underlying_0.clone(), 952_0000000)],
lot: map![
&e,
(underlying_0.clone(), 100_0000000),
(underlying_1.clone(), 25_0000000)
],
block: 51,
};

e.as_contract(&pool_address, || {
e.mock_all_auths_allowing_non_root_auth();
storage::set_pool_config(&e, &pool_config);
storage::set_auction(
&e,
&(AuctionType::UserLiquidation as u32),
&samwise,
&auction_data,
);

let mut pool = Pool::load(&e);

let requests = vec![
&e,
Request {
request_type: 9,
address: Address::random(&e),
amount: 0,
},
];
let (actions, _, health_check) =
build_actions_from_request(&e, &mut pool, &samwise, requests);

assert_eq!(health_check, true);
assert_eq!(
storage::has_auction(&e, &(AuctionType::UserLiquidation as u32), &samwise),
false
);
assert_eq!(actions.pool_transfer.len(), 0);
assert_eq!(actions.spender_transfer.len(), 0);
});
}
}
2 changes: 0 additions & 2 deletions test-suites/src/test_fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ impl TestFixture<'_> {
&Asset::Other(Symbol::new(&e, "USD")),
&svec![
&e,
Asset::Stellar(blnd_id.clone()),
Asset::Stellar(eth_id.clone()),
Asset::Stellar(usdc_id),
Asset::Stellar(xlm_id.clone()),
Expand All @@ -131,7 +130,6 @@ impl TestFixture<'_> {
);
mock_oracle_client.set_price_stable(&svec![
&e,
0_2500000, // blnd
2000_0000000, // eth
1_0000000, // usdc
0_1000000, // xlm
Expand Down
Loading