Skip to content

Commit

Permalink
Cellar v2.2 governance functions
Browse files Browse the repository at this point in the history
  • Loading branch information
cbrit committed Jul 7, 2023
1 parent 187c446 commit b3d40c5
Show file tree
Hide file tree
Showing 9 changed files with 1,081 additions and 800 deletions.
65 changes: 49 additions & 16 deletions proto/steward/v4/cellar_v2.proto
Original file line number Diff line number Diff line change
Expand Up @@ -225,14 +225,6 @@ message CellarV2_2 {
SetStrategistPlatformCut set_strategist_platform_cut = 10;
// Represents function `liftShutdown()`
LiftShutdown lift_shutdown = 11;
// Represents function `addAdaptorToCatalogue(address adaptor)`
AddAdaptorToCatalogue add_adaptor_to_catalogue = 12;
// Represents function `addPositionToCatalogue(uint32 positionId)`
AddPositionToCatalogue add_position_to_catalogue = 13;
// Represents function `removeAdaptorFromCatalogue(address adaptor)`
RemoveAdaptorFromCatalogue remove_adaptor_from_catalogue = 14;
// Represents function `removePositionFromCatalogue(uint32 positionId)`
RemovePositionFromCatalogue remove_position_from_catalogue = 15;
}
}

Expand Down Expand Up @@ -350,21 +342,42 @@ message CellarV2_2 {
message LiftShutdown {}

/*
* Allows the owner to add an adaptor to the Cellar's adaptor catalogue
* Allows caller to call multiple functions in a single TX.
*
* Represents function `addAdaptorToCatalogue(address adaptor)`
* Represents function `multicall(bytes[] data)`
*/
message AddAdaptorToCatalogue {
string adaptor = 1;
message Multicall {
repeated FunctionCall function_calls = 1;
}
}

/*
* Represent a function call initiated through a governance proposal
*/
message CellarV2_2Governance {
// The function to call on the target cellar
oneof function {
// Represents function `addAdaptorToCatalogue(address adaptor)`
AddAdaptorToCatalogue add_adaptor_to_catalogue = 1;
// Represents function `addPositionToCatalogue(uint32 positionId)`
AddPositionToCatalogue add_position_to_catalogue = 2;
// Represents function `removeAdaptorFromCatalogue(address adaptor)`
RemoveAdaptorFromCatalogue remove_adaptor_from_catalogue = 3;
// Represents function `removePositionFromCatalogue(uint32 positionId)`
RemovePositionFromCatalogue remove_position_from_catalogue = 4;
// Represents function `forcePositionOut(uint32 index, uint32 positionId, bool inDebtArray)`
ForcePositionOut force_position_out = 5;
// Represents function `toggleIgnorePause(bool ignore)`
ToggleIgnorePause toggle_ignore_pause = 6;
}

/*
* Allows caller to call multiple functions in a single TX.
* Allows the owner to add an adaptor to the Cellar's adaptor catalogue
*
* Represents function `multicall(bytes[] data)`
* Represents function `addAdaptorToCatalogue(address adaptor)`
*/
message Multicall {
repeated FunctionCall function_calls = 1;
message AddAdaptorToCatalogue {
string adaptor = 1;
}

/*
Expand Down Expand Up @@ -393,6 +406,26 @@ message CellarV2_2 {
message RemovePositionFromCatalogue {
uint32 position_id = 1;
}

/*
* Allows caller to force a position out of the cellar
*
* Represents function `forcePositionOut(uint32 index, uint32 positionId, bool inDebtArray)`
*/
message ForcePositionOut {
uint32 index = 1;
uint32 position_id = 2;
bool in_debt_array = 3;
}

/*
* Allows caller to toggle the ignorePause flag on the cellar
*
* Represents function `toggleIgnorePause(bool ignore)`
*/
message ToggleIgnorePause {
bool ignore = 1;
}
}

// Represents a call to adaptor an. The cellar must be authorized to call the target adaptor.
Expand Down
2 changes: 2 additions & 0 deletions proto/steward/v4/governance.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@ message GovernanceCall {
CellarV1Governance cellar_v1 = 3;
// Governance function calls to V2 cellars
CellarV2Governance cellar_v2 = 4;
// Governance function calls to the V2.2 cellars
CellarV2_2Governance cellar_v2_2 = 5;
}
}
168 changes: 2 additions & 166 deletions src/cellars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,8 @@ pub(crate) mod cellar_v2_2;
// constants
// addresses are normalized by removing the 0x prefix and converting to lowercase for reliable comparison

// allow/block lists

pub const ALLOWED_CATALOGUE_ADAPTORS: [(&str, &str); 6] = [
(CELLAR_RYETH, ADAPTOR_MORPHO_AAVE_V2_A_TOKEN_V1),
(CELLAR_RYETH, ADAPTOR_MORPHO_AAVE_V2_DEBT_TOKEN_V1),
(CELLAR_RYETH, ADAPTOR_MORPHO_AAVE_V3_A_TOKEN_COLLATERAL_V1),
(CELLAR_RYETH, ADAPTOR_MORPHO_AAVE_V3_DEBT_TOKEN_V1),
(CELLAR_RYETH, ADAPTOR_MORPHO_AAVE_V3_P2P_V1),
(CELLAR_RYLINK, ADAPTOR_CELLAR_V2),
];
pub const ALLOWED_CATALOGUE_POSITIONS: [(&str, u32); 8] = [
(CELLAR_RYETH, 155),
(CELLAR_RYETH, 156),
(CELLAR_RYETH, 161),
(CELLAR_RYETH, 162),
(CELLAR_RYETH, 163),
(CELLAR_RYETH, 165),
(CELLAR_RYETH, 166),
(CELLAR_RYLINK, 154),
];
pub const ALLOWED_SETUP_ADAPTORS: [(&str, &str); 1] =
[(CELLAR_RYUSD, ADAPTOR_MORPHO_AAVE_V2_A_TOKEN_V1)];
// block lists

pub const BLOCKED_ADAPTORS: [&str; 3] = [
ADAPTOR_UNIV3_V1,
ADAPTOR_VESTING_SIMPLE_V1,
Expand Down Expand Up @@ -121,44 +101,6 @@ pub fn check_blocked_position(position: &u32) -> Result<(), Error> {
Ok(())
}

pub fn validate_add_adaptor_to_catalogue(cellar_id: &str, adaptor_id: &str) -> Result<(), Error> {
let adaptor_id = normalize_address(adaptor_id.to_string());
check_blocked_adaptor(&adaptor_id)?;
let cellar_id = normalize_address(cellar_id.to_string());
if !ALLOWED_CATALOGUE_ADAPTORS.contains(&(&cellar_id, &adaptor_id)) {
return Err(sp_call_error(format!(
"adaptor {adaptor_id} not allowed to be added to catalogue for {cellar_id}"
)));
}

Ok(())
}

pub fn validate_add_position_to_catalogue(cellar_id: &str, position: u32) -> Result<(), Error> {
check_blocked_position(&position)?;
let cellar_id = normalize_address(cellar_id.to_string());
if !ALLOWED_CATALOGUE_POSITIONS.contains(&(&cellar_id, position)) {
return Err(sp_call_error(format!(
"position {position} not allowed to be added to catalogue for {cellar_id}"
)));
}

Ok(())
}

pub fn validate_setup_adaptor(cellar_id: &str, adaptor_id: &str) -> Result<(), Error> {
let adaptor_id = normalize_address(adaptor_id.to_string());
check_blocked_adaptor(&adaptor_id)?;
let cellar_id = normalize_address(cellar_id.to_string());
if !ALLOWED_SETUP_ADAPTORS.contains(&(&cellar_id, &adaptor_id)) {
return Err(sp_call_error(format!(
"adaptor {adaptor_id} not allowed to be setup for {cellar_id}"
)));
}

Ok(())
}

pub fn is_evm_address(address: &str) -> bool {
address.parse::<Address>().is_ok()
}
Expand Down Expand Up @@ -226,9 +168,6 @@ mod tests {
assert!(BLOCKED_ADAPTORS.contains(&normalize_address(blocked3).as_str()));
assert!(BLOCKED_ADAPTORS.contains(&normalize_address(blocked4).as_str()));

assert!(!ALLOWED_SETUP_ADAPTORS
.contains(&(CELLAR_RYUSD, normalize_address(blocked1.clone()).as_ref())));

// idempotent
let once = normalize_address(blocked1);
let twice = normalize_address(once.clone());
Expand Down Expand Up @@ -266,107 +205,4 @@ mod tests {
assert!(res.is_err());
assert_eq!(expected_err, res.unwrap_err().to_string());
}

#[test]
fn test_validate_add_adaptor_to_catalogue() {
// allows approved cellar/adaptor ID pairs
let (cellar_id, approved_adaptor_id) = (CELLAR_RYETH, ADAPTOR_MORPHO_AAVE_V2_A_TOKEN_V1);
assert!(validate_add_adaptor_to_catalogue(cellar_id, approved_adaptor_id).is_ok());

let error_prefix = "SP call error: ".to_string();

// rejects blocked adaptor ID
let blocked_adaptor_id = ADAPTOR_UNIV3_V1;
let res = validate_add_adaptor_to_catalogue(cellar_id, blocked_adaptor_id);
let expected_err =
error_prefix.clone() + &format!("adaptor {blocked_adaptor_id} is blocked");
assert!(res.is_err());
assert_eq!(expected_err, res.unwrap_err().to_string());

// rejects unapproved cellar/adaptor ID pair
let unapproved_adaptor_id = ADAPTOR_CELLAR_V2;
let res = validate_add_adaptor_to_catalogue(cellar_id, unapproved_adaptor_id);
let expected_err = error_prefix.clone()
+ &format!(
"adaptor {unapproved_adaptor_id} not allowed to be added to catalogue for {cellar_id}"
);
assert!(res.is_err());
assert_eq!(expected_err, res.unwrap_err().to_string());

let unapproved_cellar = "0000000000000000000000000000000000000000";
let res = validate_add_adaptor_to_catalogue(unapproved_cellar, approved_adaptor_id);
let expected_err = error_prefix
+ &format!("adaptor {approved_adaptor_id} not allowed to be added to catalogue for {unapproved_cellar}");
assert!(res.is_err());
assert_eq!(expected_err, res.unwrap_err().to_string());
}

#[test]
fn test_validate_add_position_to_catalogue() {
// allows approved cellar/position ID pairs
let (cellar_id, approved_pos) = (CELLAR_RYLINK, 154);
assert!(validate_add_position_to_catalogue(cellar_id, approved_pos).is_ok());

let error_prefix = "SP call error: ".to_string();

// rejects blocked position ID
let blocked_pos = 4;
let res = validate_add_position_to_catalogue(cellar_id, blocked_pos);
let expected_err = error_prefix.clone() + &format!("position {blocked_pos} is blocked");
assert!(res.is_err());
assert_eq!(expected_err, res.unwrap_err().to_string());

// rejects unapproved cellar/position ID pair
let unapproved_pos = 153;
let res = validate_add_position_to_catalogue(cellar_id, unapproved_pos);
let expected_err = error_prefix.clone()
+ &format!(
"position {unapproved_pos} not allowed to be added to catalogue for {cellar_id}"
);
assert!(res.is_err());
assert_eq!(expected_err, res.unwrap_err().to_string());

let unapproved_cellar = "0000000000000000000000000000000000000000";
let res = validate_add_position_to_catalogue(unapproved_cellar, approved_pos);
let expected_err = error_prefix
+ &format!(
"position {approved_pos} not allowed to be added to catalogue for {unapproved_cellar}"
);
assert!(res.is_err());
assert_eq!(expected_err, res.unwrap_err().to_string());
}

#[test]
fn test_validate_setup_adaptor() {
// allows approved cellar/adaptor ID pairs
let (cellar_id, approved_adaptor_id) = (CELLAR_RYUSD, ADAPTOR_MORPHO_AAVE_V2_A_TOKEN_V1);
assert!(validate_setup_adaptor(cellar_id, approved_adaptor_id).is_ok());

let error_prefix = "SP call error: ".to_string();

// rejects blocked adaptor ID
let blocked_adaptor_id = ADAPTOR_UNIV3_V1;
let res = validate_setup_adaptor(cellar_id, blocked_adaptor_id);
let expected_err =
error_prefix.clone() + &format!("adaptor {blocked_adaptor_id} is blocked");
assert!(res.is_err());
assert_eq!(expected_err, res.unwrap_err().to_string());

// rejects unapproved cellar/adaptor ID pair
let unapproved_adaptor_id = ADAPTOR_CELLAR_V2;
let res = validate_setup_adaptor(cellar_id, unapproved_adaptor_id);
let expected_err = error_prefix.clone()
+ &format!("adaptor {unapproved_adaptor_id} not allowed to be setup for {cellar_id}");
assert!(res.is_err());
assert_eq!(expected_err, res.unwrap_err().to_string());

let unapproved_cellar = "0000000000000000000000000000000000000000";
let res = validate_setup_adaptor(unapproved_cellar, approved_adaptor_id);
let expected_err = error_prefix
+ &format!(
"adaptor {approved_adaptor_id} not allowed to be setup for {unapproved_cellar}"
);
assert!(res.is_err());
assert_eq!(expected_err, res.unwrap_err().to_string());
}
}
Loading

0 comments on commit b3d40c5

Please sign in to comment.