nym_validator_client/nyxd/contract_traits/
multisig_query_client.rs1use crate::nyxd::contract_traits::NymContractsProvider;
5use crate::nyxd::error::NyxdError;
6use crate::nyxd::CosmWasmClient;
7use async_trait::async_trait;
8use cw3::{
9 ProposalListResponse, ProposalResponse, VoteInfo, VoteListResponse, VoteResponse, VoterDetail,
10 VoterListResponse, VoterResponse,
11};
12use cw_utils::ThresholdResponse;
13use nym_multisig_contract_common::msg::QueryMsg as MultisigQueryMsg;
14use serde::Deserialize;
15
16#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
17#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
18pub trait MultisigQueryClient {
19 async fn query_multisig_contract<T>(&self, query: MultisigQueryMsg) -> Result<T, NyxdError>
20 where
21 for<'a> T: Deserialize<'a>;
22
23 async fn query_threshold(&self) -> Result<ThresholdResponse, NyxdError> {
24 self.query_multisig_contract(MultisigQueryMsg::Threshold {})
25 .await
26 }
27
28 async fn query_proposal(&self, proposal_id: u64) -> Result<ProposalResponse, NyxdError> {
29 self.query_multisig_contract(MultisigQueryMsg::Proposal { proposal_id })
30 .await
31 }
32
33 async fn list_proposals(
34 &self,
35 start_after: Option<u64>,
36 limit: Option<u32>,
37 ) -> Result<ProposalListResponse, NyxdError> {
38 self.query_multisig_contract(MultisigQueryMsg::ListProposals { start_after, limit })
39 .await
40 }
41
42 async fn reverse_proposals(
43 &self,
44 start_before: Option<u64>,
45 limit: Option<u32>,
46 ) -> Result<ProposalListResponse, NyxdError> {
47 self.query_multisig_contract(MultisigQueryMsg::ReverseProposals {
48 start_before,
49 limit,
50 })
51 .await
52 }
53
54 async fn query_vote(&self, proposal_id: u64, voter: String) -> Result<VoteResponse, NyxdError> {
55 self.query_multisig_contract(MultisigQueryMsg::Vote { proposal_id, voter })
56 .await
57 }
58
59 async fn list_votes(
60 &self,
61 proposal_id: u64,
62 start_after: Option<String>,
63 limit: Option<u32>,
64 ) -> Result<VoteListResponse, NyxdError> {
65 self.query_multisig_contract(MultisigQueryMsg::ListVotes {
66 proposal_id,
67 start_after,
68 limit,
69 })
70 .await
71 }
72
73 async fn query_voter(&self, address: String) -> Result<VoterResponse, NyxdError> {
74 self.query_multisig_contract(MultisigQueryMsg::Voter { address })
75 .await
76 }
77
78 async fn list_voters(
79 &self,
80 start_after: Option<String>,
81 limit: Option<u32>,
82 ) -> Result<VoterListResponse, NyxdError> {
83 self.query_multisig_contract(MultisigQueryMsg::ListVoters { start_after, limit })
84 .await
85 }
86
87 async fn query_config(&self) -> Result<(), NyxdError> {
88 unimplemented!("requires exporting state::Config type")
89 }
90}
91
92#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
95#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
96pub trait PagedMultisigQueryClient: MultisigQueryClient {
97 async fn get_all_proposals(&self) -> Result<Vec<ProposalResponse>, NyxdError> {
99 let mut proposals = Vec::new();
100 let mut start_after = None;
101
102 loop {
103 let mut paged_response = self.list_proposals(start_after.take(), None).await?;
104
105 let last_id = paged_response.proposals.last().map(|prop| prop.id);
106 proposals.append(&mut paged_response.proposals);
107
108 if let Some(start_after_res) = last_id {
109 start_after = Some(start_after_res)
110 } else {
111 break;
112 }
113 }
114
115 Ok(proposals)
116 }
117
118 async fn get_all_voters(&self) -> Result<Vec<VoterDetail>, NyxdError> {
119 let mut voters = Vec::new();
120 let mut start_after = None;
121
122 loop {
123 let mut paged_response = self.list_voters(start_after.take(), None).await?;
124
125 let last_voter = paged_response.voters.last().map(|prop| prop.addr.clone());
126 voters.append(&mut paged_response.voters);
127
128 if let Some(start_after_res) = last_voter {
129 start_after = Some(start_after_res)
130 } else {
131 break;
132 }
133 }
134
135 Ok(voters)
136 }
137
138 async fn get_all_votes(&self, proposal_id: u64) -> Result<Vec<VoteInfo>, NyxdError> {
139 let mut votes = Vec::new();
140 let mut start_after = None;
141
142 loop {
143 let mut paged_response = self
144 .list_votes(proposal_id, start_after.take(), None)
145 .await?;
146
147 let last_voter = paged_response.votes.last().map(|vote| vote.voter.clone());
148 votes.append(&mut paged_response.votes);
149
150 if let Some(start_after_res) = last_voter {
151 start_after = Some(start_after_res)
152 } else {
153 break;
154 }
155 }
156
157 Ok(votes)
158 }
159}
160
161#[async_trait]
162impl<T> PagedMultisigQueryClient for T where T: MultisigQueryClient {}
163
164#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
165#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
166impl<C> MultisigQueryClient for C
167where
168 C: CosmWasmClient + NymContractsProvider + Send + Sync,
169{
170 async fn query_multisig_contract<T>(&self, query: MultisigQueryMsg) -> Result<T, NyxdError>
171 where
172 for<'a> T: Deserialize<'a>,
173 {
174 let multisig_contract_address = &self
175 .multisig_contract_address()
176 .ok_or_else(|| NyxdError::unavailable_contract_address("multisig contract"))?;
177 self.query_contract_smart(multisig_contract_address, &query)
178 .await
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185 use crate::nyxd::contract_traits::tests::IgnoreValue;
186
187 #[allow(dead_code)]
189 fn all_query_variants_are_covered<C: MultisigQueryClient + Send + Sync>(
190 client: C,
191 msg: MultisigQueryMsg,
192 ) {
193 match msg {
194 MultisigQueryMsg::Threshold {} => client.query_threshold().ignore(),
195 MultisigQueryMsg::Proposal { proposal_id } => {
196 client.query_proposal(proposal_id).ignore()
197 }
198 MultisigQueryMsg::ListProposals { start_after, limit } => {
199 client.list_proposals(start_after, limit).ignore()
200 }
201 MultisigQueryMsg::ReverseProposals {
202 start_before,
203 limit,
204 } => client.reverse_proposals(start_before, limit).ignore(),
205 MultisigQueryMsg::Vote { proposal_id, voter } => {
206 client.query_vote(proposal_id, voter).ignore()
207 }
208 MultisigQueryMsg::ListVotes {
209 proposal_id,
210 start_after,
211 limit,
212 } => client.list_votes(proposal_id, start_after, limit).ignore(),
213 MultisigQueryMsg::Voter { address } => client.query_voter(address).ignore(),
214 MultisigQueryMsg::ListVoters { start_after, limit } => {
215 client.list_voters(start_after, limit).ignore()
216 }
217 MultisigQueryMsg::Config {} => client.query_config().ignore(),
218 };
219 }
220}