iota_types/timelock/
timelock.rs1use move_core_types::{
5 ident_str,
6 identifier::IdentStr,
7 language_storage::{StructTag, TypeTag},
8};
9use serde::{Deserialize, Serialize};
10
11use crate::{
12 IOTA_FRAMEWORK_ADDRESS,
13 balance::Balance,
14 base_types::ObjectID,
15 error::IotaError,
16 gas_coin::GasCoin,
17 id::UID,
18 object::{Data, Object},
19};
20
21pub const TIMELOCK_MODULE_NAME: &IdentStr = ident_str!("timelock");
22pub const TIMELOCK_STRUCT_NAME: &IdentStr = ident_str!("TimeLock");
23
24pub const VESTED_REWARD_ID_PREFIX: &str =
27 "0xb191c4bc825ac6983789e50545d5ef07a1d293a98ad974fc9498cb18";
28
29#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
31pub struct TimeLock<T> {
32 id: UID,
33 locked: T,
35 expiration_timestamp_ms: u64,
37 label: Option<String>,
39}
40
41impl<T> TimeLock<T> {
42 pub fn new(id: UID, locked: T, expiration_timestamp_ms: u64, label: Option<String>) -> Self {
44 Self {
45 id,
46 locked,
47 expiration_timestamp_ms,
48 label,
49 }
50 }
51
52 pub fn type_(type_param: TypeTag) -> StructTag {
54 StructTag {
55 address: IOTA_FRAMEWORK_ADDRESS,
56 module: TIMELOCK_MODULE_NAME.to_owned(),
57 name: TIMELOCK_STRUCT_NAME.to_owned(),
58 type_params: vec![type_param],
59 }
60 }
61
62 pub fn id(&self) -> &ObjectID {
64 self.id.object_id()
65 }
66
67 pub fn locked(&self) -> &T {
69 &self.locked
70 }
71
72 pub fn expiration_timestamp_ms(&self) -> u64 {
74 self.expiration_timestamp_ms
75 }
76
77 pub fn label(&self) -> &Option<String> {
79 &self.label
80 }
81}
82
83impl<'de, T> TimeLock<T>
84where
85 T: Serialize + Deserialize<'de>,
86{
87 pub fn from_bcs_bytes(content: &'de [u8]) -> Result<Self, IotaError> {
89 bcs::from_bytes(content).map_err(|err| IotaError::ObjectDeserialization {
90 error: format!("Unable to deserialize TimeLock object: {err:?}"),
91 })
92 }
93
94 pub fn to_bcs_bytes(&self) -> Vec<u8> {
96 bcs::to_bytes(&self).unwrap()
97 }
98}
99
100pub fn is_timelock(other: &StructTag) -> bool {
102 other.address == IOTA_FRAMEWORK_ADDRESS
103 && other.module.as_ident_str() == TIMELOCK_MODULE_NAME
104 && other.name.as_ident_str() == TIMELOCK_STRUCT_NAME
105}
106
107pub fn is_timelocked_balance(other: &StructTag) -> bool {
109 if !is_timelock(other) {
110 return false;
111 }
112
113 if other.type_params.len() != 1 {
114 return false;
115 }
116
117 match &other.type_params[0] {
118 TypeTag::Struct(tag) => Balance::is_balance(tag),
119 _ => false,
120 }
121}
122
123pub fn is_timelocked_gas_balance(other: &StructTag) -> bool {
125 if !is_timelock(other) {
126 return false;
127 }
128
129 if other.type_params.len() != 1 {
130 return false;
131 }
132
133 match &other.type_params[0] {
134 TypeTag::Struct(tag) => GasCoin::is_gas_balance(tag),
135 _ => false,
136 }
137}
138
139impl<'de, T> TryFrom<&'de Object> for TimeLock<T>
140where
141 T: Serialize + Deserialize<'de>,
142{
143 type Error = IotaError;
144
145 fn try_from(object: &'de Object) -> Result<Self, Self::Error> {
146 match &object.data {
147 Data::Move(o) => {
148 if o.type_().is_timelock() {
149 return TimeLock::from_bcs_bytes(o.contents());
150 }
151 }
152 Data::Package(_) => {}
153 }
154
155 Err(IotaError::Type {
156 error: format!("Object type is not a TimeLock: {object:?}"),
157 })
158 }
159}