multiversx_sc/storage/mappers/timelock/
timelock_mapper.rs

1use core::{borrow::Borrow, marker::PhantomData};
2
3pub use crate::storage::mappers::{
4    single_value_mapper::SingleValue, source::CurrentStorage, StorageMapper,
5};
6use crate::{
7    abi::{TypeAbi, TypeAbiFrom, TypeDescriptionContainer, TypeName},
8    api::{BlockchainApi, BlockchainApiImpl, StorageMapperApi},
9    codec::{
10        multi_types::PlaceholderOutput, EncodeErrorHandler, TopDecode, TopEncode, TopEncodeMulti,
11        TopEncodeMultiOutput,
12    },
13    imports::StorageMapperFromAddress,
14    storage::{
15        mappers::source::StorageAddress, storage_clear, storage_overwrite, storage_set, StorageKey,
16    },
17    storage_get,
18    types::{ManagedAddress, ManagedType},
19};
20
21const UNLOCK_TIMESTAMP_KEY: &[u8] = b"unlock_timestamp";
22const FUTURE_VALUE_KEY: &[u8] = b"future_value";
23
24/// Note that TimelockMapper uses the block time in seconds.
25///
26/// TODO: create a new timelock mapper that works with millisecond timestamp.
27pub struct TimelockMapper<SA, T, A = CurrentStorage>
28where
29    SA: StorageMapperApi,
30    A: StorageAddress<SA>,
31    T: TopEncode + TopDecode + 'static,
32{
33    address: A,
34    key: StorageKey<SA>,
35    _phantom_api: PhantomData<SA>,
36    _phantom_item: PhantomData<T>,
37}
38
39impl<SA, T> StorageMapper<SA> for TimelockMapper<SA, T>
40where
41    SA: StorageMapperApi,
42    T: TopEncode + TopDecode,
43{
44    #[inline]
45    fn new(base_key: StorageKey<SA>) -> Self {
46        TimelockMapper {
47            address: CurrentStorage,
48            key: base_key,
49            _phantom_api: PhantomData,
50            _phantom_item: PhantomData,
51        }
52    }
53}
54
55impl<SA, T> TimelockMapper<SA, T>
56where
57    SA: StorageMapperApi + BlockchainApi,
58    T: TopEncode + TopDecode,
59{
60    /// Sets the `current value` without taking into account the timelock component.
61    /// Meant to be used in constructors.
62    pub fn set<BT>(&self, new_current_value: BT)
63    where
64        BT: Borrow<T>,
65    {
66        storage_set(self.key.as_ref(), new_current_value.borrow());
67    }
68
69    /// Updates current value entry with future value if unlock timestamp has passed.
70    pub fn commit(&self) -> bool {
71        let now = SA::blockchain_api_impl().get_block_timestamp();
72        let unlock_timestamp: u64 = storage_get(self.get_unlock_timestamp_key().as_ref());
73
74        if now >= unlock_timestamp {
75            storage_overwrite(self.get_future_value_key().as_ref(), self.key.as_ref());
76            storage_clear(self.get_future_value_key().as_ref());
77            return true;
78        }
79
80        false
81    }
82
83    /// Sets a value and an unlock timestamp for the value.
84    /// Setup needs to be committed after the unlock timestamp has passed.
85    /// Unlock timestamp represents the moment in time when the future value can be
86    /// updated as current value.
87    pub fn set_unlock_timestamp<BT>(&self, unlock_timestamp: u64, future_value: BT)
88    where
89        BT: Borrow<T>,
90    {
91        storage_set(self.get_unlock_timestamp_key().as_ref(), &unlock_timestamp);
92        storage_set(self.get_future_value_key().as_ref(), future_value.borrow());
93    }
94}
95
96impl<SA, T, A> TimelockMapper<SA, T, A>
97where
98    SA: StorageMapperApi,
99    T: TopEncode + TopDecode,
100    A: StorageAddress<SA>,
101{
102    /// Retrieves the current value from storage.
103    pub fn get(&self) -> T {
104        self.address.address_storage_get(self.key.as_ref())
105    }
106
107    /// Retrieves the unlock timestamp from storage.
108    pub fn get_unlock_timestamp(&self) -> u64 {
109        self.address
110            .address_storage_get(self.get_unlock_timestamp_key().as_ref())
111    }
112
113    /// Retrieves the future value from storage.
114    pub fn get_future_value(&self) -> T {
115        self.address
116            .address_storage_get(self.get_future_value_key().as_ref())
117    }
118
119    fn get_unlock_timestamp_key(&self) -> StorageKey<SA> {
120        let mut base_key = self.key.buffer.clone();
121        base_key.append_bytes(UNLOCK_TIMESTAMP_KEY);
122
123        StorageKey::from(base_key)
124    }
125
126    fn get_future_value_key(&self) -> StorageKey<SA> {
127        let mut base_key = self.key.buffer.clone();
128        base_key.append_bytes(FUTURE_VALUE_KEY);
129
130        StorageKey::from(base_key)
131    }
132}
133
134impl<SA, T> StorageMapperFromAddress<SA> for TimelockMapper<SA, T, ManagedAddress<SA>>
135where
136    SA: StorageMapperApi,
137    T: TopEncode + TopDecode,
138{
139    #[inline]
140    fn new_from_address(address: ManagedAddress<SA>, base_key: StorageKey<SA>) -> Self {
141        TimelockMapper {
142            address,
143            key: base_key,
144            _phantom_api: PhantomData,
145            _phantom_item: PhantomData,
146        }
147    }
148}
149
150impl<SA, T> TopEncodeMulti for TimelockMapper<SA, T>
151where
152    SA: StorageMapperApi,
153    T: TopEncode + TopDecode,
154{
155    fn multi_encode_or_handle_err<O, H>(&self, output: &mut O, h: H) -> Result<(), H::HandledErr>
156    where
157        O: TopEncodeMultiOutput,
158        H: EncodeErrorHandler,
159    {
160        output.push_single_value(&self.get(), h)
161    }
162}
163
164impl<SA, T, R> TypeAbiFrom<TimelockMapper<SA, T>> for SingleValue<R>
165where
166    SA: StorageMapperApi,
167    T: TopEncode + TopDecode,
168    R: TopDecode + TypeAbiFrom<T>,
169{
170}
171
172impl<SA, T> TypeAbiFrom<TimelockMapper<SA, T>> for PlaceholderOutput
173where
174    SA: StorageMapperApi,
175    T: TopEncode + TopDecode,
176{
177}
178
179impl<SA, T> TypeAbiFrom<Self> for TimelockMapper<SA, T>
180where
181    SA: StorageMapperApi,
182    T: TopEncode + TopDecode + TypeAbi,
183{
184}
185
186impl<SA, T> TypeAbi for TimelockMapper<SA, T>
187where
188    SA: StorageMapperApi,
189    T: TopEncode + TopDecode + TypeAbi,
190{
191    type Unmanaged = T::Unmanaged;
192
193    fn type_name() -> TypeName {
194        T::type_name()
195    }
196
197    fn type_name_rust() -> TypeName {
198        T::type_name_rust()
199    }
200
201    fn provide_type_descriptions<TDC: TypeDescriptionContainer>(accumulator: &mut TDC) {
202        T::provide_type_descriptions(accumulator)
203    }
204}