1use super::extrinsic_params::ExtrinsicParams;
11use crate::client::ClientState;
12use crate::config::ExtrinsicParamsEncoder;
13use crate::config::{Config, HashFor};
14use crate::error::ExtrinsicParamsError;
15use crate::utils::{Era, Static};
16use alloc::borrow::ToOwned;
17use alloc::boxed::Box;
18use alloc::vec::Vec;
19use codec::{Compact, Encode};
20use core::any::Any;
21use core::fmt::Debug;
22use derive_where::derive_where;
23use hashbrown::HashMap;
24use scale_decode::DecodeAsType;
25use scale_info::PortableRegistry;
26
27pub use super::extrinsic_params::Params;
29
30pub trait TransactionExtension<T: Config>: ExtrinsicParams<T> {
34 type Decoded: DecodeAsType;
38
39 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool;
43}
44
45pub struct VerifySignature<T: Config>(VerifySignatureDetails<T>);
50
51impl<T: Config> ExtrinsicParams<T> for VerifySignature<T> {
52 type Params = ();
53
54 fn new(_client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
55 Ok(VerifySignature(VerifySignatureDetails::Disabled))
56 }
57}
58
59impl<T: Config> ExtrinsicParamsEncoder for VerifySignature<T> {
60 fn encode_value_to(&self, v: &mut Vec<u8>) {
61 self.0.encode_to(v);
62 }
63 fn encode_signer_payload_value_to(&self, v: &mut Vec<u8>) {
64 v.clear();
68 }
69 fn encode_implicit_to(&self, v: &mut Vec<u8>) {
70 v.clear();
74 }
75
76 fn inject_signature(&mut self, account: &dyn Any, signature: &dyn Any) {
77 let account = account
79 .downcast_ref::<T::AccountId>()
80 .expect("A T::AccountId should have been provided")
81 .clone();
82 let signature = signature
83 .downcast_ref::<T::Signature>()
84 .expect("A T::Signature should have been provided")
85 .clone();
86
87 self.0 = VerifySignatureDetails::Signed { signature, account }
89 }
90}
91
92impl<T: Config> TransactionExtension<T> for VerifySignature<T> {
93 type Decoded = Static<VerifySignatureDetails<T>>;
94 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
95 identifier == "VerifyMultiSignature" || identifier == "verifySignature"
98 }
99}
100
101#[derive(codec::Encode, codec::Decode)]
104pub enum VerifySignatureDetails<T: Config> {
105 Signed {
107 signature: T::Signature,
109 account: T::AccountId,
111 },
112 Disabled,
114}
115
116pub struct CheckMetadataHash {
118 }
121
122impl<T: Config> ExtrinsicParams<T> for CheckMetadataHash {
123 type Params = ();
124
125 fn new(_client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
126 Ok(CheckMetadataHash {})
127 }
128}
129
130impl ExtrinsicParamsEncoder for CheckMetadataHash {
131 fn encode_value_to(&self, v: &mut Vec<u8>) {
132 0u8.encode_to(v);
135 }
136 fn encode_implicit_to(&self, v: &mut Vec<u8>) {
137 None::<()>.encode_to(v);
139 }
140}
141
142impl<T: Config> TransactionExtension<T> for CheckMetadataHash {
143 type Decoded = CheckMetadataHashMode;
144 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
145 identifier == "CheckMetadataHash"
146 }
147}
148
149#[derive(Copy, Clone, Debug, DecodeAsType)]
154pub enum CheckMetadataHashMode {
155 Disabled,
157 Enabled,
159}
160
161impl CheckMetadataHashMode {
162 pub fn is_enabled(&self) -> bool {
164 match self {
165 CheckMetadataHashMode::Disabled => false,
166 CheckMetadataHashMode::Enabled => true,
167 }
168 }
169}
170
171pub struct CheckSpecVersion(u32);
173
174impl<T: Config> ExtrinsicParams<T> for CheckSpecVersion {
175 type Params = ();
176
177 fn new(client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
178 Ok(CheckSpecVersion(client.runtime_version.spec_version))
179 }
180}
181
182impl ExtrinsicParamsEncoder for CheckSpecVersion {
183 fn encode_implicit_to(&self, v: &mut Vec<u8>) {
184 self.0.encode_to(v);
185 }
186}
187
188impl<T: Config> TransactionExtension<T> for CheckSpecVersion {
189 type Decoded = ();
190 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
191 identifier == "CheckSpecVersion"
192 }
193}
194
195pub struct CheckNonce(u64);
197
198impl<T: Config> ExtrinsicParams<T> for CheckNonce {
199 type Params = CheckNonceParams;
200
201 fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
202 Ok(CheckNonce(params.0.unwrap_or(0)))
203 }
204}
205
206impl ExtrinsicParamsEncoder for CheckNonce {
207 fn encode_value_to(&self, v: &mut Vec<u8>) {
208 Compact(self.0).encode_to(v);
209 }
210}
211
212impl<T: Config> TransactionExtension<T> for CheckNonce {
213 type Decoded = u64;
214 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
215 identifier == "CheckNonce"
216 }
217}
218
219#[derive(Debug, Clone, Default)]
221pub struct CheckNonceParams(Option<u64>);
222
223impl CheckNonceParams {
224 pub fn from_chain() -> Self {
226 Self(None)
227 }
228 pub fn with_nonce(nonce: u64) -> Self {
230 Self(Some(nonce))
231 }
232}
233
234impl<T: Config> Params<T> for CheckNonceParams {
235 fn inject_account_nonce(&mut self, nonce: u64) {
236 if self.0.is_none() {
237 self.0 = Some(nonce)
238 }
239 }
240}
241
242pub struct CheckTxVersion(u32);
244
245impl<T: Config> ExtrinsicParams<T> for CheckTxVersion {
246 type Params = ();
247
248 fn new(client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
249 Ok(CheckTxVersion(client.runtime_version.transaction_version))
250 }
251}
252
253impl ExtrinsicParamsEncoder for CheckTxVersion {
254 fn encode_implicit_to(&self, v: &mut Vec<u8>) {
255 self.0.encode_to(v);
256 }
257}
258
259impl<T: Config> TransactionExtension<T> for CheckTxVersion {
260 type Decoded = ();
261 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
262 identifier == "CheckTxVersion"
263 }
264}
265
266pub struct CheckGenesis<T: Config>(HashFor<T>);
268
269impl<T: Config> ExtrinsicParams<T> for CheckGenesis<T> {
270 type Params = ();
271
272 fn new(client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
273 Ok(CheckGenesis(client.genesis_hash))
274 }
275}
276
277impl<T: Config> ExtrinsicParamsEncoder for CheckGenesis<T> {
278 fn encode_implicit_to(&self, v: &mut Vec<u8>) {
279 self.0.encode_to(v);
280 }
281}
282
283impl<T: Config> TransactionExtension<T> for CheckGenesis<T> {
284 type Decoded = ();
285 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
286 identifier == "CheckGenesis"
287 }
288}
289
290pub struct CheckMortality<T: Config> {
292 params: CheckMortalityParamsInner<T>,
293 genesis_hash: HashFor<T>,
294}
295
296impl<T: Config> ExtrinsicParams<T> for CheckMortality<T> {
297 type Params = CheckMortalityParams<T>;
298
299 fn new(client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
300 if matches!(¶ms.0, CheckMortalityParamsInner::MortalForBlocks(_)) {
304 return Err(ExtrinsicParamsError::custom(
305 "CheckMortality: We cannot construct an offline extrinsic with only the number of blocks it is mortal for. Use mortal_from_unchecked instead.",
306 ));
307 }
308
309 Ok(CheckMortality {
310 params: params.0,
313 genesis_hash: client.genesis_hash,
314 })
315 }
316}
317
318impl<T: Config> ExtrinsicParamsEncoder for CheckMortality<T> {
319 fn encode_value_to(&self, v: &mut Vec<u8>) {
320 match &self.params {
321 CheckMortalityParamsInner::MortalFromBlock {
322 for_n_blocks,
323 from_block_n,
324 ..
325 } => {
326 Era::mortal(*for_n_blocks, *from_block_n).encode_to(v);
327 }
328 _ => {
329 Era::Immortal.encode_to(v);
333 }
334 }
335 }
336 fn encode_implicit_to(&self, v: &mut Vec<u8>) {
337 match &self.params {
338 CheckMortalityParamsInner::MortalFromBlock {
339 from_block_hash, ..
340 } => {
341 from_block_hash.encode_to(v);
342 }
343 _ => {
344 self.genesis_hash.encode_to(v);
345 }
346 }
347 }
348}
349
350impl<T: Config> TransactionExtension<T> for CheckMortality<T> {
351 type Decoded = Era;
352 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
353 identifier == "CheckMortality"
354 }
355}
356
357pub struct CheckMortalityParams<T: Config>(CheckMortalityParamsInner<T>);
359
360enum CheckMortalityParamsInner<T: Config> {
361 Immortal,
363 MortalForBlocks(u64),
366 MortalForBlocksOrImmortalIfNotPossible(u64),
369 MortalFromBlock {
371 for_n_blocks: u64,
372 from_block_n: u64,
373 from_block_hash: HashFor<T>,
374 },
375}
376
377impl<T: Config> Default for CheckMortalityParams<T> {
378 fn default() -> Self {
379 CheckMortalityParams(CheckMortalityParamsInner::MortalForBlocksOrImmortalIfNotPossible(32))
381 }
382}
383
384impl<T: Config> CheckMortalityParams<T> {
385 pub fn mortal(for_n_blocks: u64) -> Self {
387 Self(CheckMortalityParamsInner::MortalForBlocks(for_n_blocks))
388 }
389
390 pub fn mortal_from_unchecked(
394 for_n_blocks: u64,
395 from_block_n: u64,
396 from_block_hash: HashFor<T>,
397 ) -> Self {
398 Self(CheckMortalityParamsInner::MortalFromBlock {
399 for_n_blocks,
400 from_block_n,
401 from_block_hash,
402 })
403 }
404 pub fn immortal() -> Self {
406 Self(CheckMortalityParamsInner::Immortal)
407 }
408}
409
410impl<T: Config> Params<T> for CheckMortalityParams<T> {
411 fn inject_block(&mut self, from_block_n: u64, from_block_hash: HashFor<T>) {
412 match &self.0 {
413 CheckMortalityParamsInner::MortalForBlocks(n)
414 | CheckMortalityParamsInner::MortalForBlocksOrImmortalIfNotPossible(n) => {
415 self.0 = CheckMortalityParamsInner::MortalFromBlock {
416 for_n_blocks: *n,
417 from_block_n,
418 from_block_hash,
419 }
420 }
421 _ => {
422 }
424 }
425 }
426}
427
428#[derive(DecodeAsType)]
430#[derive_where(Clone, Debug; T::AssetId)]
431#[decode_as_type(trait_bounds = "T::AssetId: DecodeAsType")]
432pub struct ChargeAssetTxPayment<T: Config> {
433 tip: Compact<u128>,
434 asset_id: Option<T::AssetId>,
435}
436
437impl<T: Config> ChargeAssetTxPayment<T> {
438 pub fn tip(&self) -> u128 {
440 self.tip.0
441 }
442
443 pub fn asset_id(&self) -> Option<&T::AssetId> {
445 self.asset_id.as_ref()
446 }
447}
448
449impl<T: Config> ExtrinsicParams<T> for ChargeAssetTxPayment<T> {
450 type Params = ChargeAssetTxPaymentParams<T>;
451
452 fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
453 Ok(ChargeAssetTxPayment {
454 tip: Compact(params.tip),
455 asset_id: params.asset_id,
456 })
457 }
458}
459
460impl<T: Config> ExtrinsicParamsEncoder for ChargeAssetTxPayment<T> {
461 fn encode_value_to(&self, v: &mut Vec<u8>) {
462 (self.tip, &self.asset_id).encode_to(v);
463 }
464}
465
466impl<T: Config> TransactionExtension<T> for ChargeAssetTxPayment<T> {
467 type Decoded = Self;
468 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
469 identifier == "ChargeAssetTxPayment"
470 }
471}
472
473pub struct ChargeAssetTxPaymentParams<T: Config> {
475 tip: u128,
476 asset_id: Option<T::AssetId>,
477}
478
479impl<T: Config> Default for ChargeAssetTxPaymentParams<T> {
480 fn default() -> Self {
481 ChargeAssetTxPaymentParams {
482 tip: Default::default(),
483 asset_id: Default::default(),
484 }
485 }
486}
487
488impl<T: Config> ChargeAssetTxPaymentParams<T> {
489 pub fn no_tip() -> Self {
491 ChargeAssetTxPaymentParams {
492 tip: 0,
493 asset_id: None,
494 }
495 }
496 pub fn tip(tip: u128) -> Self {
498 ChargeAssetTxPaymentParams {
499 tip,
500 asset_id: None,
501 }
502 }
503 pub fn tip_of(tip: u128, asset_id: T::AssetId) -> Self {
505 ChargeAssetTxPaymentParams {
506 tip,
507 asset_id: Some(asset_id),
508 }
509 }
510}
511
512impl<T: Config> Params<T> for ChargeAssetTxPaymentParams<T> {}
513
514#[derive(Clone, Debug, DecodeAsType)]
516pub struct ChargeTransactionPayment {
517 tip: Compact<u128>,
518}
519
520impl ChargeTransactionPayment {
521 pub fn tip(&self) -> u128 {
523 self.tip.0
524 }
525}
526
527impl<T: Config> ExtrinsicParams<T> for ChargeTransactionPayment {
528 type Params = ChargeTransactionPaymentParams;
529
530 fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
531 Ok(ChargeTransactionPayment {
532 tip: Compact(params.tip),
533 })
534 }
535}
536
537impl ExtrinsicParamsEncoder for ChargeTransactionPayment {
538 fn encode_value_to(&self, v: &mut Vec<u8>) {
539 self.tip.encode_to(v);
540 }
541}
542
543impl<T: Config> TransactionExtension<T> for ChargeTransactionPayment {
544 type Decoded = Self;
545 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
546 identifier == "ChargeTransactionPayment"
547 }
548}
549
550#[derive(Default)]
552pub struct ChargeTransactionPaymentParams {
553 tip: u128,
554}
555
556impl ChargeTransactionPaymentParams {
557 pub fn no_tip() -> Self {
559 ChargeTransactionPaymentParams { tip: 0 }
560 }
561 pub fn tip(tip: u128) -> Self {
563 ChargeTransactionPaymentParams { tip }
564 }
565}
566
567impl<T: Config> Params<T> for ChargeTransactionPaymentParams {}
568
569pub struct AnyOf<T, Params> {
573 params: Vec<Box<dyn ExtrinsicParamsEncoder + Send + 'static>>,
574 _marker: core::marker::PhantomData<(T, Params)>,
575}
576
577macro_rules! impl_tuples {
578 ($($ident:ident $index:tt),+) => {
579 impl <T, $($ident),+> ExtrinsicParams<T> for AnyOf<T, ($($ident,)+)>
583 where
584 T: Config,
585 $($ident: TransactionExtension<T>,)+
586 {
587 type Params = ($($ident::Params,)+);
588
589 fn new(
590 client: &ClientState<T>,
591 params: Self::Params,
592 ) -> Result<Self, ExtrinsicParamsError> {
593 let metadata = &client.metadata;
594 let types = metadata.types();
595
596 let mut exts_by_index = HashMap::new();
599 $({
600 for (idx, e) in metadata.extrinsic().transaction_extensions_to_use_for_encoding().enumerate() {
601 if exts_by_index.contains_key(&idx) {
603 continue
604 }
605 if $ident::matches(e.identifier(), e.extra_ty(), types) {
607 let ext = $ident::new(client, params.$index)?;
608 let boxed_ext: Box<dyn ExtrinsicParamsEncoder + Send + 'static> = Box::new(ext);
609 exts_by_index.insert(idx, boxed_ext);
610 break
611 }
612 }
613 })+
614
615 let mut params = Vec::new();
617 for (idx, e) in metadata.extrinsic().transaction_extensions_to_use_for_encoding().enumerate() {
618 let Some(ext) = exts_by_index.remove(&idx) else {
619 if is_type_empty(e.extra_ty(), types) {
620 continue
621 } else {
622 return Err(ExtrinsicParamsError::UnknownTransactionExtension(e.identifier().to_owned()));
623 }
624 };
625 params.push(ext);
626 }
627
628 Ok(AnyOf {
629 params,
630 _marker: core::marker::PhantomData
631 })
632 }
633 }
634
635 impl <T, $($ident),+> ExtrinsicParamsEncoder for AnyOf<T, ($($ident,)+)>
636 where
637 T: Config,
638 $($ident: TransactionExtension<T>,)+
639 {
640 fn encode_value_to(&self, v: &mut Vec<u8>) {
641 for ext in &self.params {
642 ext.encode_value_to(v);
643 }
644 }
645 fn encode_signer_payload_value_to(&self, v: &mut Vec<u8>) {
646 for ext in &self.params {
647 ext.encode_signer_payload_value_to(v);
648 }
649 }
650 fn encode_implicit_to(&self, v: &mut Vec<u8>) {
651 for ext in &self.params {
652 ext.encode_implicit_to(v);
653 }
654 }
655 fn inject_signature(&mut self, account_id: &dyn Any, signature: &dyn Any) {
656 for ext in &mut self.params {
657 ext.inject_signature(account_id, signature);
658 }
659 }
660 }
661 }
662}
663
664#[rustfmt::skip]
665const _: () = {
666 impl_tuples!(A 0);
667 impl_tuples!(A 0, B 1);
668 impl_tuples!(A 0, B 1, C 2);
669 impl_tuples!(A 0, B 1, C 2, D 3);
670 impl_tuples!(A 0, B 1, C 2, D 3, E 4);
671 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5);
672 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6);
673 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7);
674 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8);
675 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9);
676 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10);
677 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11);
678 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12);
679 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13);
680 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14);
681 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15);
682 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16);
683 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17);
684 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18);
685 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18, U 19);
686 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18, U 19, V 20);
687};
688
689fn is_type_empty(type_id: u32, types: &scale_info::PortableRegistry) -> bool {
692 let Some(ty) = types.resolve(type_id) else {
693 return false;
695 };
696
697 use scale_info::TypeDef;
698 match &ty.type_def {
699 TypeDef::Composite(c) => c.fields.iter().all(|f| is_type_empty(f.ty.id, types)),
700 TypeDef::Array(a) => a.len == 0 || is_type_empty(a.type_param.id, types),
701 TypeDef::Tuple(t) => t.fields.iter().all(|f| is_type_empty(f.id, types)),
702 TypeDef::BitSequence(_)
704 | TypeDef::Variant(_)
705 | TypeDef::Sequence(_)
706 | TypeDef::Compact(_)
707 | TypeDef::Primitive(_) => false,
708 }
709}