iota_types/timelock/
timelock.rs

1// Copyright (c) 2024 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use iota_sdk_types::{StructTag, TypeTag};
5use serde::{Deserialize, Serialize};
6
7use crate::{
8    base_types::ObjectID,
9    error::IotaError,
10    gas_coin::GasCoin,
11    id::UID,
12    object::{Data, Object},
13};
14
15/// All basic outputs whose IDs start with this prefix represent vested rewards
16/// that were created during the stardust upgrade on IOTA mainnet.
17pub const VESTED_REWARD_ID_PREFIX: &str =
18    "0xb191c4bc825ac6983789e50545d5ef07a1d293a98ad974fc9498cb18";
19
20/// Rust version of the Move stardust::TimeLock type.
21#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
22pub struct TimeLock<T> {
23    id: UID,
24    /// The locked object.
25    locked: T,
26    /// This is the epoch time stamp of when the lock expires.
27    expiration_timestamp_ms: u64,
28    /// Timelock related label.
29    label: Option<String>,
30}
31
32impl<T> TimeLock<T> {
33    /// Constructor.
34    pub fn new(id: UID, locked: T, expiration_timestamp_ms: u64, label: Option<String>) -> Self {
35        Self {
36            id,
37            locked,
38            expiration_timestamp_ms,
39            label,
40        }
41    }
42
43    /// Get the TimeLock's `id`.
44    pub fn id(&self) -> &ObjectID {
45        self.id.object_id()
46    }
47
48    /// Get the TimeLock's `locked` object.
49    pub fn locked(&self) -> &T {
50        &self.locked
51    }
52
53    /// Get the TimeLock's `expiration_timestamp_ms`.
54    pub fn expiration_timestamp_ms(&self) -> u64 {
55        self.expiration_timestamp_ms
56    }
57
58    /// Get the TimeLock's `label``.
59    pub fn label(&self) -> &Option<String> {
60        &self.label
61    }
62}
63
64impl<'de, T> TimeLock<T>
65where
66    T: Serialize + Deserialize<'de>,
67{
68    /// Create a `TimeLock` from BCS bytes.
69    pub fn from_bcs_bytes(content: &'de [u8]) -> Result<Self, IotaError> {
70        bcs::from_bytes(content).map_err(|err| IotaError::ObjectDeserialization {
71            error: format!("Unable to deserialize TimeLock object: {err:?}"),
72        })
73    }
74
75    /// Serialize a `TimeLock` as a `Vec<u8>` of BCS.
76    pub fn to_bcs_bytes(&self) -> Vec<u8> {
77        bcs::to_bytes(&self).unwrap()
78    }
79}
80
81/// Is this other StructTag representing a `TimeLock<Balance<T>>`?
82pub fn is_timelocked_balance(other: &StructTag) -> bool {
83    if !other.is_time_lock() {
84        return false;
85    }
86
87    match &other.type_params()[0] {
88        TypeTag::Struct(tag) => tag.is_balance(),
89        _ => false,
90    }
91}
92
93/// Is this other StructTag representing a `TimeLock<Balance<IOTA>>`?
94pub fn is_timelocked_gas_balance(other: &StructTag) -> bool {
95    if !other.is_time_lock() {
96        return false;
97    }
98
99    match &other.type_params()[0] {
100        TypeTag::Struct(tag) => GasCoin::is_gas_balance(tag),
101        _ => false,
102    }
103}
104
105impl<'de, T> TryFrom<&'de Object> for TimeLock<T>
106where
107    T: Serialize + Deserialize<'de>,
108{
109    type Error = IotaError;
110
111    fn try_from(object: &'de Object) -> Result<Self, Self::Error> {
112        match &object.data {
113            Data::Struct(o) => {
114                if o.struct_tag().is_time_lock() {
115                    return TimeLock::from_bcs_bytes(o.contents());
116                }
117            }
118            Data::Package(_) => {}
119        }
120
121        Err(IotaError::Type {
122            error: format!("Object type is not a TimeLock: {object:?}"),
123        })
124    }
125}