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 == "VerifySignature"
96 }
97}
98
99#[derive(codec::Encode, codec::Decode)]
102pub enum VerifySignatureDetails<T: Config> {
103 Signed {
105 signature: T::Signature,
107 account: T::AccountId,
109 },
110 Disabled,
112}
113
114pub struct CheckMetadataHash {
116 }
119
120impl<T: Config> ExtrinsicParams<T> for CheckMetadataHash {
121 type Params = ();
122
123 fn new(_client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
124 Ok(CheckMetadataHash {})
125 }
126}
127
128impl ExtrinsicParamsEncoder for CheckMetadataHash {
129 fn encode_value_to(&self, v: &mut Vec<u8>) {
130 0u8.encode_to(v);
133 }
134 fn encode_implicit_to(&self, v: &mut Vec<u8>) {
135 None::<()>.encode_to(v);
137 }
138}
139
140impl<T: Config> TransactionExtension<T> for CheckMetadataHash {
141 type Decoded = CheckMetadataHashMode;
142 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
143 identifier == "CheckMetadataHash"
144 }
145}
146
147#[derive(Copy, Clone, Debug, DecodeAsType)]
152pub enum CheckMetadataHashMode {
153 Disabled,
155 Enabled,
157}
158
159impl CheckMetadataHashMode {
160 pub fn is_enabled(&self) -> bool {
162 match self {
163 CheckMetadataHashMode::Disabled => false,
164 CheckMetadataHashMode::Enabled => true,
165 }
166 }
167}
168
169pub struct CheckSpecVersion(u32);
171
172impl<T: Config> ExtrinsicParams<T> for CheckSpecVersion {
173 type Params = ();
174
175 fn new(client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
176 Ok(CheckSpecVersion(client.runtime_version.spec_version))
177 }
178}
179
180impl ExtrinsicParamsEncoder for CheckSpecVersion {
181 fn encode_implicit_to(&self, v: &mut Vec<u8>) {
182 self.0.encode_to(v);
183 }
184}
185
186impl<T: Config> TransactionExtension<T> for CheckSpecVersion {
187 type Decoded = ();
188 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
189 identifier == "CheckSpecVersion"
190 }
191}
192
193pub struct CheckNonce(u64);
195
196impl<T: Config> ExtrinsicParams<T> for CheckNonce {
197 type Params = CheckNonceParams;
198
199 fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
200 Ok(CheckNonce(params.0.unwrap_or(0)))
201 }
202}
203
204impl ExtrinsicParamsEncoder for CheckNonce {
205 fn encode_value_to(&self, v: &mut Vec<u8>) {
206 Compact(self.0).encode_to(v);
207 }
208}
209
210impl<T: Config> TransactionExtension<T> for CheckNonce {
211 type Decoded = u64;
212 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
213 identifier == "CheckNonce"
214 }
215}
216
217#[derive(Debug, Clone, Default)]
219pub struct CheckNonceParams(Option<u64>);
220
221impl CheckNonceParams {
222 pub fn from_chain() -> Self {
224 Self(None)
225 }
226 pub fn with_nonce(nonce: u64) -> Self {
228 Self(Some(nonce))
229 }
230}
231
232impl<T: Config> Params<T> for CheckNonceParams {
233 fn inject_account_nonce(&mut self, nonce: u64) {
234 if self.0.is_none() {
235 self.0 = Some(nonce)
236 }
237 }
238}
239
240pub struct CheckTxVersion(u32);
242
243impl<T: Config> ExtrinsicParams<T> for CheckTxVersion {
244 type Params = ();
245
246 fn new(client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
247 Ok(CheckTxVersion(client.runtime_version.transaction_version))
248 }
249}
250
251impl ExtrinsicParamsEncoder for CheckTxVersion {
252 fn encode_implicit_to(&self, v: &mut Vec<u8>) {
253 self.0.encode_to(v);
254 }
255}
256
257impl<T: Config> TransactionExtension<T> for CheckTxVersion {
258 type Decoded = ();
259 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
260 identifier == "CheckTxVersion"
261 }
262}
263
264pub struct CheckGenesis<T: Config>(HashFor<T>);
266
267impl<T: Config> ExtrinsicParams<T> for CheckGenesis<T> {
268 type Params = ();
269
270 fn new(client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
271 Ok(CheckGenesis(client.genesis_hash))
272 }
273}
274
275impl<T: Config> ExtrinsicParamsEncoder for CheckGenesis<T> {
276 fn encode_implicit_to(&self, v: &mut Vec<u8>) {
277 self.0.encode_to(v);
278 }
279}
280
281impl<T: Config> TransactionExtension<T> for CheckGenesis<T> {
282 type Decoded = ();
283 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
284 identifier == "CheckGenesis"
285 }
286}
287
288pub struct CheckMortality<T: Config> {
290 params: CheckMortalityParamsInner<T>,
291 genesis_hash: HashFor<T>,
292}
293
294impl<T: Config> ExtrinsicParams<T> for CheckMortality<T> {
295 type Params = CheckMortalityParams<T>;
296
297 fn new(client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
298 if matches!(¶ms.0, CheckMortalityParamsInner::MortalForBlocks(_)) {
302 return Err(ExtrinsicParamsError::custom(
303 "CheckMortality: We cannot construct an offline extrinsic with only the number of blocks it is mortal for. Use mortal_from_unchecked instead.",
304 ));
305 }
306
307 Ok(CheckMortality {
308 params: params.0,
311 genesis_hash: client.genesis_hash,
312 })
313 }
314}
315
316impl<T: Config> ExtrinsicParamsEncoder for CheckMortality<T> {
317 fn encode_value_to(&self, v: &mut Vec<u8>) {
318 match &self.params {
319 CheckMortalityParamsInner::MortalFromBlock {
320 for_n_blocks,
321 from_block_n,
322 ..
323 } => {
324 Era::mortal(*for_n_blocks, *from_block_n).encode_to(v);
325 }
326 _ => {
327 Era::Immortal.encode_to(v);
331 }
332 }
333 }
334 fn encode_implicit_to(&self, v: &mut Vec<u8>) {
335 match &self.params {
336 CheckMortalityParamsInner::MortalFromBlock {
337 from_block_hash, ..
338 } => {
339 from_block_hash.encode_to(v);
340 }
341 _ => {
342 self.genesis_hash.encode_to(v);
343 }
344 }
345 }
346}
347
348impl<T: Config> TransactionExtension<T> for CheckMortality<T> {
349 type Decoded = Era;
350 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
351 identifier == "CheckMortality"
352 }
353}
354
355pub struct CheckMortalityParams<T: Config>(CheckMortalityParamsInner<T>);
357
358enum CheckMortalityParamsInner<T: Config> {
359 Immortal,
361 MortalForBlocks(u64),
364 MortalForBlocksOrImmortalIfNotPossible(u64),
367 MortalFromBlock {
369 for_n_blocks: u64,
370 from_block_n: u64,
371 from_block_hash: HashFor<T>,
372 },
373}
374
375impl<T: Config> Default for CheckMortalityParams<T> {
376 fn default() -> Self {
377 CheckMortalityParams(CheckMortalityParamsInner::MortalForBlocksOrImmortalIfNotPossible(32))
379 }
380}
381
382impl<T: Config> CheckMortalityParams<T> {
383 pub fn mortal(for_n_blocks: u64) -> Self {
385 Self(CheckMortalityParamsInner::MortalForBlocks(for_n_blocks))
386 }
387
388 pub fn mortal_from_unchecked(
392 for_n_blocks: u64,
393 from_block_n: u64,
394 from_block_hash: HashFor<T>,
395 ) -> Self {
396 Self(CheckMortalityParamsInner::MortalFromBlock {
397 for_n_blocks,
398 from_block_n,
399 from_block_hash,
400 })
401 }
402 pub fn immortal() -> Self {
404 Self(CheckMortalityParamsInner::Immortal)
405 }
406}
407
408impl<T: Config> Params<T> for CheckMortalityParams<T> {
409 fn inject_block(&mut self, from_block_n: u64, from_block_hash: HashFor<T>) {
410 match &self.0 {
411 CheckMortalityParamsInner::MortalForBlocks(n)
412 | CheckMortalityParamsInner::MortalForBlocksOrImmortalIfNotPossible(n) => {
413 self.0 = CheckMortalityParamsInner::MortalFromBlock {
414 for_n_blocks: *n,
415 from_block_n,
416 from_block_hash,
417 }
418 }
419 _ => {
420 }
422 }
423 }
424}
425
426#[derive(DecodeAsType)]
428#[derive_where(Clone, Debug; T::AssetId)]
429#[decode_as_type(trait_bounds = "T::AssetId: DecodeAsType")]
430pub struct ChargeAssetTxPayment<T: Config> {
431 tip: Compact<u128>,
432 asset_id: Option<T::AssetId>,
433}
434
435impl<T: Config> ChargeAssetTxPayment<T> {
436 pub fn tip(&self) -> u128 {
438 self.tip.0
439 }
440
441 pub fn asset_id(&self) -> Option<&T::AssetId> {
443 self.asset_id.as_ref()
444 }
445}
446
447impl<T: Config> ExtrinsicParams<T> for ChargeAssetTxPayment<T> {
448 type Params = ChargeAssetTxPaymentParams<T>;
449
450 fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
451 Ok(ChargeAssetTxPayment {
452 tip: Compact(params.tip),
453 asset_id: params.asset_id,
454 })
455 }
456}
457
458impl<T: Config> ExtrinsicParamsEncoder for ChargeAssetTxPayment<T> {
459 fn encode_value_to(&self, v: &mut Vec<u8>) {
460 (self.tip, &self.asset_id).encode_to(v);
461 }
462}
463
464impl<T: Config> TransactionExtension<T> for ChargeAssetTxPayment<T> {
465 type Decoded = Self;
466 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
467 identifier == "ChargeAssetTxPayment"
468 }
469}
470
471pub struct ChargeAssetTxPaymentParams<T: Config> {
473 tip: u128,
474 asset_id: Option<T::AssetId>,
475}
476
477impl<T: Config> Default for ChargeAssetTxPaymentParams<T> {
478 fn default() -> Self {
479 ChargeAssetTxPaymentParams {
480 tip: Default::default(),
481 asset_id: Default::default(),
482 }
483 }
484}
485
486impl<T: Config> ChargeAssetTxPaymentParams<T> {
487 pub fn no_tip() -> Self {
489 ChargeAssetTxPaymentParams {
490 tip: 0,
491 asset_id: None,
492 }
493 }
494 pub fn tip(tip: u128) -> Self {
496 ChargeAssetTxPaymentParams {
497 tip,
498 asset_id: None,
499 }
500 }
501 pub fn tip_of(tip: u128, asset_id: T::AssetId) -> Self {
503 ChargeAssetTxPaymentParams {
504 tip,
505 asset_id: Some(asset_id),
506 }
507 }
508}
509
510impl<T: Config> Params<T> for ChargeAssetTxPaymentParams<T> {}
511
512#[derive(Clone, Debug, DecodeAsType)]
514pub struct ChargeTransactionPayment {
515 tip: Compact<u128>,
516}
517
518impl ChargeTransactionPayment {
519 pub fn tip(&self) -> u128 {
521 self.tip.0
522 }
523}
524
525impl<T: Config> ExtrinsicParams<T> for ChargeTransactionPayment {
526 type Params = ChargeTransactionPaymentParams;
527
528 fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
529 Ok(ChargeTransactionPayment {
530 tip: Compact(params.tip),
531 })
532 }
533}
534
535impl ExtrinsicParamsEncoder for ChargeTransactionPayment {
536 fn encode_value_to(&self, v: &mut Vec<u8>) {
537 self.tip.encode_to(v);
538 }
539}
540
541impl<T: Config> TransactionExtension<T> for ChargeTransactionPayment {
542 type Decoded = Self;
543 fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
544 identifier == "ChargeTransactionPayment"
545 }
546}
547
548#[derive(Default)]
550pub struct ChargeTransactionPaymentParams {
551 tip: u128,
552}
553
554impl ChargeTransactionPaymentParams {
555 pub fn no_tip() -> Self {
557 ChargeTransactionPaymentParams { tip: 0 }
558 }
559 pub fn tip(tip: u128) -> Self {
561 ChargeTransactionPaymentParams { tip }
562 }
563}
564
565impl<T: Config> Params<T> for ChargeTransactionPaymentParams {}
566
567pub struct AnyOf<T, Params> {
571 params: Vec<Box<dyn ExtrinsicParamsEncoder + Send + 'static>>,
572 _marker: core::marker::PhantomData<(T, Params)>,
573}
574
575macro_rules! impl_tuples {
576 ($($ident:ident $index:tt),+) => {
577 impl <T, $($ident),+> ExtrinsicParams<T> for AnyOf<T, ($($ident,)+)>
581 where
582 T: Config,
583 $($ident: TransactionExtension<T>,)+
584 {
585 type Params = ($($ident::Params,)+);
586
587 fn new(
588 client: &ClientState<T>,
589 params: Self::Params,
590 ) -> Result<Self, ExtrinsicParamsError> {
591 let metadata = &client.metadata;
592 let types = metadata.types();
593
594 let mut exts_by_index = HashMap::new();
597 $({
598 for (idx, e) in metadata.extrinsic().transaction_extensions_to_use_for_encoding().enumerate() {
599 if exts_by_index.contains_key(&idx) {
601 continue
602 }
603 if $ident::matches(e.identifier(), e.extra_ty(), types) {
605 let ext = $ident::new(client, params.$index)?;
606 let boxed_ext: Box<dyn ExtrinsicParamsEncoder + Send + 'static> = Box::new(ext);
607 exts_by_index.insert(idx, boxed_ext);
608 break
609 }
610 }
611 })+
612
613 let mut params = Vec::new();
615 for (idx, e) in metadata.extrinsic().transaction_extensions_to_use_for_encoding().enumerate() {
616 let Some(ext) = exts_by_index.remove(&idx) else {
617 if is_type_empty(e.extra_ty(), types) {
618 continue
619 } else {
620 return Err(ExtrinsicParamsError::UnknownTransactionExtension(e.identifier().to_owned()));
621 }
622 };
623 params.push(ext);
624 }
625
626 Ok(AnyOf {
627 params,
628 _marker: core::marker::PhantomData
629 })
630 }
631 }
632
633 impl <T, $($ident),+> ExtrinsicParamsEncoder for AnyOf<T, ($($ident,)+)>
634 where
635 T: Config,
636 $($ident: TransactionExtension<T>,)+
637 {
638 fn encode_value_to(&self, v: &mut Vec<u8>) {
639 for ext in &self.params {
640 ext.encode_value_to(v);
641 }
642 }
643 fn encode_signer_payload_value_to(&self, v: &mut Vec<u8>) {
644 for ext in &self.params {
645 ext.encode_signer_payload_value_to(v);
646 }
647 }
648 fn encode_implicit_to(&self, v: &mut Vec<u8>) {
649 for ext in &self.params {
650 ext.encode_implicit_to(v);
651 }
652 }
653 fn inject_signature(&mut self, account_id: &dyn Any, signature: &dyn Any) {
654 for ext in &mut self.params {
655 ext.inject_signature(account_id, signature);
656 }
657 }
658 }
659 }
660}
661
662#[rustfmt::skip]
663const _: () = {
664 impl_tuples!(A 0);
665 impl_tuples!(A 0, B 1);
666 impl_tuples!(A 0, B 1, C 2);
667 impl_tuples!(A 0, B 1, C 2, D 3);
668 impl_tuples!(A 0, B 1, C 2, D 3, E 4);
669 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5);
670 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6);
671 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7);
672 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8);
673 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9);
674 impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10);
675 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);
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, L 11, M 12);
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, M 12, N 13);
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, N 13, O 14);
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, O 14, P 15);
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, P 15, Q 16);
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, Q 16, R 17);
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, R 17, S 18);
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, S 18, U 19);
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, U 19, V 20);
685};
686
687fn is_type_empty(type_id: u32, types: &scale_info::PortableRegistry) -> bool {
690 let Some(ty) = types.resolve(type_id) else {
691 return false;
693 };
694
695 use scale_info::TypeDef;
696 match &ty.type_def {
697 TypeDef::Composite(c) => c.fields.iter().all(|f| is_type_empty(f.ty.id, types)),
698 TypeDef::Array(a) => a.len == 0 || is_type_empty(a.type_param.id, types),
699 TypeDef::Tuple(t) => t.fields.iter().all(|f| is_type_empty(f.id, types)),
700 TypeDef::BitSequence(_)
702 | TypeDef::Variant(_)
703 | TypeDef::Sequence(_)
704 | TypeDef::Compact(_)
705 | TypeDef::Primitive(_) => false,
706 }
707}