Skip to main content

nym_validator_client/nyxd/contract_traits/
mixnet_query_client.rs

1// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::collect_paged;
5use crate::nyxd::contract_traits::NymContractsProvider;
6use crate::nyxd::error::NyxdError;
7use crate::nyxd::CosmWasmClient;
8use async_trait::async_trait;
9use cosmrs::AccountId;
10use nym_contracts_common::signing::Nonce;
11use nym_mixnet_contract_common::gateway::{PreassignedGatewayIdsResponse, PreassignedId};
12use nym_mixnet_contract_common::nym_node::{
13    EpochAssignmentResponse, NodeDetailsByIdentityResponse, NodeDetailsResponse,
14    NodeOwnershipResponse, NodeRewardingDetailsResponse, PagedNymNodeBondsResponse,
15    PagedNymNodeDetailsResponse, PagedUnbondedNymNodesResponse, RewardedSetMetadata, Role,
16    RolesMetadataResponse, StakeSaturationResponse, UnbondedNodeResponse, UnbondedNymNode,
17};
18use nym_mixnet_contract_common::reward_params::WorkFactor;
19use nym_mixnet_contract_common::{
20    delegation,
21    delegation::{NodeDelegationResponse, OwnerProxySubKey},
22    mixnode::{
23        MixStakeSaturationResponse, MixnodeRewardingDetailsResponse, PagedMixnodesDetailsResponse,
24        PagedUnbondedMixnodesResponse, UnbondedMixnodeResponse,
25    },
26    reward_params::{Performance, RewardingParams},
27    rewarding::{EstimatedCurrentEpochRewardResponse, PendingRewardResponse},
28    ContractBuildInformation, ContractState, ContractStateParams, CurrentIntervalResponse,
29    CurrentNymNodeVersionResponse, Delegation, EpochEventId, EpochRewardedSet, EpochStatus,
30    GatewayBond, GatewayBondResponse, GatewayOwnershipResponse, HistoricalNymNodeVersionEntry,
31    IdentityKey, IdentityKeyRef, IntervalEventId, KeyRotationIdResponse, KeyRotationState,
32    MixNodeBond, MixNodeDetails, MixOwnershipResponse, MixnodeDetailsByIdentityResponse,
33    MixnodeDetailsResponse, NodeId, NumberOfPendingEventsResponse, NymNodeBond, NymNodeDetails,
34    NymNodeVersionHistoryResponse, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse,
35    PagedGatewayResponse, PagedMixnodeBondsResponse, PagedNodeDelegationsResponse,
36    PendingEpochEvent, PendingEpochEventResponse, PendingEpochEventsResponse, PendingIntervalEvent,
37    PendingIntervalEventResponse, PendingIntervalEventsResponse, QueryMsg as MixnetQueryMsg,
38    RewardedSet, UnbondedMixnode,
39};
40use serde::Deserialize;
41
42#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
43#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
44pub trait MixnetQueryClient {
45    async fn query_mixnet_contract<T>(&self, query: MixnetQueryMsg) -> Result<T, NyxdError>
46    where
47        for<'a> T: Deserialize<'a>;
48
49    // state/sys-params-related
50
51    async fn admin(&self) -> Result<cw_controllers::AdminResponse, NyxdError> {
52        self.query_mixnet_contract(MixnetQueryMsg::Admin {}).await
53    }
54
55    async fn get_mixnet_contract_version(&self) -> Result<ContractBuildInformation, NyxdError> {
56        self.query_mixnet_contract(MixnetQueryMsg::GetContractVersion {})
57            .await
58    }
59
60    async fn get_mixnet_contract_cw2_version(&self) -> Result<cw2::ContractVersion, NyxdError> {
61        self.query_mixnet_contract(MixnetQueryMsg::GetCW2ContractVersion {})
62            .await
63    }
64
65    async fn get_rewarding_validator_address(&self) -> Result<AccountId, NyxdError> {
66        self.query_mixnet_contract(MixnetQueryMsg::GetRewardingValidatorAddress {})
67            .await
68    }
69
70    async fn get_mixnet_contract_state_params(&self) -> Result<ContractStateParams, NyxdError> {
71        self.query_mixnet_contract(MixnetQueryMsg::GetStateParams {})
72            .await
73    }
74
75    async fn get_nym_node_version_history_paged(
76        &self,
77        start_after: Option<u32>,
78        limit: Option<u32>,
79    ) -> Result<NymNodeVersionHistoryResponse, NyxdError> {
80        self.query_mixnet_contract(MixnetQueryMsg::GetNymNodeVersionHistory { limit, start_after })
81            .await
82    }
83
84    async fn get_current_nym_node_version(
85        &self,
86    ) -> Result<CurrentNymNodeVersionResponse, NyxdError> {
87        self.query_mixnet_contract(MixnetQueryMsg::GetCurrentNymNodeVersion {})
88            .await
89    }
90
91    async fn get_mixnet_contract_state(&self) -> Result<ContractState, NyxdError> {
92        self.query_mixnet_contract(MixnetQueryMsg::GetState {})
93            .await
94    }
95
96    async fn get_rewarding_parameters(&self) -> Result<RewardingParams, NyxdError> {
97        self.query_mixnet_contract(MixnetQueryMsg::GetRewardingParams {})
98            .await
99    }
100
101    async fn get_current_epoch_status(&self) -> Result<EpochStatus, NyxdError> {
102        self.query_mixnet_contract(MixnetQueryMsg::GetEpochStatus {})
103            .await
104    }
105
106    async fn get_current_interval_details(&self) -> Result<CurrentIntervalResponse, NyxdError> {
107        self.query_mixnet_contract(MixnetQueryMsg::GetCurrentIntervalDetails {})
108            .await
109    }
110
111    // mixnode-related:
112
113    async fn get_mixnode_bonds_paged(
114        &self,
115        start_after: Option<NodeId>,
116        limit: Option<u32>,
117    ) -> Result<PagedMixnodeBondsResponse, NyxdError> {
118        self.query_mixnet_contract(MixnetQueryMsg::GetMixNodeBonds { limit, start_after })
119            .await
120    }
121
122    async fn get_mixnodes_detailed_paged(
123        &self,
124        start_after: Option<NodeId>,
125        limit: Option<u32>,
126    ) -> Result<PagedMixnodesDetailsResponse, NyxdError> {
127        self.query_mixnet_contract(MixnetQueryMsg::GetMixNodesDetailed { limit, start_after })
128            .await
129    }
130
131    async fn get_unbonded_mixnodes_paged(
132        &self,
133        start_after: Option<NodeId>,
134        limit: Option<u32>,
135    ) -> Result<PagedUnbondedMixnodesResponse, NyxdError> {
136        self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after })
137            .await
138    }
139
140    async fn get_unbonded_mixnodes_by_owner_paged(
141        &self,
142        owner: &AccountId,
143        start_after: Option<NodeId>,
144        limit: Option<u32>,
145    ) -> Result<PagedUnbondedMixnodesResponse, NyxdError> {
146        self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByOwner {
147            owner: owner.to_string(),
148            limit,
149            start_after,
150        })
151        .await
152    }
153
154    async fn get_unbonded_mixnodes_by_identity_paged(
155        &self,
156        identity_key: IdentityKeyRef<'_>,
157        start_after: Option<NodeId>,
158        limit: Option<u32>,
159    ) -> Result<PagedUnbondedMixnodesResponse, NyxdError> {
160        self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByIdentityKey {
161            identity_key: identity_key.to_string(),
162            limit,
163            start_after,
164        })
165        .await
166    }
167
168    async fn get_owned_mixnode(
169        &self,
170        address: &AccountId,
171    ) -> Result<MixOwnershipResponse, NyxdError> {
172        self.query_mixnet_contract(MixnetQueryMsg::GetOwnedMixnode {
173            address: address.to_string(),
174        })
175        .await
176    }
177
178    async fn get_mixnode_details(
179        &self,
180        mix_id: NodeId,
181    ) -> Result<MixnodeDetailsResponse, NyxdError> {
182        self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDetails { mix_id })
183            .await
184    }
185
186    async fn get_mixnode_details_by_identity(
187        &self,
188        mix_identity: IdentityKey,
189    ) -> Result<MixnodeDetailsByIdentityResponse, NyxdError> {
190        self.query_mixnet_contract(MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity {
191            mix_identity,
192        })
193        .await
194    }
195
196    async fn get_mixnode_rewarding_details(
197        &self,
198        mix_id: NodeId,
199    ) -> Result<MixnodeRewardingDetailsResponse, NyxdError> {
200        self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeRewardingDetails { mix_id })
201            .await
202    }
203
204    async fn get_mixnode_stake_saturation(
205        &self,
206        mix_id: NodeId,
207    ) -> Result<MixStakeSaturationResponse, NyxdError> {
208        self.query_mixnet_contract(MixnetQueryMsg::GetStakeSaturation { mix_id })
209            .await
210    }
211
212    async fn get_unbonded_mixnode_information(
213        &self,
214        mix_id: NodeId,
215    ) -> Result<UnbondedMixnodeResponse, NyxdError> {
216        self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodeInformation { mix_id })
217            .await
218    }
219
220    // async fn get_layer_distribution(&self) -> Result<LayerDistribution, NyxdError> {
221    //     self.query_mixnet_contract(MixnetQueryMsg::GetRoleDistribution {})
222    //         .await
223    // }
224
225    // gateway-related:
226
227    async fn get_gateways_paged(
228        &self,
229        start_after: Option<IdentityKey>,
230        limit: Option<u32>,
231    ) -> Result<PagedGatewayResponse, NyxdError> {
232        self.query_mixnet_contract(MixnetQueryMsg::GetGateways { start_after, limit })
233            .await
234    }
235
236    /// Checks whether there is a bonded gateway associated with the provided identity key
237    async fn get_gateway_bond(
238        &self,
239        identity: IdentityKey,
240    ) -> Result<GatewayBondResponse, NyxdError> {
241        self.query_mixnet_contract(MixnetQueryMsg::GetGatewayBond { identity })
242            .await
243    }
244
245    /// Checks whether there is a bonded gateway associated with the provided client's address
246    async fn get_owned_gateway(
247        &self,
248        address: &AccountId,
249    ) -> Result<GatewayOwnershipResponse, NyxdError> {
250        self.query_mixnet_contract(MixnetQueryMsg::GetOwnedGateway {
251            address: address.to_string(),
252        })
253        .await
254    }
255
256    async fn get_preassigned_gateway_ids_paged(
257        &self,
258        start_after: Option<IdentityKey>,
259        limit: Option<u32>,
260    ) -> Result<PreassignedGatewayIdsResponse, NyxdError> {
261        self.query_mixnet_contract(MixnetQueryMsg::GetPreassignedGatewayIds { start_after, limit })
262            .await
263    }
264
265    // nym-nodes related:
266    async fn get_nymnode_bonds_paged(
267        &self,
268        start_after: Option<NodeId>,
269        limit: Option<u32>,
270    ) -> Result<PagedNymNodeBondsResponse, NyxdError> {
271        self.query_mixnet_contract(MixnetQueryMsg::GetNymNodeBondsPaged { limit, start_after })
272            .await
273    }
274
275    async fn get_nymnodes_detailed_paged(
276        &self,
277        start_after: Option<NodeId>,
278        limit: Option<u32>,
279    ) -> Result<PagedNymNodeDetailsResponse, NyxdError> {
280        self.query_mixnet_contract(MixnetQueryMsg::GetNymNodesDetailedPaged { limit, start_after })
281            .await
282    }
283
284    async fn get_unbonded_nymnodes_paged(
285        &self,
286        start_after: Option<NodeId>,
287        limit: Option<u32>,
288    ) -> Result<PagedUnbondedNymNodesResponse, NyxdError> {
289        self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedNymNodesPaged { limit, start_after })
290            .await
291    }
292
293    async fn get_unbonded_nymnodes_by_owner_paged(
294        &self,
295        owner: &AccountId,
296        start_after: Option<NodeId>,
297        limit: Option<u32>,
298    ) -> Result<PagedUnbondedNymNodesResponse, NyxdError> {
299        self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedNymNodesByOwnerPaged {
300            owner: owner.to_string(),
301            limit,
302            start_after,
303        })
304        .await
305    }
306
307    async fn get_unbonded_nymnodes_by_identity_paged(
308        &self,
309        identity_key: IdentityKeyRef<'_>,
310        start_after: Option<NodeId>,
311        limit: Option<u32>,
312    ) -> Result<PagedUnbondedNymNodesResponse, NyxdError> {
313        self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedNymNodesByIdentityKeyPaged {
314            identity_key: identity_key.to_string(),
315            limit,
316            start_after,
317        })
318        .await
319    }
320
321    async fn get_owned_nymnode(
322        &self,
323        address: &AccountId,
324    ) -> Result<NodeOwnershipResponse, NyxdError> {
325        self.query_mixnet_contract(MixnetQueryMsg::GetOwnedNymNode {
326            address: address.to_string(),
327        })
328        .await
329    }
330
331    async fn get_nymnode_details(&self, node_id: NodeId) -> Result<NodeDetailsResponse, NyxdError> {
332        self.query_mixnet_contract(MixnetQueryMsg::GetNymNodeDetails { node_id })
333            .await
334    }
335
336    async fn get_nymnode_details_by_identity(
337        &self,
338        node_identity: IdentityKey,
339    ) -> Result<NodeDetailsByIdentityResponse, NyxdError> {
340        self.query_mixnet_contract(MixnetQueryMsg::GetNymNodeDetailsByIdentityKey { node_identity })
341            .await
342    }
343
344    async fn get_nymnode_rewarding_details(
345        &self,
346        node_id: NodeId,
347    ) -> Result<NodeRewardingDetailsResponse, NyxdError> {
348        self.query_mixnet_contract(MixnetQueryMsg::GetNodeRewardingDetails { node_id })
349            .await
350    }
351
352    async fn get_node_stake_saturation(
353        &self,
354        node_id: NodeId,
355    ) -> Result<StakeSaturationResponse, NyxdError> {
356        self.query_mixnet_contract(MixnetQueryMsg::GetNodeStakeSaturation { node_id })
357            .await
358    }
359
360    async fn get_unbonded_nymnode_information(
361        &self,
362        node_id: NodeId,
363    ) -> Result<UnbondedNodeResponse, NyxdError> {
364        self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedNymNode { node_id })
365            .await
366    }
367
368    async fn get_role_assignment(&self, role: Role) -> Result<EpochAssignmentResponse, NyxdError> {
369        self.query_mixnet_contract(MixnetQueryMsg::GetRoleAssignment { role })
370            .await
371    }
372
373    async fn get_rewarded_set_metadata(&self) -> Result<RolesMetadataResponse, NyxdError> {
374        self.query_mixnet_contract(MixnetQueryMsg::GetRewardedSetMetadata {})
375            .await
376    }
377
378    // delegation-related:
379
380    /// Gets list of all delegations towards particular mixnode on particular page.
381    async fn get_mixnode_delegations_paged(
382        &self,
383        node_id: NodeId,
384        start_after: Option<String>,
385        limit: Option<u32>,
386    ) -> Result<PagedNodeDelegationsResponse, NyxdError> {
387        self.query_mixnet_contract(MixnetQueryMsg::GetNodeDelegations {
388            node_id,
389            start_after,
390            limit,
391        })
392        .await
393    }
394
395    /// Gets list of all the mixnodes to which a particular address delegated.
396    async fn get_delegator_delegations_paged(
397        &self,
398        delegator: &AccountId,
399        start_after: Option<(NodeId, OwnerProxySubKey)>,
400        limit: Option<u32>,
401    ) -> Result<PagedDelegatorDelegationsResponse, NyxdError> {
402        self.query_mixnet_contract(MixnetQueryMsg::GetDelegatorDelegations {
403            delegator: delegator.to_string(),
404            start_after,
405            limit,
406        })
407        .await
408    }
409
410    /// Checks value of delegation of given client towards particular mixnode.
411    async fn get_delegation_details(
412        &self,
413        node_id: NodeId,
414        delegator: &AccountId,
415        proxy: Option<String>,
416    ) -> Result<NodeDelegationResponse, NyxdError> {
417        self.query_mixnet_contract(MixnetQueryMsg::GetDelegationDetails {
418            node_id,
419            delegator: delegator.to_string(),
420            proxy,
421        })
422        .await
423    }
424
425    /// Gets all the delegations on the entire network
426    async fn get_all_network_delegations_paged(
427        &self,
428        start_after: Option<delegation::StorageKey>,
429        limit: Option<u32>,
430    ) -> Result<PagedAllDelegationsResponse, NyxdError> {
431        self.query_mixnet_contract(MixnetQueryMsg::GetAllDelegations { start_after, limit })
432            .await
433    }
434
435    // rewards related
436    async fn get_pending_operator_reward(
437        &self,
438        operator: &AccountId,
439    ) -> Result<PendingRewardResponse, NyxdError> {
440        self.query_mixnet_contract(MixnetQueryMsg::GetPendingOperatorReward {
441            address: operator.to_string(),
442        })
443        .await
444    }
445
446    async fn get_pending_mixnode_operator_reward(
447        &self,
448        node_id: NodeId,
449    ) -> Result<PendingRewardResponse, NyxdError> {
450        self.query_mixnet_contract(MixnetQueryMsg::GetPendingNodeOperatorReward { node_id })
451            .await
452    }
453
454    async fn get_pending_delegator_reward(
455        &self,
456        delegator: &AccountId,
457        node_id: NodeId,
458        proxy: Option<String>,
459    ) -> Result<PendingRewardResponse, NyxdError> {
460        self.query_mixnet_contract(MixnetQueryMsg::GetPendingDelegatorReward {
461            address: delegator.to_string(),
462            node_id,
463            proxy,
464        })
465        .await
466    }
467
468    // given the provided performance, estimate the reward at the end of the current epoch
469    async fn get_estimated_current_epoch_operator_reward(
470        &self,
471        node_id: NodeId,
472        estimated_performance: Performance,
473        estimated_work: Option<WorkFactor>,
474    ) -> Result<EstimatedCurrentEpochRewardResponse, NyxdError> {
475        self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochOperatorReward {
476            node_id,
477            estimated_performance,
478            estimated_work,
479        })
480        .await
481    }
482
483    // given the provided performance, estimate the reward at the end of the current epoch
484    async fn get_estimated_current_epoch_delegator_reward(
485        &self,
486        delegator: &AccountId,
487        node_id: NodeId,
488        estimated_performance: Performance,
489        estimated_work: Option<WorkFactor>,
490    ) -> Result<EstimatedCurrentEpochRewardResponse, NyxdError> {
491        self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochDelegatorReward {
492            address: delegator.to_string(),
493            node_id,
494            estimated_performance,
495            estimated_work,
496        })
497        .await
498    }
499
500    // interval-related
501
502    async fn get_pending_epoch_events_paged(
503        &self,
504        start_after: Option<EpochEventId>,
505        limit: Option<u32>,
506    ) -> Result<PendingEpochEventsResponse, NyxdError> {
507        self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvents { start_after, limit })
508            .await
509    }
510
511    async fn get_pending_interval_events_paged(
512        &self,
513        start_after: Option<IntervalEventId>,
514        limit: Option<u32>,
515    ) -> Result<PendingIntervalEventsResponse, NyxdError> {
516        self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvents { start_after, limit })
517            .await
518    }
519
520    async fn get_pending_epoch_event(
521        &self,
522        event_id: EpochEventId,
523    ) -> Result<PendingEpochEventResponse, NyxdError> {
524        self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvent { event_id })
525            .await
526    }
527
528    async fn get_pending_interval_event(
529        &self,
530        event_id: IntervalEventId,
531    ) -> Result<PendingIntervalEventResponse, NyxdError> {
532        self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvent { event_id })
533            .await
534    }
535
536    async fn get_number_of_pending_events(
537        &self,
538    ) -> Result<NumberOfPendingEventsResponse, NyxdError> {
539        self.query_mixnet_contract(MixnetQueryMsg::GetNumberOfPendingEvents {})
540            .await
541    }
542
543    async fn get_signing_nonce(&self, address: &AccountId) -> Result<Nonce, NyxdError> {
544        self.query_mixnet_contract(MixnetQueryMsg::GetSigningNonce {
545            address: address.to_string(),
546        })
547        .await
548    }
549
550    async fn get_key_rotation_state(&self) -> Result<KeyRotationState, NyxdError> {
551        self.query_mixnet_contract(MixnetQueryMsg::GetKeyRotationState {})
552            .await
553    }
554
555    async fn get_key_rotation_id(&self) -> Result<KeyRotationIdResponse, NyxdError> {
556        self.query_mixnet_contract(MixnetQueryMsg::GetKeyRotationId {})
557            .await
558    }
559}
560
561// extension trait to the query client to deal with the paged queries
562// (it didn't feel appropriate to combine it with the existing trait
563#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
564#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
565pub trait PagedMixnetQueryClient: MixnetQueryClient {
566    async fn get_all_nymnode_bonds(&self) -> Result<Vec<NymNodeBond>, NyxdError> {
567        collect_paged!(self, get_nymnode_bonds_paged, nodes)
568    }
569
570    async fn get_all_nymnodes_detailed(&self) -> Result<Vec<NymNodeDetails>, NyxdError> {
571        collect_paged!(self, get_nymnodes_detailed_paged, nodes)
572    }
573
574    async fn get_all_unbonded_nymnodes(&self) -> Result<Vec<UnbondedNymNode>, NyxdError> {
575        collect_paged!(self, get_unbonded_nymnodes_paged, nodes)
576    }
577
578    async fn get_all_unbonded_nymnodes_by_owner(
579        &self,
580        owner: &AccountId,
581    ) -> Result<Vec<UnbondedNymNode>, NyxdError> {
582        collect_paged!(self, get_unbonded_nymnodes_by_owner_paged, nodes, owner)
583    }
584
585    async fn get_all_unbonded_nymnodes_by_identity(
586        &self,
587        identity_key: IdentityKeyRef<'_>,
588    ) -> Result<Vec<UnbondedNymNode>, NyxdError> {
589        collect_paged!(
590            self,
591            get_unbonded_nymnodes_by_identity_paged,
592            nodes,
593            identity_key
594        )
595    }
596
597    async fn get_all_mixnode_bonds(&self) -> Result<Vec<MixNodeBond>, NyxdError> {
598        collect_paged!(self, get_mixnode_bonds_paged, nodes)
599    }
600
601    async fn get_all_mixnodes_detailed(&self) -> Result<Vec<MixNodeDetails>, NyxdError> {
602        collect_paged!(self, get_mixnodes_detailed_paged, nodes)
603    }
604
605    async fn get_all_unbonded_mixnodes(&self) -> Result<Vec<(NodeId, UnbondedMixnode)>, NyxdError> {
606        collect_paged!(self, get_unbonded_mixnodes_paged, nodes)
607    }
608
609    async fn get_all_unbonded_mixnodes_by_owner(
610        &self,
611        owner: &AccountId,
612    ) -> Result<Vec<(NodeId, UnbondedMixnode)>, NyxdError> {
613        collect_paged!(self, get_unbonded_mixnodes_by_owner_paged, nodes, owner)
614    }
615
616    async fn get_all_unbonded_mixnodes_by_identity(
617        &self,
618        identity_key: IdentityKeyRef<'_>,
619    ) -> Result<Vec<(NodeId, UnbondedMixnode)>, NyxdError> {
620        collect_paged!(
621            self,
622            get_unbonded_mixnodes_by_identity_paged,
623            nodes,
624            identity_key
625        )
626    }
627
628    async fn get_all_gateways(&self) -> Result<Vec<GatewayBond>, NyxdError> {
629        collect_paged!(self, get_gateways_paged, nodes)
630    }
631
632    async fn get_all_preassigned_gateway_ids(&self) -> Result<Vec<PreassignedId>, NyxdError> {
633        collect_paged!(self, get_preassigned_gateway_ids_paged, ids)
634    }
635
636    async fn get_all_single_mixnode_delegations(
637        &self,
638        mix_id: NodeId,
639    ) -> Result<Vec<Delegation>, NyxdError> {
640        collect_paged!(self, get_mixnode_delegations_paged, delegations, mix_id)
641    }
642
643    async fn get_all_delegator_delegations(
644        &self,
645        delegation_owner: &AccountId,
646    ) -> Result<Vec<Delegation>, NyxdError> {
647        collect_paged!(
648            self,
649            get_delegator_delegations_paged,
650            delegations,
651            delegation_owner
652        )
653    }
654
655    async fn get_all_network_delegations(&self) -> Result<Vec<Delegation>, NyxdError> {
656        collect_paged!(self, get_all_network_delegations_paged, delegations)
657    }
658
659    async fn get_all_pending_epoch_events(&self) -> Result<Vec<PendingEpochEvent>, NyxdError> {
660        collect_paged!(self, get_pending_epoch_events_paged, events)
661    }
662
663    async fn get_all_pending_interval_events(
664        &self,
665    ) -> Result<Vec<PendingIntervalEvent>, NyxdError> {
666        collect_paged!(self, get_pending_interval_events_paged, events)
667    }
668
669    async fn get_full_nym_node_version_history(
670        &self,
671    ) -> Result<Vec<HistoricalNymNodeVersionEntry>, NyxdError> {
672        collect_paged!(self, get_nym_node_version_history_paged, history)
673    }
674}
675
676#[async_trait]
677impl<T> PagedMixnetQueryClient for T where T: MixnetQueryClient {}
678
679// extension help to provide extra functionalities based on existing queries:
680#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
681#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
682pub trait MixnetQueryClientExt: MixnetQueryClient {
683    async fn get_rewarded_set(&self) -> Result<EpochRewardedSet, NyxdError> {
684        let error_response = |message| Err(NyxdError::extension_query_failure("mixnet", message));
685
686        // bypass for catch 22 for fresh contracts. we can't refresh cache because there's no rewarded set,
687        // but we can't set the rewarded set because we didn't refresh the cache
688        let metadata = self.get_rewarded_set_metadata().await?;
689
690        let is_default = metadata.metadata == RewardedSetMetadata::default();
691        if !metadata.metadata.fully_assigned && !is_default {
692            return error_response("the rewarded set hasn't been fully assigned for this epoch");
693        }
694        let expected_epoch_id = metadata.metadata.epoch_id;
695
696        if is_default {
697            return Ok(Default::default());
698        }
699
700        // if we have to query those things more frequently, we could do it concurrently,
701        // but as it stands now, it happens so infrequently it might as well be sequential
702        let entry = self.get_role_assignment(Role::EntryGateway).await?;
703        if entry.epoch_id != expected_epoch_id {
704            return error_response("the nodes assigned for 'entry' returned unexpected epoch_id");
705        }
706
707        let exit = self.get_role_assignment(Role::ExitGateway).await?;
708        if exit.epoch_id != expected_epoch_id {
709            return error_response("the nodes assigned for 'exit' returned unexpected epoch_id");
710        }
711
712        let layer1 = self.get_role_assignment(Role::Layer1).await?;
713        if layer1.epoch_id != expected_epoch_id {
714            return error_response("the nodes assigned for 'layer1' returned unexpected epoch_id");
715        }
716
717        let layer2 = self.get_role_assignment(Role::Layer2).await?;
718        if layer2.epoch_id != expected_epoch_id {
719            return error_response("the nodes assigned for 'layer2' returned unexpected epoch_id");
720        }
721
722        let layer3 = self.get_role_assignment(Role::Layer3).await?;
723        if layer3.epoch_id != expected_epoch_id {
724            return error_response("the nodes assigned for 'layer3' returned unexpected epoch_id");
725        }
726
727        let standby = self.get_role_assignment(Role::Standby).await?;
728        if standby.epoch_id != expected_epoch_id {
729            return error_response("the nodes assigned for 'standby' returned unexpected epoch_id");
730        }
731
732        Ok(EpochRewardedSet {
733            epoch_id: expected_epoch_id,
734            assignment: RewardedSet {
735                entry_gateways: entry.nodes,
736                exit_gateways: exit.nodes,
737                layer1: layer1.nodes,
738                layer2: layer2.nodes,
739                layer3: layer3.nodes,
740                standby: standby.nodes,
741            },
742        })
743    }
744}
745
746#[async_trait]
747impl<T> MixnetQueryClientExt for T where T: MixnetQueryClient {}
748
749#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
750#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
751impl<C> MixnetQueryClient for C
752where
753    C: CosmWasmClient + NymContractsProvider + Send + Sync,
754{
755    async fn query_mixnet_contract<T>(&self, query: MixnetQueryMsg) -> Result<T, NyxdError>
756    where
757        for<'a> T: Deserialize<'a>,
758    {
759        let mixnet_contract_address = &self
760            .mixnet_contract_address()
761            .ok_or_else(|| NyxdError::unavailable_contract_address("mixnet contract"))?;
762        self.query_contract_smart(mixnet_contract_address, &query)
763            .await
764    }
765}
766
767#[cfg(test)]
768mod tests {
769    use super::*;
770    use crate::nyxd::contract_traits::tests::IgnoreValue;
771    use nym_mixnet_contract_common::QueryMsg;
772
773    // it's enough that this compiles and clippy is happy about it
774    #[allow(dead_code)]
775    fn all_query_variants_are_covered<C: MixnetQueryClient + Send + Sync>(
776        client: C,
777        msg: MixnetQueryMsg,
778    ) -> u32 {
779        match msg {
780            MixnetQueryMsg::Admin {} => client.admin().ignore(),
781            MixnetQueryMsg::GetContractVersion {} => client.get_mixnet_contract_version().ignore(),
782            MixnetQueryMsg::GetCW2ContractVersion {} => {
783                client.get_mixnet_contract_cw2_version().ignore()
784            }
785            MixnetQueryMsg::GetRewardingValidatorAddress {} => {
786                client.get_rewarding_validator_address().ignore()
787            }
788            MixnetQueryMsg::GetStateParams {} => client.get_mixnet_contract_state_params().ignore(),
789            MixnetQueryMsg::GetState {} => client.get_mixnet_contract_state().ignore(),
790            MixnetQueryMsg::GetRewardingParams {} => client.get_rewarding_parameters().ignore(),
791            MixnetQueryMsg::GetEpochStatus {} => client.get_current_epoch_status().ignore(),
792            MixnetQueryMsg::GetCurrentIntervalDetails {} => {
793                client.get_current_interval_details().ignore()
794            }
795            MixnetQueryMsg::GetMixNodeBonds { limit, start_after } => {
796                client.get_mixnode_bonds_paged(start_after, limit).ignore()
797            }
798            MixnetQueryMsg::GetMixNodesDetailed { limit, start_after } => client
799                .get_mixnodes_detailed_paged(start_after, limit)
800                .ignore(),
801            MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after } => client
802                .get_unbonded_mixnodes_paged(start_after, limit)
803                .ignore(),
804            MixnetQueryMsg::GetUnbondedMixNodesByOwner {
805                owner,
806                limit,
807                start_after,
808            } => client
809                .get_unbonded_mixnodes_by_owner_paged(&owner.parse().unwrap(), start_after, limit)
810                .ignore(),
811            MixnetQueryMsg::GetUnbondedMixNodesByIdentityKey {
812                identity_key,
813                limit,
814                start_after,
815            } => client
816                .get_unbonded_mixnodes_by_identity_paged(&identity_key, start_after, limit)
817                .ignore(),
818            MixnetQueryMsg::GetOwnedMixnode { address } => {
819                client.get_owned_mixnode(&address.parse().unwrap()).ignore()
820            }
821            MixnetQueryMsg::GetMixnodeDetails { mix_id } => {
822                client.get_mixnode_details(mix_id).ignore()
823            }
824            MixnetQueryMsg::GetMixnodeRewardingDetails { mix_id } => {
825                client.get_mixnode_rewarding_details(mix_id).ignore()
826            }
827            MixnetQueryMsg::GetStakeSaturation { mix_id } => {
828                client.get_mixnode_stake_saturation(mix_id).ignore()
829            }
830            MixnetQueryMsg::GetUnbondedMixNodeInformation { mix_id } => {
831                client.get_unbonded_mixnode_information(mix_id).ignore()
832            }
833            MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity { mix_identity } => client
834                .get_mixnode_details_by_identity(mix_identity)
835                .ignore(),
836            MixnetQueryMsg::GetGateways { start_after, limit } => {
837                client.get_gateways_paged(start_after, limit).ignore()
838            }
839            MixnetQueryMsg::GetGatewayBond { identity } => {
840                client.get_gateway_bond(identity).ignore()
841            }
842            MixnetQueryMsg::GetOwnedGateway { address } => {
843                client.get_owned_gateway(&address.parse().unwrap()).ignore()
844            }
845            MixnetQueryMsg::GetNodeDelegations {
846                node_id: mix_id,
847                start_after,
848                limit,
849            } => client
850                .get_mixnode_delegations_paged(mix_id, start_after, limit)
851                .ignore(),
852            MixnetQueryMsg::GetDelegatorDelegations {
853                delegator,
854                start_after,
855                limit,
856            } => client
857                .get_delegator_delegations_paged(&delegator.parse().unwrap(), start_after, limit)
858                .ignore(),
859            MixnetQueryMsg::GetDelegationDetails {
860                node_id: mix_id,
861                delegator,
862                proxy,
863            } => client
864                .get_delegation_details(mix_id, &delegator.parse().unwrap(), proxy)
865                .ignore(),
866            MixnetQueryMsg::GetAllDelegations { start_after, limit } => client
867                .get_all_network_delegations_paged(start_after, limit)
868                .ignore(),
869            MixnetQueryMsg::GetPendingOperatorReward { address } => client
870                .get_pending_operator_reward(&address.parse().unwrap())
871                .ignore(),
872            MixnetQueryMsg::GetPendingNodeOperatorReward { node_id: mix_id } => {
873                client.get_pending_mixnode_operator_reward(mix_id).ignore()
874            }
875            MixnetQueryMsg::GetPendingDelegatorReward {
876                address,
877                node_id: mix_id,
878                proxy,
879            } => client
880                .get_pending_delegator_reward(&address.parse().unwrap(), mix_id, proxy)
881                .ignore(),
882            MixnetQueryMsg::GetEstimatedCurrentEpochOperatorReward {
883                node_id,
884                estimated_performance,
885                estimated_work,
886            } => client
887                .get_estimated_current_epoch_operator_reward(
888                    node_id,
889                    estimated_performance,
890                    estimated_work,
891                )
892                .ignore(),
893            MixnetQueryMsg::GetEstimatedCurrentEpochDelegatorReward {
894                address,
895                node_id,
896                estimated_performance,
897                estimated_work,
898            } => client
899                .get_estimated_current_epoch_delegator_reward(
900                    &address.parse().unwrap(),
901                    node_id,
902                    estimated_performance,
903                    estimated_work,
904                )
905                .ignore(),
906            MixnetQueryMsg::GetPendingEpochEvents { limit, start_after } => client
907                .get_pending_epoch_events_paged(start_after, limit)
908                .ignore(),
909            MixnetQueryMsg::GetPendingIntervalEvents { limit, start_after } => client
910                .get_pending_interval_events_paged(start_after, limit)
911                .ignore(),
912            MixnetQueryMsg::GetPendingEpochEvent { event_id } => {
913                client.get_pending_epoch_event(event_id).ignore()
914            }
915            MixnetQueryMsg::GetPendingIntervalEvent { event_id } => {
916                client.get_pending_interval_event(event_id).ignore()
917            }
918            MixnetQueryMsg::GetNumberOfPendingEvents {} => {
919                client.get_number_of_pending_events().ignore()
920            }
921            MixnetQueryMsg::GetSigningNonce { address } => {
922                client.get_signing_nonce(&address.parse().unwrap()).ignore()
923            }
924            MixnetQueryMsg::GetPreassignedGatewayIds { start_after, limit } => client
925                .get_preassigned_gateway_ids_paged(start_after, limit)
926                .ignore(),
927            MixnetQueryMsg::GetNymNodeBondsPaged { limit, start_after } => {
928                client.get_nymnode_bonds_paged(limit, start_after).ignore()
929            }
930            MixnetQueryMsg::GetNymNodesDetailedPaged { limit, start_after } => client
931                .get_nymnodes_detailed_paged(limit, start_after)
932                .ignore(),
933            MixnetQueryMsg::GetUnbondedNymNode { node_id } => {
934                client.get_unbonded_nymnode_information(node_id).ignore()
935            }
936            MixnetQueryMsg::GetUnbondedNymNodesPaged { limit, start_after } => client
937                .get_unbonded_nymnodes_paged(limit, start_after)
938                .ignore(),
939            MixnetQueryMsg::GetUnbondedNymNodesByOwnerPaged {
940                owner,
941                limit,
942                start_after,
943            } => client
944                .get_unbonded_nymnodes_by_owner_paged(&owner.parse().unwrap(), limit, start_after)
945                .ignore(),
946            MixnetQueryMsg::GetUnbondedNymNodesByIdentityKeyPaged {
947                identity_key,
948                limit,
949                start_after,
950            } => client
951                .get_unbonded_nymnodes_by_identity_paged(&identity_key, limit, start_after)
952                .ignore(),
953            MixnetQueryMsg::GetOwnedNymNode { address } => {
954                client.get_owned_nymnode(&address.parse().unwrap()).ignore()
955            }
956            MixnetQueryMsg::GetNymNodeDetails { node_id } => {
957                client.get_nymnode_details(node_id).ignore()
958            }
959            MixnetQueryMsg::GetNymNodeDetailsByIdentityKey { node_identity } => client
960                .get_nymnode_details_by_identity(node_identity)
961                .ignore(),
962            MixnetQueryMsg::GetNodeRewardingDetails { node_id } => {
963                client.get_nymnode_rewarding_details(node_id).ignore()
964            }
965            MixnetQueryMsg::GetNodeStakeSaturation { node_id } => {
966                client.get_node_stake_saturation(node_id).ignore()
967            }
968            MixnetQueryMsg::GetRoleAssignment { role } => client.get_role_assignment(role).ignore(),
969            MixnetQueryMsg::GetRewardedSetMetadata {} => {
970                client.get_rewarded_set_metadata().ignore()
971            }
972            QueryMsg::GetCurrentNymNodeVersion {} => client.get_current_nym_node_version().ignore(),
973            QueryMsg::GetNymNodeVersionHistory { limit, start_after } => client
974                .get_nym_node_version_history_paged(start_after, limit)
975                .ignore(),
976            QueryMsg::GetKeyRotationState {} => client.get_key_rotation_state().ignore(),
977            QueryMsg::GetKeyRotationId {} => client.get_key_rotation_id().ignore(),
978        }
979    }
980}