diff --git a/smart-contracts/quasar/contracts/babylon-vault/schema/babylon-vault.json b/smart-contracts/quasar/contracts/babylon-vault/schema/babylon-vault.json index bf5028774..72e14bc9d 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/schema/babylon-vault.json +++ b/smart-contracts/quasar/contracts/babylon-vault/schema/babylon-vault.json @@ -66,6 +66,31 @@ ], "properties": { "register_lst": { + "type": "object", + "required": [ + "denom", + "interface" + ], + "properties": { + "denom": { + "type": "string" + }, + "interface": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "unregister_lst" + ], + "properties": { + "unregister_lst": { "type": "object", "required": [ "denom" @@ -214,6 +239,19 @@ } }, "additionalProperties": false + }, + { + "type": "object", + "required": [ + "lsts" + ], + "properties": { + "lsts": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false } ] }, @@ -232,6 +270,32 @@ "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", "type": "string" }, + "lsts": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_LstInfo", + "type": "array", + "items": { + "$ref": "#/definitions/LstInfo" + }, + "definitions": { + "LstInfo": { + "type": "object", + "required": [ + "denom", + "interface" + ], + "properties": { + "denom": { + "type": "string" + }, + "interface": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, "owner": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "OwnerResponse", diff --git a/smart-contracts/quasar/contracts/babylon-vault/schema/raw/execute.json b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/execute.json index ee6d7301a..2487db452 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/schema/raw/execute.json +++ b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/execute.json @@ -48,6 +48,31 @@ ], "properties": { "register_lst": { + "type": "object", + "required": [ + "denom", + "interface" + ], + "properties": { + "denom": { + "type": "string" + }, + "interface": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "unregister_lst" + ], + "properties": { + "unregister_lst": { "type": "object", "required": [ "denom" diff --git a/smart-contracts/quasar/contracts/babylon-vault/schema/raw/query.json b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/query.json index 65de6f167..5486dffe3 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/schema/raw/query.json +++ b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/query.json @@ -69,6 +69,19 @@ } }, "additionalProperties": false + }, + { + "type": "object", + "required": [ + "lsts" + ], + "properties": { + "lsts": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false } ] } diff --git a/smart-contracts/quasar/contracts/babylon-vault/schema/raw/response_to_lsts.json b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/response_to_lsts.json new file mode 100644 index 000000000..d1decf7df --- /dev/null +++ b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/response_to_lsts.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_LstInfo", + "type": "array", + "items": { + "$ref": "#/definitions/LstInfo" + }, + "definitions": { + "LstInfo": { + "type": "object", + "required": [ + "denom", + "interface" + ], + "properties": { + "denom": { + "type": "string" + }, + "interface": { + "type": "string" + } + }, + "additionalProperties": false + } + } +} diff --git a/smart-contracts/quasar/contracts/babylon-vault/schema/raw/response_to_lsts.json.orig b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/response_to_lsts.json.orig new file mode 100644 index 000000000..d1decf7df --- /dev/null +++ b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/response_to_lsts.json.orig @@ -0,0 +1,26 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_LstInfo", + "type": "array", + "items": { + "$ref": "#/definitions/LstInfo" + }, + "definitions": { + "LstInfo": { + "type": "object", + "required": [ + "denom", + "interface" + ], + "properties": { + "denom": { + "type": "string" + }, + "interface": { + "type": "string" + } + }, + "additionalProperties": false + } + } +} diff --git a/smart-contracts/quasar/contracts/babylon-vault/src/contract.rs b/smart-contracts/quasar/contracts/babylon-vault/src/contract.rs index 524d66dd3..0fe58bd27 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/src/contract.rs +++ b/smart-contracts/quasar/contracts/babylon-vault/src/contract.rs @@ -1,9 +1,11 @@ use crate::error::VaultError; -use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; -use crate::state::OWNER; +use crate::msg::{ExecuteMsg, InstantiateMsg, LstInfo, QueryMsg}; +use crate::state::{LSTS, OWNER}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response}; +use cosmwasm_std::{ + to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, +}; use cw2::set_contract_version; const CONTRACT_NAME: &str = "quasar:babylon-vault"; @@ -31,22 +33,51 @@ pub fn instantiate( pub fn execute(deps: DepsMut, _env: Env, info: MessageInfo, msg: ExecuteMsg) -> VaultResult { match msg { ExecuteMsg::UpdateOwner(update) => Ok(OWNER.update(deps, info, update)?), + ExecuteMsg::RegisterLst { denom, interface } => register_lst(deps, info, denom, interface), + ExecuteMsg::UnregisterLst { denom } => unregister_lst(deps, info, denom), _ => Ok(Response::default()), } } +fn register_lst(deps: DepsMut, info: MessageInfo, denom: String, interface: String) -> VaultResult { + OWNER.assert_owner(deps.storage, &info.sender)?; + LSTS.save(deps.storage, denom, &deps.api.addr_validate(&interface)?)?; + Ok(Response::default()) +} + +fn unregister_lst(deps: DepsMut, info: MessageInfo, denom: String) -> VaultResult { + OWNER.assert_owner(deps.storage, &info.sender)?; + let interface = LSTS.may_load(deps.storage, denom.clone())?; + if interface.is_some() { + LSTS.remove(deps.storage, denom); + Ok(Response::default()) + } else { + Err(VaultError::LstNotFound { denom }) + } +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> VaultResult { match msg { QueryMsg::Owner {} => Ok(to_json_binary(&OWNER.query(deps.storage)?)?), + QueryMsg::Lsts {} => Ok(to_json_binary(&query_lsts(deps)?)?), _ => Ok(Binary::default()), } } -#[cfg(test)] -mod tests { - #[test] - fn dummy_test() { - assert!(true); - } +fn query_lsts(deps: Deps) -> StdResult> { + let lsts: StdResult> = LSTS + .range(deps.storage, None, None, cosmwasm_std::Order::Ascending) + .collect(); + let lsts = lsts?; + let infos: Vec = lsts + .into_iter() + .map(|(denom, interface)| -> LstInfo { + LstInfo { + denom, + interface: interface.to_string(), + } + }) + .collect(); + Ok(infos) } diff --git a/smart-contracts/quasar/contracts/babylon-vault/src/error.rs b/smart-contracts/quasar/contracts/babylon-vault/src/error.rs index 4bb0c41c8..8400be28f 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/src/error.rs +++ b/smart-contracts/quasar/contracts/babylon-vault/src/error.rs @@ -9,4 +9,7 @@ pub enum VaultError { #[error("{0}")] Owner(#[from] OwnerError), + + #[error("{denom} not found")] + LstNotFound { denom: String }, } diff --git a/smart-contracts/quasar/contracts/babylon-vault/src/lib.rs b/smart-contracts/quasar/contracts/babylon-vault/src/lib.rs index 79a621cd9..56f420431 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/src/lib.rs +++ b/smart-contracts/quasar/contracts/babylon-vault/src/lib.rs @@ -3,4 +3,7 @@ mod error; pub mod msg; pub mod state; +#[cfg(test)] +mod tests; + pub use crate::error::VaultError; diff --git a/smart-contracts/quasar/contracts/babylon-vault/src/msg.rs b/smart-contracts/quasar/contracts/babylon-vault/src/msg.rs index f91a3421c..d95c02251 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/src/msg.rs +++ b/smart-contracts/quasar/contracts/babylon-vault/src/msg.rs @@ -14,7 +14,8 @@ pub enum ExecuteMsg { Withdraw {}, Claim {}, // owner methods - RegisterLst { denom: String }, + RegisterLst { denom: String, interface: String }, + UnregisterLst { denom: String }, UpdateOwner(OwnerUpdate), } @@ -24,6 +25,12 @@ pub struct Claim { pub expiration: Timestamp, } +#[cw_serde] +pub struct LstInfo { + pub denom: String, + pub interface: String, +} + #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { @@ -35,4 +42,6 @@ pub enum QueryMsg { BalanceInUnderlying {}, #[returns(OwnerResponse)] Owner {}, + #[returns(Vec)] + Lsts {}, } diff --git a/smart-contracts/quasar/contracts/babylon-vault/src/state.rs b/smart-contracts/quasar/contracts/babylon-vault/src/state.rs index 0c0e31ebd..14833498a 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/src/state.rs +++ b/smart-contracts/quasar/contracts/babylon-vault/src/state.rs @@ -1,3 +1,6 @@ +use cosmwasm_std::Addr; +use cw_storage_plus::Map; use mars_owner::Owner; pub const OWNER: Owner = Owner::new("owner"); +pub const LSTS: Map = Map::new("lsts"); diff --git a/smart-contracts/quasar/contracts/babylon-vault/src/tests/instantiate.rs b/smart-contracts/quasar/contracts/babylon-vault/src/tests/instantiate.rs new file mode 100644 index 000000000..cf86a5972 --- /dev/null +++ b/smart-contracts/quasar/contracts/babylon-vault/src/tests/instantiate.rs @@ -0,0 +1,22 @@ +use crate::tests::setup::{OWNER, USER}; +use crate::{contract::instantiate, msg::InstantiateMsg}; +use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + +#[test] +fn test_instantiate() { + let mut deps = mock_dependencies(); + let env = mock_env(); + let info = mock_info(USER, &[]); + + let result = instantiate( + deps.as_mut(), + env, + info, + InstantiateMsg { + owner: OWNER.to_string(), + }, + ); + assert!(result.is_ok()); + let response = result.unwrap(); + assert_eq!(response.messages.len(), 0); +} diff --git a/smart-contracts/quasar/contracts/babylon-vault/src/tests/mod.rs b/smart-contracts/quasar/contracts/babylon-vault/src/tests/mod.rs new file mode 100644 index 000000000..65e1fc87b --- /dev/null +++ b/smart-contracts/quasar/contracts/babylon-vault/src/tests/mod.rs @@ -0,0 +1,3 @@ +pub mod instantiate; +pub mod register; +pub mod setup; diff --git a/smart-contracts/quasar/contracts/babylon-vault/src/tests/register.rs b/smart-contracts/quasar/contracts/babylon-vault/src/tests/register.rs new file mode 100644 index 000000000..b9bb6b89d --- /dev/null +++ b/smart-contracts/quasar/contracts/babylon-vault/src/tests/register.rs @@ -0,0 +1,111 @@ +use cosmwasm_std::{ + from_json, + testing::{mock_env, mock_info}, +}; +use mars_owner::OwnerError; + +use crate::{ + contract::{execute, query}, + msg::{ExecuteMsg, LstInfo, QueryMsg}, + tests::setup::{setup, OWNER, USER}, + VaultError, +}; + +#[test] +fn register_lst_fails_for_non_owner() { + let mut deps = setup(); + let env = mock_env(); + let info = mock_info(USER, &[]); + + let denom = "lst".to_string(); + let interface = "interface".to_string(); + let msg = ExecuteMsg::RegisterLst { denom, interface }; + let result = execute(deps.as_mut(), env, info, msg); + assert!(result.is_err()); + + assert_eq!( + result.unwrap_err(), + VaultError::Owner(OwnerError::NotOwner {}) + ); +} + +#[test] +fn unregister_lst_fails_for_non_owner() { + let mut deps = setup(); + let env = mock_env(); + let info = mock_info(USER, &[]); + + let denom = "lst".to_string(); + let msg = ExecuteMsg::UnregisterLst { denom }; + let result = execute(deps.as_mut(), env, info, msg); + assert!(result.is_err()); + + assert_eq!( + result.unwrap_err(), + VaultError::Owner(OwnerError::NotOwner {}) + ); +} + +#[test] +fn register_and_unregister_lst() { + let mut deps = setup(); + let env = mock_env(); + let info = mock_info(OWNER, &[]); + + let denom = "lst".to_string(); + let interface = "interface".to_string(); + let msg = ExecuteMsg::RegisterLst { + denom: denom.clone(), + interface: interface.clone(), + }; + assert!(execute(deps.as_mut(), env.clone(), info.clone(), msg).is_ok()); + + let lsts: Vec = + from_json(&query(deps.as_ref(), env.clone(), QueryMsg::Lsts {}).unwrap()).unwrap(); + assert_eq!(lsts.len(), 1); + assert_eq!( + lsts[0], + LstInfo { + denom: denom.clone(), + interface + } + ); + + let msg = ExecuteMsg::UnregisterLst { denom }; + assert!(execute(deps.as_mut(), env.clone(), info.clone(), msg).is_ok()); + let lsts: Vec = + from_json(&query(deps.as_ref(), env.clone(), QueryMsg::Lsts {}).unwrap()).unwrap(); + assert_eq!(lsts.len(), 0); +} + +#[test] +fn unregister_fails_if_denom_is_not_registered() { + let mut deps = setup(); + let env = mock_env(); + let info = mock_info(OWNER, &[]); + + let denom = "lst".to_string(); + let interface = "interface".to_string(); + let msg = ExecuteMsg::RegisterLst { + denom: denom.clone(), + interface: interface.clone(), + }; + assert!(execute(deps.as_mut(), env.clone(), info.clone(), msg).is_ok()); + + let lsts: Vec = + from_json(&query(deps.as_ref(), env.clone(), QueryMsg::Lsts {}).unwrap()).unwrap(); + assert_eq!(lsts.len(), 1); + assert_eq!(lsts[0], LstInfo { denom, interface }); + + let other_denom = "other_lst".to_string(); + let msg = ExecuteMsg::UnregisterLst { + denom: other_denom.clone(), + }; + let result = execute(deps.as_mut(), env, info, msg); + assert!(result.is_err()); + + assert_eq!( + result.unwrap_err(), + VaultError::LstNotFound { denom: other_denom } + ); +} diff --git a/smart-contracts/quasar/contracts/babylon-vault/src/tests/setup.rs b/smart-contracts/quasar/contracts/babylon-vault/src/tests/setup.rs new file mode 100644 index 000000000..29626149b --- /dev/null +++ b/smart-contracts/quasar/contracts/babylon-vault/src/tests/setup.rs @@ -0,0 +1,25 @@ +use crate::{contract::instantiate, msg::InstantiateMsg}; +use cosmwasm_std::{ + testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, + Empty, OwnedDeps, +}; + +pub const OWNER: &str = "owner"; +pub const USER: &str = "user"; + +pub fn setup() -> OwnedDeps { + let mut deps = mock_dependencies(); + let env = mock_env(); + let info = mock_info(USER, &[]); + + assert!(instantiate( + deps.as_mut(), + env, + info, + InstantiateMsg { + owner: OWNER.to_string(), + }, + ) + .is_ok()); + deps +}