Skip to main content

iota_types/
governance.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use iota_sdk_types::{Identifier, ObjectId};
6use serde::{Deserialize, Serialize};
7
8use crate::{
9    balance::Balance,
10    committee::EpochId,
11    error::IotaError,
12    gas_coin::NANOS_PER_IOTA,
13    id::{ID, UID},
14    object::{Data, Object},
15};
16
17/// Maximum number of active validators at any moment.
18/// We do not allow the number of validators in any epoch to go above this.
19pub const MAX_VALIDATOR_COUNT: u64 = 150;
20
21/// Lower-bound on the amount of stake required to become a validator.
22///
23/// 2 million IOTA
24pub const MIN_VALIDATOR_JOINING_STAKE_NANOS: u64 = 2_000_000 * NANOS_PER_IOTA;
25
26/// Validators with stake amount below `validator_low_stake_threshold` are
27/// considered to have low stake and will be escorted out of the validator set
28/// after being below this threshold for more than
29/// `validator_low_stake_grace_period` number of epochs.
30///
31/// 1.5 million IOTA
32pub const VALIDATOR_LOW_STAKE_THRESHOLD_NANOS: u64 = 1_500_000 * NANOS_PER_IOTA;
33
34/// Validators with stake below `validator_very_low_stake_threshold` will be
35/// removed immediately at epoch change, no grace period.
36///
37/// 1 million IOTA
38pub const VALIDATOR_VERY_LOW_STAKE_THRESHOLD_NANOS: u64 = 1_000_000 * NANOS_PER_IOTA;
39
40/// A validator can have stake below `validator_low_stake_threshold`
41/// for this many epochs before being kicked out.
42pub const VALIDATOR_LOW_STAKE_GRACE_PERIOD: u64 = 7;
43
44pub const ADD_STAKE_MUL_COIN_FUN_NAME: Identifier =
45    Identifier::from_static("request_add_stake_mul_coin");
46pub const ADD_STAKE_FUN_NAME: Identifier = Identifier::from_static("request_add_stake");
47pub const WITHDRAW_STAKE_FUN_NAME: Identifier = Identifier::from_static("request_withdraw_stake");
48
49#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
50pub struct StakedIota {
51    id: UID,
52    pool_id: ID,
53    stake_activation_epoch: u64,
54    principal: Balance,
55}
56
57impl StakedIota {
58    pub fn id(&self) -> ObjectId {
59        self.id.id.bytes
60    }
61
62    pub fn pool_id(&self) -> ObjectId {
63        self.pool_id.bytes
64    }
65
66    pub fn activation_epoch(&self) -> EpochId {
67        self.stake_activation_epoch
68    }
69
70    pub fn request_epoch(&self) -> EpochId {
71        // TODO: this might change when we implement warm up period.
72        self.stake_activation_epoch.saturating_sub(1)
73    }
74
75    pub fn principal(&self) -> u64 {
76        self.principal.value()
77    }
78}
79
80impl TryFrom<&Object> for StakedIota {
81    type Error = IotaError;
82    fn try_from(object: &Object) -> Result<Self, Self::Error> {
83        match &object.data {
84            Data::Struct(o) => {
85                if o.struct_tag().is_staked_iota() {
86                    return bcs::from_bytes(o.contents()).map_err(|err| IotaError::Type {
87                        error: format!("Unable to deserialize StakedIota object: {err:?}"),
88                    });
89                }
90            }
91            Data::Package(_) => {}
92        }
93
94        Err(IotaError::Type {
95            error: format!("Object type is not a StakedIota: {object:?}"),
96        })
97    }
98}