Merge branch 'develop' into merge/release/2026.10-waterloo
This commit is contained in:
@@ -18,6 +18,7 @@ use nym_api_requests::ecash::VerificationKeyResponse;
|
||||
use nym_api_requests::models::network_monitor::{
|
||||
KnownNetworkMonitorResponse, StressTestBatchSubmission,
|
||||
};
|
||||
use nym_api_requests::models::node_families::NodeFamily;
|
||||
use nym_api_requests::models::{
|
||||
AnnotationResponseV1, ApiHealthResponse, BinaryBuildInformationOwned,
|
||||
ChainBlocksStatusResponse, ChainStatusResponse, KeyRotationInfoResponse,
|
||||
@@ -393,6 +394,45 @@ pub trait NymApiClientExt: ApiClient {
|
||||
Ok(bonds)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn get_node_families(
|
||||
&self,
|
||||
page: Option<u32>,
|
||||
per_page: Option<u32>,
|
||||
) -> Result<PaginatedResponse<NodeFamily>, NymAPIError> {
|
||||
let mut params = Vec::new();
|
||||
if let Some(page) = page {
|
||||
params.push(("page", page.to_string()))
|
||||
}
|
||||
if let Some(per_page) = per_page {
|
||||
params.push(("per_page", per_page.to_string()))
|
||||
}
|
||||
self.get_json(
|
||||
&[routes::V1_API_VERSION, routes::NODE_FAMILIES_ROUTES],
|
||||
¶ms,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_node_families(&self) -> Result<Vec<NodeFamily>, NymAPIError> {
|
||||
// TODO: deal with paging in macro or some helper function or something, because it's the same pattern everywhere
|
||||
let mut page = 0;
|
||||
let mut families = Vec::new();
|
||||
|
||||
loop {
|
||||
let mut res = self.get_node_families(Some(page), None).await?;
|
||||
|
||||
families.append(&mut res.data);
|
||||
if families.len() < res.pagination.total {
|
||||
page += 1
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(families)
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn get_basic_mixnodes(&self) -> Result<CachedNodesResponse<SkimmedNodeV1>, NymAPIError> {
|
||||
|
||||
@@ -38,6 +38,7 @@ pub mod ecash {
|
||||
}
|
||||
|
||||
pub const NYM_NODES_ROUTES: &str = "nym-nodes";
|
||||
pub const NODE_FAMILIES_ROUTES: &str = "node-families";
|
||||
|
||||
pub use nym_nodes::*;
|
||||
pub mod nym_nodes {
|
||||
|
||||
@@ -867,6 +867,10 @@ mod tests {
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents { .. } => {
|
||||
client.testing_resolve_all_pending_events(None).ignore()
|
||||
}
|
||||
// not expected to be exposed by the client
|
||||
ExecuteMsg::AdminMigrateVestedMixNode { .. }
|
||||
| ExecuteMsg::AdminMigrateVestedDelegation { .. }
|
||||
| ExecuteMsg::AdminBatchMigrateVestedDelegations { .. } => ().ignore(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ pub mod group_query_client;
|
||||
pub mod mixnet_query_client;
|
||||
pub mod multisig_query_client;
|
||||
pub mod network_monitors_query_client;
|
||||
pub mod node_families_query_client;
|
||||
pub mod performance_query_client;
|
||||
pub mod vesting_query_client;
|
||||
|
||||
@@ -24,6 +25,7 @@ pub mod group_signing_client;
|
||||
pub mod mixnet_signing_client;
|
||||
pub mod multisig_signing_client;
|
||||
pub mod network_monitors_signing_client;
|
||||
pub mod node_families_signing_client;
|
||||
pub mod performance_signing_client;
|
||||
pub mod vesting_signing_client;
|
||||
|
||||
@@ -36,6 +38,7 @@ pub use multisig_query_client::{MultisigQueryClient, PagedMultisigQueryClient};
|
||||
pub use network_monitors_query_client::{
|
||||
NetworkMonitorsQueryClient, PagedNetworkMonitorsQueryClient,
|
||||
};
|
||||
pub use node_families_query_client::{NodeFamiliesQueryClient, PagedNodeFamiliesQueryClient};
|
||||
pub use performance_query_client::{PagedPerformanceQueryClient, PerformanceQueryClient};
|
||||
pub use vesting_query_client::{PagedVestingQueryClient, VestingQueryClient};
|
||||
|
||||
@@ -46,6 +49,7 @@ pub use group_signing_client::GroupSigningClient;
|
||||
pub use mixnet_signing_client::MixnetSigningClient;
|
||||
pub use multisig_signing_client::MultisigSigningClient;
|
||||
pub use network_monitors_signing_client::NetworkMonitorsSigningClient;
|
||||
pub use node_families_signing_client::NodeFamiliesSigningClient;
|
||||
pub use performance_signing_client::PerformanceSigningClient;
|
||||
pub use vesting_signing_client::VestingSigningClient;
|
||||
|
||||
@@ -56,6 +60,7 @@ pub trait NymContractsProvider {
|
||||
fn vesting_contract_address(&self) -> Option<&AccountId>;
|
||||
fn performance_contract_address(&self) -> Option<&AccountId>;
|
||||
fn network_monitors_contract_address(&self) -> Option<&AccountId>;
|
||||
fn node_families_contract_address(&self) -> Option<&AccountId>;
|
||||
|
||||
// coconut-related
|
||||
fn ecash_contract_address(&self) -> Option<&AccountId>;
|
||||
@@ -70,6 +75,7 @@ pub struct TypedNymContracts {
|
||||
pub vesting_contract_address: Option<AccountId>,
|
||||
pub performance_contract_address: Option<AccountId>,
|
||||
pub network_monitors_contract_address: Option<AccountId>,
|
||||
pub node_families_contract_address: Option<AccountId>,
|
||||
|
||||
pub ecash_contract_address: Option<AccountId>,
|
||||
pub group_contract_address: Option<AccountId>,
|
||||
@@ -98,6 +104,10 @@ impl TryFrom<NymContracts> for TypedNymContracts {
|
||||
.network_monitors_contract_address
|
||||
.map(|addr| addr.parse())
|
||||
.transpose()?,
|
||||
node_families_contract_address: value
|
||||
.node_families_contract_address
|
||||
.map(|addr| addr.parse())
|
||||
.transpose()?,
|
||||
ecash_contract_address: value
|
||||
.ecash_contract_address
|
||||
.map(|addr| addr.parse())
|
||||
|
||||
+441
@@ -0,0 +1,441 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::collect_paged;
|
||||
use crate::nyxd::contract_traits::NymContractsProvider;
|
||||
use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::CosmWasmClient;
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
use serde::Deserialize;
|
||||
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
pub use nym_node_families_contract_common::{
|
||||
msg::QueryMsg as NodeFamiliesQueryMsg, AllFamilyMembersPagedResponse,
|
||||
AllPastFamilyInvitationsPagedResponse, FamiliesPagedResponse, FamilyMemberRecord,
|
||||
FamilyMembersPagedResponse, GlobalPastFamilyInvitationCursor, NodeFamily,
|
||||
NodeFamilyByNameResponse, NodeFamilyByOwnerResponse, NodeFamilyId,
|
||||
NodeFamilyMembershipResponse, NodeFamilyResponse, PastFamilyInvitation,
|
||||
PastFamilyInvitationCursor, PastFamilyInvitationForNodeCursor,
|
||||
PastFamilyInvitationsForNodePagedResponse, PastFamilyInvitationsPagedResponse,
|
||||
PastFamilyMember, PastFamilyMemberCursor, PastFamilyMemberForNodeCursor,
|
||||
PastFamilyMembersForNodePagedResponse, PastFamilyMembersPagedResponse,
|
||||
PendingFamilyInvitationDetails, PendingFamilyInvitationResponse,
|
||||
PendingFamilyInvitationsPagedResponse, PendingInvitationsForNodePagedResponse,
|
||||
PendingInvitationsPagedResponse,
|
||||
};
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait NodeFamiliesQueryClient {
|
||||
async fn query_node_families_contract<T>(
|
||||
&self,
|
||||
query: NodeFamiliesQueryMsg,
|
||||
) -> Result<T, NyxdError>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>;
|
||||
|
||||
async fn get_family_by_id(
|
||||
&self,
|
||||
family_id: NodeFamilyId,
|
||||
) -> Result<NodeFamilyResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetFamilyById { family_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_family_by_owner(
|
||||
&self,
|
||||
owner: &AccountId,
|
||||
) -> Result<NodeFamilyByOwnerResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetFamilyByOwner {
|
||||
owner: owner.to_string(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_family_by_name(
|
||||
&self,
|
||||
name: String,
|
||||
) -> Result<NodeFamilyByNameResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetFamilyByName { name })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_families_paged(
|
||||
&self,
|
||||
start_after: Option<NodeFamilyId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<FamiliesPagedResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetFamiliesPaged {
|
||||
start_after,
|
||||
limit,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_family_membership(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<NodeFamilyMembershipResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetFamilyMembership { node_id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_family_members_paged(
|
||||
&self,
|
||||
family_id: NodeFamilyId,
|
||||
start_after: Option<NodeId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<FamilyMembersPagedResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetFamilyMembersPaged {
|
||||
family_id,
|
||||
start_after,
|
||||
limit,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_family_members_paged(
|
||||
&self,
|
||||
start_after: Option<NodeId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<AllFamilyMembersPagedResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetAllFamilyMembersPaged {
|
||||
start_after,
|
||||
limit,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_pending_invitation(
|
||||
&self,
|
||||
family_id: NodeFamilyId,
|
||||
node_id: NodeId,
|
||||
) -> Result<PendingFamilyInvitationResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetPendingInvitation {
|
||||
family_id,
|
||||
node_id,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_pending_invitations_for_family_paged(
|
||||
&self,
|
||||
family_id: NodeFamilyId,
|
||||
start_after: Option<NodeId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PendingFamilyInvitationsPagedResponse, NyxdError> {
|
||||
self.query_node_families_contract(
|
||||
NodeFamiliesQueryMsg::GetPendingInvitationsForFamilyPaged {
|
||||
family_id,
|
||||
start_after,
|
||||
limit,
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_pending_invitations_for_node_paged(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
start_after: Option<NodeFamilyId>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PendingInvitationsForNodePagedResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetPendingInvitationsForNodePaged {
|
||||
node_id,
|
||||
start_after,
|
||||
limit,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_pending_invitations_paged(
|
||||
&self,
|
||||
start_after: Option<(NodeFamilyId, NodeId)>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PendingInvitationsPagedResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetAllPendingInvitationsPaged {
|
||||
start_after,
|
||||
limit,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_past_invitations_for_family_paged(
|
||||
&self,
|
||||
family_id: NodeFamilyId,
|
||||
start_after: Option<PastFamilyInvitationCursor>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PastFamilyInvitationsPagedResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetPastInvitationsForFamilyPaged {
|
||||
family_id,
|
||||
start_after,
|
||||
limit,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_past_invitations_for_node_paged(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
start_after: Option<PastFamilyInvitationForNodeCursor>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PastFamilyInvitationsForNodePagedResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetPastInvitationsForNodePaged {
|
||||
node_id,
|
||||
start_after,
|
||||
limit,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_past_invitations_paged(
|
||||
&self,
|
||||
start_after: Option<GlobalPastFamilyInvitationCursor>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<AllPastFamilyInvitationsPagedResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetAllPastInvitationsPaged {
|
||||
start_after,
|
||||
limit,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_past_members_for_family_paged(
|
||||
&self,
|
||||
family_id: NodeFamilyId,
|
||||
start_after: Option<PastFamilyMemberCursor>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PastFamilyMembersPagedResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetPastMembersForFamilyPaged {
|
||||
family_id,
|
||||
start_after,
|
||||
limit,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_past_members_for_node_paged(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
start_after: Option<PastFamilyMemberForNodeCursor>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PastFamilyMembersForNodePagedResponse, NyxdError> {
|
||||
self.query_node_families_contract(NodeFamiliesQueryMsg::GetPastMembersForNodePaged {
|
||||
node_id,
|
||||
start_after,
|
||||
limit,
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
// extension trait to the query client to deal with the paged queries
|
||||
// (it didn't feel appropriate to combine it with the existing trait)
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait PagedNodeFamiliesQueryClient: NodeFamiliesQueryClient {
|
||||
async fn get_all_families(&self) -> Result<Vec<NodeFamily>, NyxdError> {
|
||||
collect_paged!(self, get_families_paged, families)
|
||||
}
|
||||
|
||||
async fn get_all_family_members_for_family(
|
||||
&self,
|
||||
family_id: NodeFamilyId,
|
||||
) -> Result<Vec<FamilyMemberRecord>, NyxdError> {
|
||||
collect_paged!(self, get_family_members_paged, members, family_id)
|
||||
}
|
||||
|
||||
async fn get_all_family_members(&self) -> Result<Vec<FamilyMemberRecord>, NyxdError> {
|
||||
collect_paged!(self, get_all_family_members_paged, members)
|
||||
}
|
||||
|
||||
async fn get_all_pending_invitations_for_family(
|
||||
&self,
|
||||
family_id: NodeFamilyId,
|
||||
) -> Result<Vec<PendingFamilyInvitationDetails>, NyxdError> {
|
||||
collect_paged!(
|
||||
self,
|
||||
get_pending_invitations_for_family_paged,
|
||||
invitations,
|
||||
family_id
|
||||
)
|
||||
}
|
||||
|
||||
async fn get_all_pending_invitations_for_node(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<Vec<PendingFamilyInvitationDetails>, NyxdError> {
|
||||
collect_paged!(
|
||||
self,
|
||||
get_pending_invitations_for_node_paged,
|
||||
invitations,
|
||||
node_id
|
||||
)
|
||||
}
|
||||
|
||||
async fn get_all_pending_invitations(
|
||||
&self,
|
||||
) -> Result<Vec<PendingFamilyInvitationDetails>, NyxdError> {
|
||||
collect_paged!(self, get_all_pending_invitations_paged, invitations)
|
||||
}
|
||||
|
||||
async fn get_all_past_invitations_for_family(
|
||||
&self,
|
||||
family_id: NodeFamilyId,
|
||||
) -> Result<Vec<PastFamilyInvitation>, NyxdError> {
|
||||
collect_paged!(
|
||||
self,
|
||||
get_past_invitations_for_family_paged,
|
||||
invitations,
|
||||
family_id
|
||||
)
|
||||
}
|
||||
|
||||
async fn get_all_past_invitations_for_node(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<Vec<PastFamilyInvitation>, NyxdError> {
|
||||
collect_paged!(
|
||||
self,
|
||||
get_past_invitations_for_node_paged,
|
||||
invitations,
|
||||
node_id
|
||||
)
|
||||
}
|
||||
|
||||
async fn get_all_past_invitations(&self) -> Result<Vec<PastFamilyInvitation>, NyxdError> {
|
||||
collect_paged!(self, get_all_past_invitations_paged, invitations)
|
||||
}
|
||||
|
||||
async fn get_all_past_members_for_family(
|
||||
&self,
|
||||
family_id: NodeFamilyId,
|
||||
) -> Result<Vec<PastFamilyMember>, NyxdError> {
|
||||
collect_paged!(self, get_past_members_for_family_paged, members, family_id)
|
||||
}
|
||||
|
||||
async fn get_all_past_members_for_node(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
) -> Result<Vec<PastFamilyMember>, NyxdError> {
|
||||
collect_paged!(self, get_past_members_for_node_paged, members, node_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T> PagedNodeFamiliesQueryClient for T where T: NodeFamiliesQueryClient {}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<C> NodeFamiliesQueryClient for C
|
||||
where
|
||||
C: CosmWasmClient + NymContractsProvider + Send + Sync,
|
||||
{
|
||||
async fn query_node_families_contract<T>(
|
||||
&self,
|
||||
query: NodeFamiliesQueryMsg,
|
||||
) -> Result<T, NyxdError>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
{
|
||||
let node_families_contract_address = &self
|
||||
.node_families_contract_address()
|
||||
.ok_or_else(|| NyxdError::unavailable_contract_address("node families contract"))?;
|
||||
self.query_contract_smart(node_families_contract_address, &query)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::nyxd::contract_traits::tests::IgnoreValue;
|
||||
use nym_node_families_contract_common::QueryMsg;
|
||||
|
||||
// it's enough that this compiles and clippy is happy about it
|
||||
#[allow(dead_code)]
|
||||
fn all_query_variants_are_covered<C: NodeFamiliesQueryClient + Send + Sync>(
|
||||
client: C,
|
||||
msg: NodeFamiliesQueryMsg,
|
||||
) {
|
||||
match msg {
|
||||
NodeFamiliesQueryMsg::GetFamilyById { family_id } => {
|
||||
client.get_family_by_id(family_id).ignore()
|
||||
}
|
||||
NodeFamiliesQueryMsg::GetFamilyByOwner { owner } => {
|
||||
client.get_family_by_owner(&owner.parse().unwrap()).ignore()
|
||||
}
|
||||
NodeFamiliesQueryMsg::GetFamilyByName { name } => {
|
||||
client.get_family_by_name(name).ignore()
|
||||
}
|
||||
NodeFamiliesQueryMsg::GetFamiliesPaged { start_after, limit } => {
|
||||
client.get_families_paged(start_after, limit).ignore()
|
||||
}
|
||||
NodeFamiliesQueryMsg::GetFamilyMembership { node_id } => {
|
||||
client.get_family_membership(node_id).ignore()
|
||||
}
|
||||
NodeFamiliesQueryMsg::GetFamilyMembersPaged {
|
||||
family_id,
|
||||
start_after,
|
||||
limit,
|
||||
} => client
|
||||
.get_family_members_paged(family_id, start_after, limit)
|
||||
.ignore(),
|
||||
NodeFamiliesQueryMsg::GetAllFamilyMembersPaged { start_after, limit } => client
|
||||
.get_all_family_members_paged(start_after, limit)
|
||||
.ignore(),
|
||||
NodeFamiliesQueryMsg::GetPendingInvitation { family_id, node_id } => {
|
||||
client.get_pending_invitation(family_id, node_id).ignore()
|
||||
}
|
||||
NodeFamiliesQueryMsg::GetPendingInvitationsForFamilyPaged {
|
||||
family_id,
|
||||
start_after,
|
||||
limit,
|
||||
} => client
|
||||
.get_pending_invitations_for_family_paged(family_id, start_after, limit)
|
||||
.ignore(),
|
||||
NodeFamiliesQueryMsg::GetPendingInvitationsForNodePaged {
|
||||
node_id,
|
||||
start_after,
|
||||
limit,
|
||||
} => client
|
||||
.get_pending_invitations_for_node_paged(node_id, start_after, limit)
|
||||
.ignore(),
|
||||
NodeFamiliesQueryMsg::GetAllPendingInvitationsPaged { start_after, limit } => client
|
||||
.get_all_pending_invitations_paged(start_after, limit)
|
||||
.ignore(),
|
||||
NodeFamiliesQueryMsg::GetPastInvitationsForFamilyPaged {
|
||||
family_id,
|
||||
start_after,
|
||||
limit,
|
||||
} => client
|
||||
.get_past_invitations_for_family_paged(family_id, start_after, limit)
|
||||
.ignore(),
|
||||
NodeFamiliesQueryMsg::GetPastInvitationsForNodePaged {
|
||||
node_id,
|
||||
start_after,
|
||||
limit,
|
||||
} => client
|
||||
.get_past_invitations_for_node_paged(node_id, start_after, limit)
|
||||
.ignore(),
|
||||
NodeFamiliesQueryMsg::GetAllPastInvitationsPaged { start_after, limit } => client
|
||||
.get_all_past_invitations_paged(start_after, limit)
|
||||
.ignore(),
|
||||
NodeFamiliesQueryMsg::GetPastMembersForFamilyPaged {
|
||||
family_id,
|
||||
start_after,
|
||||
limit,
|
||||
} => client
|
||||
.get_past_members_for_family_paged(family_id, start_after, limit)
|
||||
.ignore(),
|
||||
QueryMsg::GetPastMembersForNodePaged {
|
||||
node_id,
|
||||
start_after,
|
||||
limit,
|
||||
} => client
|
||||
.get_past_members_for_node_paged(node_id, start_after, limit)
|
||||
.ignore(),
|
||||
};
|
||||
}
|
||||
}
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::nyxd::coin::Coin;
|
||||
use crate::nyxd::contract_traits::NymContractsProvider;
|
||||
use crate::nyxd::cosmwasm_client::types::ExecuteResult;
|
||||
use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::{Fee, SigningCosmWasmClient};
|
||||
use crate::signing::signer::OfflineSigner;
|
||||
use async_trait::async_trait;
|
||||
use nym_mixnet_contract_common::NodeId;
|
||||
use nym_node_families_contract_common::{
|
||||
Config, ExecuteMsg as NodeFamiliesExecuteMsg, NodeFamilyId,
|
||||
};
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait NodeFamiliesSigningClient {
|
||||
async fn execute_node_families_contract(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
msg: NodeFamiliesExecuteMsg,
|
||||
memo: String,
|
||||
funds: Vec<Coin>,
|
||||
) -> Result<ExecuteResult, NyxdError>;
|
||||
|
||||
async fn update_node_families_config(
|
||||
&self,
|
||||
config: Config,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_node_families_contract(
|
||||
fee,
|
||||
NodeFamiliesExecuteMsg::UpdateConfig { config },
|
||||
"NodeFamiliesContract::UpdateConfig".to_string(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn create_family(
|
||||
&self,
|
||||
name: String,
|
||||
description: String,
|
||||
fee: Option<Fee>,
|
||||
creation_fee: Vec<Coin>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_node_families_contract(
|
||||
fee,
|
||||
NodeFamiliesExecuteMsg::CreateFamily { name, description },
|
||||
"NodeFamiliesContract::CreateFamily".to_string(),
|
||||
creation_fee,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn disband_family(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_node_families_contract(
|
||||
fee,
|
||||
NodeFamiliesExecuteMsg::DisbandFamily {},
|
||||
"NodeFamiliesContract::DisbandFamily".to_string(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn invite_to_family(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
validity_secs: Option<u64>,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_node_families_contract(
|
||||
fee,
|
||||
NodeFamiliesExecuteMsg::InviteToFamily {
|
||||
node_id,
|
||||
validity_secs,
|
||||
},
|
||||
"NodeFamiliesContract::InviteToFamily".to_string(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn revoke_family_invitation(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_node_families_contract(
|
||||
fee,
|
||||
NodeFamiliesExecuteMsg::RevokeFamilyInvitation { node_id },
|
||||
"NodeFamiliesContract::RevokeFamilyInvitation".to_string(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn accept_family_invitation(
|
||||
&self,
|
||||
family_id: NodeFamilyId,
|
||||
node_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_node_families_contract(
|
||||
fee,
|
||||
NodeFamiliesExecuteMsg::AcceptFamilyInvitation { family_id, node_id },
|
||||
"NodeFamiliesContract::AcceptFamilyInvitation".to_string(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn reject_family_invitation(
|
||||
&self,
|
||||
family_id: NodeFamilyId,
|
||||
node_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_node_families_contract(
|
||||
fee,
|
||||
NodeFamiliesExecuteMsg::RejectFamilyInvitation { family_id, node_id },
|
||||
"NodeFamiliesContract::RejectFamilyInvitation".to_string(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn leave_family(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_node_families_contract(
|
||||
fee,
|
||||
NodeFamiliesExecuteMsg::LeaveFamily { node_id },
|
||||
"NodeFamiliesContract::LeaveFamily".to_string(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn kick_from_family(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_node_families_contract(
|
||||
fee,
|
||||
NodeFamiliesExecuteMsg::KickFromFamily { node_id },
|
||||
"NodeFamiliesContract::KickFromFamily".to_string(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Cross-contract callback fired by the mixnet contract on node unbonding.
|
||||
/// Exposed for completeness; the families contract rejects this call from
|
||||
/// any sender other than the configured mixnet contract address.
|
||||
async fn on_nym_node_unbond(
|
||||
&self,
|
||||
node_id: NodeId,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_node_families_contract(
|
||||
fee,
|
||||
NodeFamiliesExecuteMsg::OnNymNodeUnbond { node_id },
|
||||
"NodeFamiliesContract::OnNymNodeUnbond".to_string(),
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<C> NodeFamiliesSigningClient for C
|
||||
where
|
||||
C: SigningCosmWasmClient + NymContractsProvider + Sync,
|
||||
NyxdError: From<<Self as OfflineSigner>::Error>,
|
||||
{
|
||||
async fn execute_node_families_contract(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
msg: NodeFamiliesExecuteMsg,
|
||||
memo: String,
|
||||
funds: Vec<Coin>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
let node_families_contract_address = &self
|
||||
.node_families_contract_address()
|
||||
.ok_or_else(|| NyxdError::unavailable_contract_address("node families contract"))?;
|
||||
|
||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
|
||||
|
||||
let signer_address = &self.signer_addresses()[0];
|
||||
self.execute(
|
||||
signer_address,
|
||||
node_families_contract_address,
|
||||
&msg,
|
||||
fee,
|
||||
memo,
|
||||
funds,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::nyxd::contract_traits::tests::IgnoreValue;
|
||||
use nym_node_families_contract_common::ExecuteMsg;
|
||||
|
||||
// it's enough that this compiles and clippy is happy about it
|
||||
#[allow(dead_code)]
|
||||
fn all_execute_variants_are_covered<C: NodeFamiliesSigningClient + Send + Sync>(
|
||||
client: C,
|
||||
msg: NodeFamiliesExecuteMsg,
|
||||
) {
|
||||
match msg {
|
||||
NodeFamiliesExecuteMsg::UpdateConfig { config } => {
|
||||
client.update_node_families_config(config, None).ignore()
|
||||
}
|
||||
NodeFamiliesExecuteMsg::CreateFamily { name, description } => client
|
||||
.create_family(name, description, None, vec![])
|
||||
.ignore(),
|
||||
NodeFamiliesExecuteMsg::DisbandFamily {} => client.disband_family(None).ignore(),
|
||||
NodeFamiliesExecuteMsg::InviteToFamily {
|
||||
node_id,
|
||||
validity_secs,
|
||||
} => client
|
||||
.invite_to_family(node_id, validity_secs, None)
|
||||
.ignore(),
|
||||
NodeFamiliesExecuteMsg::RevokeFamilyInvitation { node_id } => {
|
||||
client.revoke_family_invitation(node_id, None).ignore()
|
||||
}
|
||||
NodeFamiliesExecuteMsg::AcceptFamilyInvitation { family_id, node_id } => client
|
||||
.accept_family_invitation(family_id, node_id, None)
|
||||
.ignore(),
|
||||
NodeFamiliesExecuteMsg::RejectFamilyInvitation { family_id, node_id } => client
|
||||
.reject_family_invitation(family_id, node_id, None)
|
||||
.ignore(),
|
||||
NodeFamiliesExecuteMsg::LeaveFamily { node_id } => {
|
||||
client.leave_family(node_id, None).ignore()
|
||||
}
|
||||
NodeFamiliesExecuteMsg::KickFromFamily { node_id } => {
|
||||
client.kick_from_family(node_id, None).ignore()
|
||||
}
|
||||
ExecuteMsg::OnNymNodeUnbond { node_id } => {
|
||||
client.on_nym_node_unbond(node_id, None).ignore()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -304,6 +304,10 @@ impl<C, S> NyxdClient<C, S> {
|
||||
self.config.contracts.multisig_contract_address = Some(address);
|
||||
}
|
||||
|
||||
pub fn set_node_families_contract_address(&mut self, address: AccountId) {
|
||||
self.config.contracts.node_families_contract_address = Some(address);
|
||||
}
|
||||
|
||||
pub fn set_simulated_gas_multiplier(&mut self, multiplier: f32) {
|
||||
self.config.simulated_gas_multiplier = multiplier;
|
||||
}
|
||||
@@ -332,6 +336,13 @@ impl<C, S> NymContractsProvider for NyxdClient<C, S> {
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
fn node_families_contract_address(&self) -> Option<&AccountId> {
|
||||
self.config
|
||||
.contracts
|
||||
.node_families_contract_address
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
fn ecash_contract_address(&self) -> Option<&AccountId> {
|
||||
self.config.contracts.ecash_contract_address.as_ref()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user