radix_engine_interface/blueprints/
component.rs

1use core::hash::{Hash, Hasher};
2use core::marker::PhantomData;
3use radix_common::prelude::*;
4
5pub trait TypeInfoMarker {
6    const PACKAGE_ADDRESS: Option<PackageAddress>;
7    const BLUEPRINT_NAME: &'static str;
8    const OWNED_TYPE_NAME: &'static str;
9    const GLOBAL_TYPE_NAME: &'static str;
10}
11
12// This type is added for backwards compatibility so that this change is not apparent at all to
13// Scrypto Developers
14pub type Global<T> = GenericGlobal<ComponentAddress, T>;
15
16#[derive(
17    Clone,
18    Copy,
19    Debug,
20    PartialEq,
21    Eq,
22    PartialOrd,
23    Ord,
24    ScryptoEncode,
25    ScryptoDecode,
26    ScryptoCategorize,
27    ManifestEncode,
28    ManifestDecode,
29    ManifestCategorize,
30)]
31#[sbor(transparent, child_types = "A")]
32pub struct GenericGlobal<A, M>(pub A, #[sbor(skip)] PhantomData<M>)
33where
34    M: TypeInfoMarker;
35
36impl<A, M> GenericGlobal<A, M>
37where
38    M: TypeInfoMarker,
39{
40    pub fn new(address: A) -> Self {
41        Self(address, PhantomData)
42    }
43}
44
45impl<A, M> Hash for GenericGlobal<A, M>
46where
47    A: Hash,
48    M: TypeInfoMarker,
49{
50    fn hash<H: Hasher>(&self, state: &mut H) {
51        self.0.hash(state);
52    }
53}
54
55impl<A, M> From<A> for GenericGlobal<A, M>
56where
57    M: TypeInfoMarker,
58{
59    fn from(value: A) -> Self {
60        Self(value, PhantomData)
61    }
62}
63
64impl<M> From<ComponentAddress> for GenericGlobal<ManifestComponentAddress, M>
65where
66    M: TypeInfoMarker,
67{
68    fn from(value: ComponentAddress) -> Self {
69        Self(value.into_manifest_address(), PhantomData)
70    }
71}
72
73impl<A, M> Describe<ScryptoCustomTypeKind> for GenericGlobal<A, M>
74where
75    M: TypeInfoMarker,
76{
77    const TYPE_ID: RustTypeId =
78        RustTypeId::Novel(const_sha1::sha1(M::GLOBAL_TYPE_NAME.as_bytes()).as_bytes());
79
80    fn type_data() -> TypeData<ScryptoCustomTypeKind, RustTypeId> {
81        TypeData {
82            kind: TypeKind::Custom(ScryptoCustomTypeKind::Reference),
83            metadata: TypeMetadata::no_child_names(M::GLOBAL_TYPE_NAME),
84            validation: TypeValidation::Custom(ScryptoCustomTypeValidation::Reference(
85                ReferenceValidation::IsGlobalTyped(
86                    M::PACKAGE_ADDRESS,
87                    M::BLUEPRINT_NAME.to_string(),
88                ),
89            )),
90        }
91    }
92
93    fn add_all_dependencies(_aggregator: &mut TypeAggregator<ScryptoCustomTypeKind>) {}
94}
95
96// This type is added for backwards compatibility so that this change is not apparent at all to
97// Scrypto Developers
98pub type Owned<T> = GenericOwned<InternalAddress, T>;
99
100#[derive(
101    Clone,
102    Copy,
103    Debug,
104    PartialEq,
105    Eq,
106    PartialOrd,
107    Ord,
108    ScryptoEncode,
109    ScryptoDecode,
110    ScryptoCategorize,
111    ManifestEncode,
112    ManifestDecode,
113    ManifestCategorize,
114)]
115#[sbor(transparent, child_types = "A")]
116pub struct GenericOwned<A, M>(pub A, #[sbor(skip)] PhantomData<M>)
117where
118    M: TypeInfoMarker;
119
120impl<A, M> GenericOwned<A, M>
121where
122    M: TypeInfoMarker,
123{
124    pub fn new(address: A) -> Self {
125        Self(address, PhantomData)
126    }
127}
128
129impl<A, M> Hash for GenericOwned<A, M>
130where
131    A: Hash,
132    M: TypeInfoMarker,
133{
134    fn hash<H: Hasher>(&self, state: &mut H) {
135        self.0.hash(state);
136    }
137}
138
139impl<A, M> From<A> for GenericOwned<A, M>
140where
141    M: TypeInfoMarker,
142{
143    fn from(value: A) -> Self {
144        Self(value, PhantomData)
145    }
146}
147
148impl<A, M> Describe<ScryptoCustomTypeKind> for GenericOwned<A, M>
149where
150    M: TypeInfoMarker,
151{
152    const TYPE_ID: RustTypeId =
153        RustTypeId::Novel(const_sha1::sha1(M::OWNED_TYPE_NAME.as_bytes()).as_bytes());
154
155    fn type_data() -> TypeData<ScryptoCustomTypeKind, RustTypeId> {
156        TypeData {
157            kind: TypeKind::Custom(ScryptoCustomTypeKind::Own),
158            metadata: TypeMetadata::no_child_names(M::OWNED_TYPE_NAME),
159            validation: TypeValidation::Custom(ScryptoCustomTypeValidation::Own(
160                OwnValidation::IsTypedObject(M::PACKAGE_ADDRESS, M::BLUEPRINT_NAME.to_string()),
161            )),
162        }
163    }
164
165    fn add_all_dependencies(_aggregator: &mut TypeAggregator<ScryptoCustomTypeKind>) {}
166}
167
168macro_rules! define_type_marker {
169    ($package_address: expr, $blueprint_name: ident) => {
170        paste::paste! {
171            #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
172            pub struct [< $blueprint_name Marker >];
173
174            impl crate::blueprints::component::TypeInfoMarker
175                for [< $blueprint_name Marker >]
176            {
177                const PACKAGE_ADDRESS: Option<PackageAddress> = $package_address;
178                const BLUEPRINT_NAME: &'static str = stringify!($blueprint_name);
179                const OWNED_TYPE_NAME: &'static str = stringify!([< Owned $blueprint_name >]);
180                const GLOBAL_TYPE_NAME: &'static str = stringify!([< Global $blueprint_name >]);
181            }
182        }
183    };
184}
185pub(crate) use define_type_marker;
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190    #[cfg(feature = "alloc")]
191    use sbor::prelude::Vec;
192
193    pub const SOME_ADDRESS: PackageAddress =
194        PackageAddress::new_or_panic([EntityType::GlobalPackage as u8; NodeId::LENGTH]);
195
196    define_type_marker!(Some(SOME_ADDRESS), SomeType);
197
198    #[test]
199    fn global_encode_decode() {
200        let addr = ComponentAddress::new_or_panic(
201            [EntityType::GlobalGenericComponent as u8; NodeId::LENGTH],
202        );
203
204        let object = Global::<SomeTypeMarker>::new(addr);
205        let mut buf = Vec::new();
206        let mut encoder = VecEncoder::<ScryptoCustomValueKind>::new(&mut buf, 1);
207        assert!(object.encode_value_kind(&mut encoder).is_ok());
208        assert!(object.encode_body(&mut encoder).is_ok());
209
210        let buf_decode = buf.into_iter().skip(1).collect::<Vec<u8>>(); // skip Global value kind, not used in decode_body_with_value_kind() decoding function
211
212        let mut decoder = VecDecoder::<ScryptoCustomValueKind>::new(&buf_decode, 1);
213        let output = Global::<SomeTypeMarker>::decode_body_with_value_kind(
214            &mut decoder,
215            ComponentAddress::value_kind(),
216        );
217        assert!(output.is_ok());
218
219        let describe = Global::<SomeTypeMarker>::type_data();
220        assert_eq!(
221            describe.kind,
222            TypeKind::Custom(ScryptoCustomTypeKind::Reference)
223        );
224        assert_eq!(
225            describe.metadata.type_name.unwrap().to_string(),
226            "GlobalSomeType"
227        );
228    }
229
230    #[test]
231    fn owned_encode_decode() {
232        let addr = InternalAddress::new_or_panic(
233            [EntityType::InternalGenericComponent as u8; NodeId::LENGTH],
234        );
235
236        let object = Owned::<SomeTypeMarker>::new(addr);
237        let mut buf = Vec::new();
238        let mut encoder = VecEncoder::<ScryptoCustomValueKind>::new(&mut buf, 1);
239        assert!(object.encode_value_kind(&mut encoder).is_ok());
240        assert!(object.encode_body(&mut encoder).is_ok());
241
242        let buf_decode = buf.into_iter().skip(1).collect::<Vec<u8>>(); // skip Owned value kind, not used in decode_body_with_value_kind() decoding function
243
244        let mut decoder = VecDecoder::<ScryptoCustomValueKind>::new(&buf_decode, 1);
245        let output = Owned::<SomeTypeMarker>::decode_body_with_value_kind(
246            &mut decoder,
247            InternalAddress::value_kind(),
248        );
249        assert_eq!(output.err(), None);
250
251        let describe = Owned::<SomeTypeMarker>::type_data();
252        assert_eq!(describe.kind, TypeKind::Custom(ScryptoCustomTypeKind::Own));
253        assert_eq!(
254            describe.metadata.type_name.unwrap().to_string(),
255            "OwnedSomeType"
256        );
257    }
258}