Skip to main content

iota_types/timelock/
timelock.rs

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