From 6556498dbeebbd22a39831750db04c349b952909 Mon Sep 17 00:00:00 2001 From: markuspluna <59978114+markuspluna@users.noreply.github.com> Date: Thu, 14 Dec 2023 16:19:33 -0800 Subject: [PATCH] Pool: cleaned status code --- pool/src/contract.rs | 8 +- pool/src/errors.rs | 1 + pool/src/pool/mod.rs | 4 +- pool/src/pool/pool.rs | 14 +-- pool/src/pool/status.rs | 224 +++++++++++++++++++++++++--------------- 5 files changed, 159 insertions(+), 92 deletions(-) diff --git a/pool/src/contract.rs b/pool/src/contract.rs index 6c77851f..eb7f1dfc 100644 --- a/pool/src/contract.rs +++ b/pool/src/contract.rs @@ -310,7 +310,7 @@ impl Pool for PoolContract { fn update_status(e: Env) -> u32 { storage::extend_instance(&e); - let new_status = pool::execute_update_pool_status(&e, 11); //status input is not used here + let new_status = pool::execute_update_pool_status(&e); e.events() .publish((Symbol::new(&e, "set_status"),), new_status); @@ -319,7 +319,11 @@ impl Pool for PoolContract { fn set_status(e: Env, pool_status: u32) { storage::extend_instance(&e); - pool::execute_update_pool_status(&e, pool_status); + let admin = storage::get_admin(&e); + admin.require_auth(); + pool::execute_set_pool_status(&e, pool_status); + e.events() + .publish((Symbol::new(&e, "set_status"), admin), pool_status); } /********* Emission Functions **********/ diff --git a/pool/src/errors.rs b/pool/src/errors.rs index a3d83b5f..6c89b029 100644 --- a/pool/src/errors.rs +++ b/pool/src/errors.rs @@ -12,6 +12,7 @@ pub enum PoolError { NegativeAmount = 4, InvalidPoolInitArgs = 5, InvalidReserveMetadata = 6, + StatusNotAllowed = 8, // Pool State Errors (10-19) InvalidHf = 10, InvalidPoolStatus = 11, diff --git a/pool/src/pool/mod.rs b/pool/src/pool/mod.rs index d501510a..83adf8e4 100644 --- a/pool/src/pool/mod.rs +++ b/pool/src/pool/mod.rs @@ -29,4 +29,6 @@ mod user; pub use user::{Positions, User}; mod status; -pub use status::{calc_pool_backstop_threshold, execute_update_pool_status}; +pub use status::{ + calc_pool_backstop_threshold, execute_set_pool_status, execute_update_pool_status, +}; diff --git a/pool/src/pool/pool.rs b/pool/src/pool/pool.rs index b621585a..f1cef37d 100644 --- a/pool/src/pool/pool.rs +++ b/pool/src/pool/pool.rs @@ -267,7 +267,7 @@ mod tests { let pool_config = PoolConfig { oracle, bstop_rate: 0_200_000_000, - status: 3, + status: 2, }; e.as_contract(&pool, || { storage::set_pool_config(&e, &pool_config); @@ -286,7 +286,7 @@ mod tests { let pool_config = PoolConfig { oracle, bstop_rate: 0_200_000_000, - status: 0, + status: 1, }; e.as_contract(&pool, || { storage::set_pool_config(&e, &pool_config); @@ -306,7 +306,7 @@ mod tests { let pool_config = PoolConfig { oracle, bstop_rate: 0_200_000_000, - status: 3, + status: 2, }; e.as_contract(&pool, || { storage::set_pool_config(&e, &pool_config); @@ -325,7 +325,7 @@ mod tests { let pool_config = PoolConfig { oracle, bstop_rate: 0_200_000_000, - status: 0, + status: 1, }; e.as_contract(&pool, || { storage::set_pool_config(&e, &pool_config); @@ -345,7 +345,7 @@ mod tests { let pool_config = PoolConfig { oracle, bstop_rate: 0_200_000_000, - status: 5, + status: 4, }; e.as_contract(&pool, || { storage::set_pool_config(&e, &pool_config); @@ -365,7 +365,7 @@ mod tests { let pool_config = PoolConfig { oracle, bstop_rate: 0_200_000_000, - status: 5, + status: 4, }; e.as_contract(&pool, || { storage::set_pool_config(&e, &pool_config); @@ -384,7 +384,7 @@ mod tests { let pool_config = PoolConfig { oracle, bstop_rate: 0_200_000_000, - status: 2, + status: 4, }; e.as_contract(&pool, || { storage::set_pool_config(&e, &pool_config); diff --git a/pool/src/pool/status.rs b/pool/src/pool/status.rs index 3541c76c..283d8da0 100644 --- a/pool/src/pool/status.rs +++ b/pool/src/pool/status.rs @@ -3,13 +3,12 @@ use crate::{ dependencies::{BackstopClient, PoolBackstopData}, storage, PoolError, }; -use soroban_sdk::{panic_with_error, Env, Symbol}; +use soroban_sdk::{panic_with_error, Env}; /// Update the pool status based on the backstop module -/// #[allow(clippy::zero_prefixed_literal)] #[allow(clippy::inconsistent_digit_grouping)] -pub fn execute_update_pool_status(e: &Env, pool_status: u32) -> u32 { +pub fn execute_update_pool_status(e: &Env) -> u32 { let mut pool_config = storage::get_pool_config(e); // check the pool has met minimum backstop deposits @@ -22,70 +21,89 @@ pub fn execute_update_pool_status(e: &Env, pool_status: u32) -> u32 { if threshold < SCALAR_7 { met_threshold = false; } - // even numbered statuses are admin controlled - if pool_status % 2 == 0 { - let admin = storage::get_admin(&e); - // admin auth required to set admin status' - admin.require_auth(); - match pool_status { - 0 => { - // Threshold must be met and q4w must be under 50% for the admin to set Active - if !met_threshold || pool_backstop_data.q4w_pct >= 0_5000000 { - panic_with_error!(e, PoolError::BadRequest); - } - // Admin Active - pool_config.status = 0; + + match pool_config.status { + // Admin frozen + 4 => { + // Admin frozen supersedes all other statuses + panic_with_error!(e, PoolError::StatusNotAllowed); + } + // Admin on-ice + 2 => { + if pool_backstop_data.q4w_pct >= 0_7500000 { + // Q4W over 75% freezes the pool + pool_config.status = 5; + } + } + // Admin active + 0 => { + if !met_threshold || pool_backstop_data.q4w_pct >= 0_5000000 { + // Q4w over 50% or being under threshold puts the pool on-ice + pool_config.status = 3; } - 2 => { - // Q4w must be under 75% for admin to set On-Ice - if pool_backstop_data.q4w_pct >= 0_7500000 { - panic_with_error!(e, PoolError::BadRequest); - } - // Admin On-Ice - pool_config.status = 2; + } + // Admin status isn't set + _ => { + if pool_backstop_data.q4w_pct >= 0_6000000 { + // Q4w over 60% freezes the pool + pool_config.status = 5; + } else if pool_backstop_data.q4w_pct >= 0_3000000 || !met_threshold { + // Q4w over 30% or being under threshold puts the pool on-ice + pool_config.status = 3; + } else { + // Backstop is healthy and the pool can be activated + pool_config.status = 1; } - 4 => { - // Admin can always freeze the pool - // Admin Frozen - pool_config.status = 4; + } + } + storage::set_pool_config(e, &pool_config); + pool_config.status +} + +/// Admin set the pool status +#[allow(clippy::zero_prefixed_literal)] +#[allow(clippy::inconsistent_digit_grouping)] +pub fn execute_set_pool_status(e: &Env, pool_status: u32) { + let mut pool_config = storage::get_pool_config(e); + + // check the pool has met minimum backstop deposits + let backstop_id = storage::get_backstop(e); + let backstop_client = BackstopClient::new(e, &backstop_id); + + let pool_backstop_data = backstop_client.pool_data(&e.current_contract_address()); + // Admins cannot set non-admin status' + if pool_status % 2 != 0 { + panic_with_error!(e, PoolError::BadRequest); + } + match pool_status { + 0 => { + // Threshold must be met and q4w must be under 50% for the admin to set Active + if calc_pool_backstop_threshold(&pool_backstop_data) < SCALAR_7 + || pool_backstop_data.q4w_pct >= 0_5000000 + { + panic_with_error!(e, PoolError::StatusNotAllowed); } - _ => { - panic_with_error!(e, PoolError::BadRequest); + // Admin Active + pool_config.status = 0; + } + 2 => { + // Q4w must be under 75% for admin to set On-Ice + if pool_backstop_data.q4w_pct >= 0_7500000 { + panic_with_error!(e, PoolError::StatusNotAllowed); } + // Admin On-Ice + pool_config.status = 2; + } + 4 => { + // Admin can always freeze the pool + // Admin Frozen + pool_config.status = 4; } - e.events() - .publish((Symbol::new(&e, "set_status"), admin), pool_status); - } else { - // Admin Frozen supersedes all other statuses - if pool_config.status == 4 { + _ => { panic_with_error!(e, PoolError::BadRequest); - // Q4W over 75% freezes the pool - // Q4W over 60% freezes the pool if it is not Admin On-Ice - } else if pool_backstop_data.q4w_pct >= 0_7500000 - || (pool_backstop_data.q4w_pct >= 0_6000000 && pool_config.status != 2) - { - // Frozen - pool_config.status = 5; - // Admin On-Ice supersedes backstop triggered on-ice - // Q4w over 50% puts the pool on-ice - // Threshold not being met puts the pool on-ice - // As long as pool isn't admin active q4w over 30% puts pool on-ice - } else if pool_config.status != 2 - && (pool_backstop_data.q4w_pct >= 0_5000000 - || !met_threshold - || (pool_config.status != 0 && pool_backstop_data.q4w_pct >= 0_3000000)) - { - // On-Ice - pool_config.status = 3; - // Admin status' all supersede backstop triggered active - } else if pool_config.status % 2 != 0 { - // Active - pool_config.status = 1; } } storage::set_pool_config(e, &pool_config); - - pool_config.status } /// Calculate the threshold for the pool's backstop balance @@ -170,7 +188,7 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - execute_update_pool_status(&e, 0); + execute_set_pool_status(&e, 0); let new_pool_config = storage::get_pool_config(&e); assert_eq!(new_pool_config.status, 0); @@ -178,7 +196,7 @@ mod tests { } #[test] - #[should_panic(expected = "Error(Contract, #2)")] + #[should_panic(expected = "Error(Contract, #8)")] fn test_set_pool_status_active_blocks_without_backstop_minimum() { let e = Env::default(); e.budget().reset_unlimited(); @@ -217,12 +235,12 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - execute_update_pool_status(&e, 0); + execute_set_pool_status(&e, 0); }); } #[test] - #[should_panic(expected = "Error(Contract, #2)")] + #[should_panic(expected = "Error(Contract, #8)")] fn test_set_pool_status_active_blocks_with_too_high_q4w() { let e = Env::default(); e.budget().reset_unlimited(); @@ -251,7 +269,7 @@ mod tests { ); backstop_client.deposit(&samwise, &pool_id, &50_000_0000000); backstop_client.update_tkn_val(); - backstop_client.withdraw(&samwise, &pool_id, &30_000_0000000); + backstop_client.queue_withdrawal(&samwise, &pool_id, &30_000_0000000); let pool_config = PoolConfig { oracle: oracle_id, @@ -262,7 +280,7 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - execute_update_pool_status(&e, 0); + execute_set_pool_status(&e, 0); }); } #[test] @@ -304,7 +322,7 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - execute_update_pool_status(&e, 2); + execute_set_pool_status(&e, 2); let new_pool_config = storage::get_pool_config(&e); assert_eq!(new_pool_config.status, 2); @@ -312,7 +330,7 @@ mod tests { } #[test] - #[should_panic(expected = "Error(Contract, #2)")] + #[should_panic(expected = "Error(Contract, #8)")] fn test_set_pool_status_on_ice_blocks_with_too_high_q4w() { let e = Env::default(); e.budget().reset_unlimited(); @@ -341,7 +359,7 @@ mod tests { ); backstop_client.deposit(&samwise, &pool_id, &50_000_0000000); backstop_client.update_tkn_val(); - backstop_client.withdraw(&samwise, &pool_id, &40_000_0000000); + backstop_client.queue_withdrawal(&samwise, &pool_id, &40_000_0000000); let pool_config = PoolConfig { oracle: oracle_id, @@ -352,7 +370,7 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - execute_update_pool_status(&e, 2); + execute_set_pool_status(&e, 2); }); } #[test] @@ -394,12 +412,55 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - execute_update_pool_status(&e, 4); + execute_set_pool_status(&e, 4); let new_pool_config = storage::get_pool_config(&e); assert_eq!(new_pool_config.status, 4); }); } + #[test] + #[should_panic(expected = "Error(Contract, #2)")] + fn test_set_non_admin_pool_status_panics() { + let e = Env::default(); + e.budget().reset_unlimited(); + e.mock_all_auths_allowing_non_root_auth(); + let pool_id = create_pool(&e); + let oracle_id = Address::generate(&e); + + let bombadil = Address::generate(&e); + let samwise = Address::generate(&e); + + let (blnd, blnd_client) = create_token_contract(&e, &bombadil); + let (usdc, usdc_client) = create_token_contract(&e, &bombadil); + let (lp_token, lp_token_client) = create_comet_lp_pool(&e, &bombadil, &blnd, &usdc); + let (backstop_id, backstop_client) = create_backstop(&e); + setup_backstop(&e, &pool_id, &backstop_id, &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_id, &50_000_0000000); + backstop_client.update_tkn_val(); + + let pool_config = PoolConfig { + oracle: oracle_id, + bstop_rate: 0, + status: 2, + }; + e.as_contract(&pool_id, || { + storage::set_admin(&e, &bombadil); + storage::set_pool_config(&e, &pool_config); + + execute_set_pool_status(&e, 1); + }); + } #[test] fn test_update_pool_status_active() { @@ -440,7 +501,7 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - let status = execute_update_pool_status(&e, 11); + let status = execute_update_pool_status(&e); let new_pool_config = storage::get_pool_config(&e); assert_eq!(new_pool_config.status, status); @@ -487,7 +548,7 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - let status = execute_update_pool_status(&e, 11); + let status = execute_update_pool_status(&e); let new_pool_config = storage::get_pool_config(&e); assert_eq!(new_pool_config.status, status); @@ -534,7 +595,7 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - let status = execute_update_pool_status(&e, 11); + let status = execute_update_pool_status(&e); let new_pool_config = storage::get_pool_config(&e); assert_eq!(new_pool_config.status, status); @@ -582,7 +643,7 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - let status = execute_update_pool_status(&e, 11); + let status = execute_update_pool_status(&e); let new_pool_config = storage::get_pool_config(&e); assert_eq!(new_pool_config.status, status); @@ -630,7 +691,7 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - let status = execute_update_pool_status(&e, 11); + let status = execute_update_pool_status(&e); let new_pool_config = storage::get_pool_config(&e); assert_eq!(new_pool_config.status, status); @@ -678,7 +739,7 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - let status = execute_update_pool_status(&e, 11); + let status = execute_update_pool_status(&e); let new_pool_config = storage::get_pool_config(&e); assert_eq!(new_pool_config.status, status); @@ -726,7 +787,7 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - let status = execute_update_pool_status(&e, 11); + let status = execute_update_pool_status(&e); let new_pool_config = storage::get_pool_config(&e); assert_eq!(new_pool_config.status, status); @@ -773,7 +834,7 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - let status = execute_update_pool_status(&e, 11); + let status = execute_update_pool_status(&e); let new_pool_config = storage::get_pool_config(&e); assert_eq!(new_pool_config.status, status); @@ -821,7 +882,7 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - let status = execute_update_pool_status(&e, 11); + let status = execute_update_pool_status(&e); let new_pool_config = storage::get_pool_config(&e); assert_eq!(new_pool_config.status, status); @@ -869,7 +930,7 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - execute_update_pool_status(&e, 11); + execute_update_pool_status(&e); }); } @@ -914,11 +975,10 @@ mod tests { storage::set_admin(&e, &bombadil); storage::set_pool_config(&e, &pool_config); - let status = execute_update_pool_status(&e, 0); + execute_set_pool_status(&e, 0); let new_pool_config = storage::get_pool_config(&e); - assert_eq!(new_pool_config.status, status); - assert_eq!(status, 0); + assert_eq!(new_pool_config.status, 0); }); }