Skip to content

Commit

Permalink
Add query helpers to Item and Map and use them in cw4 helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanfrey committed Sep 10, 2021
1 parent ec8219a commit 5371f7d
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 40 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/cw4/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ homepage = "https://cosmwasm.com"
documentation = "https://docs.cosmwasm.com"

[dependencies]
cw-storage-plus = { path = "../storage-plus", version = "0.8.1" }
cosmwasm-std = { version = "0.16.0" }
schemars = "0.8.1"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
Expand Down
44 changes: 7 additions & 37 deletions packages/cw4/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use cosmwasm_std::{
from_slice, to_binary, to_vec, Addr, Binary, ContractResult, CosmosMsg, Empty, QuerierWrapper,
QueryRequest, StdError, StdResult, SystemResult, WasmMsg, WasmQuery,
to_binary, Addr, CosmosMsg, Empty, QuerierWrapper, QueryRequest, StdResult, WasmMsg, WasmQuery,
};

use crate::msg::Cw4ExecuteMsg;
use crate::query::HooksResponse;
use crate::{
member_key, AdminResponse, Cw4QueryMsg, Member, MemberListResponse, MemberResponse, TOTAL_KEY,
AdminResponse, Cw4QueryMsg, Member, MemberListResponse, MemberResponse, MEMBERS_KEY, TOTAL_KEY,
};
use cw_storage_plus::{Item, Map};

/// Cw4Contract is a wrapper around Addr that provides a lot of helpers
/// for working with cw4 contracts
Expand Down Expand Up @@ -62,14 +62,6 @@ impl Cw4Contract {
.into())
}

fn encode_raw_query<T: Into<Binary>>(&self, key: T) -> QueryRequest<Empty> {
WasmQuery::Raw {
contract_addr: self.addr().into(),
key: key.into(),
}
.into()
}

/// Show the hooks
pub fn hooks(&self, querier: &QuerierWrapper) -> StdResult<Vec<String>> {
let query = self.encode_smart_query(Cw4QueryMsg::Hooks {})?;
Expand All @@ -79,36 +71,14 @@ impl Cw4Contract {

/// Read the total weight
pub fn total_weight(&self, querier: &QuerierWrapper) -> StdResult<u64> {
let query = self.encode_raw_query(TOTAL_KEY.as_bytes());
querier.query(&query)
const TOTAL: Item<u64> = Item::new(TOTAL_KEY);
TOTAL.query(querier, self.addr())
}

/// Check if this address is a member, and if so, with which weight
pub fn is_member(&self, querier: &QuerierWrapper, addr: &Addr) -> StdResult<Option<u64>> {
let path = member_key(addr.as_ref());
let query = self.encode_raw_query(path);

// We have to copy the logic of Querier.query to handle the empty case, and not
// try to decode empty result into a u64.
// TODO: add similar API on Querier - this is not the first time I came across it
let raw = to_vec(&query)?;
match querier.raw_query(&raw) {
SystemResult::Err(system_err) => Err(StdError::generic_err(format!(
"Querier system error: {}",
system_err
))),
SystemResult::Ok(ContractResult::Err(contract_err)) => Err(StdError::generic_err(
format!("Querier contract error: {}", contract_err),
)),
SystemResult::Ok(ContractResult::Ok(value)) => {
// This is the only place we customize
if value.is_empty() {
Ok(None)
} else {
from_slice(&value)
}
}
}
const MEMBERS: Map<&Addr, u64> = Map::new(MEMBERS_KEY);
MEMBERS.query(querier, self.addr(), addr)
}

/// Return the member's weight at the given snapshot - requires a smart query
Expand Down
14 changes: 13 additions & 1 deletion packages/storage-plus/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use serde::de::DeserializeOwned;
use serde::Serialize;
use std::marker::PhantomData;

use cosmwasm_std::{to_vec, StdError, StdResult, Storage};
use cosmwasm_std::{to_vec, Addr, QuerierWrapper, StdError, StdResult, Storage, WasmQuery};

use crate::helpers::{may_deserialize, must_deserialize};

Expand Down Expand Up @@ -72,6 +72,18 @@ where
self.save(store, &output)?;
Ok(output)
}

/// If you import the proper Item from the remote contract, this will let you read the data
/// from a remote contract in a type-safe way using WasmQuery::RawQuery.
///
/// Note that we expect an Item to be set, and error if there is no data there
pub fn query(&self, querier: &QuerierWrapper, remote_contract: Addr) -> StdResult<T> {
let request = WasmQuery::Raw {
contract_addr: remote_contract.into(),
key: self.storage_key.into(),
};
querier.query(&request.into())
}
}

#[cfg(test)]
Expand Down
40 changes: 39 additions & 1 deletion packages/storage-plus/src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ use crate::keys::{EmptyPrefix, Prefixer};
use crate::path::Path;
#[cfg(feature = "iterator")]
use crate::prefix::{Bound, Prefix};
use cosmwasm_std::{StdError, StdResult, Storage};
use cosmwasm_std::{
from_slice, to_vec, Addr, ContractResult, Empty, QuerierWrapper, QueryRequest, StdError,
StdResult, Storage, SystemResult, WasmQuery,
};

#[derive(Debug, Clone)]
pub struct Map<'a, K, T> {
Expand Down Expand Up @@ -83,6 +86,41 @@ where
{
self.key(k).update(store, action)
}

/// If you import the proper Map from the remote contract, this will let you read the data
/// from a remote contract in a type-safe way using WasmQuery::RawQuery
pub fn query(
&self,
querier: &QuerierWrapper,
remote_contract: Addr,
k: K,
) -> StdResult<Option<T>> {
let key = self.key(k).storage_key.into();
let request: QueryRequest<Empty> = WasmQuery::Raw {
contract_addr: remote_contract.into(),
key,
}
.into();

let raw = to_vec(&request).map_err(|serialize_err| {
StdError::generic_err(format!("Serializing QueryRequest: {}", serialize_err))
})?;
let result = match querier.raw_query(&raw) {
SystemResult::Err(system_err) => Err(StdError::generic_err(format!(
"Querier system error: {}",
system_err
))),
SystemResult::Ok(ContractResult::Err(contract_err)) => Err(StdError::generic_err(
format!("Querier contract error: {}", contract_err),
)),
SystemResult::Ok(ContractResult::Ok(value)) => Ok(value),
}?;
if result.is_empty() {
Ok(None)
} else {
from_slice(&result).map(Some)
}
}
}

// short-cut for simple keys, rather than .prefix(()).range(...)
Expand Down
2 changes: 1 addition & 1 deletion packages/storage-plus/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ where
T: Serialize + DeserializeOwned,
{
/// all namespaces prefixes and concatenated with the key
storage_key: Vec<u8>,
pub(crate) storage_key: Vec<u8>,
// see https://doc.rust-lang.org/std/marker/struct.PhantomData.html#unused-type-parameters for why this is needed
data: PhantomData<T>,
}
Expand Down

0 comments on commit 5371f7d

Please sign in to comment.