Governance
Tenzro Network features on-chain governance powered by the GovernanceEngine, enabling TNZO token holders to propose and vote on protocol upgrades, parameter changes, treasury spending, and network policies. The governance system uses stake-weighted voting with epoch-based reward distribution.
Governance Token: TNZO
TNZO is the native governance token used for all protocol operations on the Tenzro Ledger L1. Token holders participate in network governance by staking TNZO and voting on proposals.
Token Name: Tenzro Network Token
Symbol: TNZO
Decimals: 18
Total Supply: 1,000,000,000 TNZO (1 billion)
Community Allocation: 35-40%
Default Network Fee: 0.5%
Testnet Faucet: 100 TNZO per request (24h cooldown)
TNZO Use Cases
TNZO serves four primary functions in the Tenzro Network:
// 1. Transaction Fees (Gas)
// All transactions on Tenzro Ledger pay gas in TNZO
let tx = Transaction {
from: sender_address,
to: recipient_address,
value: amount,
gas_limit: 21000,
gas_price: 1_000_000_000, // 1 Gwei in TNZO
..
};
// 2. AI Inference and TEE Service Payments
// Settle payments for intelligence and security providers
let settlement = SettlementRequest {
from: user_address,
to: provider_address,
amount: inference_cost, // In TNZO
asset: Asset::Tnzo,
proof: inference_proof,
};
// 3. Staking for Validation and Governance
// Validators and providers stake TNZO to secure the network
staking_manager.stake(validator_address, stake_amount)?;
// 4. Governance Voting
// Vote on proposals with stake-weighted voting power
governance_engine.vote(proposal_id, Vote::Yes, voting_power)?;GovernanceEngine
The GovernanceEngine manages on-chain proposals and voting. Proposals can modify network parameters, upgrade protocol contracts, allocate treasury funds, or change governance rules themselves.
Proposal Lifecycle
// Proposal states
pub enum ProposalState {
Pending, // Submitted, voting not started
Active, // Open for voting
Succeeded, // Quorum reached, majority yes
Defeated, // Quorum not reached or majority no
Queued, // Passed, waiting for timelock
Executed, // Applied to network
Canceled, // Proposer canceled
Expired, // Voting period ended without quorum
}
// Proposal lifecycle
Pending (submission)
↓
Active (voting period: 7 days)
↓
Succeeded / Defeated (vote tallying)
↓
Queued (timelock: 2 days)
↓
Executed (on-chain application)
// Timeline constants
const VOTING_PERIOD: u64 = 7 * 24 * 3600; // 7 days
const TIMELOCK_PERIOD: u64 = 2 * 24 * 3600; // 2 days
const QUORUM_THRESHOLD: f64 = 0.04; // 4% of total supply
const APPROVAL_THRESHOLD: f64 = 0.51; // 51% of votes castCreating Proposals
Any address holding at least 1,000 TNZO can submit a governance proposal. Proposals must include a title, description, and executable actions.
// Proposal structure
pub struct Proposal {
pub id: u64,
pub proposer: Address,
pub title: String,
pub description: String,
pub actions: Vec<ProposalAction>,
pub start_block: u64,
pub end_block: u64,
pub votes_for: u128,
pub votes_against: u128,
pub votes_abstain: u128,
pub state: ProposalState,
pub created_at: Timestamp,
}
// Proposal actions
pub enum ProposalAction {
ParameterChange { key: String, value: String },
TreasurySpend { recipient: Address, amount: u128 },
ContractUpgrade { contract: Address, code: Vec<u8> },
ValidatorSetChange { add: Vec<Address>, remove: Vec<Address> },
Custom { target: Address, calldata: Vec<u8> },
}
// Submit proposal
let proposal = Proposal {
proposer: my_address,
title: "Increase block gas limit to 40M".to_string(),
description: "To support higher throughput...".to_string(),
actions: vec![
ProposalAction::ParameterChange {
key: "max_gas_limit".to_string(),
value: "40000000".to_string(),
}
],
..
};
governance_engine.submit_proposal(proposal)?;Voting on Proposals
Voting power is proportional to staked TNZO. Validators and stakers can vote Yes, No, or Abstain. Votes are weighted by stake amount.
// Vote enum
pub enum Vote {
Yes,
No,
Abstain,
}
// Cast vote
pub fn vote(
&mut self,
proposal_id: u64,
voter: Address,
vote: Vote,
voting_power: u128,
) -> Result<()> {
let proposal = self.proposals.get_mut(&proposal_id)
.ok_or(GovernanceError::ProposalNotFound)?;
// Check proposal is active
if proposal.state != ProposalState::Active {
return Err(GovernanceError::ProposalNotActive);
}
// Verify voting power matches staked amount
let actual_power = self.staking.get_stake(&voter)?;
if voting_power > actual_power {
return Err(GovernanceError::InsufficientVotingPower);
}
// Record vote
match vote {
Vote::Yes => proposal.votes_for += voting_power,
Vote::No => proposal.votes_against += voting_power,
Vote::Abstain => proposal.votes_abstain += voting_power,
}
self.voters.insert((proposal_id, voter), vote);
info!("Vote recorded: proposal={}, voter={}, vote={:?}, power={}",
proposal_id, voter, vote, voting_power);
Ok(())
}
// Example: Vote with staked TNZO
let my_stake = staking_manager.get_stake(&my_address)?;
governance_engine.vote(proposal_id, my_address, Vote::Yes, my_stake)?;Proposal Execution
Proposals that pass quorum and approval thresholds enter a 2-day timelock before execution. This allows the community to review changes and prepare for upgrades.
// Tally votes and execute proposal
pub fn finalize_proposal(&mut self, proposal_id: u64) -> Result<()> {
let proposal = self.proposals.get_mut(&proposal_id)
.ok_or(GovernanceError::ProposalNotFound)?;
// Check voting period ended
if current_block() < proposal.end_block {
return Err(GovernanceError::VotingNotEnded);
}
let total_votes = proposal.votes_for
+ proposal.votes_against
+ proposal.votes_abstain;
let total_supply = self.token.total_supply();
let quorum = (total_votes as f64) / (total_supply as f64);
// Check quorum (4% of total supply)
if quorum < QUORUM_THRESHOLD {
proposal.state = ProposalState::Defeated;
return Ok(());
}
// Check approval (51% of votes cast)
let approval = (proposal.votes_for as f64) / (total_votes as f64);
if approval >= APPROVAL_THRESHOLD {
proposal.state = ProposalState::Succeeded;
// Queue for timelock
proposal.execution_time = current_timestamp() + TIMELOCK_PERIOD;
proposal.state = ProposalState::Queued;
} else {
proposal.state = ProposalState::Defeated;
}
Ok(())
}
// Execute after timelock
pub fn execute_proposal(&mut self, proposal_id: u64) -> Result<()> {
let proposal = self.proposals.get(&proposal_id)
.ok_or(GovernanceError::ProposalNotFound)?;
// Check state is Queued
if proposal.state != ProposalState::Queued {
return Err(GovernanceError::NotQueued);
}
// Check timelock passed
if current_timestamp() < proposal.execution_time {
return Err(GovernanceError::TimelockNotExpired);
}
// Execute all actions
for action in &proposal.actions {
match action {
ProposalAction::ParameterChange { key, value } => {
self.update_parameter(key, value)?;
}
ProposalAction::TreasurySpend { recipient, amount } => {
self.treasury.withdraw(recipient, *amount)?;
}
ProposalAction::ContractUpgrade { contract, code } => {
self.upgrade_contract(contract, code)?;
}
ProposalAction::ValidatorSetChange { add, remove } => {
self.consensus.update_validator_set(add, remove)?;
}
ProposalAction::Custom { target, calldata } => {
self.execute_custom_action(target, calldata)?;
}
}
}
proposal.state = ProposalState::Executed;
info!("Proposal {} executed", proposal_id);
Ok(())
}Staking System
The StakingManager handles validator and provider staking. Staked TNZO secures the network and provides voting power in governance. Validators must stake a minimum amount to participate in consensus.
Validator Staking
// Staking configuration
const MIN_VALIDATOR_STAKE: u128 = 100_000 * 10u128.pow(18); // 100k TNZO
const MIN_PROVIDER_STAKE: u128 = 10_000 * 10u128.pow(18); // 10k TNZO
const UNBONDING_PERIOD: u64 = 7 * 24 * 3600; // 7 days
const MAX_SLASHING: f64 = 0.10; // 10% max slash
// Stake TNZO
pub fn stake(&mut self, staker: Address, amount: u128) -> Result<()> {
// Transfer TNZO to staking contract
self.token.transfer_from(&staker, &self.address, amount)?;
// Update stake
let current_stake = self.stakes.get(&staker).unwrap_or(&0);
let new_stake = current_stake + amount;
self.stakes.insert(staker, new_stake);
// Update total staked
self.total_staked += amount;
info!("Staked {} TNZO for {}", amount, staker);
Ok(())
}
// Unstake (initiates unbonding)
pub fn unstake(&mut self, staker: Address, amount: u128) -> Result<()> {
let current_stake = self.stakes.get(&staker)
.ok_or(StakingError::NoStake)?;
if amount > *current_stake {
return Err(StakingError::InsufficientStake);
}
// Create unbonding request
let unbonding = UnbondingRequest {
staker,
amount,
completion_time: current_timestamp() + UNBONDING_PERIOD,
};
self.unbonding_requests.push(unbonding);
// Reduce stake immediately (but funds locked)
self.stakes.insert(staker, current_stake - amount);
self.total_staked -= amount;
info!("Unstake initiated: {} TNZO, unlocks in 7 days", amount);
Ok(())
}
// Withdraw after unbonding period
pub fn withdraw_unbonded(&mut self, staker: Address) -> Result<u128> {
let mut total_withdrawn = 0;
self.unbonding_requests.retain(|req| {
if req.staker == staker && current_timestamp() >= req.completion_time {
self.token.transfer(&self.address, &staker, req.amount).unwrap();
total_withdrawn += req.amount;
false // Remove from list
} else {
true // Keep in list
}
});
Ok(total_withdrawn)
}Slashing
Validators who misbehave (double signing, downtime, invalid attestations, equivocation) are subject to automated slashing. A portion of their staked TNZO is burned as a penalty. Validators caught voting for multiple blocks in the same view are automatically slashed at 10%.
// Slash validator for misbehavior
pub fn slash(
&mut self,
validator: Address,
offense: SlashingOffense,
) -> Result<u128> {
let stake = self.stakes.get(&validator)
.ok_or(StakingError::NoStake)?;
// Determine slash percentage based on offense
let slash_percent = match offense {
SlashingOffense::DoubleSign => 0.05, // 5%
SlashingOffense::Downtime => 0.01, // 1%
SlashingOffense::InvalidAttestation => 0.03, // 3%
SlashingOffense::EquivocationDetected => 0.10, // 10% (FULLY IMPLEMENTED)
};
let slash_amount = ((*stake as f64) * slash_percent) as u128;
// Cap at MAX_SLASHING
let slash_amount = slash_amount.min(((*stake as f64) * MAX_SLASHING) as u128);
// Reduce stake
let new_stake = stake - slash_amount;
self.stakes.insert(validator, new_stake);
self.total_staked -= slash_amount;
// Burn slashed TNZO
self.token.burn(&self.address, slash_amount)?;
warn!("Slashed validator {}: offense={:?}, amount={}",
validator, offense, slash_amount);
Ok(slash_amount)
}
// Slashing offenses
pub enum SlashingOffense {
DoubleSign, // Signing conflicting blocks
Downtime, // Offline for extended period
InvalidAttestation, // Providing false TEE attestation
EquivocationDetected, // Voting for multiple blocks in same view
}Reward Distribution
The RewardDistributor allocates TNZO rewards to validators and providers at the end of each epoch. Rewards come from transaction fees, network commissions, and token inflation.
Epoch-Based Rewards
// Epoch configuration
const EPOCH_DURATION: u64 = 24 * 3600; // 24 hours
const BLOCK_REWARD: u128 = 10 * 10u128.pow(18); // 10 TNZO per block
const INFLATION_RATE: f64 = 0.05; // 5% annual inflation
// Reward distribution
pub struct RewardDistributor {
treasury: NetworkTreasury,
staking: StakingManager,
distributed_epochs: HashSet<u64>,
}
impl RewardDistributor {
// Distribute rewards at epoch end
pub fn distribute_epoch_rewards(&mut self, epoch: u64) -> Result<()> {
// Check not already distributed
if self.distributed_epochs.contains(&epoch) {
return Err(RewardError::AlreadyDistributed);
}
// Calculate total rewards for epoch
let blocks_in_epoch = self.get_epoch_block_count(epoch)?;
let block_rewards = blocks_in_epoch as u128 * BLOCK_REWARD;
// Add transaction fees collected
let fee_rewards = self.treasury.get_epoch_fees(epoch)?;
// Add network commissions (AI inference, TEE services)
let commission_rewards = self.treasury.get_epoch_commissions(epoch)?;
let total_rewards = block_rewards + fee_rewards + commission_rewards;
info!("Distributing {} TNZO for epoch {}", total_rewards, epoch);
// Distribute proportionally to validators by stake
let validators = self.get_active_validators(epoch)?;
let total_stake = self.staking.total_staked;
for validator in validators {
let stake = self.staking.get_stake(&validator)?;
let share = (stake as f64) / (total_stake as f64);
let reward = (total_rewards as f64 * share) as u128;
// Transfer reward
self.treasury.transfer(&validator, reward)?;
info!("Reward: validator={}, stake={}, reward={}",
validator, stake, reward);
}
// Mark epoch as distributed
self.distributed_epochs.insert(epoch);
Ok(())
}
}Treasury Management
The NetworkTreasury accumulates transaction fees and network commissions. Treasury funds are controlled by governance and require multisig approval for withdrawals.
// Treasury structure
pub struct NetworkTreasury {
balances: HashMap<Asset, u128>,
pending_withdrawals: Vec<WithdrawalRequest>,
multisig_threshold: u32, // e.g., 3-of-5
signers: Vec<Address>,
}
// Collect fees from transactions
pub fn collect_fee(&mut self, asset: Asset, amount: u128) -> Result<()> {
let current = self.balances.get(&asset).map(|v| *v).unwrap_or(0);
self.balances.insert(asset, current + amount);
Ok(())
}
// Multisig withdrawal (requires governance proposal)
pub fn request_withdrawal(
&mut self,
recipient: Address,
asset: Asset,
amount: u128,
proposer: Address,
) -> Result<u64> {
let request = WithdrawalRequest {
id: self.next_withdrawal_id(),
recipient,
asset,
amount,
proposer,
approvals: HashSet::new(),
created_at: current_timestamp(),
};
let id = request.id;
self.pending_withdrawals.push(request);
Ok(id)
}
// Approve withdrawal (multisig signer)
pub fn approve_withdrawal(
&mut self,
withdrawal_id: u64,
signer: Address,
) -> Result<()> {
// Verify signer is authorized
if !self.signers.contains(&signer) {
return Err(TreasuryError::UnauthorizedSigner);
}
let request = self.pending_withdrawals.iter_mut()
.find(|r| r.id == withdrawal_id)
.ok_or(TreasuryError::WithdrawalNotFound)?;
request.approvals.insert(signer);
// Execute if threshold reached
if request.approvals.len() >= self.multisig_threshold as usize {
self.execute_withdrawal(withdrawal_id)?;
}
Ok(())
}
// Treasury balance query
pub fn get_balance(&self, asset: &Asset) -> u128 {
self.balances.get(asset).map(|v| *v).unwrap_or(0)
}
// Example: Treasury holds TNZO, USDC, USDT, ETH, SOL, BTC
treasury.get_balance(&Asset::Tnzo); // Fee accumulation
treasury.get_balance(&Asset::Usdc); // Commission accumulationLiquid Staking (stTNZO)
The LiquidStakingPool enables users to stake TNZO and receive stTNZO (staked TNZO) tokens representing their stake. stTNZO rebases automatically to reflect staking rewards, maintaining liquidity while earning rewards.
// Liquid staking configuration
const STTNZO_DECIMALS: u8 = 18;
const PROTOCOL_FEE_BPS: u16 = 1000; // 10% (1000 basis points)
const UNBONDING_PERIOD: u64 = 7 * 24 * 3600; // 7 days
// Deposit TNZO, receive stTNZO
pub fn deposit(&mut self, depositor: Address, amount: u128) -> Result<u128> {
// Transfer TNZO to pool
self.tnzo.transfer_from(&depositor, &self.address, amount)?;
// Calculate stTNZO to mint (based on exchange rate)
let exchange_rate = self.exchange_rate()?;
let sttnzo_amount = self.tnzo_to_sttnzo(amount, exchange_rate)?;
// Mint stTNZO to depositor
self.sttnzo.mint(&depositor, sttnzo_amount)?;
// Update pool state
self.total_tnzo_staked += amount;
self.total_sttnzo_supply += sttnzo_amount;
// Delegate to validators (multi-validator strategy)
self.delegate_to_validators(amount)?;
info!("Liquid stake: {} TNZO → {} stTNZO", amount, sttnzo_amount);
Ok(sttnzo_amount)
}
// Exchange rate calculation (rebases automatically)
pub fn exchange_rate(&self) -> Result<f64> {
if self.total_sttnzo_supply == 0 {
return Ok(1.0); // Initial 1:1 rate
}
// Total TNZO value = staked + accumulated rewards
let total_value = self.total_tnzo_staked + self.accumulated_rewards;
// Exchange rate = total_value / sttnzo_supply
let rate = (total_value as f64) / (self.total_sttnzo_supply as f64);
Ok(rate)
}
// Request withdrawal (initiates unbonding)
pub fn request_withdrawal(&mut self, withdrawer: Address, sttnzo_amount: u128)
-> Result<u64>
{
// Calculate TNZO value
let exchange_rate = self.exchange_rate()?;
let tnzo_amount = self.sttnzo_to_tnzo(sttnzo_amount, exchange_rate)?;
// Burn stTNZO
self.sttnzo.burn(&withdrawer, sttnzo_amount)?;
// Create unbonding request
let request_id = self.next_request_id();
let request = UnbondingRequest {
id: request_id,
withdrawer,
sttnzo_amount,
tnzo_amount,
completion_time: current_timestamp() + UNBONDING_PERIOD,
};
self.unbonding_requests.push(request);
self.total_sttnzo_supply -= sttnzo_amount;
info!("Withdrawal requested: {} stTNZO → {} TNZO (unlocks in 7 days)",
sttnzo_amount, tnzo_amount);
Ok(request_id)
}
// Claim unbonded TNZO
pub fn claim_unbonded(&mut self, withdrawer: Address) -> Result<u128> {
let mut total_claimed = 0;
self.unbonding_requests.retain(|req| {
if req.withdrawer == withdrawer
&& current_timestamp() >= req.completion_time
{
// Transfer TNZO to withdrawer
self.tnzo.transfer(&self.address, &withdrawer, req.tnzo_amount)
.unwrap();
total_claimed += req.tnzo_amount;
self.total_tnzo_staked -= req.tnzo_amount;
false // Remove from list
} else {
true // Keep in list
}
});
Ok(total_claimed)
}Governance RPC Methods
The tenzro-node JSON-RPC server exposes governance methods under the tenzro_* namespace.
// List all proposals
{
"jsonrpc": "2.0",
"method": "tenzro_listProposals",
"params": [{ "state": "Active" }],
"id": 1
}
// Response
{
"jsonrpc": "2.0",
"result": [
{
"id": 42,
"title": "Increase block gas limit to 40M",
"description": "...",
"proposer": "0x...",
"state": "Active",
"votes_for": "15000000000000000000000000",
"votes_against": "8000000000000000000000000",
"votes_abstain": "2000000000000000000000000",
"end_block": 150000,
"actions": [...]
}
],
"id": 1
}
// Vote on proposal
{
"jsonrpc": "2.0",
"method": "tenzro_vote",
"params": [{
"proposal_id": 42,
"vote": "Yes",
"voting_power": "1000000000000000000000" // 1000 TNZO
}],
"id": 2
}
// Get voting power
{
"jsonrpc": "2.0",
"method": "tenzro_getVotingPower",
"params": ["0x..."],
"id": 3
}
// Response
{
"jsonrpc": "2.0",
"result": {
"address": "0x...",
"staked": "1000000000000000000000", // 1000 TNZO
"voting_power": "1000000000000000000000"
},
"id": 3
}Governance Constants
Voting Period: 7 days
Timelock Period: 2 days
Quorum Threshold: 4% of total supply
Approval Threshold: 51% of votes cast
Min Proposal Stake: 1,000 TNZO
Min Validator Stake: 100,000 TNZO
Min Provider Stake: 10,000 TNZO
Unbonding Period: 7 days
Liquid Staking Protocol Fee: 10%
Max Slashing: 10%
Best Practices
# Participate in governance # 1. Acquire TNZO tokens tenzro-cli wallet balance # 2. Stake TNZO for voting power tenzro-cli stake --amount 1000 # 3. Review active proposals tenzro-cli governance list-proposals --state Active # 4. Vote on proposals tenzro-cli governance vote --proposal-id 42 --vote yes # 5. Monitor proposal progress tenzro-cli governance show-proposal --id 42 # Use liquid staking for liquidity # Stake TNZO but maintain transferability via stTNZO tenzro-cli liquid-stake deposit --amount 5000 # Check stTNZO balance (automatically rebases) tenzro-cli wallet balance --token stTNZO