diff --git a/examples/Cargo.lock b/examples/Cargo.lock index 4a897b66..7e7fef3e 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -228,6 +228,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "custom" +version = "0.5.0" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "cw-storage-plus", + "serde", + "sylvia", +] + [[package]] name = "cw-multi-test" version = "0.16.5" diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 7314a3b6..a8a0e3ad 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -12,6 +12,7 @@ members = [ "contracts/cw1-subkeys", "contracts/cw20-base", "contracts/entry-points-overriding", + "contracts/custom", ] resolver = "2" diff --git a/examples/contracts/custom/.cargo/config b/examples/contracts/custom/.cargo/config new file mode 100644 index 00000000..d8ab80fe --- /dev/null +++ b/examples/contracts/custom/.cargo/config @@ -0,0 +1,6 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown --lib" +wasm-debug = "build --target wasm32-unknown-unknown --lib" +unit-test = "test --lib" +integration-test = "test --test integration" +schema = "run --bin schema" diff --git a/examples/contracts/custom/Cargo.toml b/examples/contracts/custom/Cargo.toml new file mode 100644 index 00000000..57dd33b6 --- /dev/null +++ b/examples/contracts/custom/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "custom" +version = { workspace = true } +authors = ["Jan Woźniak "] +edition = { workspace = true } +description = "Example of custom message usage." +license = "Apache-2.0" +repository = "https://github.com/CosmWasm/sylvia" +homepage = "https://cosmwasm.com" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +library = [] +mt = ["library"] + +[dependencies] +cosmwasm-schema = "1.2" +cosmwasm-std = { version = "1.3", features = ["staking"] } +cw-storage-plus = "1.0" +serde = { version = "1.0", default-features = false, features = ["derive"] } +sylvia = { path = "../../../sylvia" } + +[dev-dependencies] +anyhow = "1.0" +cw-multi-test = "0.16" +sylvia = { path = "../../../sylvia", features = ["mt"] } diff --git a/examples/contracts/custom/src/bin/schema.rs b/examples/contracts/custom/src/bin/schema.rs new file mode 100644 index 00000000..a60261b8 --- /dev/null +++ b/examples/contracts/custom/src/bin/schema.rs @@ -0,0 +1,12 @@ +use cosmwasm_schema::write_api; + +use custom::contract::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; + +#[cfg(not(tarpaulin_include))] +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: ContractExecMsg, + query: ContractQueryMsg, + } +} diff --git a/examples/contracts/custom/src/contract.rs b/examples/contracts/custom/src/contract.rs new file mode 100644 index 00000000..f4d30075 --- /dev/null +++ b/examples/contracts/custom/src/contract.rs @@ -0,0 +1,41 @@ +use cosmwasm_std::{CosmosMsg, QueryRequest, Response, StdResult}; +use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx}; +use sylvia::{contract, entry_points, schemars}; + +use crate::messages::{CountResponse, CounterMsg, CounterQuery}; + +pub struct CustomContract; + +#[cfg_attr(not(feature = "mt"), entry_points)] +#[contract] +#[sv::custom(query=CounterQuery, msg=CounterMsg)] +impl CustomContract { + pub const fn new() -> Self { + Self + } + + #[msg(instantiate)] + pub fn instantiate( + &self, + _ctx: InstantiateCtx, + ) -> StdResult> { + Ok(Response::default()) + } + + #[msg(exec)] + pub fn send_custom(&self, _ctx: ExecCtx) -> StdResult> { + let msg = CosmosMsg::Custom(CounterMsg::Increment {}); + let resp = Response::default().add_message(msg); + Ok(resp) + } + + #[msg(query)] + pub fn query_custom(&self, ctx: QueryCtx) -> StdResult { + let resp = ctx + .deps + .querier + .query::(&QueryRequest::Custom(CounterQuery::Count {}))?; + + Ok(resp) + } +} diff --git a/examples/contracts/custom/src/lib.rs b/examples/contracts/custom/src/lib.rs new file mode 100644 index 00000000..cceb968f --- /dev/null +++ b/examples/contracts/custom/src/lib.rs @@ -0,0 +1,4 @@ +pub mod contract; +pub mod messages; +#[cfg(any(test, feature = "mt"))] +pub mod multitest; diff --git a/examples/contracts/custom/src/messages.rs b/examples/contracts/custom/src/messages.rs new file mode 100644 index 00000000..9461363f --- /dev/null +++ b/examples/contracts/custom/src/messages.rs @@ -0,0 +1,21 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{CustomMsg, CustomQuery}; + +#[cw_serde] +pub struct CountResponse { + pub count: u64, +} + +#[cw_serde] +pub enum CounterMsg { + Increment {}, +} + +#[cw_serde] +pub enum CounterQuery { + Count {}, +} + +impl CustomMsg for CounterMsg {} + +impl CustomQuery for CounterQuery {} diff --git a/examples/contracts/custom/src/multitest.rs b/examples/contracts/custom/src/multitest.rs new file mode 100644 index 00000000..f44455f7 --- /dev/null +++ b/examples/contracts/custom/src/multitest.rs @@ -0,0 +1,2 @@ +mod custom_module; +mod tests; diff --git a/examples/contracts/custom/src/multitest/custom_module.rs b/examples/contracts/custom/src/multitest/custom_module.rs new file mode 100644 index 00000000..7a10d52e --- /dev/null +++ b/examples/contracts/custom/src/multitest/custom_module.rs @@ -0,0 +1,96 @@ +use cosmwasm_schema::schemars::JsonSchema; +use cosmwasm_std::testing::{MockApi, MockStorage}; +use cosmwasm_std::{ + to_binary, Addr, Api, Binary, BlockInfo, CustomQuery, Empty, Querier, StdError, StdResult, + Storage, +}; +use cw_multi_test::{AppResponse, BankKeeper, CosmosRouter, Module, WasmKeeper}; +use cw_storage_plus::Item; +use serde::de::DeserializeOwned; +use std::fmt::Debug; + +use crate::messages::{CountResponse, CounterMsg, CounterQuery}; + +pub type CustomApp = cw_multi_test::App< + BankKeeper, + MockApi, + MockStorage, + CustomModule, + WasmKeeper, +>; + +pub struct CustomModule { + pub counter: Item<'static, u64>, +} + +impl CustomModule { + pub fn new() -> Self { + Self { + counter: Item::new("counter"), + } + } + + pub fn save_counter(&self, storage: &mut dyn Storage, value: u64) -> StdResult<()> { + self.counter.save(storage, &value) + } +} + +impl Module for CustomModule { + type ExecT = CounterMsg; + type QueryT = CounterQuery; + type SudoT = Empty; + + fn execute( + &self, + _api: &dyn Api, + storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _sender: Addr, + msg: Self::ExecT, + ) -> anyhow::Result + where + ExecC: Debug + Clone + PartialEq + JsonSchema + DeserializeOwned + 'static, + QueryC: CustomQuery + DeserializeOwned + 'static, + { + match msg { + CounterMsg::Increment {} => { + self.counter + .update(storage, |value| Ok::<_, StdError>(value + 1))?; + Ok(AppResponse::default()) + } + } + } + + fn sudo( + &self, + _api: &dyn Api, + _storage: &mut dyn Storage, + _router: &dyn CosmosRouter, + _block: &BlockInfo, + _msg: Self::SudoT, + ) -> anyhow::Result + where + ExecC: Debug + Clone + PartialEq + JsonSchema + DeserializeOwned + 'static, + QueryC: CustomQuery + DeserializeOwned + 'static, + { + Ok(AppResponse::default()) + } + + fn query( + &self, + _api: &dyn Api, + storage: &dyn Storage, + _querier: &dyn Querier, + _block: &BlockInfo, + request: Self::QueryT, + ) -> anyhow::Result { + match request { + CounterQuery::Count {} => { + let count = self.counter.load(storage)?; + let res = CountResponse { count }; + to_binary(&res).map_err(Into::into) + } + } + } +} diff --git a/examples/contracts/custom/src/multitest/tests.rs b/examples/contracts/custom/src/multitest/tests.rs new file mode 100644 index 00000000..a2daac1f --- /dev/null +++ b/examples/contracts/custom/src/multitest/tests.rs @@ -0,0 +1,28 @@ +use sylvia::multitest::App; + +use crate::contract::multitest_utils::CodeId; +use crate::messages::{CounterMsg, CounterQuery}; + +use super::custom_module::{CustomApp, CustomModule}; + +#[test] +fn test_custom() { + let owner = "owner"; + + let mt_app = cw_multi_test::BasicAppBuilder::::new_custom() + .with_custom(CustomModule::new()) + .build(|router, _, storage| { + router.custom.save_counter(storage, 0).unwrap(); + }); + + let app = App::::new(mt_app); + + let code_id = CodeId::store_code(&app); + + let contract = code_id.instantiate().call(owner).unwrap(); + + contract.send_custom().call(owner).unwrap(); + + let count = contract.query_custom().unwrap().count; + assert_eq!(count, 1); +} diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index e0456c82..b86402c4 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -1096,6 +1096,7 @@ impl<'a> EntryPoints<'a> { let sylvia = crate_module(); let custom_msg = custom.msg_or_default(); + let custom_query = custom.query_or_default(); #[cfg(not(tarpaulin_include))] { @@ -1106,6 +1107,7 @@ impl<'a> EntryPoints<'a> { Some(_) => quote! {}, None => OverrideEntryPoint::emit_default_entry_point( &custom_msg, + &custom_query, name, error, msg_type, @@ -1120,7 +1122,7 @@ impl<'a> EntryPoints<'a> { Some(reply) => quote! { #[#sylvia ::cw_std::entry_point] pub fn reply( - deps: #sylvia ::cw_std::DepsMut, + deps: #sylvia ::cw_std::DepsMut< #custom_query >, env: #sylvia ::cw_std::Env, msg: #sylvia ::cw_std::Reply, ) -> Result<#sylvia ::cw_std::Response < #custom_msg >, #error> { diff --git a/sylvia-derive/src/parser.rs b/sylvia-derive/src/parser.rs index b809d70d..96717708 100644 --- a/sylvia-derive/src/parser.rs +++ b/sylvia-derive/src/parser.rs @@ -83,20 +83,20 @@ impl MsgType { } #[cfg(not(tarpaulin_include))] - pub fn emit_ctx_params(self) -> TokenStream { + pub fn emit_ctx_params(self, query_type: &Type) -> TokenStream { use MsgType::*; let sylvia = crate_module(); match self { Exec | Instantiate => quote! { - deps: #sylvia ::cw_std::DepsMut, env: #sylvia ::cw_std::Env, info: #sylvia ::cw_std::MessageInfo + deps: #sylvia ::cw_std::DepsMut< #query_type>, env: #sylvia ::cw_std::Env, info: #sylvia ::cw_std::MessageInfo }, Migrate | Reply | Sudo => quote! { - deps: #sylvia ::cw_std::DepsMut, env: #sylvia ::cw_std::Env + deps: #sylvia ::cw_std::DepsMut< #query_type>, env: #sylvia ::cw_std::Env }, Query => quote! { - deps: #sylvia ::cw_std::Deps, env: #sylvia ::cw_std::Env + deps: #sylvia ::cw_std::Deps< #query_type>, env: #sylvia ::cw_std::Env }, } } @@ -540,6 +540,7 @@ impl OverrideEntryPoint { #[cfg(not(tarpaulin_include))] pub fn emit_default_entry_point( custom_msg: &Type, + custom_query: &Type, name: &Type, error: &Type, msg_type: MsgType, @@ -550,7 +551,7 @@ impl OverrideEntryPoint { MsgType::Query => quote! { #sylvia ::cw_std::Binary }, _ => quote! { #sylvia ::cw_std::Response < #custom_msg > }, }; - let params = msg_type.emit_ctx_params(); + let params = msg_type.emit_ctx_params(custom_query); let values = msg_type.emit_ctx_values(); let ep_name = msg_type.emit_ep_name(); let msg_name = msg_type.emit_msg_name(); diff --git a/sylvia/tests/custom_msg.rs b/sylvia/tests/custom_msg.rs index 03d2dae3..9365ba44 100644 --- a/sylvia/tests/custom_msg.rs +++ b/sylvia/tests/custom_msg.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{CustomMsg, CustomQuery, Response, StdResult}; +use cosmwasm_std::{CustomMsg, Response, StdResult}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use sylvia::contract; @@ -14,11 +14,6 @@ pub struct OtherMsg; impl CustomMsg for OtherMsg {} -#[derive(Clone, PartialEq, Serialize, Deserialize, Debug, JsonSchema)] -pub struct MyQuery; - -impl CustomQuery for MyQuery {} - pub struct MyContract; #[derive(Clone, PartialEq, Serialize, Deserialize, Debug, JsonSchema)]