multiversx_sc/storage/mappers/token/
token_attributes_mapper.rs

1use core::marker::PhantomData;
2
3use crate::{
4    codec::{NestedDecode, NestedEncode, TopDecode, TopEncode},
5    storage::mappers::{
6        source::{CurrentStorage, StorageAddress},
7        StorageMapperFromAddress,
8    },
9    types::ManagedAddress,
10};
11
12use super::super::StorageMapper;
13use crate::{
14    api::{ErrorApiImpl, ManagedTypeApi, StorageMapperApi},
15    storage::{storage_clear, storage_get, storage_get_len, storage_set, StorageKey},
16    types::{ManagedType, TokenIdentifier},
17};
18
19const MAPPING_SUFFIX: &[u8] = b".mapping";
20const COUNTER_SUFFIX: &[u8] = b".counter";
21const ATTR_SUFFIX: &[u8] = b".attr";
22const NONCE_SUFFIX: &[u8] = b".nonce";
23
24const VALUE_ALREADY_SET_ERROR_MESSAGE: &[u8] = b"A value was already set";
25
26const UNKNOWN_TOKEN_ID_ERROR_MESSAGE: &[u8] = b"Unknown token id";
27
28const VALUE_NOT_PREVIOUSLY_SET_ERROR_MESSAGE: &[u8] = b"A value was not previously set";
29
30const COUNTER_OVERFLOW_ERROR_MESSAGE: &[u8] =
31    b"Counter overflow. This module can hold evidence for maximum u8::MAX different token IDs";
32
33pub struct TokenAttributesMapper<SA, A = CurrentStorage>
34where
35    SA: StorageMapperApi,
36{
37    _phantom_api: PhantomData<SA>,
38    base_key: StorageKey<SA>,
39    address: A,
40}
41
42impl<SA> StorageMapper<SA> for TokenAttributesMapper<SA, CurrentStorage>
43where
44    SA: StorageMapperApi,
45{
46    fn new(base_key: StorageKey<SA>) -> Self {
47        TokenAttributesMapper {
48            _phantom_api: PhantomData,
49            base_key,
50            address: CurrentStorage,
51        }
52    }
53}
54
55impl<SA> StorageMapperFromAddress<SA> for TokenAttributesMapper<SA, ManagedAddress<SA>>
56where
57    SA: StorageMapperApi,
58{
59    fn new_from_address(address: ManagedAddress<SA>, base_key: StorageKey<SA>) -> Self {
60        Self {
61            _phantom_api: PhantomData,
62            base_key,
63            address,
64        }
65    }
66}
67
68impl<SA> TokenAttributesMapper<SA, CurrentStorage>
69where
70    SA: StorageMapperApi,
71{
72    pub fn set<T: TopEncode + TopDecode + NestedEncode + NestedDecode, M: ManagedTypeApi>(
73        &self,
74        token_id: &TokenIdentifier<M>,
75        token_nonce: u64,
76        attributes: &T,
77    ) {
78        let has_mapping = self.has_mapping_value(token_id);
79
80        let mapping = if has_mapping {
81            self.get_mapping_value(token_id)
82        } else {
83            let mut counter = self.get_counter_value();
84            if counter == u8::MAX {
85                SA::error_api_impl().signal_error(COUNTER_OVERFLOW_ERROR_MESSAGE);
86            }
87
88            counter += 1;
89            self.set_mapping_value(token_id, counter);
90            self.set_counter_value(counter);
91            counter
92        };
93
94        let has_value = self.has_token_attributes_value(mapping, token_nonce);
95        if has_value {
96            SA::error_api_impl().signal_error(VALUE_ALREADY_SET_ERROR_MESSAGE);
97        }
98
99        self.set_token_attributes_value(mapping, token_nonce, attributes);
100        self.set_attributes_to_nonce_mapping(mapping, attributes, token_nonce);
101    }
102
103    ///Use carefully. Update should be used mainly when backed up by the protocol.
104    pub fn update<T: TopEncode + TopDecode + NestedEncode + NestedDecode, M: ManagedTypeApi>(
105        &self,
106        token_id: &TokenIdentifier<M>,
107        token_nonce: u64,
108        attributes: &T,
109    ) {
110        let has_mapping = self.has_mapping_value(token_id);
111        if !has_mapping {
112            SA::error_api_impl().signal_error(UNKNOWN_TOKEN_ID_ERROR_MESSAGE);
113        }
114
115        let mapping = self.get_mapping_value(token_id);
116        let has_value = self.has_token_attributes_value(mapping, token_nonce);
117        if !has_value {
118            SA::error_api_impl().signal_error(VALUE_NOT_PREVIOUSLY_SET_ERROR_MESSAGE);
119        }
120
121        let old_attr = self.get_token_attributes_value::<T>(mapping, token_nonce);
122        self.clear_attributes_to_nonce_mapping(mapping, &old_attr);
123
124        self.set_token_attributes_value(mapping, token_nonce, attributes);
125        self.set_attributes_to_nonce_mapping(mapping, attributes, token_nonce);
126    }
127
128    pub fn clear<T: TopEncode + TopDecode + NestedEncode + NestedDecode, M: ManagedTypeApi>(
129        &self,
130        token_id: &TokenIdentifier<M>,
131        token_nonce: u64,
132    ) {
133        let has_mapping = self.has_mapping_value(token_id);
134        if !has_mapping {
135            return;
136        }
137
138        let mapping = self.get_mapping_value(token_id);
139        let has_value = self.has_token_attributes_value(mapping, token_nonce);
140        if !has_value {
141            return;
142        }
143
144        let attr: T = self.get_token_attributes_value(mapping, token_nonce);
145        self.clear_token_attributes_value(mapping, token_nonce);
146        self.clear_attributes_to_nonce_mapping(mapping, &attr);
147    }
148
149    fn set_counter_value(&self, value: u8) {
150        storage_set(self.build_key_token_id_counter().as_ref(), &value);
151    }
152
153    fn set_mapping_value<M: ManagedTypeApi>(&self, token_id: &TokenIdentifier<M>, value: u8) {
154        storage_set(self.build_key_token_id_mapping(token_id).as_ref(), &value);
155    }
156
157    fn set_attributes_to_nonce_mapping<T: TopEncode + TopDecode + NestedEncode + NestedDecode>(
158        &self,
159        mapping: u8,
160        attr: &T,
161        token_nonce: u64,
162    ) {
163        storage_set(
164            self.build_key_attr_to_nonce_mapping(mapping, attr).as_ref(),
165            &token_nonce,
166        );
167    }
168
169    fn clear_attributes_to_nonce_mapping<T: TopEncode + TopDecode + NestedEncode + NestedDecode>(
170        &self,
171        mapping: u8,
172        attr: &T,
173    ) {
174        storage_clear(self.build_key_attr_to_nonce_mapping(mapping, attr).as_ref());
175    }
176
177    fn set_token_attributes_value<T: TopEncode + TopDecode + NestedEncode + NestedDecode>(
178        &self,
179        mapping: u8,
180        token_nonce: u64,
181        value: &T,
182    ) {
183        storage_set(
184            self.build_key_token_attr_value(mapping, token_nonce)
185                .as_ref(),
186            value,
187        );
188    }
189
190    fn clear_token_attributes_value(&self, mapping: u8, token_nonce: u64) {
191        storage_clear(
192            self.build_key_token_attr_value(mapping, token_nonce)
193                .as_ref(),
194        );
195    }
196}
197
198impl<SA, A> TokenAttributesMapper<SA, A>
199where
200    SA: StorageMapperApi,
201    A: StorageAddress<SA>,
202{
203    pub fn has_attributes<M: ManagedTypeApi>(
204        &self,
205        token_id: &TokenIdentifier<M>,
206        token_nonce: u64,
207    ) -> bool {
208        let has_mapping = self.has_mapping_value(token_id);
209        if !has_mapping {
210            return true;
211        }
212
213        let mapping = self.get_mapping_value(token_id);
214        self.is_empty_token_attributes_value(mapping, token_nonce)
215    }
216
217    pub fn has_nonce<T: TopEncode + TopDecode + NestedEncode + NestedDecode, M: ManagedTypeApi>(
218        &self,
219        token_id: &TokenIdentifier<M>,
220        attr: &T,
221    ) -> bool {
222        let has_mapping = self.has_mapping_value(token_id);
223        if !has_mapping {
224            return true;
225        }
226
227        let mapping = self.get_mapping_value(token_id);
228        self.is_empty_attributes_to_nonce_mapping(mapping, attr)
229    }
230
231    pub fn get_attributes<
232        T: TopEncode + TopDecode + NestedEncode + NestedDecode,
233        M: ManagedTypeApi,
234    >(
235        &self,
236        token_id: &TokenIdentifier<M>,
237        token_nonce: u64,
238    ) -> T {
239        let has_mapping = self.has_mapping_value(token_id);
240        if !has_mapping {
241            SA::error_api_impl().signal_error(UNKNOWN_TOKEN_ID_ERROR_MESSAGE);
242        }
243
244        let mapping = self.get_mapping_value(token_id);
245        let has_value = self.has_token_attributes_value(mapping, token_nonce);
246        if !has_value {
247            SA::error_api_impl().signal_error(VALUE_NOT_PREVIOUSLY_SET_ERROR_MESSAGE);
248        }
249
250        self.get_token_attributes_value(mapping, token_nonce)
251    }
252
253    pub fn get_nonce<T: TopEncode + TopDecode + NestedEncode + NestedDecode, M: ManagedTypeApi>(
254        &self,
255        token_id: &TokenIdentifier<M>,
256        attr: &T,
257    ) -> u64 {
258        let has_mapping = self.has_mapping_value(token_id);
259        if !has_mapping {
260            SA::error_api_impl().signal_error(UNKNOWN_TOKEN_ID_ERROR_MESSAGE);
261        }
262
263        let mapping = self.get_mapping_value(token_id);
264        let has_value = self.has_attr_to_nonce_mapping::<T>(mapping, attr);
265        if !has_value {
266            SA::error_api_impl().signal_error(VALUE_NOT_PREVIOUSLY_SET_ERROR_MESSAGE);
267        }
268
269        self.get_attributes_to_nonce_mapping(mapping, attr)
270    }
271
272    fn has_mapping_value<M: ManagedTypeApi>(&self, token_id: &TokenIdentifier<M>) -> bool {
273        !self.is_empty_mapping_value(token_id)
274    }
275
276    fn has_token_attributes_value(&self, mapping: u8, token_nonce: u64) -> bool {
277        !self.is_empty_token_attributes_value(mapping, token_nonce)
278    }
279
280    fn has_attr_to_nonce_mapping<T: TopEncode + TopDecode + NestedEncode + NestedDecode>(
281        &self,
282        mapping: u8,
283        attr: &T,
284    ) -> bool {
285        !self.is_empty_attributes_to_nonce_mapping(mapping, attr)
286    }
287
288    fn build_key_token_id_counter(&self) -> StorageKey<SA> {
289        let mut key = self.base_key.clone();
290        key.append_bytes(COUNTER_SUFFIX);
291        key
292    }
293
294    fn build_key_token_id_mapping<M: ManagedTypeApi>(
295        &self,
296        token_id: &TokenIdentifier<M>,
297    ) -> StorageKey<SA> {
298        let mut key = self.base_key.clone();
299        key.append_bytes(MAPPING_SUFFIX);
300        key.append_item(token_id);
301        key
302    }
303
304    fn build_key_token_attr_value(&self, mapping: u8, token_nonce: u64) -> StorageKey<SA> {
305        let mut key = self.base_key.clone();
306        key.append_bytes(ATTR_SUFFIX);
307        key.append_item(&mapping);
308        key.append_item(&token_nonce);
309        key
310    }
311
312    fn build_key_attr_to_nonce_mapping<T: TopEncode + TopDecode + NestedEncode + NestedDecode>(
313        &self,
314        mapping: u8,
315        attr: &T,
316    ) -> StorageKey<SA> {
317        let mut key = self.base_key.clone();
318        key.append_bytes(NONCE_SUFFIX);
319        key.append_item(&mapping);
320        key.append_item(attr);
321        key
322    }
323
324    fn get_counter_value(&self) -> u8 {
325        storage_get(self.build_key_token_id_counter().as_ref())
326    }
327
328    fn get_mapping_value<M: ManagedTypeApi>(&self, token_id: &TokenIdentifier<M>) -> u8 {
329        storage_get(self.build_key_token_id_mapping(token_id).as_ref())
330    }
331
332    fn is_empty_mapping_value<M: ManagedTypeApi>(&self, token_id: &TokenIdentifier<M>) -> bool {
333        storage_get_len(self.build_key_token_id_mapping(token_id).as_ref()) == 0
334    }
335
336    fn get_attributes_to_nonce_mapping<T: TopEncode + TopDecode + NestedEncode + NestedDecode>(
337        &self,
338        mapping: u8,
339        attr: &T,
340    ) -> u64 {
341        storage_get(self.build_key_attr_to_nonce_mapping(mapping, attr).as_ref())
342    }
343
344    fn is_empty_attributes_to_nonce_mapping<
345        T: TopEncode + TopDecode + NestedEncode + NestedDecode,
346    >(
347        &self,
348        mapping: u8,
349        attr: &T,
350    ) -> bool {
351        storage_get_len(self.build_key_attr_to_nonce_mapping(mapping, attr).as_ref()) == 0
352    }
353
354    fn get_token_attributes_value<T: TopEncode + TopDecode + NestedEncode + NestedDecode>(
355        &self,
356        mapping: u8,
357        token_nonce: u64,
358    ) -> T {
359        storage_get(
360            self.build_key_token_attr_value(mapping, token_nonce)
361                .as_ref(),
362        )
363    }
364
365    fn is_empty_token_attributes_value(&self, mapping: u8, token_nonce: u64) -> bool {
366        storage_get_len(
367            self.build_key_token_attr_value(mapping, token_nonce)
368                .as_ref(),
369        ) == 0
370    }
371}