From b4779572296280ec894da2e54837ac26fa4fbd5f Mon Sep 17 00:00:00 2001 From: mootz12 Date: Wed, 3 Jan 2024 14:11:12 -0500 Subject: [PATCH] pool: chore: make reserve caching system more obvious to use --- pool/src/auctions/auction.rs | 16 +-- .../src/auctions/backstop_interest_auction.rs | 60 ++++---- pool/src/auctions/bad_debt_auction.rs | 2 +- pool/src/errors.rs | 1 + pool/src/pool/actions.rs | 52 +++---- pool/src/pool/bad_debt.rs | 4 +- pool/src/pool/config.rs | 5 +- pool/src/pool/health_factor.rs | 4 +- pool/src/pool/pool.rs | 130 ++++++++++++++---- pool/src/pool/user.rs | 16 +-- pool/src/testutils.rs | 2 +- 11 files changed, 170 insertions(+), 122 deletions(-) diff --git a/pool/src/auctions/auction.rs b/pool/src/auctions/auction.rs index 2e4170ae..52a5995c 100644 --- a/pool/src/auctions/auction.rs +++ b/pool/src/auctions/auction.rs @@ -435,8 +435,10 @@ mod tests { 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.b_rate = 1_100_000_000; reserve_data_0.last_time = 12345; + reserve_data_0.backstop_credit = 100_0000000; + reserve_data_0.b_supply = 1000_0000000; + reserve_data_0.d_supply = 750_0000000; reserve_config_0.index = 0; testutils::create_reserve( &e, @@ -448,8 +450,10 @@ mod tests { 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.b_rate = 1_100_000_000; reserve_data_1.last_time = 12345; + reserve_data_1.backstop_credit = 25_0000000; + reserve_data_1.b_supply = 250_0000000; + reserve_data_1.d_supply = 187_5000000; reserve_config_1.index = 1; testutils::create_reserve( &e, @@ -495,13 +499,7 @@ mod tests { }; e.as_contract(&pool_address, || { storage::set_pool_config(&e, &pool_config); - let pool = Pool::load(&e); - let mut reserve_0 = pool.load_reserve(&e, &underlying_0); - reserve_0.backstop_credit += 100_0000000; - reserve_0.store(&e); - let mut reserve_1 = pool.load_reserve(&e, &underlying_1); - reserve_1.backstop_credit += 25_0000000; - reserve_1.store(&e); + create(&e, 2); assert!(storage::has_auction(&e, &2, &backstop_address)); }); diff --git a/pool/src/auctions/backstop_interest_auction.rs b/pool/src/auctions/backstop_interest_auction.rs index 91cb65e9..96648350 100644 --- a/pool/src/auctions/backstop_interest_auction.rs +++ b/pool/src/auctions/backstop_interest_auction.rs @@ -25,7 +25,7 @@ pub fn create_interest_auction_data(e: &Env, backstop: &Address) -> AuctionData for i in 0..reserve_list.len() { let res_asset_address = reserve_list.get_unchecked(i); // don't store updated reserve data back to ledger. This will occur on the the auction's fill. - let reserve = pool.load_reserve(e, &res_asset_address); + let reserve = pool.load_reserve(e, &res_asset_address, false); if reserve.backstop_credit > 0 { let asset_to_base = pool.load_price(e, &res_asset_address); interest_value += i128(asset_to_base) @@ -78,9 +78,9 @@ pub fn fill_interest_auction( // lot contains underlying tokens, but the backstop credit must be updated on the reserve for (res_asset_address, lot_amount) in auction_data.lot.iter() { - let mut reserve = pool.load_reserve(e, &res_asset_address); + let mut reserve = pool.load_reserve(e, &res_asset_address, true); reserve.backstop_credit -= lot_amount; - pool.cache_reserve(reserve, true); + pool.cache_reserve(reserve); TokenClient::new(e, &res_asset_address).transfer( &e.current_contract_address(), filler, @@ -178,6 +178,7 @@ mod tests { let (mut reserve_config_0, mut reserve_data_0) = testutils::default_reserve_meta(); reserve_data_0.b_rate = 1_100_000_000; reserve_data_0.last_time = 12345; + reserve_data_0.backstop_credit = 10_0000000; reserve_config_0.index = 0; testutils::create_reserve( &e, @@ -191,6 +192,7 @@ mod tests { let (mut reserve_config_1, mut reserve_data_1) = testutils::default_reserve_meta(); reserve_data_1.b_rate = 1_100_000_000; reserve_data_1.last_time = 12345; + reserve_data_1.backstop_credit = 2_5000000; reserve_config_1.index = 1; testutils::create_reserve( &e, @@ -236,15 +238,8 @@ mod tests { }; e.as_contract(&pool_address, || { storage::set_pool_config(&e, &pool_config); - let pool = Pool::load(&e); - let mut reserve_0 = pool.load_reserve(&e, &underlying_0); - reserve_0.backstop_credit += 10_0000000; - reserve_0.store(&e); - let mut reserve_1 = pool.load_reserve(&e, &underlying_1); - reserve_1.backstop_credit += 2_5000000; - reserve_1.store(&e); - let result = create_interest_auction_data(&e, &backstop_address); + let result = create_interest_auction_data(&e, &backstop_address); assert_eq!(result.block, 51); assert_eq!(result.bid.get_unchecked(usdc_id), 42_0000000); assert_eq!(result.bid.len(), 1); @@ -288,8 +283,10 @@ mod tests { 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.b_rate = 1_100_000_000; reserve_data_0.last_time = 12345; + reserve_data_0.backstop_credit = 100_0000000; + reserve_data_0.b_supply = 1000_0000000; + reserve_data_0.d_supply = 750_0000000; reserve_config_0.index = 0; testutils::create_reserve( &e, @@ -301,8 +298,10 @@ mod tests { 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.b_rate = 1_100_000_000; reserve_data_1.last_time = 12345; + reserve_data_1.backstop_credit = 25_0000000; + reserve_data_1.b_supply = 250_0000000; + reserve_data_1.d_supply = 187_5000000; reserve_config_1.index = 1; testutils::create_reserve( &e, @@ -314,7 +313,6 @@ mod tests { 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( @@ -348,15 +346,8 @@ mod tests { }; e.as_contract(&pool_address, || { storage::set_pool_config(&e, &pool_config); - let pool = Pool::load(&e); - let mut reserve_0 = pool.load_reserve(&e, &underlying_0); - reserve_0.backstop_credit += 100_0000000; - reserve_0.store(&e); - let mut reserve_1 = pool.load_reserve(&e, &underlying_1); - reserve_1.backstop_credit += 25_0000000; - reserve_1.store(&e); - let result = create_interest_auction_data(&e, &backstop_address); + let result = create_interest_auction_data(&e, &backstop_address); assert_eq!(result.block, 51); assert_eq!(result.bid.get_unchecked(usdc_id), 420_0000000); assert_eq!(result.bid.len(), 1); @@ -400,8 +391,10 @@ mod tests { 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.b_rate = 1_100_000_000; reserve_data_0.last_time = 11845; + reserve_data_0.backstop_credit = 100_0000000; + reserve_data_0.b_supply = 1000_0000000; + reserve_data_0.d_supply = 750_0000000; reserve_config_0.index = 0; testutils::create_reserve( &e, @@ -413,8 +406,10 @@ mod tests { 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.b_rate = 1_100_000_000; reserve_data_1.last_time = 11845; + reserve_data_1.backstop_credit = 25_0000000; + reserve_data_1.b_supply = 250_0000000; + reserve_data_1.d_supply = 187_5000000; reserve_config_1.index = 1; testutils::create_reserve( &e, @@ -426,7 +421,6 @@ mod tests { 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 = 11845; reserve_config_2.index = 2; testutils::create_reserve( @@ -461,21 +455,13 @@ mod tests { e.as_contract(&pool_address, || { storage::set_pool_config(&e, &pool_config); - let pool = Pool::load(&e); - let mut reserve_0 = pool.load_reserve(&e, &underlying_0); - reserve_0.backstop_credit += 100_0000000; - reserve_0.store(&e); - let mut reserve_1 = pool.load_reserve(&e, &underlying_1); - reserve_1.backstop_credit += 25_0000000; - reserve_1.store(&e); - let result = create_interest_auction_data(&e, &backstop_address); assert_eq!(result.block, 151); - assert_eq!(result.bid.get_unchecked(usdc_id), 420_0009794); + assert_eq!(result.bid.get_unchecked(usdc_id), 420_0012936); assert_eq!(result.bid.len(), 1); - assert_eq!(result.lot.get_unchecked(underlying_0), 100_0000066); - assert_eq!(result.lot.get_unchecked(underlying_1), 25_0000066); - assert_eq!(result.lot.get_unchecked(underlying_2), 66); + assert_eq!(result.lot.get_unchecked(underlying_0), 100_0000714); + assert_eq!(result.lot.get_unchecked(underlying_1), 25_0000178); + assert_eq!(result.lot.get_unchecked(underlying_2), 71); assert_eq!(result.lot.len(), 3); }); } diff --git a/pool/src/auctions/bad_debt_auction.rs b/pool/src/auctions/bad_debt_auction.rs index 5bdb7d43..c758d3c2 100644 --- a/pool/src/auctions/bad_debt_auction.rs +++ b/pool/src/auctions/bad_debt_auction.rs @@ -29,7 +29,7 @@ pub fn create_bad_debt_auction_data(e: &Env, backstop: &Address) -> AuctionData for (reserve_index, liability_balance) in backstop_positions.liabilities.iter() { let res_asset_address = reserve_list.get_unchecked(reserve_index); if liability_balance > 0 { - let reserve = pool.load_reserve(e, &res_asset_address); + let reserve = pool.load_reserve(e, &res_asset_address, false); let asset_to_base = pool.load_price(e, &res_asset_address); let asset_balance = reserve.to_asset_from_d_token(liability_balance); debt_value += i128(asset_to_base) diff --git a/pool/src/errors.rs b/pool/src/errors.rs index 645ca2bc..fd580d05 100644 --- a/pool/src/errors.rs +++ b/pool/src/errors.rs @@ -19,6 +19,7 @@ pub enum PoolError { InvalidPoolStatus = 11, InvalidUtilRate = 12, MaxPositionsExceeded = 13, + InternalReserveNotFound = 14, // Emission Errors (20-29) EmissionFailure = 20, // Oracle Errors (30-39) diff --git a/pool/src/pool/actions.rs b/pool/src/pool/actions.rs index 23e23acf..34cced60 100644 --- a/pool/src/pool/actions.rs +++ b/pool/src/pool/actions.rs @@ -117,12 +117,11 @@ pub fn build_actions_from_request( pool.require_action_allowed(e, request.request_type); match RequestType::from_u32(e, request.request_type) { RequestType::Supply => { - // supply - let mut reserve = pool.load_reserve(e, &request.address); + let mut reserve = pool.load_reserve(e, &request.address, true); let b_tokens_minted = reserve.to_b_token_down(request.amount); from_state.add_supply(e, &mut reserve, b_tokens_minted); actions.add_for_spender_transfer(&reserve.asset, request.amount); - pool.cache_reserve(reserve, true); + pool.cache_reserve(reserve); e.events().publish( ( Symbol::new(e, "supply"), @@ -133,8 +132,7 @@ pub fn build_actions_from_request( ); } RequestType::Withdraw => { - // withdraw - let mut reserve = pool.load_reserve(e, &request.address); + let mut reserve = pool.load_reserve(e, &request.address, true); let cur_b_tokens = from_state.get_supply(reserve.index); let mut to_burn = reserve.to_b_token_up(request.amount); let mut tokens_out = request.amount; @@ -144,7 +142,7 @@ pub fn build_actions_from_request( } from_state.remove_supply(e, &mut reserve, to_burn); actions.add_for_pool_transfer(&reserve.asset, tokens_out); - pool.cache_reserve(reserve, true); + pool.cache_reserve(reserve); e.events().publish( ( Symbol::new(e, "withdraw"), @@ -155,12 +153,11 @@ pub fn build_actions_from_request( ); } RequestType::SupplyCollateral => { - // supply collateral - let mut reserve = pool.load_reserve(e, &request.address); + let mut reserve = pool.load_reserve(e, &request.address, true); let b_tokens_minted = reserve.to_b_token_down(request.amount); from_state.add_collateral(e, &mut reserve, b_tokens_minted); actions.add_for_spender_transfer(&reserve.asset, request.amount); - pool.cache_reserve(reserve, true); + pool.cache_reserve(reserve); e.events().publish( ( Symbol::new(e, "supply_collateral"), @@ -171,8 +168,7 @@ pub fn build_actions_from_request( ); } RequestType::WithdrawCollateral => { - // withdraw collateral - let mut reserve = pool.load_reserve(e, &request.address); + let mut reserve = pool.load_reserve(e, &request.address, true); let cur_b_tokens = from_state.get_collateral(reserve.index); let mut to_burn = reserve.to_b_token_up(request.amount); let mut tokens_out = request.amount; @@ -183,7 +179,7 @@ pub fn build_actions_from_request( from_state.remove_collateral(e, &mut reserve, to_burn); actions.add_for_pool_transfer(&reserve.asset, tokens_out); check_health = true; - pool.cache_reserve(reserve, true); + pool.cache_reserve(reserve); e.events().publish( ( Symbol::new(e, "withdraw_collateral"), @@ -194,14 +190,13 @@ pub fn build_actions_from_request( ); } RequestType::Borrow => { - // borrow - let mut reserve = pool.load_reserve(e, &request.address); + let mut reserve = pool.load_reserve(e, &request.address, true); let d_tokens_minted = reserve.to_d_token_up(request.amount); from_state.add_liabilities(e, &mut reserve, d_tokens_minted); reserve.require_utilization_below_max(e); actions.add_for_pool_transfer(&reserve.asset, request.amount); check_health = true; - pool.cache_reserve(reserve, true); + pool.cache_reserve(reserve); e.events().publish( ( Symbol::new(e, "borrow"), @@ -212,8 +207,7 @@ pub fn build_actions_from_request( ); } RequestType::Repay => { - // repay - let mut reserve = pool.load_reserve(e, &request.address); + let mut reserve = pool.load_reserve(e, &request.address, true); let cur_d_tokens = from_state.get_liabilities(reserve.index); let d_tokens_burnt = reserve.to_d_token_down(request.amount); actions.add_for_spender_transfer(&reserve.asset, request.amount); @@ -242,10 +236,9 @@ pub fn build_actions_from_request( (request.amount, d_tokens_burnt), ); } - pool.cache_reserve(reserve, true); + pool.cache_reserve(reserve); } RequestType::FillUserLiquidationAuction => { - // fill user liquidation auction auctions::fill( e, pool, @@ -266,7 +259,6 @@ pub fn build_actions_from_request( ); } RequestType::FillBadDebtAuction => { - // fill bad debt auction // Note: will fail if input address is not the backstop since there cannot be a bad debt auction for a different address in storage auctions::fill( e, @@ -288,7 +280,6 @@ pub fn build_actions_from_request( ); } RequestType::FillInterestAuction => { - // fill interest auction // Note: will fail if input address is not the backstop since there cannot be an interest auction for a different address in storage auctions::fill( e, @@ -308,7 +299,6 @@ pub fn build_actions_from_request( ); } RequestType::DeleteLiquidationAuction => { - // delete liquidation auction // Note: request object is ignored besides type auctions::delete_liquidation(e, &from); check_health = true; @@ -409,7 +399,7 @@ mod tests { assert_eq!(positions.supply.len(), 1); assert_eq!(user.get_supply(0), 10_1234488); - let reserve = pool.load_reserve(&e, &underlying); + let reserve = pool.load_reserve(&e, &underlying, false); assert_eq!(reserve.b_supply, reserve_data.b_supply + user.get_supply(0)); }); } @@ -482,7 +472,7 @@ mod tests { assert_eq!(positions.supply.len(), 1); assert_eq!(user.get_supply(0), 9_8765502); - let reserve = pool.load_reserve(&e, &underlying); + let reserve = pool.load_reserve(&e, &underlying, false); assert_eq!( reserve.b_supply, reserve_data.b_supply - (20_0000000 - 9_8765502) @@ -554,7 +544,7 @@ mod tests { assert_eq!(positions.collateral.len(), 0); assert_eq!(positions.supply.len(), 0); - let reserve = pool.load_reserve(&e, &underlying.clone()); + let reserve = pool.load_reserve(&e, &underlying.clone(), false); assert_eq!(reserve.b_supply, reserve_data.b_supply - 20_0000000); }); } @@ -623,7 +613,7 @@ mod tests { assert_eq!(positions.supply.len(), 0); assert_eq!(user.get_collateral(0), 10_1234488); - let reserve = pool.load_reserve(&e, &underlying.clone()); + let reserve = pool.load_reserve(&e, &underlying.clone(), false); assert_eq!( reserve.b_supply, reserve_data.b_supply + user.get_collateral(0) @@ -698,7 +688,7 @@ mod tests { assert_eq!(positions.supply.len(), 0); assert_eq!(user.get_collateral(0), 9_8765502); - let reserve = pool.load_reserve(&e, &underlying); + let reserve = pool.load_reserve(&e, &underlying, false); assert_eq!( reserve.b_supply, reserve_data.b_supply - (20_0000000 - 9_8765502) @@ -770,7 +760,7 @@ mod tests { assert_eq!(positions.collateral.len(), 0); assert_eq!(positions.supply.len(), 0); - let reserve = pool.load_reserve(&e, &underlying); + let reserve = pool.load_reserve(&e, &underlying, false); assert_eq!(reserve.b_supply, reserve_data.b_supply - 20_0000000); }); } @@ -834,7 +824,7 @@ mod tests { assert_eq!(positions.supply.len(), 0); assert_eq!(user.get_liabilities(0), 10_1234452); - let reserve = pool.load_reserve(&e, &underlying); + let reserve = pool.load_reserve(&e, &underlying, false); assert_eq!(reserve.d_supply, reserve_data.d_supply + 10_1234452); }); } @@ -910,7 +900,7 @@ mod tests { let d_tokens_repaid = 10_1234451; assert_eq!(user.get_liabilities(0), 20_0000000 - d_tokens_repaid); - let reserve = pool.load_reserve(&e, &underlying); + let reserve = pool.load_reserve(&e, &underlying, false); assert_eq!(reserve.d_supply, reserve_data.d_supply - d_tokens_repaid); }); } @@ -983,7 +973,7 @@ mod tests { assert_eq!(positions.collateral.len(), 0); assert_eq!(positions.supply.len(), 0); - let reserve = pool.load_reserve(&e, &underlying); + let reserve = pool.load_reserve(&e, &underlying, false); assert_eq!(reserve.d_supply, reserve_data.d_supply - 20_0000000); }); } diff --git a/pool/src/pool/bad_debt.rs b/pool/src/pool/bad_debt.rs index 99fa806f..432a3d1a 100644 --- a/pool/src/pool/bad_debt.rs +++ b/pool/src/pool/bad_debt.rs @@ -35,10 +35,10 @@ pub fn transfer_bad_debt_to_backstop(e: &Env, user: &Address) { let mut new_backstop_state = backstop_state.clone(); for (reserve_index, liability_balance) in user_state.positions.liabilities.iter() { let asset = reserve_list.get_unchecked(reserve_index); - let mut reserve = pool.load_reserve(e, &asset); + let mut reserve = pool.load_reserve(e, &asset, true); new_backstop_state.add_liabilities(e, &mut reserve, liability_balance); new_user_state.remove_liabilities(e, &mut reserve, liability_balance); - pool.cache_reserve(reserve, true); + pool.cache_reserve(reserve); e.events().publish( (Symbol::new(e, "bad_debt"), user), diff --git a/pool/src/pool/config.rs b/pool/src/pool/config.rs index d1533cf7..6a90e49d 100644 --- a/pool/src/pool/config.rs +++ b/pool/src/pool/config.rs @@ -103,8 +103,9 @@ fn initialize_reserve(e: &Env, asset: &Address, config: &ReserveConfig) -> u32 { // if reserve already exists, ensure index and scalar do not change if storage::has_res(e, asset) { // accrue and store reserve data to the ledger - let pool = Pool::load(e); - let mut reserve = pool.load_reserve(e, asset); + let mut pool = Pool::load(e); + // @dev: Store the reserve to ledger manually + let mut reserve = pool.load_reserve(e, asset, false); index = reserve.index; let reserve_config = storage::get_res_config(e, asset); // decimals cannot change diff --git a/pool/src/pool/health_factor.rs b/pool/src/pool/health_factor.rs index 5d376402..d68f1664 100644 --- a/pool/src/pool/health_factor.rs +++ b/pool/src/pool/health_factor.rs @@ -38,7 +38,7 @@ impl PositionData { if b_token_balance == 0 && d_token_balance == 0 { continue; } - let reserve = pool.load_reserve(e, &reserve_list.get_unchecked(i)); + let reserve = pool.load_reserve(e, &reserve_list.get_unchecked(i), false); let asset_to_base = pool.load_price(e, &reserve.asset); if b_token_balance > 0 { @@ -69,7 +69,7 @@ impl PositionData { .unwrap_optimized(); } - pool.cache_reserve(reserve, false); + pool.cache_reserve(reserve); } PositionData { diff --git a/pool/src/pool/pool.rs b/pool/src/pool/pool.rs index bdb8eb11..679013c9 100644 --- a/pool/src/pool/pool.rs +++ b/pool/src/pool/pool.rs @@ -36,29 +36,34 @@ impl Pool { /// /// ### Arguments /// * asset - The address of the underlying asset - pub fn load_reserve(&self, e: &Env, asset: &Address) -> Reserve { + /// * store - If the reserve is expected to be stored to the ledger + pub fn load_reserve(&mut self, e: &Env, asset: &Address, store: bool) -> Reserve { + if store && !self.reserves_to_store.contains(asset) { + self.reserves_to_store.push_back(asset.clone()); + } + if let Some(reserve) = self.reserves.get(asset.clone()) { return reserve; + } else { + Reserve::load(e, &self.config, asset) } - Reserve::load(e, &self.config, asset) } /// Cache the updated reserve in the pool. /// /// ### Arguments /// * reserve - The updated reserve - /// * write - If the reserve needs to be written to the ledger - pub fn cache_reserve(&mut self, reserve: Reserve, write: bool) { - if !self.reserves_to_store.contains(&reserve.asset) && write { - self.reserves_to_store.push_back(reserve.asset.clone()); - } + pub fn cache_reserve(&mut self, reserve: Reserve) { self.reserves.set(reserve.asset.clone(), reserve); } /// Store the cached reserves to the ledger that need to be written. pub fn store_cached_reserves(&self, e: &Env) { for address in self.reserves_to_store.iter() { - let reserve = self.reserves.get_unchecked(address); + let reserve = self + .reserves + .get(address) + .unwrap_or_else(|| panic_with_error!(e, PoolError::InternalReserveNotFound)); reserve.store(e); } } @@ -171,8 +176,8 @@ mod tests { e.as_contract(&pool, || { storage::set_pool_config(&e, &pool_config); let mut pool = Pool::load(&e); - let reserve = pool.load_reserve(&e, &underlying); - pool.cache_reserve(reserve.clone(), true); + let reserve = pool.load_reserve(&e, &underlying, true); + pool.cache_reserve(reserve.clone()); // delete the reserve data from the ledger to ensure it is loaded from the cache storage::set_res_data( @@ -189,7 +194,7 @@ mod tests { }, ); - let new_reserve = pool.load_reserve(&e, &underlying); + let new_reserve = pool.load_reserve(&e, &underlying, true); assert_eq!(new_reserve.d_rate, reserve.d_rate); // store all cached reserves and verify the data is updated @@ -219,14 +224,19 @@ mod tests { let pool = testutils::create_pool(&e); let oracle = Address::generate(&e); - let (underlying, _) = testutils::create_token_contract(&e, &bombadil); - let (reserve_config, reserve_data) = testutils::default_reserve_meta(); - testutils::create_reserve(&e, &pool, &underlying, &reserve_config, &reserve_data); + let (underlying_0, _) = testutils::create_token_contract(&e, &bombadil); + let (mut reserve_config, mut reserve_data) = testutils::default_reserve_meta(); + testutils::create_reserve(&e, &pool, &underlying_0, &reserve_config, &reserve_data); - let mut reserve_1 = testutils::default_reserve(&e); - reserve_1.index = 1; - let mut reserve_2 = testutils::default_reserve(&e); - reserve_2.index = 2; + let (underlying_1, _) = testutils::create_token_contract(&e, &bombadil); + reserve_config.index = 1; + reserve_data.d_rate = 1_001_000_000; + testutils::create_reserve(&e, &pool, &underlying_1, &reserve_config, &reserve_data); + + let (underlying_2, _) = testutils::create_token_contract(&e, &bombadil); + reserve_config.index = 2; + reserve_data.d_rate = 1_002_000_000; + testutils::create_reserve(&e, &pool, &underlying_2, &reserve_config, &reserve_data); let pool_config = PoolConfig { oracle, @@ -237,20 +247,25 @@ mod tests { e.as_contract(&pool, || { storage::set_pool_config(&e, &pool_config); let mut pool = Pool::load(&e); - pool.cache_reserve(reserve_2.clone(), true); - pool.cache_reserve(reserve_1.clone(), true); + let reserve_0 = pool.load_reserve(&e, &underlying_0, false); + let mut reserve_1 = pool.load_reserve(&e, &underlying_1, true); + let mut reserve_2 = pool.load_reserve(&e, &underlying_2, true); + reserve_2.d_rate = 456; + pool.cache_reserve(reserve_0.clone()); + pool.cache_reserve(reserve_1.clone()); + pool.cache_reserve(reserve_2.clone()); // verify a duplicate cache takes the most recently cached reserve_1.d_rate = 123; - pool.cache_reserve(reserve_1.clone(), true); + pool.cache_reserve(reserve_1.clone()); - let reserve = pool.load_reserve(&e, &underlying); - pool.cache_reserve(reserve.clone(), false); + // verify reloading without store flag still stores reserve + let _ = pool.load_reserve(&e, &underlying_2, false); // delete the reserve data from the ledger to ensure it is loaded from the cache storage::set_res_data( &e, - &underlying, + &underlying_0, &ReserveData { b_rate: 0, d_rate: 0, @@ -262,17 +277,74 @@ mod tests { }, ); - let new_reserve = pool.load_reserve(&e, &underlying); - assert_eq!(new_reserve.d_rate, reserve.d_rate); + let new_reserve = pool.load_reserve(&e, &underlying_0, false); + assert_eq!(new_reserve.d_rate, reserve_0.d_rate); - // store all cached reserves and verify the unmarked one was not updated + // store all cached reserves and verify the temp one was not stored pool.store_cached_reserves(&e); - let new_reserve_data = storage::get_res_data(&e, &underlying); + let new_reserve_data = storage::get_res_data(&e, &underlying_0); assert_eq!(new_reserve_data.d_rate, 0); let new_reserve_data = storage::get_res_data(&e, &reserve_1.asset); assert_eq!(new_reserve_data.d_rate, 123); let new_reserve_data = storage::get_res_data(&e, &reserve_2.asset); - assert_eq!(new_reserve_data.d_rate, reserve_2.d_rate); + assert_eq!(new_reserve_data.d_rate, 456); + }); + } + + #[test] + #[should_panic(expected = "Error(Contract, #14)")] + fn test_reserve_cache_panics_if_missing_reserve_to_store() { + let e = Env::default(); + e.mock_all_auths(); + + e.ledger().set(LedgerInfo { + timestamp: 123456 * 5, + protocol_version: 20, + sequence_number: 123456, + 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 pool = testutils::create_pool(&e); + let oracle = Address::generate(&e); + + let (underlying_0, _) = testutils::create_token_contract(&e, &bombadil); + let (mut reserve_config, mut reserve_data) = testutils::default_reserve_meta(); + testutils::create_reserve(&e, &pool, &underlying_0, &reserve_config, &reserve_data); + + let (underlying_1, _) = testutils::create_token_contract(&e, &bombadil); + reserve_config.index = 1; + reserve_data.d_rate = 1_001_000_000; + testutils::create_reserve(&e, &pool, &underlying_1, &reserve_config, &reserve_data); + + let (underlying_2, _) = testutils::create_token_contract(&e, &bombadil); + reserve_config.index = 2; + reserve_data.d_rate = 1_002_000_000; + testutils::create_reserve(&e, &pool, &underlying_2, &reserve_config, &reserve_data); + + let pool_config = PoolConfig { + oracle, + bstop_rate: 0_200_000_000, + status: 0, + max_positions: 2, + }; + e.as_contract(&pool, || { + storage::set_pool_config(&e, &pool_config); + let mut pool = Pool::load(&e); + let reserve_0 = pool.load_reserve(&e, &underlying_0, false); + let mut reserve_1 = pool.load_reserve(&e, &underlying_1, true); + let mut reserve_2 = pool.load_reserve(&e, &underlying_2, true); + reserve_1.b_rate = 123; + reserve_2.d_rate = 456; + pool.cache_reserve(reserve_0.clone()); + pool.cache_reserve(reserve_1.clone()); + // pool.cache_reserve(reserve_2.clone()); + + pool.store_cached_reserves(&e); }); } diff --git a/pool/src/pool/user.rs b/pool/src/pool/user.rs index d7ba61ee..e81f818b 100644 --- a/pool/src/pool/user.rs +++ b/pool/src/pool/user.rs @@ -158,14 +158,14 @@ impl User { liability_amounts: Map, ) { for (asset, amount) in collateral_amounts.iter() { - let mut reserve = pool.load_reserve(e, &asset); + let mut reserve = pool.load_reserve(e, &asset, true); self.remove_collateral(e, &mut reserve, amount); - pool.cache_reserve(reserve, true); + pool.cache_reserve(reserve); } for (asset, amount) in liability_amounts.iter() { - let mut reserve = pool.load_reserve(e, &asset); + let mut reserve = pool.load_reserve(e, &asset, true); self.remove_liabilities(e, &mut reserve, amount); - pool.cache_reserve(reserve, true); + pool.cache_reserve(reserve); } } @@ -178,14 +178,14 @@ impl User { liability_amounts: Map, ) { for (asset, amount) in collateral_amounts.iter() { - let mut reserve = pool.load_reserve(e, &asset); + let mut reserve = pool.load_reserve(e, &asset, true); self.add_collateral(e, &mut reserve, amount); - pool.cache_reserve(reserve, true); + pool.cache_reserve(reserve); } for (asset, amount) in liability_amounts.iter() { - let mut reserve = pool.load_reserve(e, &asset); + let mut reserve = pool.load_reserve(e, &asset, true); self.add_liabilities(e, &mut reserve, amount); - pool.cache_reserve(reserve, true); + pool.cache_reserve(reserve); } } diff --git a/pool/src/testutils.rs b/pool/src/testutils.rs index db27867b..a5007ba5 100644 --- a/pool/src/testutils.rs +++ b/pool/src/testutils.rs @@ -256,7 +256,7 @@ pub(crate) fn create_reserve( .d_supply .fixed_mul_floor(reserve_data.d_rate, SCALAR_9) .unwrap_optimized(); - let to_mint_pool = total_supply - total_liabilities - reserve_data.backstop_credit; + let to_mint_pool = total_supply - total_liabilities + reserve_data.backstop_credit; underlying_client .mock_all_auths() .mint(&pool_address, &to_mint_pool);