diff --git a/Cargo.lock b/Cargo.lock index 4cc1d182..83d5a41e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4688,7 +4688,7 @@ dependencies = [ [[package]] name = "sommelier_steward" -version = "3.6.0" +version = "3.6.1" dependencies = [ "lazy_static", "steward", @@ -4779,7 +4779,7 @@ checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" [[package]] name = "steward" -version = "3.6.0" +version = "3.6.1" dependencies = [ "abscissa_core", "abscissa_tokio", @@ -4832,7 +4832,7 @@ dependencies = [ [[package]] name = "steward_abi" -version = "3.6.0" +version = "3.6.1" dependencies = [ "ethers", "serde", @@ -4852,7 +4852,7 @@ dependencies = [ [[package]] name = "steward_proto" -version = "3.6.0" +version = "3.6.1" dependencies = [ "prost 0.7.0", "prost-types 0.7.0", diff --git a/Cargo.toml b/Cargo.toml index 04b8b28f..32030085 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sommelier_steward" -version = "3.6.0" +version = "3.6.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/proto/cellar_v2.proto b/proto/cellar_v2.proto index 4903f75d..d798d113 100644 --- a/proto/cellar_v2.proto +++ b/proto/cellar_v2.proto @@ -396,7 +396,7 @@ message CellarV2_2 { } /* - * Updates the cellar to use the latest price router in the registry. + * Updates the cellar to use the latest price router in the registry. * * Represents function `cachePriceRouter(bool checkTotalAssets, uint16 allowableRange)` */ @@ -457,6 +457,8 @@ message CellarV2_5 { SetSharePriceOracle set_share_price_oracle = 18; // Represents function `cachePriceRouter(bool checkTotalAssets, uint16 allowableRange, address expectedPriceRouter)` CachePriceRouter cache_price_router = 19; + // Represents function `forcePositionOut(uint32 index, uint32 positionId, bool inDebtArray)` + ForcePositionOut force_position_out = 20; } } @@ -649,7 +651,7 @@ message CellarV2_5 { } /* - * Updates the cellar to use the latest price router in the registry. + * Updates the cellar to use the latest price router in the registry. * * Represents function `cachePriceRouter(bool checkTotalAssets, uint16 allowableRange, address expectedPriceRouter)` */ @@ -661,6 +663,20 @@ message CellarV2_5 { // The expected price router address string expected_price_router = 3; } + + /* + * Forceably remove a position from the cellar without checking its balance is zero. + * + * Represents function `forcePositionOut(uint32 index, uint32 positionId, bool inDebtArray)` + */ + message ForcePositionOut { + // Index of the position. + uint32 index = 1; + // Position ID to force out. + uint32 position_id = 2; + // Whether to switch positions in the debt array, or the credit array. + bool in_debt_array = 3; + } } // Represents a call to adaptor an. The cellar must be authorized to call the target adaptor. diff --git a/steward/Cargo.toml b/steward/Cargo.toml index 53449737..1dc46ea7 100644 --- a/steward/Cargo.toml +++ b/steward/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "steward" authors = [] -version = "3.6.0" +version = "3.6.1" edition = "2018" [dependencies] diff --git a/steward/src/cellars.rs b/steward/src/cellars.rs index b203d23a..da4aba89 100644 --- a/steward/src/cellars.rs +++ b/steward/src/cellars.rs @@ -24,20 +24,33 @@ pub(crate) mod cellar_v2_5; // oracles -pub const ORACLE1: (U256, &str) = ( +pub const TURBOSWETH_ORACLE1: (U256, &str) = ( U256([3, 0, 0, 0]), "72249f0199eacf6230def33a31e80cf76de78f67", ); -pub const ORACLE2: (U256, &str) = ( +pub const TURBOSWETH_ORACLE2: (U256, &str) = ( U256([5, 0, 0, 0]), "c47278b65443ce71cf47e8455bb343f2db11b70e", ); -pub const ORACLE3: (U256, &str) = ( +pub const TURBOSWETH_ORACLE3: (U256, &str) = ( U256([5, 0, 0, 0]), "26cde3f5db92ea91c84c838e664fe42dec1b6747", ); -pub const ALLOWED_PRICE_ORACLES: [(U256, &str); 3] = [ORACLE1, ORACLE2, ORACLE3]; +pub const ALLOWED_TURBOSWETH_PRICE_ORACLES: [(U256, &str); 3] = + [TURBOSWETH_ORACLE1, TURBOSWETH_ORACLE2, TURBOSWETH_ORACLE3]; + +pub const TURBOSOMM_ORACLE1: (U256, &str) = ( + U256([8, 0, 0, 0]), + "30510876b377941f23d7322845de0ca734da59e0", +); +pub const TURBOSOMM_ORACLE2: (U256, &str) = ( + U256([8, 0, 0, 0]), + "84785287f0c9c282462da7927aaed9773b32d9cb", +); + +pub const ALLOWED_TURBOSOMM_PRICE_ORACLES: [(U256, &str); 2] = + [TURBOSOMM_ORACLE1, TURBOSOMM_ORACLE2]; // price routers @@ -62,7 +75,8 @@ lazy_static! { pub const ALLOWED_V2_0_SETUP_ADAPTORS: [(&str, &str); 0] = []; pub const ALLOWED_V2_2_CATALOGUE_ADAPTORS: [(&str, &str); 0] = []; -pub const ALLOWED_V2_5_CATALOGUE_ADAPTORS: [(&str, &str); 0] = []; +pub const ALLOWED_V2_5_CATALOGUE_ADAPTORS: [(&str, &str); 1] = + [(CELLAR_TURBO_SOMM, ADAPTOR_VESTING_SIMPLE_V1_1_DEPLOYMENT2)]; // due to position size limits in v2.0, positions must be added and removed from the limited list // and thus approved positions need to be allowed to be re-added, hence this large list @@ -89,7 +103,7 @@ pub const ALLOWED_V2_0_POSITIONS: [(&str, u32); 20] = [ (CELLAR_RYUSD, 29), ]; pub const ALLOWED_V2_2_CATALOGUE_POSITIONS: [(&str, u32); 0] = []; -pub const ALLOWED_V2_5_CATALOGUE_POSITIONS: [(&str, u32); 0] = []; +pub const ALLOWED_V2_5_CATALOGUE_POSITIONS: [(&str, u32); 1] = [(CELLAR_TURBO_SOMM, 100000005)]; pub const BLOCKED_ADAPTORS: [&str; 3] = [ ADAPTOR_UNIV3_V1, @@ -136,6 +150,7 @@ pub const CELLAR_RYBTC: &str = "0274a704a6d9129f90a62ddc6f6024b33ecdad36"; pub const CELLAR_TURBO_SWETH: &str = "d33dad974b938744dac81fe00ac67cb5aa13958e"; pub const CELLAR_TURBO_GHO: &str = "0c190ded9be5f512bd72827bdad4003e9cc7975c"; pub const CELLAR_TURBO_STETH: &str = "fd6db5011b171b05e1ea3b92f9eacaeeb055e971"; +pub const CELLAR_TURBO_SOMM: &str = "5195222f69c5821f8095ec565e71e18ab6a2298f"; // deprecated adaptors @@ -157,7 +172,10 @@ pub const ADAPTOR_MORPHO_AAVE_V3_A_TOKEN_COLLATERAL_V1: &str = pub const ADAPTOR_MORPHO_AAVE_V3_DEBT_TOKEN_V1: &str = "25a61f771af9a38c10ddd93c2bbab39a88926fa9"; pub const ADAPTOR_MORPHO_AAVE_V3_P2P_V1: &str = "4fe068caad05b82bf3f86e1f7d1a7b8bbf516111"; pub const ADAPTOR_UNIV3_V3: &str = "92611574ec9bc13c6137917481dab7bb7b173c9b"; -pub const ADAPTOR_VESTING_SIMPLE_V2: &str = "3b98ba00f981342664969e609fb88280704ac479"; +pub const ADAPTOR_VESTING_SIMPLE_V1_1_DEPLOYMENT1: &str = + "3b98ba00f981342664969e609fb88280704ac479"; +pub const ADAPTOR_VESTING_SIMPLE_V1_1_DEPLOYMENT2: &str = + "8a95bbabb0039480f6dd90fe856c1e0c3d575aa1"; // utils @@ -229,13 +247,18 @@ pub fn validate_oracle( let cellar_id_normalized = normalize_address(cellar_id.to_string()); let oracle_in = normalize_address(oracle_in.to_string()); let registry_id_in = string_to_u256(registry_id_in.to_string())?; - if !cellar_id_normalized.eq(CELLAR_TURBO_SWETH) - || !ALLOWED_PRICE_ORACLES.contains(&(registry_id_in, oracle_in.as_str())) + if cellar_id_normalized.eq(CELLAR_TURBO_SWETH) + && ALLOWED_TURBOSWETH_PRICE_ORACLES.contains(&(registry_id_in, oracle_in.as_str())) + { + return Ok(()); + } + if cellar_id_normalized.eq(CELLAR_TURBO_SOMM) + && ALLOWED_TURBOSOMM_PRICE_ORACLES.contains(&(registry_id_in, oracle_in.as_str())) { - return Err(sp_call_error("unauthorized oracle update".to_string())); + return Ok(()); } - Ok(()) + Err(sp_call_error("unauthorized oracle update".to_string())) } pub fn validate_cache_price_router( @@ -262,6 +285,26 @@ pub fn validate_cache_price_router( Ok(()) } +pub fn validate_force_position_out( + cellar_id: &str, + index: u32, + position_id: u32, + in_debt_array: bool, +) -> Result<(), Error> { + let cellar_id_normalized = normalize_address(cellar_id.to_string()); + if cellar_id_normalized.eq(CELLAR_TURBO_SOMM) + && index > 0 // we expect it to be present + && position_id == 100000004 + && !in_debt_array + { + return Ok(()); + } + + Err(sp_call_error( + "force position out not authorized for cellar".to_string(), + )) +} + pub fn check_blocked_adaptor(adaptor_id: &str) -> Result<(), Error> { let adaptor_id = normalize_address(adaptor_id.to_string()); if BLOCKED_ADAPTORS.contains(&adaptor_id.as_str()) { @@ -457,12 +500,60 @@ mod tests { #[test] fn test_validate_oracle() { - assert!(validate_oracle(CELLAR_RYBTC, &ORACLE1.0.to_string(), ORACLE1.1).is_err()); - assert!( - validate_oracle(CELLAR_TURBO_SWETH, &U256::from(6).to_string(), ORACLE2.1).is_err() - ); - assert!(validate_oracle(CELLAR_TURBO_SWETH, &ORACLE1.0.to_string(), ORACLE1.1).is_ok()); - assert!(validate_oracle(CELLAR_TURBO_SWETH, &ORACLE2.0.to_string(), ORACLE2.1).is_ok()); + assert!(validate_oracle( + CELLAR_RYBTC, + &TURBOSWETH_ORACLE1.0.to_string(), + TURBOSWETH_ORACLE1.1 + ) + .is_err()); + assert!(validate_oracle( + CELLAR_RYBTC, + &TURBOSOMM_ORACLE1.0.to_string(), + TURBOSOMM_ORACLE1.1 + ) + .is_err()); + assert!(validate_oracle( + CELLAR_TURBO_SWETH, + &U256::from(6).to_string(), + TURBOSWETH_ORACLE2.1 + ) + .is_err()); + assert!(validate_oracle( + CELLAR_TURBO_SOMM, + &U256::from(6).to_string(), + TURBOSOMM_ORACLE2.1 + ) + .is_err()); + assert!(validate_oracle( + CELLAR_TURBO_SWETH, + &TURBOSWETH_ORACLE1.0.to_string(), + TURBOSWETH_ORACLE1.1 + ) + .is_ok()); + assert!(validate_oracle( + CELLAR_TURBO_SWETH, + &TURBOSWETH_ORACLE2.0.to_string(), + TURBOSWETH_ORACLE2.1 + ) + .is_ok()); + assert!(validate_oracle( + CELLAR_TURBO_SWETH, + &TURBOSWETH_ORACLE3.0.to_string(), + TURBOSWETH_ORACLE3.1 + ) + .is_ok()); + assert!(validate_oracle( + CELLAR_TURBO_SOMM, + &TURBOSOMM_ORACLE1.0.to_string(), + TURBOSOMM_ORACLE1.1 + ) + .is_ok()); + assert!(validate_oracle( + CELLAR_TURBO_SOMM, + &TURBOSOMM_ORACLE2.0.to_string(), + TURBOSOMM_ORACLE2.1 + ) + .is_ok()); } #[test] diff --git a/steward/src/cellars/cellar_v2_5.rs b/steward/src/cellars/cellar_v2_5.rs index efc7f2e5..185436d7 100644 --- a/steward/src/cellars/cellar_v2_5.rs +++ b/steward/src/cellars/cellar_v2_5.rs @@ -25,7 +25,8 @@ use crate::{ use super::{ check_blocked_adaptor, check_blocked_position, log_cellar_call, validate_cache_price_router, - validate_new_adaptor, validate_new_position, validate_oracle, V2_5_PERMISSIONS, + validate_force_position_out, validate_new_adaptor, validate_new_position, validate_oracle, + V2_5_PERMISSIONS, }; const CELLAR_NAME: &str = "CellarV2.5"; @@ -294,6 +295,26 @@ pub fn get_encoded_function(call: FunctionCall, cellar_id: String) -> Result { + validate_force_position_out( + &cellar_id, + params.index, + params.position_id, + params.in_debt_array, + )?; + log_cellar_call( + CELLAR_NAME, + &ForcePositionOutCall::function_name(), + &cellar_id, + ); + let call = ForcePositionOutCall { + index: params.index, + position_id: params.position_id, + in_debt_array: params.in_debt_array, + }; + + Ok(CellarV2_5Calls::ForcePositionOut(call).encode()) + } } } diff --git a/steward_abi/Cargo.toml b/steward_abi/Cargo.toml index 05c23e64..0cc96470 100644 --- a/steward_abi/Cargo.toml +++ b/steward_abi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steward_abi" -version = "3.6.0" +version = "3.6.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/steward_proto_rust/Cargo.toml b/steward_proto_rust/Cargo.toml index f89f282d..e03b5ec9 100644 --- a/steward_proto_rust/Cargo.toml +++ b/steward_proto_rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steward_proto" -version = "3.6.0" +version = "3.6.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/steward_proto_rust/src/prost/descriptor.bin b/steward_proto_rust/src/prost/descriptor.bin index 158c81bf..079d40fd 100644 Binary files a/steward_proto_rust/src/prost/descriptor.bin and b/steward_proto_rust/src/prost/descriptor.bin differ diff --git a/steward_proto_rust/src/prost/steward.v3.rs b/steward_proto_rust/src/prost/steward.v3.rs index 9b77d477..648475ae 100644 --- a/steward_proto_rust/src/prost/steward.v3.rs +++ b/steward_proto_rust/src/prost/steward.v3.rs @@ -3083,7 +3083,7 @@ pub mod cellar_v2_5 { pub struct FunctionCall { #[prost( oneof = "function_call::Function", - tags = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19" + tags = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20" )] pub function: ::core::option::Option, } @@ -3148,6 +3148,9 @@ pub mod cellar_v2_5 { /// Represents function `cachePriceRouter(bool checkTotalAssets, uint16 allowableRange, address expectedPriceRouter)` #[prost(message, tag = "19")] CachePriceRouter(super::CachePriceRouter), + /// Represents function `forcePositionOut(uint32 index, uint32 positionId, bool inDebtArray)` + #[prost(message, tag = "20")] + ForcePositionOut(super::ForcePositionOut), } } /// @@ -3359,6 +3362,22 @@ pub mod cellar_v2_5 { #[prost(string, tag = "3")] pub expected_price_router: ::prost::alloc::string::String, } + /// + /// Forceably remove a position from the cellar without checking its balance is zero. + /// + /// Represents function `forcePositionOut(uint32 index, uint32 positionId, bool inDebtArray)` + #[derive(serde::Deserialize, serde::Serialize, Clone, PartialEq, ::prost::Message)] + pub struct ForcePositionOut { + /// Index of the position. + #[prost(uint32, tag = "1")] + pub index: u32, + /// Position ID to force out. + #[prost(uint32, tag = "2")] + pub position_id: u32, + /// Whether to switch positions in the debt array, or the credit array. + #[prost(bool, tag = "3")] + pub in_debt_array: bool, + } #[derive(serde::Deserialize, serde::Serialize, Clone, PartialEq, ::prost::Oneof)] pub enum CallType { /// Represents a single function call