radix_engine_interface/object_modules/metadata/models/
mod.rs

1mod discriminators;
2mod origin;
3mod url;
4
5pub use self::url::*;
6pub use discriminators::*;
7pub use origin::*;
8
9use crate::internal_prelude::*;
10use crate::types::KeyValueStoreInit;
11#[cfg(feature = "fuzzing")]
12use arbitrary::Arbitrary;
13use radix_common::crypto::PublicKey;
14use radix_common::crypto::PublicKeyHash;
15use radix_common::data::scrypto::model::NonFungibleLocalId;
16use radix_common::data::scrypto::*;
17use radix_common::math::Decimal;
18use radix_common::time::Instant;
19use radix_common::types::GlobalAddress;
20use radix_common::types::NonFungibleGlobalId;
21use sbor::SborEnum;
22
23#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
24#[derive(Debug, Clone, Eq, PartialEq, ScryptoSbor, ManifestSbor)]
25#[sbor(categorize_types = "U, O")]
26pub enum GenericMetadataValue<U, O> {
27    #[sbor(discriminator(METADATA_VALUE_STRING_DISCRIMINATOR))]
28    String(String),
29    #[sbor(discriminator(METADATA_VALUE_BOOLEAN_DISCRIMINATOR))]
30    Bool(bool),
31    #[sbor(discriminator(METADATA_VALUE_U8_DISCRIMINATOR))]
32    U8(u8),
33    #[sbor(discriminator(METADATA_VALUE_U32_DISCRIMINATOR))]
34    U32(u32),
35    #[sbor(discriminator(METADATA_VALUE_U64_DISCRIMINATOR))]
36    U64(u64),
37    #[sbor(discriminator(METADATA_VALUE_I32_DISCRIMINATOR))]
38    I32(i32),
39    #[sbor(discriminator(METADATA_VALUE_I64_DISCRIMINATOR))]
40    I64(i64),
41    #[sbor(discriminator(METADATA_VALUE_DECIMAL_DISCRIMINATOR))]
42    Decimal(Decimal),
43    #[sbor(discriminator(METADATA_VALUE_GLOBAL_ADDRESS_DISCRIMINATOR))]
44    GlobalAddress(GlobalAddress),
45    #[sbor(discriminator(METADATA_VALUE_PUBLIC_KEY_DISCRIMINATOR))]
46    PublicKey(PublicKey),
47    #[sbor(discriminator(METADATA_VALUE_NON_FUNGIBLE_GLOBAL_ID_DISCRIMINATOR))]
48    NonFungibleGlobalId(NonFungibleGlobalId),
49    #[sbor(discriminator(METADATA_VALUE_NON_FUNGIBLE_LOCAL_ID_DISCRIMINATOR))]
50    NonFungibleLocalId(NonFungibleLocalId),
51    #[sbor(discriminator(METADATA_VALUE_INSTANT_DISCRIMINATOR))]
52    Instant(Instant),
53    #[sbor(discriminator(METADATA_VALUE_URL_DISCRIMINATOR))]
54    Url(U),
55    #[sbor(discriminator(METADATA_VALUE_ORIGIN_DISCRIMINATOR))]
56    Origin(O),
57    #[sbor(discriminator(METADATA_VALUE_PUBLIC_KEY_HASH_DISCRIMINATOR))]
58    PublicKeyHash(PublicKeyHash),
59
60    #[sbor(discriminator(METADATA_VALUE_STRING_ARRAY_DISCRIMINATOR))]
61    StringArray(Vec<String>),
62    #[sbor(discriminator(METADATA_VALUE_BOOLEAN_ARRAY_DISCRIMINATOR))]
63    BoolArray(Vec<bool>),
64    #[sbor(discriminator(METADATA_VALUE_U8_ARRAY_DISCRIMINATOR))]
65    U8Array(Vec<u8>),
66    #[sbor(discriminator(METADATA_VALUE_U32_ARRAY_DISCRIMINATOR))]
67    U32Array(Vec<u32>),
68    #[sbor(discriminator(METADATA_VALUE_U64_ARRAY_DISCRIMINATOR))]
69    U64Array(Vec<u64>),
70    #[sbor(discriminator(METADATA_VALUE_I32_ARRAY_DISCRIMINATOR))]
71    I32Array(Vec<i32>),
72    #[sbor(discriminator(METADATA_VALUE_I64_ARRAY_DISCRIMINATOR))]
73    I64Array(Vec<i64>),
74    #[sbor(discriminator(METADATA_VALUE_DECIMAL_ARRAY_DISCRIMINATOR))]
75    DecimalArray(Vec<Decimal>),
76    #[sbor(discriminator(METADATA_VALUE_GLOBAL_ADDRESS_ARRAY_DISCRIMINATOR))]
77    GlobalAddressArray(Vec<GlobalAddress>),
78    #[sbor(discriminator(METADATA_VALUE_PUBLIC_KEY_ARRAY_DISCRIMINATOR))]
79    PublicKeyArray(Vec<PublicKey>),
80    #[sbor(discriminator(METADATA_VALUE_NON_FUNGIBLE_GLOBAL_ID_ARRAY_DISCRIMINATOR))]
81    NonFungibleGlobalIdArray(Vec<NonFungibleGlobalId>),
82    #[sbor(discriminator(METADATA_VALUE_NON_FUNGIBLE_LOCAL_ID_ARRAY_DISCRIMINATOR))]
83    NonFungibleLocalIdArray(Vec<NonFungibleLocalId>),
84    #[sbor(discriminator(METADATA_VALUE_INSTANT_ARRAY_DISCRIMINATOR))]
85    InstantArray(Vec<Instant>),
86    #[sbor(discriminator(METADATA_VALUE_URL_ARRAY_DISCRIMINATOR))]
87    UrlArray(Vec<U>),
88    #[sbor(discriminator(METADATA_VALUE_ORIGIN_ARRAY_DISCRIMINATOR))]
89    OriginArray(Vec<O>),
90    #[sbor(discriminator(METADATA_VALUE_PUBLIC_KEY_HASH_ARRAY_DISCRIMINATOR))]
91    PublicKeyHashArray(Vec<PublicKeyHash>),
92}
93
94pub type MetadataValue = GenericMetadataValue<UncheckedUrl, UncheckedOrigin>;
95pub type CheckedMetadataValue = GenericMetadataValue<CheckedUrl, CheckedOrigin>;
96
97#[derive(Debug, Clone, Eq, PartialEq, ScryptoSbor)]
98pub enum MetadataConversionError {
99    UnexpectedType {
100        expected_type_id: u8,
101        actual_type_id: u8,
102    },
103}
104
105pub trait MetadataVal: ScryptoEncode + ScryptoDecode + ToMetadataEntry {
106    const DISCRIMINATOR: u8;
107
108    fn to_metadata_value(self) -> MetadataValue;
109
110    fn from_metadata_value(entry: MetadataValue) -> Result<Self, MetadataConversionError>;
111}
112
113pub trait ToMetadataEntry {
114    fn to_metadata_entry(self) -> Option<MetadataValue>;
115}
116
117pub trait SingleMetadataVal: MetadataVal {
118    fn to_array_metadata_value(vec: Vec<Self>) -> MetadataValue;
119
120    fn from_array_metadata_value(
121        entry: MetadataValue,
122    ) -> Result<Vec<Self>, MetadataConversionError>;
123}
124
125pub trait ArrayMetadataVal: MetadataVal {}
126
127macro_rules! impl_metadata_val {
128    ($rust_type:ty, $metadata_type:tt, $metadata_array_type:tt, $type_id:expr) => {
129        impl MetadataVal for $rust_type {
130            const DISCRIMINATOR: u8 = $type_id;
131
132            fn to_metadata_value(self) -> MetadataValue {
133                MetadataValue::$metadata_type(self)
134            }
135
136            fn from_metadata_value(entry: MetadataValue) -> Result<Self, MetadataConversionError> {
137                match entry {
138                    MetadataValue::$metadata_type(x) => Ok(x),
139                    _ => Err(MetadataConversionError::UnexpectedType {
140                        expected_type_id: Self::DISCRIMINATOR,
141                        actual_type_id: SborEnum::<ScryptoCustomValueKind>::get_discriminator(
142                            &entry,
143                        ),
144                    }),
145                }
146            }
147        }
148
149        impl ToMetadataEntry for $rust_type {
150            fn to_metadata_entry(self) -> Option<MetadataValue> {
151                Some(self.to_metadata_value())
152            }
153        }
154
155        impl SingleMetadataVal for $rust_type {
156            fn to_array_metadata_value(vec: Vec<Self>) -> MetadataValue {
157                vec.to_metadata_value()
158            }
159
160            fn from_array_metadata_value(
161                entry: MetadataValue,
162            ) -> Result<Vec<Self>, MetadataConversionError> {
163                Vec::<Self>::from_metadata_value(entry)
164            }
165        }
166
167        impl MetadataVal for Vec<$rust_type> {
168            const DISCRIMINATOR: u8 = METADATA_DISCRIMINATOR_ARRAY_BASE + $type_id;
169
170            fn to_metadata_value(self) -> MetadataValue {
171                MetadataValue::$metadata_array_type(self)
172            }
173
174            fn from_metadata_value(entry: MetadataValue) -> Result<Self, MetadataConversionError> {
175                match entry {
176                    MetadataValue::$metadata_array_type(x) => Ok(x),
177                    _ => Err(MetadataConversionError::UnexpectedType {
178                        expected_type_id: Self::DISCRIMINATOR,
179                        actual_type_id: SborEnum::<ScryptoCustomValueKind>::get_discriminator(
180                            &entry,
181                        ),
182                    }),
183                }
184            }
185        }
186
187        impl ArrayMetadataVal for Vec<$rust_type> {}
188
189        impl ToMetadataEntry for Vec<$rust_type> {
190            fn to_metadata_entry(self) -> Option<MetadataValue> {
191                Some(self.to_metadata_value())
192            }
193        }
194
195        impl ToMetadataEntry for &[$rust_type] {
196            fn to_metadata_entry(self) -> Option<MetadataValue> {
197                Some(<$rust_type as SingleMetadataVal>::to_array_metadata_value(
198                    self.iter().cloned().collect(),
199                ))
200            }
201        }
202
203        impl<const N: usize> ToMetadataEntry for [$rust_type; N] {
204            fn to_metadata_entry(self) -> Option<MetadataValue> {
205                Some(<$rust_type as SingleMetadataVal>::to_array_metadata_value(
206                    self.into_iter().collect(),
207                ))
208            }
209        }
210
211        impl<const N: usize> ToMetadataEntry for &[$rust_type; N] {
212            fn to_metadata_entry(self) -> Option<MetadataValue> {
213                Some(<$rust_type as SingleMetadataVal>::to_array_metadata_value(
214                    self.iter().cloned().collect(),
215                ))
216            }
217        }
218    };
219}
220
221macro_rules! impl_metadata_val_alias {
222    ($metadata_rust_type:ty, $(| <$($generic:tt),+> |)? $alias_rust_type:ty) => {
223        impl$(<$($generic),+>)? ToMetadataEntry for $alias_rust_type {
224            fn to_metadata_entry(self) -> Option<MetadataValue> {
225                Some(<$metadata_rust_type as MetadataVal>::to_metadata_value(self.into()))
226            }
227        }
228
229        impl$(<$($generic),+>)? ToMetadataEntry for Vec<$alias_rust_type> {
230            fn to_metadata_entry(self) -> Option<MetadataValue> {
231                Some(<$metadata_rust_type as SingleMetadataVal>::to_array_metadata_value(
232                    self.into_iter().map(Into::into).collect()
233                ))
234            }
235        }
236
237        impl$(<$($generic),+>)? ToMetadataEntry for &[$alias_rust_type] {
238            fn to_metadata_entry(self) -> Option<MetadataValue> {
239                Some(<$metadata_rust_type as SingleMetadataVal>::to_array_metadata_value(
240                    self.iter().map(|x| (*x).into()).collect()
241                ))
242            }
243        }
244
245        impl<$($($generic,)+)? const N: usize> ToMetadataEntry for [$alias_rust_type; N] {
246            fn to_metadata_entry(self) -> Option<MetadataValue> {
247                Some(<$metadata_rust_type as SingleMetadataVal>::to_array_metadata_value(
248                    self.iter().map(|x| (*x).into()).collect()
249                ))
250            }
251        }
252
253        impl<$($($generic,)+)? const N: usize> ToMetadataEntry for &[$alias_rust_type; N] {
254            fn to_metadata_entry(self) -> Option<MetadataValue> {
255                Some(<$metadata_rust_type as SingleMetadataVal>::to_array_metadata_value(
256                    self.iter().map(|x| (*x).into()).collect()
257                ))
258            }
259        }
260    };
261}
262
263impl_metadata_val!(
264    String,
265    String,
266    StringArray,
267    METADATA_VALUE_STRING_DISCRIMINATOR
268);
269impl_metadata_val!(bool, Bool, BoolArray, METADATA_VALUE_BOOLEAN_DISCRIMINATOR);
270impl_metadata_val!(u8, U8, U8Array, METADATA_VALUE_U8_DISCRIMINATOR);
271impl_metadata_val!(u32, U32, U32Array, METADATA_VALUE_U32_DISCRIMINATOR);
272impl_metadata_val!(u64, U64, U64Array, METADATA_VALUE_U64_DISCRIMINATOR);
273impl_metadata_val!(i32, I32, I32Array, METADATA_VALUE_I32_DISCRIMINATOR);
274impl_metadata_val!(i64, I64, I64Array, METADATA_VALUE_I64_DISCRIMINATOR);
275impl_metadata_val!(
276    Decimal,
277    Decimal,
278    DecimalArray,
279    METADATA_VALUE_DECIMAL_DISCRIMINATOR
280);
281impl_metadata_val!(
282    GlobalAddress,
283    GlobalAddress,
284    GlobalAddressArray,
285    METADATA_VALUE_GLOBAL_ADDRESS_DISCRIMINATOR
286);
287impl_metadata_val!(
288    PublicKey,
289    PublicKey,
290    PublicKeyArray,
291    METADATA_VALUE_PUBLIC_KEY_DISCRIMINATOR
292);
293impl_metadata_val!(
294    NonFungibleGlobalId,
295    NonFungibleGlobalId,
296    NonFungibleGlobalIdArray,
297    METADATA_VALUE_NON_FUNGIBLE_GLOBAL_ID_DISCRIMINATOR
298);
299impl_metadata_val!(
300    NonFungibleLocalId,
301    NonFungibleLocalId,
302    NonFungibleLocalIdArray,
303    METADATA_VALUE_NON_FUNGIBLE_LOCAL_ID_DISCRIMINATOR
304);
305impl_metadata_val!(
306    Instant,
307    Instant,
308    InstantArray,
309    METADATA_VALUE_INSTANT_DISCRIMINATOR
310);
311impl_metadata_val!(
312    UncheckedUrl,
313    Url,
314    UrlArray,
315    METADATA_VALUE_URL_DISCRIMINATOR
316);
317impl_metadata_val!(
318    UncheckedOrigin,
319    Origin,
320    OriginArray,
321    METADATA_VALUE_ORIGIN_DISCRIMINATOR
322);
323impl_metadata_val!(
324    PublicKeyHash,
325    PublicKeyHash,
326    PublicKeyHashArray,
327    METADATA_VALUE_PUBLIC_KEY_HASH_DISCRIMINATOR
328);
329
330// Additional to metadata value implementations
331
332impl_metadata_val_alias!(String, |<'a>| &'a str);
333impl_metadata_val_alias!(GlobalAddress, ComponentAddress);
334impl_metadata_val_alias!(GlobalAddress, ResourceAddress);
335impl_metadata_val_alias!(GlobalAddress, PackageAddress);
336
337impl ToMetadataEntry for MetadataValue {
338    fn to_metadata_entry(self) -> Option<MetadataValue> {
339        Some(self)
340    }
341}
342
343impl ToMetadataEntry for Option<MetadataValue> {
344    fn to_metadata_entry(self) -> Option<MetadataValue> {
345        self
346    }
347}
348
349pub type MetadataInit = KeyValueStoreInit<String, MetadataValue>;
350
351impl MetadataInit {
352    pub fn set_metadata<S: ToString, V: ToMetadataEntry>(&mut self, key: S, value: V) {
353        match value.to_metadata_entry() {
354            None => {}
355            Some(value) => {
356                self.set(key.to_string(), value);
357            }
358        }
359    }
360
361    pub fn set_and_lock_metadata<S: ToString, V: ToMetadataEntry>(&mut self, key: S, value: V) {
362        match value.to_metadata_entry() {
363            None => {
364                self.lock_empty(key.to_string());
365            }
366            Some(value) => {
367                self.set_and_lock(key.to_string(), value);
368            }
369        }
370    }
371}
372
373impl From<BTreeMap<String, MetadataValue>> for MetadataInit {
374    fn from(data: BTreeMap<String, MetadataValue>) -> Self {
375        let mut metadata_init = MetadataInit::new();
376        for (key, value) in data {
377            metadata_init.set(key, value);
378        }
379        metadata_init
380    }
381}
382
383#[macro_export]
384macro_rules! metadata_init_set_entry {
385    ($metadata:expr, $key:expr, $value:expr, updatable) => {{
386        $metadata.set_metadata($key, $value);
387    }};
388    ($metadata:expr, $key:expr, $value:expr, locked) => {{
389        $metadata.set_and_lock_metadata($key, $value);
390    }};
391}
392
393#[macro_export]
394macro_rules! metadata_init {
395    () => ({
396        $crate::object_modules::metadata::MetadataInit::new()
397    });
398    ( $($key:expr => $value:expr, $lock:ident;)* ) => ({
399        let mut metadata_init = $crate::object_modules::metadata::MetadataInit::new();
400        $(
401            $crate::metadata_init_set_entry!(metadata_init, $key, $value, $lock);
402        )*
403        metadata_init
404    });
405}
406
407#[macro_export]
408macro_rules! metadata {
409    {} => ({
410        ModuleConfig {
411            init: metadata_init!(),
412            roles: RoleAssignmentInit::default(),
413        }
414    });
415    {
416        roles {
417            $($role:ident => $rule:expr;)*
418        },
419        init {
420            $($key:expr => $value:expr, $locked:ident;)*
421        }
422    } => ({
423        let metadata_roles = metadata_roles!($($role => $rule;)*);
424        let metadata = metadata_init!($($key => $value, $locked;)*);
425        ModuleConfig {
426            init: metadata,
427            roles: metadata_roles
428        }
429    });
430
431    {
432        init {
433            $($key:expr => $value:expr, $locked:ident;)*
434        }
435    } => ({
436        let metadata = metadata_init!($($key => $value, $locked;)*);
437        ModuleConfig {
438            init: metadata,
439            roles: RoleAssignmentInit::new(),
440        }
441    });
442
443    {
444        roles {
445            $($role:ident => $rule:expr;)*
446        }
447    } => ({
448        let roles = metadata_roles!($($role => $rule;)*);
449        ModuleConfig {
450            init: metadata_init!(),
451            roles,
452        }
453    });
454}
455
456#[cfg(test)]
457mod tests {
458    use radix_common::prelude::*;
459
460    use super::*;
461
462    #[test]
463    pub fn can_encode_and_decode_all_metadata_values() {
464        encode_decode(&["Hello".to_string(), "world!".to_string()]);
465        encode_decode(&[true, false]);
466        encode_decode(&[1u8, 2u8]);
467        encode_decode(&[2u32, 3u32]);
468        encode_decode(&[3u64, 4u64]);
469        encode_decode(&[4i32, 5i32]);
470        encode_decode(&[5i64, 6i64]);
471        encode_decode(&[dec!("1"), dec!("2.1")]);
472        encode_decode(&[GlobalAddress::from(XRD)]);
473        encode_decode(&[
474            PublicKey::Ed25519(Ed25519PublicKey([0; Ed25519PublicKey::LENGTH])),
475            PublicKey::Secp256k1(Secp256k1PublicKey([0; Secp256k1PublicKey::LENGTH])),
476        ]);
477        encode_decode(&[NonFungibleGlobalId::package_of_direct_caller_badge(
478            POOL_PACKAGE,
479        )]);
480        encode_decode(&[
481            NonFungibleLocalId::String(StringNonFungibleLocalId::new("Hello_world").unwrap()),
482            NonFungibleLocalId::Integer(IntegerNonFungibleLocalId::new(42)),
483            NonFungibleLocalId::Bytes(BytesNonFungibleLocalId::new(vec![1u8]).unwrap()),
484            NonFungibleLocalId::RUID(RUIDNonFungibleLocalId::new([1; 32])),
485        ]);
486        encode_decode(&[Instant {
487            seconds_since_unix_epoch: 1687446137,
488        }]);
489        encode_decode(&[UncheckedUrl::of("https://www.radixdlt.com")]);
490        encode_decode(&[UncheckedOrigin::of("https://www.radixdlt.com")]);
491        encode_decode(&[
492            PublicKeyHash::Ed25519(Ed25519PublicKey([0; Ed25519PublicKey::LENGTH]).get_hash()),
493            PublicKeyHash::Secp256k1(
494                Secp256k1PublicKey([0; Secp256k1PublicKey::LENGTH]).get_hash(),
495            ),
496        ]);
497    }
498
499    fn encode_decode<T: SingleMetadataVal + Clone>(values: &[T]) {
500        check_can_encode_decode(values[0].clone().to_metadata_value());
501        check_can_encode_decode(T::to_array_metadata_value(values.to_vec()));
502    }
503
504    fn check_can_encode_decode(value: MetadataValue) {
505        let bytes = scrypto_encode(&value).unwrap();
506        // The outputting of bytes is for test vectors for other impls (eg the Gateway)
507        #[cfg(not(feature = "alloc"))]
508        println!("{}", hex::encode(&bytes));
509        let decoded_value: MetadataValue = scrypto_decode(&bytes).unwrap();
510        assert_eq!(decoded_value, value);
511    }
512}