iota_types/timelock/
timelocked_staked_iota.rs

1// Copyright (c) 2024 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use move_core_types::{ident_str, identifier::IdentStr, language_storage::StructTag};
5use serde::{Deserialize, Serialize};
6
7use crate::{
8    IOTA_SYSTEM_ADDRESS,
9    base_types::ObjectID,
10    committee::EpochId,
11    error::IotaError,
12    governance::StakedIota,
13    id::UID,
14    object::{Data, Object},
15};
16
17pub const TIMELOCKED_STAKED_IOTA_MODULE_NAME: &IdentStr = ident_str!("timelocked_staking");
18pub const TIMELOCKED_STAKED_IOTA_STRUCT_NAME: &IdentStr = ident_str!("TimelockedStakedIota");
19
20/// Rust version of the Move
21/// stardust::timelocked_staked_iota::TimelockedStakedIota type.
22#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
23pub struct TimelockedStakedIota {
24    id: UID,
25    /// A self-custodial object holding the staked IOTA tokens.
26    staked_iota: StakedIota,
27    /// This is the epoch time stamp of when the lock expires.
28    expiration_timestamp_ms: u64,
29    /// Timelock related label.
30    label: Option<String>,
31}
32
33impl TimelockedStakedIota {
34    /// Get the TimeLock's `type`.
35    pub fn type_() -> StructTag {
36        StructTag {
37            address: IOTA_SYSTEM_ADDRESS,
38            module: TIMELOCKED_STAKED_IOTA_MODULE_NAME.to_owned(),
39            name: TIMELOCKED_STAKED_IOTA_STRUCT_NAME.to_owned(),
40            type_params: vec![],
41        }
42    }
43
44    /// Is this other StructTag representing a TimelockedStakedIota?
45    pub fn is_timelocked_staked_iota(s: &StructTag) -> bool {
46        s.address == IOTA_SYSTEM_ADDRESS
47            && s.module.as_ident_str() == TIMELOCKED_STAKED_IOTA_MODULE_NAME
48            && s.name.as_ident_str() == TIMELOCKED_STAKED_IOTA_STRUCT_NAME
49            && s.type_params.is_empty()
50    }
51
52    /// Get the TimelockedStakedIota's `id`.
53    pub fn id(&self) -> ObjectID {
54        self.id.id.bytes
55    }
56
57    /// Get the wrapped StakedIota's `pool_id`.
58    pub fn pool_id(&self) -> ObjectID {
59        self.staked_iota.pool_id()
60    }
61
62    /// Get the wrapped StakedIota's `activation_epoch`.
63    pub fn activation_epoch(&self) -> EpochId {
64        self.staked_iota.activation_epoch()
65    }
66
67    /// Get the wrapped StakedIota's `request_epoch`.
68    pub fn request_epoch(&self) -> EpochId {
69        // TODO: this might change when we implement warm up period.
70        self.staked_iota.activation_epoch().saturating_sub(1)
71    }
72
73    /// Get the wrapped StakedIota's `principal`.
74    pub fn principal(&self) -> u64 {
75        self.staked_iota.principal()
76    }
77
78    /// Get the TimelockedStakedIota's `expiration_timestamp_ms`.
79    pub fn expiration_timestamp_ms(&self) -> u64 {
80        self.expiration_timestamp_ms
81    }
82
83    /// Get the TimelockedStakedIota's `label``.
84    pub fn label(&self) -> &Option<String> {
85        &self.label
86    }
87}
88
89impl TryFrom<&Object> for TimelockedStakedIota {
90    type Error = IotaError;
91    fn try_from(object: &Object) -> Result<Self, Self::Error> {
92        match &object.data {
93            Data::Move(o) => {
94                if o.type_().is_timelocked_staked_iota() {
95                    return bcs::from_bytes(o.contents()).map_err(|err| IotaError::Type {
96                        error: format!(
97                            "Unable to deserialize TimelockedStakedIota object: {:?}",
98                            err
99                        ),
100                    });
101                }
102            }
103            Data::Package(_) => {}
104        }
105
106        Err(IotaError::Type {
107            error: format!("Object type is not a TimelockedStakedIota: {:?}", object),
108        })
109    }
110}