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