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