Skip to content

Commit

Permalink
cachePriceRouter call permissions (#228)
Browse files Browse the repository at this point in the history
* Add permissions for cachePriceRouter calls

* Bump to v3.6.0

* Check for range when validating cachePriceRouter call

* Bandaid broken test

* Fix validation check

* Add unit test for cachePriceRouter call validation

* Fmt and name fix

* Fmt

* Fix price router validation bug

* More unit tests
  • Loading branch information
cbrit authored Nov 1, 2023
1 parent f27ba5e commit 124d985
Show file tree
Hide file tree
Showing 13 changed files with 4,108 additions and 358 deletions.
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sommelier_steward"
version = "3.5.3"
version = "3.6.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
16 changes: 16 additions & 0 deletions proto/cellar_v2.proto
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,8 @@ message CellarV2_5 {
DecreaseShareSupplyCap decrease_share_supply_cap = 17;
// Represents function `setSharePriceOracle(uint256, address)
SetSharePriceOracle set_share_price_oracle = 18;
// Represents function `cachePriceRouter(bool checkTotalAssets, uint16 allowableRange, address expectedPriceRouter)`
CachePriceRouter cache_price_router = 19;
}
}

Expand Down Expand Up @@ -645,6 +647,20 @@ message CellarV2_5 {
// The oracle contract address
string oracle = 2;
}

/*
* Updates the cellar to use the latest price router in the registry.
*
* Represents function `cachePriceRouter(bool checkTotalAssets, uint16 allowableRange, address expectedPriceRouter)`
*/
message CachePriceRouter {
// Whether to check the total assets of the cellar
bool check_total_assets = 1;
// The allowable range of the cellar's total assets to deviate between old and new routers
uint32 allowable_range = 2;
// The expected price router address
string expected_price_router = 3;
}
}

// Represents a call to adaptor an. The cellar must be authorized to call the target adaptor.
Expand Down
2 changes: 1 addition & 1 deletion steward/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "steward"
authors = []
version = "3.5.3"
version = "3.6.0"
edition = "2018"

[dependencies]
Expand Down
99 changes: 78 additions & 21 deletions steward/src/cellars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ use std::result::Result;

use abscissa_core::tracing::log::info;
use ethers::prelude::*;
use lazy_static::lazy_static;

use crate::{
error::Error,
utils::{sp_call_error, string_to_u256},
};

pub type CachePriceRouterArgs = (bool, u32, Option<String>);

pub(crate) mod aave_v2_stablecoin;
pub(crate) mod adaptors;
pub(crate) mod cellar_v1;
Expand All @@ -35,15 +38,30 @@ pub const ORACLE3: (U256, &str) = (
);

pub const ALLOWED_PRICE_ORACLES: [(U256, &str); 3] = [ORACLE1, ORACLE2, ORACLE3];
pub const ALLOWED_CACHE_PRICE_ROUTER: [&str; 1] = [CELLAR_RYETH];

// price routers

// since we're wrapping price router addresses in as Option<T>, it vastly simplifies comparisons for them to be
// owned values (String) vs. a borrowed &str since we have to mutate the passed in values to normalize them.
lazy_static! {
pub static ref PRICEROUTER1: String = String::from("a1a0bc3d59e4ee5840c9530e49bdc2d1f88aaf92");
pub static ref PRICEROUTER2: String = String::from("8e46f30b09fdfae6c97db27fecf3304f86dd88c2");

// mapping of cellar address to allowable arguments for a cachePriceRouter call.
// args map to params as (checkTotalAssets, allowableRange, expectedPriceRouterAddress). The third
// param is only present for 2.5 cellars so it's an Option<&str>.
pub static ref ALLOWED_CACHE_PRICE_ROUTER: [(&'static str, CachePriceRouterArgs); 4] = [
(CELLAR_RYBTC, (true, 500, None)),
(CELLAR_RYETH, (true, 500, None)),
(CELLAR_TURBO_STETH, (false, 500, Some(PRICEROUTER1.clone()))),
(CELLAR_TURBO_STETH, (true, 500, Some(PRICEROUTER2.clone()))),
];
}

// permissions

pub const ALLOWED_V2_0_SETUP_ADAPTORS: [(&str, &str); 1] = [(CELLAR_RYUSD, ADAPTOR_CELLAR_V2)];
pub const ALLOWED_V2_2_CATALOGUE_ADAPTORS: [(&str, &str); 1] = [
// According to Joe RYBTC already has this adaptor in its catalogue
(CELLAR_RYETH, ADAPTOR_CELLAR_V2),
];
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] = [];

// due to position size limits in v2.0, positions must be added and removed from the limited list
Expand All @@ -70,11 +88,7 @@ pub const ALLOWED_V2_0_POSITIONS: [(&str, u32); 20] = [
(CELLAR_RYUSD, 28),
(CELLAR_RYUSD, 29),
];
pub const ALLOWED_V2_2_CATALOGUE_POSITIONS: [(&str, u32); 2] = [
// 199 is Turbo stETH position ID
(CELLAR_RYBTC, 199),
(CELLAR_RYETH, 199),
];
pub const ALLOWED_V2_2_CATALOGUE_POSITIONS: [(&str, u32); 0] = [];
pub const ALLOWED_V2_5_CATALOGUE_POSITIONS: [(&str, u32); 0] = [];

pub const BLOCKED_ADAPTORS: [&str; 3] = [
Expand Down Expand Up @@ -228,14 +242,20 @@ pub fn validate_cache_price_router(
cellar_id: &str,
check_total_assets_value: bool,
allowable_range_value: u32,
expected_price_router: Option<String>,
) -> Result<(), Error> {
if !check_total_assets_value || allowable_range_value != 500 {
return Err(sp_call_error(
"unauthorized arguments for cachePriceRouter call".to_string(),
));
}
let cellar_id_normalized = normalize_address(cellar_id.to_string());
if !ALLOWED_CACHE_PRICE_ROUTER.contains(&cellar_id_normalized.as_str()) {
let expected_price_router = expected_price_router.map(normalize_address);

if !ALLOWED_CACHE_PRICE_ROUTER
.iter()
.any(|(cellar_id, permissions)| {
cellar_id_normalized.eq(cellar_id)
&& permissions.0 == check_total_assets_value
&& permissions.1 >= allowable_range_value
&& permissions.2 == expected_price_router
})
{
return Err(sp_call_error("call not authorized for cellar".to_string()));
}

Expand Down Expand Up @@ -358,10 +378,10 @@ mod tests {

// allows approved cellar/adaptor ID pairs
let (v2_0_cellar_id, v2_0_approved_adaptor_id) = (CELLAR_RYUSD, ADAPTOR_CELLAR_V2);
assert!(
validate_new_adaptor(v2_0_cellar_id, v2_0_approved_adaptor_id, &V2_0_PERMISSIONS)
.is_ok()
);
// assert!(
// validate_new_adaptor(v2_0_cellar_id, v2_0_approved_adaptor_id, &V2_0_PERMISSIONS)
// .is_ok()
// );

// rejects blocked adaptor ID
let blocked_adaptor_id = ADAPTOR_UNIV3_V1;
Expand Down Expand Up @@ -444,4 +464,41 @@ mod tests {
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());
}

#[test]
fn test_validate_cache_price_router() {
// valid
assert!(validate_cache_price_router(
CELLAR_TURBO_STETH,
true,
400,
Some(PRICEROUTER2.clone())
)
.is_ok());
assert!(validate_cache_price_router(
CELLAR_TURBO_STETH,
true,
500,
Some(PRICEROUTER2.clone().to_uppercase())
)
.is_ok());

// invalid
assert!(validate_cache_price_router(
CELLAR_TURBO_STETH,
true,
500,
Some("notreal".to_string())
)
.is_err());
assert!(validate_cache_price_router(
CELLAR_TURBO_SWETH,
true,
500,
Some(PRICEROUTER2.clone())
)
.is_err());
assert!(validate_cache_price_router(CELLAR_RYETH, false, 500, None).is_err());
assert!(validate_cache_price_router(CELLAR_RYBTC, true, 600, None).is_err());
}
}
1 change: 1 addition & 0 deletions steward/src/cellars/cellar_v2_2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ pub fn get_encoded_function(call: FunctionCall, cellar_id: String) -> Result<Vec
&cellar_id,
params.check_total_assets,
params.allowable_range,
None,
)?;
log_cellar_call(
CELLAR_NAME,
Expand Down
24 changes: 22 additions & 2 deletions steward/src/cellars/cellar_v2_5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ use crate::{
};

use super::{
check_blocked_adaptor, check_blocked_position, log_cellar_call, validate_new_adaptor,
validate_new_position, validate_oracle, V2_5_PERMISSIONS,
check_blocked_adaptor, check_blocked_position, log_cellar_call, validate_cache_price_router,
validate_new_adaptor, validate_new_position, validate_oracle, V2_5_PERMISSIONS,
};

const CELLAR_NAME: &str = "CellarV2.5";
Expand Down Expand Up @@ -274,6 +274,26 @@ pub fn get_encoded_function(call: FunctionCall, cellar_id: String) -> Result<Vec

Ok(CellarV2_5Calls::SetSharePriceOracle(call).encode())
}
Function::CachePriceRouter(params) => {
validate_cache_price_router(
&cellar_id,
params.check_total_assets,
params.allowable_range,
Some(params.expected_price_router.clone()),
)?;
log_cellar_call(
CELLAR_NAME,
&CachePriceRouterCall::function_name(),
&cellar_id,
);
let call = CachePriceRouterCall {
check_total_assets: params.check_total_assets,
allowable_range: params.allowable_range as u16,
expected_price_router: sp_call_parse_address(params.expected_price_router)?,
};

Ok(CellarV2_5Calls::CachePriceRouter(call).encode())
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion steward_abi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "steward_abi"
version = "3.5.3"
version = "3.6.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
Loading

0 comments on commit 124d985

Please sign in to comment.