1use crate::collect_paged;
5use crate::nyxd::coin::Coin;
6use crate::nyxd::contract_traits::NymContractsProvider;
7use crate::nyxd::error::NyxdError;
8use crate::nyxd::CosmWasmClient;
9use async_trait::async_trait;
10use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp};
11use nym_contracts_common::ContractBuildInformation;
12use nym_mixnet_contract_common::NodeId;
13use nym_vesting_contract_common::{
14 messages::QueryMsg as VestingQueryMsg, Account, AccountVestingCoins, AccountsResponse,
15 AllDelegationsResponse, BaseVestingAccountInfo, DelegationTimesResponse,
16 OriginalVestingResponse, Period, PledgeData, VestingCoinsResponse, VestingDelegation,
17};
18use serde::Deserialize;
19
20#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
21#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
22pub trait VestingQueryClient {
23 async fn query_vesting_contract<T>(&self, query: VestingQueryMsg) -> Result<T, NyxdError>
24 where
25 for<'a> T: Deserialize<'a>;
26
27 async fn get_vesting_contract_version(&self) -> Result<ContractBuildInformation, NyxdError> {
28 self.query_vesting_contract(VestingQueryMsg::GetContractVersion {})
29 .await
30 }
31
32 async fn get_vesting_contract_cw2_version(&self) -> Result<cw2::ContractVersion, NyxdError> {
33 self.query_vesting_contract(VestingQueryMsg::GetCW2ContractVersion {})
34 .await
35 }
36
37 async fn get_all_accounts_paged(
38 &self,
39 start_next_after: Option<String>,
40 limit: Option<u32>,
41 ) -> Result<AccountsResponse, NyxdError> {
42 self.query_vesting_contract(VestingQueryMsg::GetAccountsPaged {
43 start_next_after,
44 limit,
45 })
46 .await
47 }
48
49 async fn get_all_accounts_vesting_coins_paged(
50 &self,
51 start_next_after: Option<String>,
52 limit: Option<u32>,
53 ) -> Result<VestingCoinsResponse, NyxdError> {
54 self.query_vesting_contract(VestingQueryMsg::GetAccountsVestingCoinsPaged {
55 start_next_after,
56 limit,
57 })
58 .await
59 }
60
61 async fn locked_coins(
62 &self,
63 vesting_account_address: &str,
64 block_time: Option<Timestamp>,
65 ) -> Result<Coin, NyxdError> {
66 self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::LockedCoins {
67 vesting_account_address: vesting_account_address.to_string(),
68 block_time,
69 })
70 .await
71 .map(Into::into)
72 }
73
74 async fn spendable_coins(
75 &self,
76 vesting_account_address: &str,
77 block_time: Option<Timestamp>,
78 ) -> Result<Coin, NyxdError> {
79 self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::SpendableCoins {
80 vesting_account_address: vesting_account_address.to_string(),
81 block_time,
82 })
83 .await
84 .map(Into::into)
85 }
86
87 async fn vested_coins(
88 &self,
89 vesting_account_address: &str,
90 block_time: Option<Timestamp>,
91 ) -> Result<Coin, NyxdError> {
92 self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::GetVestedCoins {
93 vesting_account_address: vesting_account_address.to_string(),
94 block_time,
95 })
96 .await
97 .map(Into::into)
98 }
99
100 async fn vesting_coins(
101 &self,
102 vesting_account_address: &str,
103 block_time: Option<Timestamp>,
104 ) -> Result<Coin, NyxdError> {
105 self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::GetVestingCoins {
106 vesting_account_address: vesting_account_address.to_string(),
107 block_time,
108 })
109 .await
110 .map(Into::into)
111 }
112
113 async fn vesting_start_time(
114 &self,
115 vesting_account_address: &str,
116 ) -> Result<Timestamp, NyxdError> {
117 self.query_vesting_contract(VestingQueryMsg::GetStartTime {
118 vesting_account_address: vesting_account_address.to_string(),
119 })
120 .await
121 }
122
123 async fn vesting_end_time(
124 &self,
125 vesting_account_address: &str,
126 ) -> Result<Timestamp, NyxdError> {
127 self.query_vesting_contract(VestingQueryMsg::GetEndTime {
128 vesting_account_address: vesting_account_address.to_string(),
129 })
130 .await
131 }
132
133 async fn original_vesting(
134 &self,
135 vesting_account_address: &str,
136 ) -> Result<OriginalVestingResponse, NyxdError> {
137 self.query_vesting_contract(VestingQueryMsg::GetOriginalVesting {
138 vesting_account_address: vesting_account_address.to_string(),
139 })
140 .await
141 }
142
143 async fn get_historical_vesting_staking_reward(
144 &self,
145 vesting_account_address: &str,
146 ) -> Result<Coin, NyxdError> {
147 self.query_vesting_contract::<CosmWasmCoin>(
148 VestingQueryMsg::GetHistoricalVestingStakingReward {
149 vesting_account_address: vesting_account_address.to_string(),
150 },
151 )
152 .await
153 .map(Into::into)
154 }
155
156 async fn get_spendable_vested_coins(
157 &self,
158 vesting_account_address: &str,
159 ) -> Result<Coin, NyxdError> {
160 self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::GetSpendableVestedCoins {
161 vesting_account_address: vesting_account_address.to_string(),
162 })
163 .await
164 .map(Into::into)
165 }
166
167 async fn get_spendable_reward_coins(
168 &self,
169 vesting_account_address: &str,
170 ) -> Result<Coin, NyxdError> {
171 self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::GetSpendableRewardCoins {
172 vesting_account_address: vesting_account_address.to_string(),
173 })
174 .await
175 .map(Into::into)
176 }
177
178 async fn get_delegated_coins(&self, vesting_account_address: &str) -> Result<Coin, NyxdError> {
179 self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::GetDelegatedCoins {
180 vesting_account_address: vesting_account_address.to_string(),
181 })
182 .await
183 .map(Into::into)
184 }
185
186 async fn get_pledged_coins(&self, vesting_account_address: &str) -> Result<Coin, NyxdError> {
187 self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::GetPledgedCoins {
188 vesting_account_address: vesting_account_address.to_string(),
189 })
190 .await
191 .map(Into::into)
192 }
193
194 async fn get_staked_coins(&self, vesting_account_address: &str) -> Result<Coin, NyxdError> {
195 self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::GetStakedCoins {
196 vesting_account_address: vesting_account_address.to_string(),
197 })
198 .await
199 .map(Into::into)
200 }
201
202 async fn get_withdrawn_coins(&self, vesting_account_address: &str) -> Result<Coin, NyxdError> {
203 self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::GetWithdrawnCoins {
204 vesting_account_address: vesting_account_address.to_string(),
205 })
206 .await
207 .map(Into::into)
208 }
209
210 async fn get_account(&self, address: &str) -> Result<Account, NyxdError> {
211 self.query_vesting_contract(VestingQueryMsg::GetAccount {
212 address: address.to_string(),
213 })
214 .await
215 }
216
217 async fn get_mixnode_pledge(&self, address: &str) -> Result<Option<PledgeData>, NyxdError> {
218 self.query_vesting_contract(VestingQueryMsg::GetMixnode {
219 address: address.to_string(),
220 })
221 .await
222 }
223
224 async fn get_gateway_pledge(&self, address: &str) -> Result<Option<PledgeData>, NyxdError> {
225 self.query_vesting_contract(VestingQueryMsg::GetGateway {
226 address: address.to_string(),
227 })
228 .await
229 }
230
231 async fn get_current_vesting_period(&self, address: &str) -> Result<Period, NyxdError> {
232 self.query_vesting_contract(VestingQueryMsg::GetCurrentVestingPeriod {
233 address: address.to_string(),
234 })
235 .await
236 }
237
238 async fn get_vesting_delegation(
239 &self,
240 address: &str,
241 mix_id: NodeId,
242 block_timestamp_secs: u64,
243 ) -> Result<VestingDelegation, NyxdError> {
244 self.query_vesting_contract(VestingQueryMsg::GetDelegation {
245 address: address.to_string(),
246 mix_id,
247 block_timestamp_secs,
248 })
249 .await
250 }
251
252 async fn get_total_delegation_amount(
253 &self,
254 address: &str,
255 mix_id: NodeId,
256 ) -> Result<Coin, NyxdError> {
257 self.query_vesting_contract(VestingQueryMsg::GetTotalDelegationAmount {
258 address: address.to_string(),
259 mix_id,
260 })
261 .await
262 }
263
264 async fn get_delegation_timestamps(
265 &self,
266 address: &str,
267 mix_id: NodeId,
268 ) -> Result<DelegationTimesResponse, NyxdError> {
269 self.query_vesting_contract(VestingQueryMsg::GetDelegationTimes {
270 address: address.to_string(),
271 mix_id,
272 })
273 .await
274 }
275
276 async fn get_all_vesting_delegations_paged(
277 &self,
278 start_after: Option<(u32, NodeId, u64)>,
279 limit: Option<u32>,
280 ) -> Result<AllDelegationsResponse, NyxdError> {
281 self.query_vesting_contract(VestingQueryMsg::GetAllDelegations { start_after, limit })
282 .await
283 }
284}
285
286#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
287#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
288pub trait PagedVestingQueryClient: VestingQueryClient {
289 async fn get_all_vesting_delegations(&self) -> Result<Vec<VestingDelegation>, NyxdError> {
290 collect_paged!(self, get_all_vesting_delegations_paged, delegations)
291 }
292
293 async fn get_all_accounts_info(&self) -> Result<Vec<BaseVestingAccountInfo>, NyxdError> {
294 collect_paged!(self, get_all_accounts_paged, accounts)
295 }
296
297 async fn get_all_accounts_vesting_coins(&self) -> Result<Vec<AccountVestingCoins>, NyxdError> {
298 collect_paged!(self, get_all_accounts_vesting_coins_paged, accounts)
299 }
300}
301
302#[async_trait]
303impl<T> PagedVestingQueryClient for T where T: VestingQueryClient {}
304
305#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
306#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
307impl<C> VestingQueryClient for C
308where
309 C: CosmWasmClient + NymContractsProvider + Send + Sync,
310{
311 async fn query_vesting_contract<T>(&self, query: VestingQueryMsg) -> Result<T, NyxdError>
312 where
313 for<'a> T: Deserialize<'a>,
314 {
315 let vesting_contract_address = &self
316 .vesting_contract_address()
317 .ok_or_else(|| NyxdError::unavailable_contract_address("vesting contract"))?;
318 self.query_contract_smart(vesting_contract_address, &query)
319 .await
320 }
321}
322
323#[cfg(test)]
324mod tests {
325 use super::*;
326 use crate::nyxd::contract_traits::tests::IgnoreValue;
327
328 #[allow(dead_code)]
330 fn all_query_variants_are_covered<C: VestingQueryClient + Send + Sync>(
331 client: C,
332 msg: VestingQueryMsg,
333 ) {
334 match msg {
335 VestingQueryMsg::GetContractVersion {} => {
336 client.get_vesting_contract_version().ignore()
337 }
338 VestingQueryMsg::GetCW2ContractVersion {} => {
339 client.get_vesting_contract_cw2_version().ignore()
340 }
341 VestingQueryMsg::GetAccountsPaged {
342 start_next_after,
343 limit,
344 } => client
345 .get_all_accounts_paged(start_next_after, limit)
346 .ignore(),
347 VestingQueryMsg::GetAccountsVestingCoinsPaged {
348 start_next_after,
349 limit,
350 } => client
351 .get_all_accounts_vesting_coins_paged(start_next_after, limit)
352 .ignore(),
353 VestingQueryMsg::LockedCoins {
354 vesting_account_address,
355 block_time,
356 } => client
357 .locked_coins(&vesting_account_address, block_time)
358 .ignore(),
359 VestingQueryMsg::SpendableCoins {
360 vesting_account_address,
361 block_time,
362 } => client
363 .spendable_coins(&vesting_account_address, block_time)
364 .ignore(),
365 VestingQueryMsg::GetVestedCoins {
366 vesting_account_address,
367 block_time,
368 } => client
369 .vested_coins(&vesting_account_address, block_time)
370 .ignore(),
371 VestingQueryMsg::GetVestingCoins {
372 vesting_account_address,
373 block_time,
374 } => client
375 .vesting_coins(&vesting_account_address, block_time)
376 .ignore(),
377 VestingQueryMsg::GetStartTime {
378 vesting_account_address,
379 } => client.vesting_start_time(&vesting_account_address).ignore(),
380 VestingQueryMsg::GetEndTime {
381 vesting_account_address,
382 } => client.vesting_end_time(&vesting_account_address).ignore(),
383 VestingQueryMsg::GetOriginalVesting {
384 vesting_account_address,
385 } => client.original_vesting(&vesting_account_address).ignore(),
386 VestingQueryMsg::GetHistoricalVestingStakingReward {
387 vesting_account_address,
388 } => client
389 .get_historical_vesting_staking_reward(&vesting_account_address)
390 .ignore(),
391 VestingQueryMsg::GetSpendableVestedCoins {
392 vesting_account_address,
393 } => client
394 .get_spendable_vested_coins(&vesting_account_address)
395 .ignore(),
396 VestingQueryMsg::GetSpendableRewardCoins {
397 vesting_account_address,
398 } => client
399 .get_spendable_reward_coins(&vesting_account_address)
400 .ignore(),
401 VestingQueryMsg::GetDelegatedCoins {
402 vesting_account_address,
403 } => client
404 .get_delegated_coins(&vesting_account_address)
405 .ignore(),
406 VestingQueryMsg::GetPledgedCoins {
407 vesting_account_address,
408 } => client.get_pledged_coins(&vesting_account_address).ignore(),
409 VestingQueryMsg::GetStakedCoins {
410 vesting_account_address,
411 } => client.get_staked_coins(&vesting_account_address).ignore(),
412 VestingQueryMsg::GetWithdrawnCoins {
413 vesting_account_address,
414 } => client
415 .get_withdrawn_coins(&vesting_account_address)
416 .ignore(),
417 VestingQueryMsg::GetAccount { address } => client.get_account(&address).ignore(),
418 VestingQueryMsg::GetMixnode { address } => client.get_mixnode_pledge(&address).ignore(),
419 VestingQueryMsg::GetGateway { address } => client.get_gateway_pledge(&address).ignore(),
420 VestingQueryMsg::GetCurrentVestingPeriod { address } => {
421 client.get_current_vesting_period(&address).ignore()
422 }
423 VestingQueryMsg::GetDelegation {
424 address,
425 mix_id,
426 block_timestamp_secs,
427 } => client
428 .get_vesting_delegation(&address, mix_id, block_timestamp_secs)
429 .ignore(),
430 VestingQueryMsg::GetTotalDelegationAmount { address, mix_id } => client
431 .get_total_delegation_amount(&address, mix_id)
432 .ignore(),
433 VestingQueryMsg::GetDelegationTimes { address, mix_id } => {
434 client.get_delegation_timestamps(&address, mix_id).ignore()
435 }
436 VestingQueryMsg::GetAllDelegations { start_after, limit } => client
437 .get_all_vesting_delegations_paged(start_after, limit)
438 .ignore(),
439 };
440 }
441}