radix_common/
macros.rs

1/// Creates a safe integer from literals.
2/// You must specify the type of the
3/// integer you want to create.
4///
5#[macro_export]
6macro_rules! i {
7    ($x:expr) => {
8        $x.try_into().expect("Parse Error")
9    };
10}
11
12/// A macro for implementing sbor traits (for statically sized types).
13#[macro_export]
14macro_rules! well_known_scrypto_custom_type {
15    // with describe
16    ($t:ty, $value_kind:expr, $schema_type:expr, $size:expr, $well_known_type:ident, $well_known_type_data_method:ident$(,)?) => {
17        impl sbor::Categorize<$crate::data::scrypto::ScryptoCustomValueKind> for $t {
18            #[inline]
19            fn value_kind() -> sbor::ValueKind<$crate::data::scrypto::ScryptoCustomValueKind> {
20                sbor::ValueKind::Custom($value_kind)
21            }
22        }
23
24        impl<E: sbor::Encoder<$crate::data::scrypto::ScryptoCustomValueKind>>
25            sbor::Encode<$crate::data::scrypto::ScryptoCustomValueKind, E> for $t
26        {
27            #[inline]
28            fn encode_value_kind(&self, encoder: &mut E) -> Result<(), sbor::EncodeError> {
29                encoder.write_value_kind(Self::value_kind())
30            }
31
32            #[inline]
33            fn encode_body(&self, encoder: &mut E) -> Result<(), sbor::EncodeError> {
34                encoder.write_slice(&self.to_vec())
35            }
36        }
37
38        impl<D: sbor::Decoder<$crate::data::scrypto::ScryptoCustomValueKind>>
39            sbor::Decode<$crate::data::scrypto::ScryptoCustomValueKind, D> for $t
40        {
41            fn decode_body_with_value_kind(
42                decoder: &mut D,
43                value_kind: sbor::ValueKind<$crate::data::scrypto::ScryptoCustomValueKind>,
44            ) -> Result<Self, sbor::DecodeError> {
45                decoder.check_preloaded_value_kind(value_kind, Self::value_kind())?;
46                let slice = decoder.read_slice($size)?;
47                Self::try_from(slice).map_err(|_| sbor::DecodeError::InvalidCustomValue)
48            }
49        }
50
51        impl sbor::Describe<$crate::data::scrypto::ScryptoCustomTypeKind> for $t {
52            const TYPE_ID: sbor::RustTypeId = sbor::RustTypeId::WellKnown(
53                $crate::data::scrypto::well_known_scrypto_custom_types::$well_known_type,
54            );
55
56            fn type_data() -> sbor::TypeData<$crate::data::scrypto::ScryptoCustomTypeKind, sbor::RustTypeId> {
57                $crate::data::scrypto::well_known_scrypto_custom_types::$well_known_type_data_method()
58            }
59        }
60    };
61}
62
63#[macro_export]
64macro_rules! manifest_type {
65    // Without describe - if you need describe, also use scrypto_describe_for_manifest_type!
66    ($t:ty, $value_kind:expr, $size: expr$(,)?) => {
67        impl sbor::Categorize<$crate::data::manifest::ManifestCustomValueKind> for $t {
68            #[inline]
69            fn value_kind() -> sbor::ValueKind<$crate::data::manifest::ManifestCustomValueKind> {
70                sbor::ValueKind::Custom($value_kind)
71            }
72        }
73
74        impl<E: sbor::Encoder<$crate::data::manifest::ManifestCustomValueKind>>
75            sbor::Encode<$crate::data::manifest::ManifestCustomValueKind, E> for $t
76        {
77            #[inline]
78            fn encode_value_kind(&self, encoder: &mut E) -> Result<(), sbor::EncodeError> {
79                encoder.write_value_kind(Self::value_kind())
80            }
81
82            #[inline]
83            fn encode_body(&self, encoder: &mut E) -> Result<(), sbor::EncodeError> {
84                encoder.write_slice(&self.to_vec())
85            }
86        }
87
88        impl<D: sbor::Decoder<$crate::data::manifest::ManifestCustomValueKind>>
89            sbor::Decode<$crate::data::manifest::ManifestCustomValueKind, D> for $t
90        {
91            fn decode_body_with_value_kind(
92                decoder: &mut D,
93                value_kind: sbor::ValueKind<$crate::data::manifest::ManifestCustomValueKind>,
94            ) -> Result<Self, sbor::DecodeError> {
95                decoder.check_preloaded_value_kind(value_kind, Self::value_kind())?;
96                let slice = decoder.read_slice($size)?;
97                Self::try_from(slice).map_err(|_| sbor::DecodeError::InvalidCustomValue)
98            }
99        }
100    };
101}
102
103#[macro_export]
104macro_rules! scrypto_describe_for_manifest_type {
105    ($t:ty, $well_known_type:ident, $well_known_type_data_method:ident$(,)?) => {
106        impl sbor::Describe<$crate::data::scrypto::ScryptoCustomTypeKind> for $t {
107            const TYPE_ID: sbor::RustTypeId = sbor::RustTypeId::WellKnown(
108                $crate::data::scrypto::well_known_scrypto_custom_types::$well_known_type,
109            );
110
111            fn type_data() -> sbor::TypeData<$crate::data::scrypto::ScryptoCustomTypeKind, sbor::RustTypeId> {
112                $crate::data::scrypto::well_known_scrypto_custom_types::$well_known_type_data_method()
113            }
114        }
115    }
116}
117
118#[macro_export]
119macro_rules! count {
120    () => {0usize};
121    ($a:expr) => {1usize};
122    ($a:expr, $($rest:expr),*) => {1usize + $crate::count!($($rest),*)};
123}
124
125#[macro_export]
126macro_rules! scrypto_args {
127    ($($args: expr),*) => {{
128        use sbor::Encoder;
129        let mut buf = sbor::rust::vec::Vec::new();
130        let mut encoder = $crate::data::scrypto::ScryptoEncoder::new(
131            &mut buf,
132            $crate::data::scrypto::SCRYPTO_SBOR_V1_MAX_DEPTH,
133        );
134        encoder
135            .write_payload_prefix($crate::data::scrypto::SCRYPTO_SBOR_V1_PAYLOAD_PREFIX)
136            .unwrap();
137        encoder
138            .write_value_kind($crate::data::scrypto::ScryptoValueKind::Tuple)
139            .unwrap();
140        // Hack: stringify to skip ownership move semantics
141        encoder.write_size($crate::count!($(stringify!($args)),*)).unwrap();
142        $(
143            let arg = $args;
144            encoder.encode(&arg).unwrap();
145        )*
146        buf
147    }};
148}
149
150#[macro_export]
151macro_rules! manifest_args {
152    ($($args: expr),*$(,)?) => {{
153        use sbor::Encoder;
154        let mut buf = sbor::rust::vec::Vec::new();
155        let mut encoder = $crate::data::manifest::ManifestEncoder::new(&mut buf, $crate::data::manifest::MANIFEST_SBOR_V1_MAX_DEPTH);
156        encoder.write_payload_prefix($crate::data::manifest::MANIFEST_SBOR_V1_PAYLOAD_PREFIX).unwrap();
157        encoder.write_value_kind($crate::data::manifest::ManifestValueKind::Tuple).unwrap();
158        // Hack: stringify to skip ownership move semantics
159        encoder.write_size($crate::count!($(stringify!($args)),*)).unwrap();
160        $(
161            let arg = $args;
162            encoder.encode(&arg).unwrap();
163        )*
164        let value = $crate::data::manifest::manifest_decode(&buf).unwrap();
165        ManifestArgs::new_from_tuple_or_panic(value)
166    }};
167}
168
169#[macro_export]
170macro_rules! to_manifest_value_and_unwrap {
171    ( $value:expr ) => {{
172        $crate::data::manifest::to_manifest_value($value).unwrap()
173    }};
174}
175
176/// This macro is used to create types that are supposed to be equivalent to existing types that
177/// implement [`ScryptoSbor`]. These types are untyped which means that at the language-level, they
178/// don't contain any type information (e.g., enums don't contain variants, structs don't contain
179/// named fields, etc...).
180///
181/// It's a useful concepts for certain types that are too complex to create an equivalent manifest
182/// types for. For example, `AccessRules` is too complex and has a lot of dependencies and therefore
183/// creating a manifest type for it is very complex. Instead, we could opt to a create a manifest
184/// type for it that has the same schema as it and that offers two way conversion from and into it
185/// such that we can use it in any manifest invocation.
186///
187/// The `$scrypto_ty` provided to this macro must implement [`ScryptoSbor`] and [`ScryptoDescribe`]
188/// and the generated type (named `$manifest_ty_ident`) will implement [`ManifestSbor`] and also
189/// [`ScryptoDescribe`] with the same schema as the original Scrypto type.
190///
191/// # Note on Panics
192///
193/// The `From<$scrypto_ty> for $manifest_ty_ident` implementation will PANIC if it fails. Ensure
194/// that this macro is not used anywhere in the engine itself and only used in the interface.
195///
196/// [`ScryptoSbor`]: crate::prelude::ScryptoSbor
197/// [`ScryptoDescribe`]: crate::prelude::ScryptoDescribe
198/// [`ManifestSbor`]: crate::prelude::ManifestSbor
199#[macro_export]
200macro_rules! define_untyped_manifest_type_wrapper {
201    (
202        $scrypto_ty: ty => $manifest_ty_ident: ident ($inner_ty: ty)
203    ) => {
204        #[cfg_attr(
205            feature = "fuzzing",
206            derive(::arbitrary::Arbitrary, ::serde::Serialize, ::serde::Deserialize)
207        )]
208        #[derive(
209            Debug,
210            Clone,
211            PartialEq,
212            Eq,
213            $crate::prelude::ManifestEncode,
214            $crate::prelude::ManifestCategorize,
215        )]
216        #[sbor(transparent)]
217        pub struct $manifest_ty_ident($inner_ty);
218
219        const _: () = {
220            impl $manifest_ty_ident {
221                pub fn new(
222                    value: impl Into<$scrypto_ty>,
223                ) -> Result<Self, $crate::prelude::ConversionError> {
224                    let value = Into::<$scrypto_ty>::into(value);
225                    let encoded_scrypto_bytes = $crate::prelude::scrypto_encode(&value)
226                        .map_err($crate::prelude::ConversionError::EncodeError)?;
227                    let scrypto_value = $crate::prelude::scrypto_decode::<
228                        $crate::prelude::ScryptoValue,
229                    >(&encoded_scrypto_bytes)
230                    .map_err($crate::prelude::ConversionError::DecodeError)?;
231
232                    let manifest_value =
233                        $crate::prelude::scrypto_value_to_manifest_value(scrypto_value)?;
234                    let encoded_manifest_bytes = $crate::prelude::manifest_encode(&manifest_value)
235                        .map_err($crate::prelude::ConversionError::EncodeError)?;
236                    $crate::prelude::manifest_decode(&encoded_manifest_bytes)
237                        .map(Self)
238                        .map_err($crate::prelude::ConversionError::DecodeError)
239                }
240
241                pub fn try_into_typed(
242                    self,
243                ) -> Result<$scrypto_ty, $crate::prelude::ConversionError> {
244                    let value = self.0;
245                    let encoded_manifest_bytes = $crate::prelude::manifest_encode(&value)
246                        .map_err($crate::prelude::ConversionError::EncodeError)?;
247                    let manifest_value = $crate::prelude::manifest_decode::<
248                        $crate::prelude::ManifestValue,
249                    >(&encoded_manifest_bytes)
250                    .map_err($crate::prelude::ConversionError::DecodeError)?;
251
252                    let scrypto_value =
253                        $crate::prelude::manifest_value_to_scrypto_value(manifest_value)?;
254                    let encoded_scrypto_bytes = $crate::prelude::scrypto_encode(&scrypto_value)
255                        .map_err($crate::prelude::ConversionError::EncodeError)?;
256                    $crate::prelude::scrypto_decode::<$scrypto_ty>(&encoded_scrypto_bytes)
257                        .map_err($crate::prelude::ConversionError::DecodeError)
258                }
259            }
260
261            // Note: this conversion WILL PANIC if it fails.
262            impl<T> From<T> for $manifest_ty_ident
263            where
264                T: Into<$scrypto_ty>,
265            {
266                fn from(value: T) -> Self {
267                    Self::new(value).expect(concat!(
268                        "Conversion from ",
269                        stringify!($scrypto_ty),
270                        " into ",
271                        stringify!($manifest_ty_ident),
272                        " failed despite not being expected to fail"
273                    ))
274                }
275            }
276
277            impl TryFrom<$manifest_ty_ident> for $scrypto_ty {
278                type Error = $crate::prelude::ConversionError;
279
280                fn try_from(value: $manifest_ty_ident) -> Result<Self, Self::Error> {
281                    value.try_into_typed()
282                }
283            }
284
285            impl $crate::prelude::Describe<$crate::prelude::ScryptoCustomTypeKind>
286                for $manifest_ty_ident
287            {
288                const TYPE_ID: $crate::prelude::RustTypeId =
289                    <$scrypto_ty as $crate::prelude::Describe<
290                        $crate::prelude::ScryptoCustomTypeKind,
291                    >>::TYPE_ID;
292
293                fn type_data() -> $crate::prelude::TypeData<
294                    $crate::prelude::ScryptoCustomTypeKind,
295                    $crate::prelude::RustTypeId,
296                > {
297                    <$scrypto_ty as Describe<$crate::prelude::ScryptoCustomTypeKind>>::type_data()
298                }
299
300                fn add_all_dependencies(
301                    aggregator: &mut $crate::prelude::TypeAggregator<$crate::prelude::ScryptoCustomTypeKind>
302                ) {
303                    <$scrypto_ty as Describe<$crate::prelude::ScryptoCustomTypeKind>>::add_all_dependencies(aggregator)
304                }
305            }
306
307            // Manual implementation of decoding to allow us to check the schema on-decode to mirror
308            // what the Scrypto equivalent types do.
309            //
310            // The Scrypto types do it a little differently where their schema check is implicit
311            // (e.g., SBOR contains 5 fields but struct has 2) and therefore we need to do an
312            // explicit schema check here so that no types that dont match the schema can be decoded
313            // into this wrapper type.
314            impl<D: $crate::prelude::Decoder<$crate::prelude::ManifestCustomValueKind>> $crate::prelude::Decode<$crate::prelude::ManifestCustomValueKind, D> for $manifest_ty_ident
315            {
316                #[inline]
317                fn decode_body_with_value_kind(
318                    decoder: &mut D,
319                    value_kind: $crate::prelude::ValueKind<$crate::prelude::ManifestCustomValueKind>,
320                ) -> Result<Self, $crate::prelude::DecodeError> {
321                    // Store the schema of this type in a lazy-static. The schema doesn't change and
322                    // we don't want to recreate the schema each time we want to decode it.
323                    ::lazy_static::lazy_static! {
324                        static ref SCHEMA: ($crate::prelude::LocalTypeId, $crate::prelude::VersionedSchema<$crate::prelude::ScryptoCustomSchema>) = $crate::prelude::generate_full_schema_from_single_type::<
325                            $scrypto_ty,
326                            $crate::prelude::ScryptoCustomSchema
327                        >();
328                    }
329
330                    // Start by decoding the value and creating this object.
331                    let inner_value = <$inner_ty
332                        as $crate::prelude::Decode<$crate::prelude::ManifestCustomValueKind, D>
333                    >::decode_body_with_value_kind(decoder, value_kind)?;
334
335                    // Re-encode this type and do the schema check.
336                    let encoded = $crate::prelude::manifest_encode(&inner_value).map_err(|_| $crate::prelude::DecodeError::InvalidCustomValue)?;
337                    $crate::prelude::validate_payload_against_schema::<$crate::prelude::ManifestCustomExtension, _>(
338                        &encoded,
339                        SCHEMA.1.v1(),
340                        SCHEMA.0,
341                        &(),
342                        $crate::prelude::MANIFEST_SBOR_V1_MAX_DEPTH
343                    )
344                    .map_err(|_| $crate::prelude::DecodeError::InvalidCustomValue)?;
345
346                    // All succeeded, return the value
347                    Ok(Self(inner_value))
348                }
349            }
350        };
351    };
352}
353
354#[cfg(test)]
355mod test {
356    use crate::prelude::*;
357
358    #[test]
359    fn decoding_into_opaque_value_with_incorrect_schema_fails() {
360        // Arrange
361        #[derive(ScryptoSbor)]
362        pub enum MyEnum {
363            Variant(u32),
364        }
365
366        #[derive(ScryptoSbor, ManifestSbor)]
367        pub enum MyOtherEnum {
368            Variant(Decimal),
369        }
370
371        define_untyped_manifest_type_wrapper!(
372            MyEnum => ManifestMyEnum(EnumVariantValue<ManifestCustomValueKind, ManifestCustomValue>)
373        );
374
375        let encoded = manifest_encode(&MyOtherEnum::Variant(Decimal::ONE)).unwrap();
376
377        // Act
378        let decoded = manifest_decode::<ManifestMyEnum>(&encoded);
379
380        // Assert
381        assert_eq!(decoded, Err(DecodeError::InvalidCustomValue))
382    }
383}