Repository: CosmWasm/sylvia
Pull request #13 (2022-08-19 15:09:18)

Cw1 whitelist
Coverage of Cw1Whitelist in sylvia.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index c706bdd..e25b3a9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -41,6 +41,12 @@ version = "1.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
+[[package]]
+name = "bytes"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
+
 [[package]]
 name = "cfg-if"
 version = "1.0.0"
@@ -98,6 +104,16 @@ dependencies = [
  "uint",
 ]
 
+[[package]]
+name = "cosmwasm-storage"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d18403b07304d15d304dad11040d45bbcaf78d603b4be3fb5e2685c16f9229b5"
+dependencies = [
+ "cosmwasm-std",
+ "serde",
+]
+
 [[package]]
 name = "cpufeatures"
 version = "0.2.2"
@@ -148,6 +164,25 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "cw-multi-test"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca153120cf5b91af88be106b0c6c0263423d959bc813b1592982c02c4691a4ae"
+dependencies = [
+ "anyhow",
+ "cosmwasm-std",
+ "cosmwasm-storage",
+ "cw-storage-plus 0.14.0",
+ "cw-utils",
+ "derivative",
+ "itertools",
+ "prost",
+ "schemars",
+ "serde",
+ "thiserror",
+]
+
 [[package]]
 name = "cw-storage-plus"
 version = "0.13.2"
@@ -159,6 +194,31 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "cw-storage-plus"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c8b264257c4f44c49b7ce09377af63aa040768ecd3fd7bdd2d48a09323a1e90"
+dependencies = [
+ "cosmwasm-std",
+ "schemars",
+ "serde",
+]
+
+[[package]]
+name = "cw-utils"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "414b91f3d7a619bb26c835119d7095804596a1382ddc1d184c33c1d2c17f6c5e"
+dependencies = [
+ "cosmwasm-std",
+ "cw2",
+ "schemars",
+ "semver",
+ "serde",
+ "thiserror",
+]
+
 [[package]]
 name = "cw1"
 version = "0.1.2"
@@ -169,6 +229,33 @@ dependencies = [
  "sylvia",
 ]
 
+[[package]]
+name = "cw1-whitelist"
+version = "0.1.2"
+dependencies = [
+ "anyhow",
+ "cosmwasm-std",
+ "cw-multi-test",
+ "cw-storage-plus 0.13.2",
+ "cw1",
+ "schemars",
+ "serde",
+ "sylvia",
+ "thiserror",
+]
+
+[[package]]
+name = "cw2"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa74c324af8e3506fd8d50759a265bead3f87402e413c840042af5d2808463d6"
+dependencies = [
+ "cosmwasm-std",
+ "cw-storage-plus 0.14.0",
+ "schemars",
+ "serde",
+]
+
 [[package]]
 name = "der"
 version = "0.5.1"
@@ -178,6 +265,17 @@ dependencies = [
  "const-oid",
 ]
 
+[[package]]
+name = "derivative"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "digest"
 version = "0.9.0"
@@ -220,6 +318,12 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "either"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+
 [[package]]
 name = "elliptic-curve"
 version = "0.11.12"
@@ -313,6 +417,15 @@ dependencies = [
  "digest",
 ]
 
+[[package]]
+name = "itertools"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
+dependencies = [
+ "either",
+]
+
 [[package]]
 name = "itoa"
 version = "1.0.2"
@@ -398,6 +511,29 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "prost"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001"
+dependencies = [
+ "bytes",
+ "prost-derive",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe"
+dependencies = [
+ "anyhow",
+ "itertools",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "quote"
 version = "1.0.18"
@@ -479,6 +615,12 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "semver"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711"
+
 [[package]]
 name = "serde"
 version = "1.0.137"
@@ -590,7 +732,7 @@ version = "0.1.2"
 dependencies = [
  "anyhow",
  "cosmwasm-std",
- "cw-storage-plus",
+ "cw-storage-plus 0.13.2",
  "schemars",
  "serde",
  "serde-cw-value",
diff --git a/Cargo.toml b/Cargo.toml
index 410a4f5..d09e89f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,2 +1,2 @@
 [workspace]
-members = ["sylvia", "sylvia-derive", "contracts/cw1"]
+members = ["sylvia", "sylvia-derive", "contracts/cw1", "contracts/cw1-whitelist"]
diff --git a/contracts/cw1-whitelist/Cargo.toml b/contracts/cw1-whitelist/Cargo.toml
new file mode 100644
index 0000000..5bfe7ac
--- /dev/null
+++ b/contracts/cw1-whitelist/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "cw1-whitelist"
+version = "0.1.2"
+authors = ["Jan Woźniak <jan@confio.gmbh>"]
+edition = "2021"
+description = "Coverage of cw1-whitelist sylvia message generation."
+license = "Apache-2.0"
+repository = "https://github.com/CosmWasm/sylvia"
+homepage = "https://cosmwasm.com"
+
+[dependencies]
+cosmwasm-std = { version = "1.0.0", features = ["staking"] }
+schemars = "0.8.1"
+serde = { version = "1", default-features = false, features = ["derive"] }
+sylvia = { version = "0.1.2", path = "../../sylvia" }
+cw1 = { version = "0.1.2", path = "../cw1" }
+cw-storage-plus = "0.13.2"
+thiserror = { version = "1.0.23" }
+
+
+[dev-dependencies]
+anyhow = "1"
+cw-multi-test = "0.14.0"
diff --git a/contracts/cw1-whitelist/src/contract.rs b/contracts/cw1-whitelist/src/contract.rs
new file mode 100644
index 0000000..871268c
--- /dev/null
+++ b/contracts/cw1-whitelist/src/contract.rs
@@ -0,0 +1,171 @@
+use crate::error::ContractError;
+use cosmwasm_std::{Addr, DepsMut, Empty, Env, MessageInfo, Response};
+
+use cw1::Cw1;
+use cw1::*;
+use cw_storage_plus::Map;
+use sylvia::contract;
+
+pub struct Cw1WhitelistContract {
+    members: Map<'static, Addr, Empty>,
+}
+
+impl Cw1 for Cw1WhitelistContract {
+    type Error = ContractError;
+    fn add_member(
+        &self,
+        (deps, _env, _info): (
+            cosmwasm_std::DepsMut,
+            cosmwasm_std::Env,
+            cosmwasm_std::MessageInfo,
+        ),
+        member: String,
+    ) -> Result<Response, Self::Error> {
+        self.members
+            .save(deps.storage, deps.api.addr_validate(&member)?, &Empty {})?;
+
+        Ok(Response::new())
+    }
+
+    fn find_member(
+        &self,
+        (deps, _env): (cosmwasm_std::Deps, cosmwasm_std::Env),
+        member: String,
+    ) -> Result<FindMemberResponse, Self::Error> {
+        let is_present = self
+            .members
+            .has(deps.storage, deps.api.addr_validate(&member)?);
+
+        Ok(FindMemberResponse { is_present })
+    }
+}
+
+#[contract(module=contract, error=ContractError)]
+#[messages(msg as Cw1)]
+impl Cw1WhitelistContract {
+    pub const fn new() -> Self {
+        Self {
+            members: Map::new("members"),
+        }
+    }
+
+    #[msg(instantiate)]
+    pub fn instantiate(
+        &self,
+        (deps, _env, _msg): (DepsMut, Env, MessageInfo),
+        members: Vec<String>,
+    ) -> Result<Response, ContractError> {
+        for addr in members.into_iter() {
+            self.members
+                .save(deps.storage, deps.api.addr_validate(&addr)?, &Empty {})?;
+        }
+
+        Ok(Response::new())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use cosmwasm_std::{
+        from_binary, from_slice,
+        testing::{mock_dependencies, mock_env, mock_info},
+        to_binary,
+    };
+
+    use crate::contract::contract::InstantiateMsg;
+
+    use super::*;
+
+    #[test]
+    fn binary_serialize_instantiate() {
+        let original_msg = InstantiateMsg {
+            members: vec!["member1".to_owned(), "member2".to_owned()],
+        };
+
+        let serialized_msg = to_binary(&original_msg).unwrap();
+        let serialized_msg: InstantiateMsg = from_binary(&serialized_msg).unwrap();
+
+        assert_eq!(serialized_msg, original_msg);
+    }
+
+    #[test]
+    fn slice_deserialize_instantiate() {
+        let deserialized: InstantiateMsg =
+            from_slice(br#"{"members": ["member", "some_member"]}"#).unwrap();
+        assert_eq!(
+            deserialized,
+            InstantiateMsg {
+                members: vec!["member".to_owned(), "some_member".to_owned()]
+            }
+        );
+    }
+
+    #[test]
+    fn instantiate_empty() {
+        let contract = Cw1WhitelistContract::new();
+        let mut deps = mock_dependencies();
+        let info = mock_info("anyone", &[]);
+
+        contract
+            .instantiate((deps.as_mut(), mock_env(), info), vec![])
+            .unwrap();
+    }
+
+    #[test]
+    fn instantiate() {
+        let contract = Cw1WhitelistContract::new();
+        let mut deps = mock_dependencies();
+        let members = vec!["alice".to_owned(), "brian".to_owned(), "carol".to_owned()];
+        let info = mock_info("anyone", &[]);
+
+        contract
+            .instantiate((deps.as_mut(), mock_env(), info), members)
+            .unwrap();
+
+        let resp = contract
+            .find_member((deps.as_ref(), mock_env()), "brian".to_owned())
+            .unwrap();
+
+        assert!(resp.is_present);
+
+        let resp = contract
+            .find_member((deps.as_ref(), mock_env()), "alice".to_owned())
+            .unwrap();
+
+        assert!(resp.is_present);
+
+        let resp = contract
+            .find_member((deps.as_ref(), mock_env()), "carol".to_owned())
+            .unwrap();
+
+        assert!(resp.is_present);
+
+        let resp = contract
+            .find_member((deps.as_ref(), mock_env()), "someone".to_owned())
+            .unwrap();
+
+        assert!(!resp.is_present);
+    }
+
+    #[test]
+    fn add_member() {
+        let contract = Cw1WhitelistContract::new();
+        let mut deps = mock_dependencies();
+        let members = vec!["alice".to_owned(), "brian".to_owned(), "carol".to_owned()];
+        let info = mock_info("anyone", &[]);
+
+        contract
+            .instantiate((deps.as_mut(), mock_env(), info.clone()), members)
+            .unwrap();
+
+        contract
+            .add_member((deps.as_mut(), mock_env(), info), "denis".to_owned())
+            .unwrap();
+
+        let resp = contract
+            .find_member((deps.as_ref(), mock_env()), "denis".to_owned())
+            .unwrap();
+
+        assert!(resp.is_present);
+    }
+}
diff --git a/contracts/cw1-whitelist/src/error.rs b/contracts/cw1-whitelist/src/error.rs
new file mode 100644
index 0000000..40dab9f
--- /dev/null
+++ b/contracts/cw1-whitelist/src/error.rs
@@ -0,0 +1,8 @@
+use cosmwasm_std::StdError;
+use thiserror::Error;
+
+#[derive(Error, Debug)]
+pub enum ContractError {
+    #[error("{0}")]
+    Std(#[from] StdError),
+}
diff --git a/contracts/cw1-whitelist/src/lib.rs b/contracts/cw1-whitelist/src/lib.rs
new file mode 100644
index 0000000..dbad154
--- /dev/null
+++ b/contracts/cw1-whitelist/src/lib.rs
@@ -0,0 +1,45 @@
+mod contract;
+mod error;
+mod multitest;
+
+#[cfg(not(feature = "library"))]
+pub mod entry_points {
+    use cosmwasm_std::{
+        entry_point, from_slice, Binary, Deps, DepsMut, Env, MessageInfo, Response,
+    };
+
+    use crate::{
+        contract::{
+            contract::{ExecMsg, InstantiateMsg, QueryMsg},
+            Cw1WhitelistContract,
+        },
+        error::ContractError,
+    };
+
+    const CONTRACT: Cw1WhitelistContract = Cw1WhitelistContract::new();
+
+    #[entry_point]
+    pub fn instantiate(
+        deps: DepsMut,
+        env: Env,
+        info: MessageInfo,
+        msg: Binary,
+    ) -> Result<Response, ContractError> {
+        from_slice::<InstantiateMsg>(&msg)?.dispatch(&CONTRACT, (deps, env, info))
+    }
+
+    #[entry_point]
+    pub fn execute(
+        deps: DepsMut,
+        env: Env,
+        info: MessageInfo,
+        msg: Binary,
+    ) -> Result<Response, ContractError> {
+        from_slice::<ExecMsg>(&msg)?.dispatch(&CONTRACT, (deps, env, info))
+    }
+
+    #[entry_point]
+    pub fn query(deps: Deps, env: Env, msg: Binary) -> Result<Binary, ContractError> {
+        from_slice::<QueryMsg>(&msg)?.dispatch(&CONTRACT, (deps, env))
+    }
+}
diff --git a/contracts/cw1/src/lib.rs b/contracts/cw1/src/lib.rs
index be843ca..1ba94d8 100644
--- a/contracts/cw1/src/lib.rs
+++ b/contracts/cw1/src/lib.rs
@@ -1,8 +1,15 @@
 #![allow(dead_code)]
 use cosmwasm_std::{Deps, DepsMut, Env, MessageInfo, Response, StdError};
 
+use schemars::JsonSchema;
+use serde::{Deserialize, Serialize};
 use sylvia::interface;
 
+#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)]
+pub struct FindMemberResponse {
+    pub is_present: bool,
+}
+
 #[interface(module=msg)]
 pub trait Cw1 {
     type Error: From<StdError>;
@@ -15,7 +22,11 @@ pub trait Cw1 {
     ) -> Result<Response, Self::Error>;
 
     #[msg(query)]
-    fn find_member(&self, ctx: (Deps, Env), member: String) -> Result<Response, Self::Error>;
+    fn find_member(
+        &self,
+        ctx: (Deps, Env),
+        member: String,
+    ) -> Result<FindMemberResponse, Self::Error>;
 }
 
 #[cfg(test)]


Test patch:
diff --git a/contracts/cw1-whitelist/src/multitest.rs b/contracts/cw1-whitelist/src/multitest.rs
new file mode 100644
index 0000000..ed23b26
--- /dev/null
+++ b/contracts/cw1-whitelist/src/multitest.rs
@@ -0,0 +1,98 @@
+#[cfg(test)]
+mod test {
+    use crate::contract::{contract::InstantiateMsg, Cw1WhitelistContract};
+    use anyhow::{bail, Result as AnyResult};
+    use cosmwasm_std::{
+        from_slice, Addr, Binary, DepsMut, Empty, Env, MessageInfo, Reply, Response,
+    };
+    use cw1::{
+        msg::{ExecMsg, QueryMsg},
+        FindMemberResponse,
+    };
+    use cw_multi_test::{AppBuilder, Contract, Executor};
+
+    impl Contract<Empty> for Cw1WhitelistContract {
+        fn instantiate(
+            &self,
+            deps: DepsMut,
+            env: Env,
+            info: MessageInfo,
+            msg: Vec<u8>,
+        ) -> AnyResult<Response<Empty>> {
+            from_slice::<InstantiateMsg>(&msg)?
+                .dispatch(self, (deps, env, info))
+                .map_err(Into::into)
+        }
+
+        fn execute(
+            &self,
+            deps: DepsMut,
+            env: Env,
+            info: MessageInfo,
+            msg: Vec<u8>,
+        ) -> AnyResult<Response<Empty>> {
+            from_slice::<ExecMsg>(&msg)?
+                .dispatch(self, (deps, env, info))
+                .map_err(Into::into)
+        }
+
+        fn query(&self, deps: cosmwasm_std::Deps, env: Env, msg: Vec<u8>) -> AnyResult<Binary> {
+            from_slice::<QueryMsg>(&msg)?
+                .dispatch(self, (deps, env))
+                .map_err(Into::into)
+        }
+
+        fn sudo(&self, _deps: DepsMut, _env: Env, _msg: Vec<u8>) -> AnyResult<Response<Empty>> {
+            bail!("sudo not implemented for contract")
+        }
+
+        fn reply(&self, _deps: DepsMut, _env: Env, _msg: Reply) -> AnyResult<Response<Empty>> {
+            bail!("reply not implemented for contract")
+        }
+
+        fn migrate(&self, _deps: DepsMut, _env: Env, _msg: Vec<u8>) -> AnyResult<Response<Empty>> {
+            bail!("migrate not implemented for contract")
+        }
+    }
+
+    #[test]
+    fn entry_points() {
+        let mut app = AppBuilder::new().build(|_, _, _| ());
+        let contract_id = app.store_code(Box::new(Cw1WhitelistContract::new()));
+
+        let contract = app
+            .instantiate_contract(
+                contract_id,
+                Addr::unchecked("Owner"),
+                &InstantiateMsg {
+                    members: vec!["member".to_owned()],
+                },
+                &[],
+                "contract",
+                None,
+            )
+            .unwrap();
+
+        app.execute_contract(
+            Addr::unchecked("owner"),
+            contract.clone(),
+            &ExecMsg::AddMember {
+                member: "other_member".to_owned(),
+            },
+            &[],
+        )
+        .unwrap();
+
+        let resp: FindMemberResponse = app
+            .wrap()
+            .query_wasm_smart(
+                contract,
+                &QueryMsg::FindMember {
+                    member: "other_member".to_owned(),
+                },
+            )
+            .unwrap();
+
+        assert_eq!(resp, FindMemberResponse { is_present: true });
+    }
+}