Skip to main content

tenzro_types/
token.rs

1//! TNZO token economics and governance types
2//!
3//! This module defines types for the TNZO token, staking, treasury management,
4//! and governance on Tenzro Network.
5
6use crate::primitives::{Address, Timestamp};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10/// TNZO token configuration
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct TokenConfig {
13    /// Token name
14    pub name: String,
15    /// Token symbol
16    pub symbol: String,
17    /// Number of decimals
18    pub decimals: u8,
19    /// Total supply (in smallest unit) - using u128 to prevent overflow
20    pub total_supply: u128,
21    /// Initial distribution
22    pub initial_distribution: InitialDistribution,
23    /// Token economics parameters
24    pub economics: TokenEconomics,
25}
26
27impl Default for TokenConfig {
28    fn default() -> Self {
29        Self {
30            name: "Tenzro Network Token".to_string(),
31            symbol: "TNZO".to_string(),
32            decimals: 18,
33            total_supply: 1_000_000_000u128 * 10u128.pow(18), // 1 billion TNZO
34            initial_distribution: InitialDistribution::default(),
35            economics: TokenEconomics::default(),
36        }
37    }
38}
39
40/// Initial token distribution
41#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42pub struct InitialDistribution {
43    /// Treasury allocation
44    pub treasury: u128,
45    /// Team allocation
46    pub team: u128,
47    /// Investors allocation
48    pub investors: u128,
49    /// Community allocation
50    pub community: u128,
51    /// Provider incentives
52    pub provider_incentives: u128,
53    /// Liquidity pool
54    pub liquidity: u128,
55}
56
57impl Default for InitialDistribution {
58    fn default() -> Self {
59        let total = 1_000_000_000u128 * 10u128.pow(18); // 1 billion TNZO
60        Self {
61            treasury: (total * 25) / 100,          // 25% (network treasury & grants)
62            team: (total * 10) / 100,              // 10% (4-year vest, 1-year cliff)
63            investors: (total * 10) / 100,         // 10% (strategic rounds, vested)
64            community: (total * 35) / 100,         // 35% (airdrops, incentives, ecosystem growth)
65            provider_incentives: (total * 15) / 100, // 15% (TEE/compute/model providers)
66            liquidity: (total * 5) / 100,          // 5% (DEX/CEX liquidity)
67        }
68    }
69}
70
71/// Token economics parameters
72#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
73pub struct TokenEconomics {
74    /// Inflation rate (basis points per year)
75    pub inflation_rate: u32,
76    /// Transaction fee percentage (basis points)
77    pub transaction_fee_bps: u32,
78    /// Staking reward rate (basis points per year)
79    pub staking_reward_rate: u32,
80    /// Burn rate for fees (basis points)
81    pub burn_rate_bps: u32,
82    /// Minimum stake amount (in smallest unit)
83    pub min_stake: u128,
84}
85
86impl Default for TokenEconomics {
87    fn default() -> Self {
88        Self {
89            inflation_rate: 200,         // 2% per year
90            transaction_fee_bps: 10,     // 0.1%
91            staking_reward_rate: 500,    // 5% per year
92            burn_rate_bps: 5000,         // 50% of fees burned
93            min_stake: 1000u128 * 10u128.pow(18), // 1000 TNZO minimum
94        }
95    }
96}
97
98/// Treasury management
99#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
100pub struct Treasury {
101    /// Treasury address
102    pub address: Address,
103    /// Current balance (in smallest unit)
104    pub balance: u128,
105    /// Total allocated to grants
106    pub allocated_grants: u128,
107    /// Total spent on development
108    pub spent_development: u128,
109    /// Total spent on marketing
110    pub spent_marketing: u128,
111    /// Reserved for future use
112    pub reserved: u128,
113    /// Treasury parameters
114    pub parameters: TreasuryParameters,
115}
116
117impl Treasury {
118    /// Creates a new treasury
119    pub fn new(address: Address, initial_balance: u128) -> Self {
120        Self {
121            address,
122            balance: initial_balance,
123            allocated_grants: 0,
124            spent_development: 0,
125            spent_marketing: 0,
126            reserved: 0,
127            parameters: TreasuryParameters::default(),
128        }
129    }
130
131    /// Returns the available balance using checked arithmetic
132    pub fn available_balance(&self) -> Option<u128> {
133        self.balance
134            .checked_sub(self.allocated_grants)?
135            .checked_sub(self.reserved)
136    }
137}
138
139/// Treasury parameters
140#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
141pub struct TreasuryParameters {
142    /// Maximum grant amount per proposal
143    pub max_grant_amount: u128,
144    /// Minimum proposal threshold
145    pub min_proposal_threshold: u128,
146    /// Grant approval quorum (basis points)
147    pub grant_approval_quorum: u32,
148}
149
150impl Default for TreasuryParameters {
151    fn default() -> Self {
152        Self {
153            max_grant_amount: 1_000_000u128 * 10u128.pow(18), // 1M TNZO
154            min_proposal_threshold: 10_000u128 * 10u128.pow(18), // 10K TNZO
155            grant_approval_quorum: 5000,                  // 50%
156        }
157    }
158}
159
160/// Staking pool for validators and providers
161#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
162pub struct StakingPool {
163    /// Pool ID
164    pub pool_id: String,
165    /// Pool operator
166    pub operator: Address,
167    /// Pool type
168    pub pool_type: PoolType,
169    /// Total staked amount (in smallest unit)
170    pub total_staked: u128,
171    /// Number of stakers
172    pub staker_count: u64,
173    /// Pool commission rate (basis points)
174    pub commission_rate: u32,
175    /// Pool status
176    pub status: PoolStatus,
177    /// Pool metadata
178    pub metadata: HashMap<String, String>,
179}
180
181impl StakingPool {
182    /// Creates a new staking pool
183    pub fn new(operator: Address, pool_type: PoolType) -> Self {
184        Self {
185            pool_id: uuid::Uuid::new_v4().to_string(),
186            operator,
187            pool_type,
188            total_staked: 0,
189            staker_count: 0,
190            commission_rate: 1000, // 10%
191            status: PoolStatus::Active,
192            metadata: HashMap::new(),
193        }
194    }
195
196    /// Adds stake to the pool using checked arithmetic
197    pub fn add_stake(&mut self, amount: u128) -> Result<(), &'static str> {
198        self.total_staked = self.total_staked
199            .checked_add(amount)
200            .ok_or("Stake addition would overflow")?;
201        self.staker_count = self.staker_count
202            .checked_add(1)
203            .ok_or("Staker count would overflow")?;
204        Ok(())
205    }
206
207    /// Removes stake from the pool using checked arithmetic
208    pub fn remove_stake(&mut self, amount: u128) -> Result<(), &'static str> {
209        self.total_staked = self.total_staked
210            .checked_sub(amount)
211            .ok_or("Insufficient stake to remove")?;
212        self.staker_count = self.staker_count
213            .checked_sub(1)
214            .ok_or("Staker count underflow")?;
215        Ok(())
216    }
217}
218
219/// Type of staking pool
220#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
221pub enum PoolType {
222    /// Validator staking pool
223    Validator,
224    /// TEE provider pool
225    TeeProvider,
226    /// Model provider pool
227    ModelProvider,
228    /// Storage provider pool
229    StorageProvider,
230    /// Tenzro Train: trainer (proposes outer gradients) bonding pool
231    Trainer,
232    /// Tenzro Train: syncer (aggregates outer gradients, publishes rounds) bonding pool
233    Syncer,
234}
235
236/// Staking pool status
237#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
238pub enum PoolStatus {
239    /// Pool is active
240    Active,
241    /// Pool is full
242    Full,
243    /// Pool is paused
244    Paused,
245    /// Pool is closed
246    Closed,
247}
248
249/// Provider stake information
250#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
251pub struct ProviderStake {
252    /// Provider address
253    pub provider: Address,
254    /// Provider type
255    pub provider_type: ProviderType,
256    /// Staked amount (in smallest unit)
257    pub staked_amount: u128,
258    /// Stake timestamp
259    pub staked_at: Timestamp,
260    /// Lock period end (if any)
261    pub lock_until: Option<Timestamp>,
262    /// Rewards earned (in smallest unit)
263    pub rewards_earned: u128,
264    /// Stake status
265    pub status: StakeStatus,
266}
267
268impl ProviderStake {
269    /// Creates a new provider stake
270    pub fn new(provider: Address, provider_type: ProviderType, amount: u128) -> Self {
271        Self {
272            provider,
273            provider_type,
274            staked_amount: amount,
275            staked_at: Timestamp::now(),
276            lock_until: None,
277            rewards_earned: 0,
278            status: StakeStatus::Active,
279        }
280    }
281
282    /// Sets a lock period
283    pub fn with_lock_period(mut self, duration_ms: i64) -> Self {
284        self.lock_until = Some(Timestamp::new(
285            Timestamp::now().as_millis() + duration_ms,
286        ));
287        self
288    }
289
290    /// Checks if stake is locked
291    pub fn is_locked(&self) -> bool {
292        if let Some(lock_until) = self.lock_until {
293            Timestamp::now() < lock_until
294        } else {
295            false
296        }
297    }
298
299    /// Adds rewards using checked arithmetic
300    pub fn add_rewards(&mut self, amount: u128) -> Result<(), &'static str> {
301        self.rewards_earned = self.rewards_earned
302            .checked_add(amount)
303            .ok_or("Reward addition would overflow")?;
304        Ok(())
305    }
306}
307
308/// Type of provider
309#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
310pub enum ProviderType {
311    /// Validator
312    Validator,
313    /// TEE provider
314    TeeProvider,
315    /// Model provider
316    ModelProvider,
317    /// Storage provider
318    StorageProvider,
319    /// Tenzro Train trainer — proposes outer gradients for a fragment
320    Trainer,
321    /// Tenzro Train syncer — aggregates outer gradients and publishes rounds
322    Syncer,
323}
324
325/// Stake status
326#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
327pub enum StakeStatus {
328    /// Stake is active
329    Active,
330    /// Stake is being unbonded
331    Unbonding,
332    /// Stake has been slashed
333    Slashed,
334    /// Stake has been withdrawn
335    Withdrawn,
336}
337
338/// Governance proposal
339#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
340pub struct GovernanceProposal {
341    /// Proposal ID
342    pub proposal_id: String,
343    /// Proposal title
344    pub title: String,
345    /// Proposal description
346    pub description: String,
347    /// Proposer address
348    pub proposer: Address,
349    /// Proposal type
350    pub proposal_type: ProposalType,
351    /// Voting start time
352    pub voting_start: Timestamp,
353    /// Voting end time
354    pub voting_end: Timestamp,
355    /// Current status
356    pub status: ProposalStatus,
357    /// Votes in favor
358    pub votes_for: u128,
359    /// Votes against
360    pub votes_against: u128,
361    /// Total voting power
362    pub total_voting_power: u128,
363    /// Execution data (if applicable)
364    pub execution_data: Option<Vec<u8>>,
365}
366
367impl GovernanceProposal {
368    /// Creates a new governance proposal
369    pub fn new(
370        title: String,
371        description: String,
372        proposer: Address,
373        proposal_type: ProposalType,
374        voting_duration_ms: i64,
375    ) -> Self {
376        let now = Timestamp::now();
377        Self {
378            proposal_id: uuid::Uuid::new_v4().to_string(),
379            title,
380            description,
381            proposer,
382            proposal_type,
383            voting_start: now,
384            voting_end: Timestamp::new(now.as_millis() + voting_duration_ms),
385            status: ProposalStatus::Active,
386            votes_for: 0,
387            votes_against: 0,
388            total_voting_power: 0,
389            execution_data: None,
390        }
391    }
392
393    /// Checks if voting is still open
394    pub fn is_voting_open(&self) -> bool {
395        let now = Timestamp::now();
396        now >= self.voting_start && now < self.voting_end && self.status == ProposalStatus::Active
397    }
398
399    /// Returns the current approval percentage (basis points)
400    pub fn approval_percentage(&self) -> u32 {
401        if self.total_voting_power == 0 {
402            0
403        } else {
404            ((self.votes_for as f64 / self.total_voting_power as f64) * 10000.0) as u32
405        }
406    }
407}
408
409/// Type of governance proposal
410#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
411pub enum ProposalType {
412    /// Parameter change proposal
413    ParameterChange { parameter: String, new_value: String },
414    /// Treasury grant proposal
415    TreasuryGrant { recipient: Address, amount: u128 },
416    /// Protocol upgrade proposal
417    ProtocolUpgrade { version: String, code_hash: Vec<u8> },
418    /// Adaptive-burn dial update (Spec 8). Sets the live `BurnRateConfig`
419    /// applied by the EIP-1559 fee market and Spec 6 local-fee router.
420    /// `paymaster_burn_bps` is invariant-locked to 10_000 (100%) — proposals
421    /// that violate this are rejected at execution time.
422    AdaptiveBurnConfigUpdate {
423        base_fee_burn_bps: u16,
424        local_fee_burn_bps: u16,
425        paymaster_burn_bps: u16,
426    },
427    /// Adaptive-burn supply-targets update (Spec 8). Adjusts the rolling
428    /// window, neutral band, alarm thresholds, gain, and magnitude caps
429    /// the auto-proposal generator uses to draft `AdaptiveBurnConfigUpdate`
430    /// proposals.
431    SupplyTargetsUpdate {
432        epoch_neutral_band_bps: u16,
433        rolling_window_epochs: u32,
434        inflation_alarm_bps: u16,
435        deflation_alarm_bps: u16,
436        target_annual_supply_bps: i32,
437        gain_bps_per_pct: u16,
438        magnitude_cap_normal_bps: u16,
439        magnitude_cap_alarm_bps: u16,
440        auto_proposal_min_magnitude_bps: u16,
441        alarm_fast_track_enabled: bool,
442        alarm_timelock_hours: u32,
443    },
444    /// SeedAgent earmark adjustment (Spec 10). Governs the master enable
445    /// flag, allocation top-ups, and the sunset surplus disposition. Other
446    /// fields on `TreasuryEarmark` (decay schedule, bootstrap window,
447    /// charter id list, draw counters) are mutated by protocol code, not
448    /// directly by proposal.
449    SeedAgentEarmarkUpdate {
450        /// Master enable flag. When `false`, no new SeedAgent provisioning
451        /// is admitted and the daemon should wind down.
452        enabled: bool,
453        /// TNZO base units to *add* to `allocation_remaining_wei` (and to
454        /// `initial_allocation_wei` if `is_initial_seed` is set). Zero
455        /// leaves the balance unchanged.
456        allocation_topup_wei: u128,
457        /// If `true`, this proposal also sets `initial_allocation_wei`
458        /// (genesis seeding). After genesis, top-ups should leave the
459        /// initial figure intact for audit purposes.
460        is_initial_seed: bool,
461        /// New sunset surplus disposition in basis points to burn (the
462        /// remainder returns to general treasury). `<= 10_000`.
463        surplus_burn_bps: u16,
464    },
465    /// SeedAgent charter upsert/disable (Spec 10). The charter id is
466    /// `Hash([u8; 32])` rendered as 32 raw bytes; downstream executor
467    /// resolves it against the SeedAgentEarmarkManager. Disabling sets
468    /// `enabled = false` on the existing charter without removing it,
469    /// which signals existing agents under that charter to wind down.
470    SeedAgentCharterUpsert {
471        /// Bincode-serialized [`Charter`] payload. The executor decodes
472        /// and runs `Charter::validate()` before applying.
473        charter_blob: Vec<u8>,
474    },
475    /// SeedAgent per-agent status transition (Spec 10). Used by
476    /// governance to Pause / Quarantine / Terminate a misbehaving agent
477    /// without touching the charter under which it operates.
478    SeedAgentStatusSet {
479        agent_did: String,
480        /// Target status as `SeedAgentStatus::as_str()`:
481        /// `"active" | "paused" | "quarantined" | "terminated"`.
482        status: String,
483    },
484    /// Custom proposal
485    Custom { proposal_data: Vec<u8> },
486}
487
488/// Status of a governance proposal
489#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
490pub enum ProposalStatus {
491    /// Proposal is active and accepting votes
492    Active,
493    /// Proposal passed
494    Passed,
495    /// Proposal failed
496    Failed,
497    /// Proposal was cancelled
498    Cancelled,
499    /// Proposal has been executed
500    Executed,
501}