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