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