subxt_core/custom_values/
mod.rs1pub mod address;
34
35use crate::utils::Yes;
36use crate::{Error, Metadata, error::MetadataError, metadata::DecodeWithMetadata};
37use address::Address;
38use alloc::vec::Vec;
39
40pub fn validate<Addr: Address + ?Sized>(address: &Addr, metadata: &Metadata) -> Result<(), Error> {
44 if let Some(actual_hash) = address.validation_hash() {
45 let custom = metadata.custom();
46 let custom_value = custom
47 .get(address.name())
48 .ok_or_else(|| MetadataError::CustomValueNameNotFound(address.name().into()))?;
49 let expected_hash = custom_value.hash();
50 if actual_hash != expected_hash {
51 return Err(MetadataError::IncompatibleCodegen.into());
52 }
53 }
54 if metadata.custom().get(address.name()).is_none() {
55 return Err(MetadataError::IncompatibleCodegen.into());
56 }
57 Ok(())
58}
59
60pub fn get<Addr: Address<IsDecodable = Yes> + ?Sized>(
63 address: &Addr,
64 metadata: &Metadata,
65) -> Result<Addr::Target, Error> {
66 validate(address, metadata)?;
68
69 let custom_value = metadata.custom_value_by_name_err(address.name())?;
71 let value = <Addr::Target as DecodeWithMetadata>::decode_with_metadata(
72 &mut custom_value.bytes(),
73 custom_value.type_id(),
74 metadata,
75 )?;
76 Ok(value)
77}
78
79pub fn get_bytes<Addr: Address + ?Sized>(
81 address: &Addr,
82 metadata: &Metadata,
83) -> Result<Vec<u8>, Error> {
84 validate(address, metadata)?;
86
87 let custom_value = metadata.custom_value_by_name_err(address.name())?;
89 Ok(custom_value.bytes().to_vec())
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 use alloc::collections::BTreeMap;
97 use codec::Encode;
98 use scale_decode::DecodeAsType;
99 use scale_info::TypeInfo;
100 use scale_info::form::PortableForm;
101
102 use alloc::borrow::ToOwned;
103 use alloc::string::String;
104 use alloc::vec;
105
106 use crate::custom_values;
107
108 #[derive(Debug, Clone, PartialEq, Eq, Encode, TypeInfo, DecodeAsType)]
109 pub struct Person {
110 age: u16,
111 name: String,
112 }
113
114 fn mock_metadata() -> Metadata {
115 let person_ty = scale_info::MetaType::new::<Person>();
116 let unit = scale_info::MetaType::new::<()>();
117 let mut types = scale_info::Registry::new();
118 let person_ty_id = types.register_type(&person_ty);
119 let unit_id = types.register_type(&unit);
120 let types: scale_info::PortableRegistry = types.into();
121
122 let person = Person {
123 age: 42,
124 name: "Neo".into(),
125 };
126
127 let person_value_metadata: frame_metadata::v15::CustomValueMetadata<PortableForm> =
128 frame_metadata::v15::CustomValueMetadata {
129 ty: person_ty_id,
130 value: person.encode(),
131 };
132
133 let frame_metadata = frame_metadata::v15::RuntimeMetadataV15 {
134 types,
135 pallets: vec![],
136 extrinsic: frame_metadata::v15::ExtrinsicMetadata {
137 version: 0,
138 address_ty: unit_id,
139 call_ty: unit_id,
140 signature_ty: unit_id,
141 extra_ty: unit_id,
142 signed_extensions: vec![],
143 },
144 ty: unit_id,
145 apis: vec![],
146 outer_enums: frame_metadata::v15::OuterEnums {
147 call_enum_ty: unit_id,
148 event_enum_ty: unit_id,
149 error_enum_ty: unit_id,
150 },
151 custom: frame_metadata::v15::CustomMetadata {
152 map: BTreeMap::from_iter([("Mr. Robot".to_owned(), person_value_metadata)]),
153 },
154 };
155
156 let metadata: subxt_metadata::Metadata = frame_metadata.try_into().unwrap();
157 Metadata::from(metadata)
158 }
159
160 #[test]
161 fn test_decoding() {
162 let metadata = mock_metadata();
163
164 assert!(custom_values::get("Invalid Address", &metadata).is_err());
165 let person_decoded_value_thunk = custom_values::get("Mr. Robot", &metadata).unwrap();
166 let person: Person = person_decoded_value_thunk.as_type().unwrap();
167 assert_eq!(
168 person,
169 Person {
170 age: 42,
171 name: "Neo".into()
172 }
173 )
174 }
175}