cw4/
helpers.rs

1use cosmwasm_schema::cw_serde;
2use cosmwasm_std::{
3    to_json_binary, Addr, CosmosMsg, CustomQuery, QuerierWrapper, QueryRequest, StdResult, WasmMsg,
4    WasmQuery,
5};
6
7use crate::msg::Cw4ExecuteMsg;
8use crate::query::HooksResponse;
9use crate::{
10    AdminResponse, Cw4QueryMsg, Member, MemberListResponse, MemberResponse, MEMBERS_KEY, TOTAL_KEY,
11};
12use cw_storage_plus::{Item, Map};
13
14/// Cw4Contract is a wrapper around Addr that provides a lot of helpers
15/// for working with cw4 contracts
16///
17/// If you wish to persist this, convert to Cw4CanonicalContract via .canonical()
18#[cw_serde]
19pub struct Cw4Contract(pub Addr);
20
21impl Cw4Contract {
22    pub fn new(addr: Addr) -> Self {
23        Cw4Contract(addr)
24    }
25
26    pub fn addr(&self) -> Addr {
27        self.0.clone()
28    }
29
30    fn encode_msg(&self, msg: Cw4ExecuteMsg) -> StdResult<CosmosMsg> {
31        Ok(WasmMsg::Execute {
32            contract_addr: self.addr().into(),
33            msg: to_json_binary(&msg)?,
34            funds: vec![],
35        }
36        .into())
37    }
38
39    pub fn add_hook<T: Into<String>>(&self, addr: T) -> StdResult<CosmosMsg> {
40        let msg = Cw4ExecuteMsg::AddHook { addr: addr.into() };
41        self.encode_msg(msg)
42    }
43
44    pub fn remove_hook<T: Into<String>>(&self, addr: T) -> StdResult<CosmosMsg> {
45        let msg = Cw4ExecuteMsg::RemoveHook { addr: addr.into() };
46        self.encode_msg(msg)
47    }
48
49    pub fn update_admin<T: Into<String>>(&self, admin: Option<T>) -> StdResult<CosmosMsg> {
50        let msg = Cw4ExecuteMsg::UpdateAdmin {
51            admin: admin.map(|x| x.into()),
52        };
53        self.encode_msg(msg)
54    }
55
56    fn encode_smart_query<Q: CustomQuery>(&self, msg: Cw4QueryMsg) -> StdResult<QueryRequest<Q>> {
57        Ok(WasmQuery::Smart {
58            contract_addr: self.addr().into(),
59            msg: to_json_binary(&msg)?,
60        }
61        .into())
62    }
63
64    /// Show the hooks
65    pub fn hooks<Q: CustomQuery>(&self, querier: &QuerierWrapper<Q>) -> StdResult<Vec<String>> {
66        let query = self.encode_smart_query(Cw4QueryMsg::Hooks {})?;
67        let res: HooksResponse = querier.query(&query)?;
68        Ok(res.hooks)
69    }
70
71    /// Read the total weight
72    pub fn total_weight(&self, querier: &QuerierWrapper) -> StdResult<u64> {
73        Item::new(TOTAL_KEY).query(querier, self.addr())
74    }
75
76    /// Check if this address is a member and returns its weight
77    pub fn is_member(
78        &self,
79        querier: &QuerierWrapper,
80        member: &Addr,
81        height: Option<u64>,
82    ) -> StdResult<Option<u64>> {
83        match height {
84            Some(height) => self.member_at_height(querier, member.to_string(), height.into()),
85            None => Map::new(MEMBERS_KEY).query(querier, self.addr(), member),
86        }
87    }
88
89    /// Check if this address is a member, and if its weight is >= 1
90    /// Returns member's weight in positive case
91    pub fn is_voting_member(
92        &self,
93        querier: &QuerierWrapper,
94        member: &Addr,
95        height: impl Into<Option<u64>>,
96    ) -> StdResult<Option<u64>> {
97        if let Some(weight) = self.member_at_height(querier, member.to_string(), height.into())? {
98            if weight >= 1 {
99                return Ok(Some(weight));
100            }
101        }
102        Ok(None)
103    }
104
105    /// Return the member's weight at the given snapshot - requires a smart query
106    pub fn member_at_height(
107        &self,
108        querier: &QuerierWrapper,
109        member: impl Into<String>,
110        at_height: Option<u64>,
111    ) -> StdResult<Option<u64>> {
112        let query = self.encode_smart_query(Cw4QueryMsg::Member {
113            addr: member.into(),
114            at_height,
115        })?;
116        let res: MemberResponse = querier.query(&query)?;
117        Ok(res.weight)
118    }
119
120    pub fn list_members(
121        &self,
122        querier: &QuerierWrapper,
123        start_after: Option<String>,
124        limit: Option<u32>,
125    ) -> StdResult<Vec<Member>> {
126        let query = self.encode_smart_query(Cw4QueryMsg::ListMembers { start_after, limit })?;
127        let res: MemberListResponse = querier.query(&query)?;
128        Ok(res.members)
129    }
130
131    /// Read the admin
132    pub fn admin(&self, querier: &QuerierWrapper) -> StdResult<Option<String>> {
133        let query = self.encode_smart_query(Cw4QueryMsg::Admin {})?;
134        let res: AdminResponse = querier.query(&query)?;
135        Ok(res.admin)
136    }
137}