Skip to content

Commit

Permalink
cw1-subkeys-ng: Improved some splitting
Browse files Browse the repository at this point in the history
  • Loading branch information
hashedone committed Oct 21, 2021
1 parent 5aee758 commit b7ca929
Show file tree
Hide file tree
Showing 9 changed files with 456 additions and 246 deletions.
12 changes: 9 additions & 3 deletions contracts/cw1-whitelist-ng/examples/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fs::create_dir_all;

use cosmwasm_schema::{export_schema, export_schema_with_title, remove_schemas, schema_for};

use cw1_whitelist_ng::msg::{AdminListResponse, ExecuteMsg, InstantiateMsg, QueryMsg};
use cw1_whitelist_ng::msg::*;

fn main() {
let mut out_dir = current_dir().unwrap();
Expand All @@ -12,7 +12,13 @@ fn main() {
remove_schemas(&out_dir).unwrap();

export_schema(&schema_for!(InstantiateMsg), &out_dir);
export_schema_with_title(&schema_for!(ExecuteMsg), &out_dir, "ExecuteMsg");
export_schema_with_title(&schema_for!(QueryMsg), &out_dir, "QueryMsg");
export_schema_with_title(&schema_for!(Cw1ExecMsg), &out_dir, "Cw1ExecMsg");
export_schema_with_title(&schema_for!(WhitelistExecMsg), &out_dir, "WhitelistExecMsg");
export_schema_with_title(&schema_for!(Cw1QueryMsg), &out_dir, "Cw1QueryMsg");
export_schema_with_title(
&schema_for!(WhitelistQueryMsg),
&out_dir,
"WhitelistQueryMsg",
);
export_schema(&schema_for!(AdminListResponse), &out_dir);
}
165 changes: 141 additions & 24 deletions contracts/cw1-whitelist-ng/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
use cosmwasm_std::{Addr, Api, CosmosMsg, Deps, DepsMut, Env, MessageInfo, Response, StdResult};
use cosmwasm_std::{
from_slice, Addr, Api, Binary, CosmosMsg, Deps, DepsMut, Env, MessageInfo, Response, StdError,
StdResult,
};
use serde::de::DeserializeOwned;

use crate::error::ContractError;
use crate::interfaces::Cw1Whitelist;
use crate::msg::AdminListResponse;
use crate::interfaces::*;
use crate::msg::{
AdminListResponse, Cw1ExecMsg, Cw1QueryMsg, InstantiateMsg, WhitelistExecMsg, WhitelistQueryMsg,
};
use crate::state::{AdminList, Cw1WhitelistContract};

use cw1::CanExecuteResponse;
Expand All @@ -12,7 +18,7 @@ use cw2::set_contract_version;
const CONTRACT_NAME: &str = "crates.io:cw1-whitelist";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

fn validate_admins(api: &dyn Api, admins: &[String]) -> StdResult<Vec<Addr>> {
pub fn validate_admins(api: &dyn Api, admins: &[String]) -> StdResult<Vec<Addr>> {
admins.iter().map(|addr| api.addr_validate(&addr)).collect()
}

Expand All @@ -35,13 +41,91 @@ impl<T> Cw1WhitelistContract<T> {
Ok(Response::new())
}

fn is_admin(&self, deps: Deps, addr: &str) -> Result<bool, ContractError> {
pub fn is_admin(&self, deps: Deps, addr: &str) -> Result<bool, ContractError> {
let cfg = self.admin_list.load(deps.storage)?;
Ok(cfg.is_admin(addr))
}

// Entry points, to be called only in actual entry points and by multitest `Contract`
// implementation
pub(crate) fn entry_instantiate(
&self,
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: &[u8],
) -> Result<Response<T>, ContractError> {
let msg: InstantiateMsg = from_slice(msg)?;
msg.dispatch(deps, env, info, self)
}

pub(crate) fn entry_execute(
&self,
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: &[u8],
) -> Result<Response<T>, ContractError>
where
T: DeserializeOwned,
{
let mut errs = vec![];

match from_slice::<Cw1ExecMsg<T>>(msg) {
Ok(msg) => return msg.dispatch(deps, env, info, self),
Err(err) => errs.push(err),
}

match from_slice::<WhitelistExecMsg>(msg) {
Ok(msg) => return msg.dispatch(deps, env, info, self),
Err(err) => errs.push(err),
}

let msg: String = errs
.into_iter()
.flat_map(|err| {
std::iter::once(err.to_string()).chain(std::iter::once("\n".to_owned()))
})
.collect();

let err = StdError::parse_err("Cw1WhitelistExecMsg", msg);
Err(err.into())
}

pub(crate) fn entry_query(
&self,
deps: Deps,
env: Env,
msg: &[u8],
) -> Result<Binary, ContractError>
where
T: DeserializeOwned,
{
let mut errs = vec![];

match from_slice::<Cw1QueryMsg<T>>(msg) {
Ok(msg) => return msg.dispatch(deps, env, self),
Err(err) => errs.push(err),
}

match from_slice::<WhitelistQueryMsg>(msg) {
Ok(msg) => return msg.dispatch(deps, env, self),
Err(err) => errs.push(err),
}

let msg: String = errs
.into_iter()
.flat_map(|err| {
std::iter::once(err.to_string()).chain(std::iter::once("\n".to_owned()))
})
.collect();

let err = StdError::parse_err("Cw1WhitelistExecMsg", msg);
Err(err.into())
}
}

impl<T> Cw1Whitelist<T> for Cw1WhitelistContract<T> {
impl<T> Cw1<T> for Cw1WhitelistContract<T> {
type Error = ContractError;

fn execute(
Expand All @@ -61,6 +145,22 @@ impl<T> Cw1Whitelist<T> for Cw1WhitelistContract<T> {
}
}

fn can_execute(
&self,
deps: Deps,
_env: Env,
sender: String,
_msg: CosmosMsg<T>,
) -> Result<CanExecuteResponse, Self::Error> {
Ok(CanExecuteResponse {
can_execute: self.is_admin(deps, &sender)?,
})
}
}

impl<T> Whitelist<T> for Cw1WhitelistContract<T> {
type Error = ContractError;

fn freeze(
&self,
deps: DepsMut,
Expand Down Expand Up @@ -108,30 +208,18 @@ impl<T> Cw1Whitelist<T> for Cw1WhitelistContract<T> {
mutable: cfg.mutable,
})
}

fn can_execute(
&self,
deps: Deps,
_env: Env,
sender: String,
_msg: CosmosMsg<T>,
) -> Result<CanExecuteResponse, Self::Error> {
Ok(CanExecuteResponse {
can_execute: self.is_admin(deps, &sender)?,
})
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::msg::ExecuteMsg;
use crate::msg::*;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{coin, coins, to_binary, BankMsg, Empty, StakingMsg, SubMsg, WasmMsg};
use cosmwasm_std::{coin, coins, to_binary, BankMsg, StakingMsg, SubMsg, WasmMsg};

#[test]
fn instantiate_and_modify_config() {
let contract = Cw1WhitelistContract::<Empty>::new();
let contract = Cw1WhitelistContract::native();

let mut deps = mock_dependencies(&[]);

Expand Down Expand Up @@ -211,7 +299,7 @@ mod tests {

#[test]
fn execute_messages_has_proper_permissions() {
let contract = Cw1WhitelistContract::<Empty>::new();
let contract = Cw1WhitelistContract::native();
let mut deps = mock_dependencies(&[]);

let alice = "alice";
Expand All @@ -225,7 +313,7 @@ mod tests {
.instantiate(deps.as_mut(), mock_env(), info, admins, false)
.unwrap();

let freeze: ExecuteMsg = ExecuteMsg::Freeze {};
let freeze = WhitelistExecMsg::Freeze {};
let msgs = vec![
BankMsg::Send {
to_address: bob.to_string(),
Expand Down Expand Up @@ -259,9 +347,38 @@ mod tests {
assert_eq!(res.attributes, [("action", "execute")]);
}

#[test]
fn execute_custom_messages_works() {
let contract = Cw1WhitelistContract::<String>::new();
let mut deps = mock_dependencies(&[]);
let alice = "alice";

let admins = vec![alice.to_owned()];
let info = mock_info(&alice, &[]);
contract
.instantiate(deps.as_mut(), mock_env(), info, admins, false)
.unwrap();

let msgs = vec![CosmosMsg::Custom("msg".to_owned())];

let res = contract
.execute(
deps.as_mut(),
mock_env(),
mock_info(&alice, &[]),
msgs.clone(),
)
.unwrap();

assert_eq!(
res.messages,
msgs.into_iter().map(SubMsg::new).collect::<Vec<_>>()
);
}

#[test]
fn can_execute_query_works() {
let contract = Cw1WhitelistContract::<Empty>::new();
let contract = Cw1WhitelistContract::native();
let mut deps = mock_dependencies(&[]);

let alice = "alice";
Expand Down
22 changes: 13 additions & 9 deletions contracts/cw1-whitelist-ng/src/interfaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::msg::AdminListResponse;
use cosmwasm_std::{CosmosMsg, Deps, DepsMut, Env, MessageInfo, Response};
use cw1::query::CanExecuteResponse;

pub trait Cw1Whitelist<T> {
pub trait Cw1<T> {
type Error;

fn execute(
Expand All @@ -13,6 +13,18 @@ pub trait Cw1Whitelist<T> {
msgs: Vec<CosmosMsg<T>>,
) -> Result<Response<T>, Self::Error>;

fn can_execute(
&self,
deps: Deps,
env: Env,
sender: String,
msg: CosmosMsg<T>,
) -> Result<CanExecuteResponse, Self::Error>;
}

pub trait Whitelist<T> {
type Error;

fn freeze(
&self,
deps: DepsMut,
Expand All @@ -29,12 +41,4 @@ pub trait Cw1Whitelist<T> {
) -> Result<Response<T>, Self::Error>;

fn admin_list(&self, deps: Deps, env: Env) -> Result<AdminListResponse, Self::Error>;

fn can_execute(
&self,
deps: Deps,
env: Env,
sender: String,
msg: CosmosMsg<T>,
) -> Result<CanExecuteResponse, Self::Error>;
}
29 changes: 7 additions & 22 deletions contracts/cw1-whitelist-ng/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,34 @@ pub mod state;
#[cfg(not(feature = "library"))]
mod binary {
use crate::error::ContractError;
use crate::msg::*;
use crate::state::Cw1WhitelistContract;
use cosmwasm_std::{Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response};
use cosmwasm_std::{entry_point, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response};

const CONTRACT: Cw1WhitelistContract<Empty> = Cw1WhitelistContract::new();

use cosmwasm_std::entry_point;

#[entry_point]
pub fn instantiate(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: InstantiateMsg,
msg: Binary,
) -> Result<Response, ContractError> {
let InstantiateMsg { admins, mutable } = msg;
CONTRACT.instantiate(deps, env, info, admins, mutable)
CONTRACT.entry_instantiate(deps, env, info, &msg)
}

// This would be probably generated with `msg` being `Binary` or equivalent, and the
// deserialization would be generated in. The idea is to allow deserialize different messages
// (so different interfaces), trying top-to bottom and handle first successfully deserialized.
//
// There are two open questions:
// 1. How to ensure the message doesn't deserialize to many message types? The simplest and
// probably best approach is not to. Just well define order in which messages are tried.
// 2. Which error to return if no message type matches the received type. The easy approach is
// to return the first or the last failure, however I think the best would be somehow
// collect are failures and return accumulated error
#[entry_point]
pub fn execute(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: ExecuteMsg<Empty>,
msg: Binary,
) -> Result<Response, ContractError> {
msg.dispatch(deps, env, info, &CONTRACT)
CONTRACT.entry_execute(deps, env, info, &msg)
}

// Same note as for `execute`
#[entry_point]
pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result<Binary, ContractError> {
msg.dispatch(deps, env, &CONTRACT)
pub fn query(deps: Deps, env: Env, msg: Binary) -> Result<Binary, ContractError> {
CONTRACT.entry_query(deps, env, &msg)
}
}

Expand Down

0 comments on commit b7ca929

Please sign in to comment.