Skip to main content

nym_api_requests/models/
node_families.rs

1// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: GPL-3.0-only
3
4use super::CoinSchema;
5use cosmwasm_std::Coin;
6use nym_mixnet_contract_common::{NodeId, NodeRewarding};
7use serde::{Deserialize, Serialize};
8use std::time::Duration;
9use time::OffsetDateTime;
10use utoipa::ToSchema;
11
12/// Pending family invitation as exposed by the nym-api node-families endpoints.
13#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
14pub struct PendingFamilyInvitation {
15    /// Node the invitation is addressed to.
16    pub node_id: NodeId,
17
18    /// Block-time after which the invitation can no longer be accepted.
19    #[serde(with = "time::serde::rfc3339")]
20    #[schema(value_type = String)]
21    pub expires_at: OffsetDateTime,
22}
23
24/// Per-node stake snapshot derived from the mixnet contract's rewarding state.
25#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
26pub struct NodeStakeInformation {
27    /// Operator bond + all delegations, with accrued rewards applied.
28    #[schema(value_type = CoinSchema)]
29    pub stake: Coin,
30
31    /// Operator pledge component of `stake`.
32    #[schema(value_type = CoinSchema)]
33    pub bond: Coin,
34
35    /// Delegations component of `stake`.
36    #[schema(value_type = CoinSchema)]
37    pub delegations: Coin,
38
39    /// Number of unique delegators backing this node.
40    pub delegators: usize,
41}
42
43impl From<&NodeRewarding> for NodeStakeInformation {
44    fn from(rewarding: &NodeRewarding) -> Self {
45        let denom = &rewarding.cost_params.interval_operating_cost.denom;
46
47        let bond = rewarding.operator_pledge_with_reward(denom);
48        let delegations = rewarding.delegations_with_reward(denom);
49        let mut stake = bond.clone();
50        stake.amount += delegations.amount;
51
52        NodeStakeInformation {
53            stake,
54            bond,
55            delegations,
56            delegators: rewarding.unique_delegations as usize,
57        }
58    }
59}
60
61/// Family member view as exposed by the nym-api node-families endpoints.
62#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
63pub struct NodeFamilyMember {
64    pub node_id: NodeId,
65
66    /// Block-time at which the node joined the family.
67    #[serde(with = "time::serde::rfc3339")]
68    #[schema(value_type = String)]
69    pub joined_at: OffsetDateTime,
70
71    /// Stake/bond/delegation snapshot; `None` if the node was not in the
72    /// mixnet-contract cache at refresh time.
73    pub stake_information: Option<NodeStakeInformation>,
74}
75
76/// Family view as exposed by the nym-api node-families endpoints, carrying
77/// current members, pending invitations and aggregated stats.
78#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
79pub struct NodeFamily {
80    /// Unique family identifier assigned by the contract.
81    pub id: u32,
82
83    /// Display name (canonical form — see `normalise_family_name`).
84    pub name: String,
85
86    /// Free-form family description.
87    pub description: String,
88
89    /// Owner address (cosmos `Addr` rendered as a string).
90    pub owner: String,
91
92    /// Average age of members.
93    #[serde(with = "humantime_serde")]
94    #[schema(value_type = String)]
95    pub average_node_age: Duration,
96
97    /// Sum of member stakes; `None` when no member has reportable stake.
98    #[schema(value_type = Option<CoinSchema>)]
99    pub total_stake: Option<Coin>,
100
101    /// Block-time the family was created.
102    #[serde(with = "time::serde::rfc3339")]
103    #[schema(value_type = String)]
104    pub created_at: OffsetDateTime,
105
106    /// Current members of the family.
107    pub members: Vec<NodeFamilyMember>,
108
109    /// Outstanding invitations issued by the family owner.
110    pub pending_invitations: Vec<PendingFamilyInvitation>,
111}
112
113/// Response wrapper for endpoints that look up a single family. `family` is
114/// `None` when the lookup did not match (rather than returning a 404).
115#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
116pub struct NodeFamilyResponse {
117    pub family: Option<NodeFamily>,
118}
119
120/// Response wrapper for endpoints that look up the family a given node
121/// belongs to. `family` is `None` when the node is not currently a member of
122/// any cached family.
123#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
124pub struct NodeFamilyForNodeResponse {
125    /// The node the lookup was performed for.
126    pub node_id: NodeId,
127
128    /// The family this node belongs to, if any.
129    pub family: Option<NodeFamily>,
130}