1use crate::Error;
9use crate::error::MetadataError;
10use crate::metadata::Metadata;
11use alloc::borrow::{Cow, ToOwned};
12use alloc::boxed::Box;
13use alloc::string::String;
14
15use alloc::vec::Vec;
16use codec::Encode;
17use scale_encode::EncodeAsFields;
18use scale_value::{Composite, Value, ValueDef, Variant};
19
20pub trait Payload {
23 fn encode_call_data_to(&self, metadata: &Metadata, out: &mut Vec<u8>) -> Result<(), Error>;
25
26 fn encode_call_data(&self, metadata: &Metadata) -> Result<Vec<u8>, Error> {
29 let mut v = Vec::new();
30 self.encode_call_data_to(metadata, &mut v)?;
31 Ok(v)
32 }
33
34 fn validation_details(&self) -> Option<ValidationDetails<'_>> {
38 None
39 }
40}
41
42macro_rules! boxed_payload {
43 ($ty:path) => {
44 impl<T: Payload + ?Sized> Payload for $ty {
45 fn encode_call_data_to(
46 &self,
47 metadata: &Metadata,
48 out: &mut Vec<u8>,
49 ) -> Result<(), Error> {
50 self.as_ref().encode_call_data_to(metadata, out)
51 }
52 fn encode_call_data(&self, metadata: &Metadata) -> Result<Vec<u8>, Error> {
53 self.as_ref().encode_call_data(metadata)
54 }
55 fn validation_details(&self) -> Option<ValidationDetails<'_>> {
56 self.as_ref().validation_details()
57 }
58 }
59 };
60}
61
62boxed_payload!(Box<T>);
63#[cfg(feature = "std")]
64boxed_payload!(std::sync::Arc<T>);
65#[cfg(feature = "std")]
66boxed_payload!(std::rc::Rc<T>);
67
68pub struct ValidationDetails<'a> {
70 pub pallet_name: &'a str,
72 pub call_name: &'a str,
74 pub hash: [u8; 32],
77}
78
79#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
81pub struct DefaultPayload<CallData> {
82 pallet_name: Cow<'static, str>,
83 call_name: Cow<'static, str>,
84 call_data: CallData,
85 validation_hash: Option<[u8; 32]>,
86}
87
88pub type StaticPayload<Calldata> = DefaultPayload<Calldata>;
90pub type DynamicPayload = DefaultPayload<Composite<()>>;
92
93impl<CallData> DefaultPayload<CallData> {
94 pub fn new(
96 pallet_name: impl Into<String>,
97 call_name: impl Into<String>,
98 call_data: CallData,
99 ) -> Self {
100 DefaultPayload {
101 pallet_name: Cow::Owned(pallet_name.into()),
102 call_name: Cow::Owned(call_name.into()),
103 call_data,
104 validation_hash: None,
105 }
106 }
107
108 #[doc(hidden)]
111 pub fn new_static(
112 pallet_name: &'static str,
113 call_name: &'static str,
114 call_data: CallData,
115 validation_hash: [u8; 32],
116 ) -> Self {
117 DefaultPayload {
118 pallet_name: Cow::Borrowed(pallet_name),
119 call_name: Cow::Borrowed(call_name),
120 call_data,
121 validation_hash: Some(validation_hash),
122 }
123 }
124
125 pub fn unvalidated(self) -> Self {
127 Self {
128 validation_hash: None,
129 ..self
130 }
131 }
132
133 pub fn call_data(&self) -> &CallData {
135 &self.call_data
136 }
137
138 pub fn pallet_name(&self) -> &str {
140 &self.pallet_name
141 }
142
143 pub fn call_name(&self) -> &str {
145 &self.call_name
146 }
147}
148
149impl DefaultPayload<Composite<()>> {
150 pub fn into_value(self) -> Value<()> {
154 let call = Value {
155 context: (),
156 value: ValueDef::Variant(Variant {
157 name: self.call_name.into_owned(),
158 values: self.call_data,
159 }),
160 };
161
162 Value::unnamed_variant(self.pallet_name, [call])
163 }
164}
165
166impl<CallData: EncodeAsFields> Payload for DefaultPayload<CallData> {
167 fn encode_call_data_to(&self, metadata: &Metadata, out: &mut Vec<u8>) -> Result<(), Error> {
168 let pallet = metadata.pallet_by_name_err(&self.pallet_name)?;
169 let call = pallet
170 .call_variant_by_name(&self.call_name)
171 .ok_or_else(|| MetadataError::CallNameNotFound((*self.call_name).to_owned()))?;
172
173 let pallet_index = pallet.index();
174 let call_index = call.index;
175
176 pallet_index.encode_to(out);
177 call_index.encode_to(out);
178
179 let mut fields = call
180 .fields
181 .iter()
182 .map(|f| scale_encode::Field::new(f.ty.id, f.name.as_deref()));
183
184 self.call_data
185 .encode_as_fields_to(&mut fields, metadata.types(), out)
186 .expect("The fields are valid types from the metadata, qed;");
187 Ok(())
188 }
189
190 fn validation_details(&self) -> Option<ValidationDetails<'_>> {
191 self.validation_hash.map(|hash| ValidationDetails {
192 pallet_name: &self.pallet_name,
193 call_name: &self.call_name,
194 hash,
195 })
196 }
197}
198
199pub fn dynamic(
202 pallet_name: impl Into<String>,
203 call_name: impl Into<String>,
204 call_data: impl Into<Composite<()>>,
205) -> DynamicPayload {
206 DefaultPayload::new(pallet_name, call_name, call_data.into())
207}