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
330impl_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 #[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}