diff --git a/smart-contracts/quasar/Cargo.lock b/smart-contracts/quasar/Cargo.lock index 43dac11af..42186bb1f 100644 --- a/smart-contracts/quasar/Cargo.lock +++ b/smart-contracts/quasar/Cargo.lock @@ -136,6 +136,8 @@ dependencies = [ "cw-storage-plus", "cw2", "mars-owner", + "prost", + "quasar-std", "thiserror", ] diff --git a/smart-contracts/quasar/contracts/babylon-vault/Cargo.toml b/smart-contracts/quasar/contracts/babylon-vault/Cargo.toml index 02b1b7e7a..d87b70dcd 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/Cargo.toml +++ b/smart-contracts/quasar/contracts/babylon-vault/Cargo.toml @@ -30,3 +30,7 @@ cw-storage-plus = { workspace = true } thiserror = { workspace = true } cw2 = { workspace = true } mars-owner = { workspace = true } +quasar-std = { workspace = true } + +[dev-dependencies] +prost = { workspace = true } \ No newline at end of file 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 72e14bc9d..a3f59c270 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/schema/babylon-vault.json +++ b/smart-contracts/quasar/contracts/babylon-vault/schema/babylon-vault.json @@ -7,11 +7,15 @@ "title": "InstantiateMsg", "type": "object", "required": [ - "owner" + "owner", + "subdenom" ], "properties": { "owner": { "type": "string" + }, + "subdenom": { + "type": "string" } }, "additionalProperties": false @@ -175,19 +179,11 @@ { "type": "object", "required": [ - "pending" + "value" ], "properties": { - "pending": { + "value": { "type": "object", - "required": [ - "address" - ], - "properties": { - "address": { - "type": "string" - } - }, "additionalProperties": false } }, @@ -196,31 +192,10 @@ { "type": "object", "required": [ - "claimable" - ], - "properties": { - "claimable": { - "type": "object", - "required": [ - "address" - ], - "properties": { - "address": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "balance_in_underlying" + "owner" ], "properties": { - "balance_in_underlying": { + "owner": { "type": "object", "additionalProperties": false } @@ -230,10 +205,10 @@ { "type": "object", "required": [ - "owner" + "lsts" ], "properties": { - "owner": { + "lsts": { "type": "object", "additionalProperties": false } @@ -243,10 +218,10 @@ { "type": "object", "required": [ - "lsts" + "denom" ], "properties": { - "lsts": { + "denom": { "type": "object", "additionalProperties": false } @@ -258,16 +233,9 @@ "migrate": null, "sudo": null, "responses": { - "balance_in_underlying": { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Uint128", - "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" - }, - "claimable": { + "denom": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Uint128", - "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); ```", + "title": "String", "type": "string" }, "lsts": { @@ -327,47 +295,11 @@ }, "additionalProperties": false }, - "pending": { + "value": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Array_of_Claim", - "type": "array", - "items": { - "$ref": "#/definitions/Claim" - }, - "definitions": { - "Claim": { - "type": "object", - "required": [ - "amount", - "expiration" - ], - "properties": { - "amount": { - "$ref": "#/definitions/Uint128" - }, - "expiration": { - "$ref": "#/definitions/Timestamp" - } - }, - "additionalProperties": false - }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, - "Uint128": { - "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" - }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 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 `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - } - } + "title": "Uint128", + "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" } } } diff --git a/smart-contracts/quasar/contracts/babylon-vault/schema/raw/instantiate.json b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/instantiate.json index 66e351258..a2546c50b 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/schema/raw/instantiate.json +++ b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/instantiate.json @@ -3,11 +3,15 @@ "title": "InstantiateMsg", "type": "object", "required": [ - "owner" + "owner", + "subdenom" ], "properties": { "owner": { "type": "string" + }, + "subdenom": { + "type": "string" } }, "additionalProperties": false 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 5486dffe3..637f052f5 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/schema/raw/query.json +++ b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/query.json @@ -5,19 +5,11 @@ { "type": "object", "required": [ - "pending" + "value" ], "properties": { - "pending": { + "value": { "type": "object", - "required": [ - "address" - ], - "properties": { - "address": { - "type": "string" - } - }, "additionalProperties": false } }, @@ -26,31 +18,10 @@ { "type": "object", "required": [ - "claimable" - ], - "properties": { - "claimable": { - "type": "object", - "required": [ - "address" - ], - "properties": { - "address": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "balance_in_underlying" + "owner" ], "properties": { - "balance_in_underlying": { + "owner": { "type": "object", "additionalProperties": false } @@ -60,10 +31,10 @@ { "type": "object", "required": [ - "owner" + "lsts" ], "properties": { - "owner": { + "lsts": { "type": "object", "additionalProperties": false } @@ -73,10 +44,10 @@ { "type": "object", "required": [ - "lsts" + "denom" ], "properties": { - "lsts": { + "denom": { "type": "object", "additionalProperties": false } diff --git a/smart-contracts/quasar/contracts/babylon-vault/schema/raw/response_to_denom.json b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/response_to_denom.json new file mode 100644 index 000000000..f689acebf --- /dev/null +++ b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/response_to_denom.json @@ -0,0 +1,5 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "String", + "type": "string" +} diff --git a/smart-contracts/quasar/contracts/babylon-vault/schema/raw/response_to_value.json b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/response_to_value.json new file mode 100644 index 000000000..25b73e8f2 --- /dev/null +++ b/smart-contracts/quasar/contracts/babylon-vault/schema/raw/response_to_value.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Uint128", + "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" +} diff --git a/smart-contracts/quasar/contracts/babylon-vault/src/contract.rs b/smart-contracts/quasar/contracts/babylon-vault/src/contract.rs index 0fe58bd27..dcf7b3682 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/src/contract.rs +++ b/smart-contracts/quasar/contracts/babylon-vault/src/contract.rs @@ -1,22 +1,28 @@ use crate::error::VaultError; use crate::msg::{ExecuteMsg, InstantiateMsg, LstInfo, QueryMsg}; -use crate::state::{LSTS, OWNER}; +use crate::state::{LSTS, OWNER, VAULT_DENOM}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, + to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, + SubMsg, }; use cw2::set_contract_version; +use quasar_std::quasarlabs::quasarnode::tokenfactory::v1beta1::{ + MsgCreateDenom, MsgCreateDenomResponse, +}; const CONTRACT_NAME: &str = "quasar:babylon-vault"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub type VaultResult = Result; +pub(crate) const CREATE_DENOM_REPLY_ID: u64 = 1; + #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, - _env: Env, + env: Env, _info: MessageInfo, msg: InstantiateMsg, ) -> VaultResult { @@ -26,7 +32,24 @@ pub fn instantiate( mars_owner::OwnerInit::SetInitialOwner { owner: msg.owner }, )?; set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - Ok(Response::default()) + let msg = MsgCreateDenom { + sender: env.contract.address.to_string(), + subdenom: msg.subdenom, + }; + Ok(Response::new().add_submessage(SubMsg::reply_on_success(msg, CREATE_DENOM_REPLY_ID))) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply(deps: DepsMut, _env: Env, reply: Reply) -> VaultResult { + match reply.id { + CREATE_DENOM_REPLY_ID => { + let response: MsgCreateDenomResponse = reply.result.try_into()?; + VAULT_DENOM.save(deps.storage, &response.new_token_denom)?; + + Ok(Response::new().add_attribute("vault_denom", response.new_token_denom)) + } + _ => unimplemented!(), + } } #[cfg_attr(not(feature = "library"), entry_point)] @@ -61,6 +84,7 @@ 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)?)?), + QueryMsg::Denom {} => Ok(to_json_binary(&VAULT_DENOM.load(deps.storage)?)?), _ => Ok(Binary::default()), } } diff --git a/smart-contracts/quasar/contracts/babylon-vault/src/msg.rs b/smart-contracts/quasar/contracts/babylon-vault/src/msg.rs index d95c02251..cd20bb32d 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/src/msg.rs +++ b/smart-contracts/quasar/contracts/babylon-vault/src/msg.rs @@ -5,6 +5,7 @@ use mars_owner::{OwnerResponse, OwnerUpdate}; #[cw_serde] pub struct InstantiateMsg { pub owner: String, + pub subdenom: String, } #[cw_serde] @@ -34,14 +35,12 @@ pub struct LstInfo { #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { - #[returns(Vec)] - Pending { address: String }, #[returns(Uint128)] - Claimable { address: String }, - #[returns(Uint128)] - BalanceInUnderlying {}, + Value {}, #[returns(OwnerResponse)] Owner {}, #[returns(Vec)] Lsts {}, + #[returns(String)] + Denom {}, } diff --git a/smart-contracts/quasar/contracts/babylon-vault/src/state.rs b/smart-contracts/quasar/contracts/babylon-vault/src/state.rs index 14833498a..45adb5723 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/src/state.rs +++ b/smart-contracts/quasar/contracts/babylon-vault/src/state.rs @@ -1,6 +1,7 @@ use cosmwasm_std::Addr; -use cw_storage_plus::Map; +use cw_storage_plus::{Item, Map}; use mars_owner::Owner; pub const OWNER: Owner = Owner::new("owner"); pub const LSTS: Map = Map::new("lsts"); +pub const VAULT_DENOM: Item = Item::new("denom"); diff --git a/smart-contracts/quasar/contracts/babylon-vault/src/tests/instantiate.rs b/smart-contracts/quasar/contracts/babylon-vault/src/tests/instantiate.rs index cf86a5972..6397f3cf8 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/src/tests/instantiate.rs +++ b/smart-contracts/quasar/contracts/babylon-vault/src/tests/instantiate.rs @@ -1,6 +1,17 @@ -use crate::tests::setup::{OWNER, USER}; -use crate::{contract::instantiate, msg::InstantiateMsg}; -use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; +use crate::tests::setup::{OWNER, SUBDENOM, USER}; +use crate::{ + contract::{instantiate, query, reply, CREATE_DENOM_REPLY_ID}, + msg::{InstantiateMsg, QueryMsg}, +}; +use cosmwasm_std::{ + from_json, + testing::{mock_dependencies, mock_env, mock_info}, + Reply, SubMsgResponse, SubMsgResult, +}; +use prost::Message; +use quasar_std::quasarlabs::quasarnode::tokenfactory::v1beta1::{ + MsgCreateDenom, MsgCreateDenomResponse, +}; #[test] fn test_instantiate() { @@ -10,13 +21,47 @@ fn test_instantiate() { let result = instantiate( deps.as_mut(), - env, + env.clone(), info, InstantiateMsg { owner: OWNER.to_string(), + subdenom: SUBDENOM.to_string(), }, ); assert!(result.is_ok()); let response = result.unwrap(); - assert_eq!(response.messages.len(), 0); + assert_eq!(response.messages.len(), 1); + assert_eq!( + response.messages[0].msg, + MsgCreateDenom { + sender: env.contract.address.to_string(), + subdenom: SUBDENOM.to_string(), + } + .into() + ); + + let new_token = "new_token".to_string(); + + assert!(reply( + deps.as_mut(), + env.clone(), + Reply { + id: CREATE_DENOM_REPLY_ID, + result: SubMsgResult::Ok(SubMsgResponse { + events: vec![], + data: Some( + MsgCreateDenomResponse { + new_token_denom: new_token.clone(), + } + .encode_to_vec() + .into() + ), + }) + } + ) + .is_ok()); + + let vault_token = + from_json::(&query(deps.as_ref(), env, QueryMsg::Denom {}).unwrap()).unwrap(); + assert_eq!(vault_token, new_token); } diff --git a/smart-contracts/quasar/contracts/babylon-vault/src/tests/setup.rs b/smart-contracts/quasar/contracts/babylon-vault/src/tests/setup.rs index 29626149b..000780bd3 100644 --- a/smart-contracts/quasar/contracts/babylon-vault/src/tests/setup.rs +++ b/smart-contracts/quasar/contracts/babylon-vault/src/tests/setup.rs @@ -6,6 +6,7 @@ use cosmwasm_std::{ pub const OWNER: &str = "owner"; pub const USER: &str = "user"; +pub const SUBDENOM: &str = "subdenom"; pub fn setup() -> OwnedDeps { let mut deps = mock_dependencies(); @@ -18,6 +19,7 @@ pub fn setup() -> OwnedDeps { info, InstantiateMsg { owner: OWNER.to_string(), + subdenom: SUBDENOM.to_string(), }, ) .is_ok());