subxt_core/blocks/
extrinsic_transaction_extensions.rs

1// Copyright 2019-2024 Parity Technologies (UK) Ltd.
2// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3// see LICENSE for license details.
4
5use crate::config::TransactionExtension;
6use crate::config::transaction_extensions::{
7    ChargeAssetTxPayment, ChargeTransactionPayment, CheckNonce,
8};
9use crate::dynamic::Value;
10use crate::{Metadata, config::Config, error::Error};
11use frame_decode::extrinsics::ExtrinsicExtensions;
12use scale_decode::DecodeAsType;
13
14/// The signed extensions of an extrinsic.
15#[derive(Debug, Clone)]
16pub struct ExtrinsicTransactionExtensions<'a, T: Config> {
17    bytes: &'a [u8],
18    metadata: &'a Metadata,
19    decoded_info: &'a ExtrinsicExtensions<'static, u32>,
20    _marker: core::marker::PhantomData<T>,
21}
22
23impl<'a, T: Config> ExtrinsicTransactionExtensions<'a, T> {
24    pub(crate) fn new(
25        bytes: &'a [u8],
26        metadata: &'a Metadata,
27        decoded_info: &'a ExtrinsicExtensions<'static, u32>,
28    ) -> Self {
29        Self {
30            bytes,
31            metadata,
32            decoded_info,
33            _marker: core::marker::PhantomData,
34        }
35    }
36
37    /// Returns an iterator over each of the signed extension details of the extrinsic.
38    pub fn iter(&self) -> impl Iterator<Item = ExtrinsicTransactionExtension<'a, T>> + use<'a, T> {
39        self.decoded_info
40            .iter()
41            .map(|s| ExtrinsicTransactionExtension {
42                bytes: &self.bytes[s.range()],
43                ty_id: *s.ty(),
44                identifier: s.name(),
45                metadata: self.metadata,
46                _marker: core::marker::PhantomData,
47            })
48    }
49
50    /// Searches through all signed extensions to find a specific one.
51    /// If the Signed Extension is not found `Ok(None)` is returned.
52    /// If the Signed Extension is found but decoding failed `Err(_)` is returned.
53    pub fn find<S: TransactionExtension<T>>(&self) -> Result<Option<S::Decoded>, Error> {
54        for ext in self.iter() {
55            match ext.as_signed_extension::<S>() {
56                // We found a match; return it:
57                Ok(Some(e)) => return Ok(Some(e)),
58                // No error, but no match either; next!
59                Ok(None) => continue,
60                // Error? return it
61                Err(e) => return Err(e),
62            }
63        }
64        Ok(None)
65    }
66
67    /// The tip of an extrinsic, extracted from the ChargeTransactionPayment or ChargeAssetTxPayment
68    /// signed extension, depending on which is present.
69    ///
70    /// Returns `None` if  `tip` was not found or decoding failed.
71    pub fn tip(&self) -> Option<u128> {
72        // Note: the overhead of iterating multiple time should be negligible.
73        self.find::<ChargeTransactionPayment>()
74            .ok()
75            .flatten()
76            .map(|e| e.tip())
77            .or_else(|| {
78                self.find::<ChargeAssetTxPayment<T>>()
79                    .ok()
80                    .flatten()
81                    .map(|e| e.tip())
82            })
83    }
84
85    /// The nonce of the account that submitted the extrinsic, extracted from the CheckNonce signed extension.
86    ///
87    /// Returns `None` if `nonce` was not found or decoding failed.
88    pub fn nonce(&self) -> Option<u64> {
89        self.find::<CheckNonce>().ok()?
90    }
91}
92
93/// A single signed extension
94#[derive(Debug, Clone)]
95pub struct ExtrinsicTransactionExtension<'a, T: Config> {
96    bytes: &'a [u8],
97    ty_id: u32,
98    identifier: &'a str,
99    metadata: &'a Metadata,
100    _marker: core::marker::PhantomData<T>,
101}
102
103impl<'a, T: Config> ExtrinsicTransactionExtension<'a, T> {
104    /// The bytes representing this signed extension.
105    pub fn bytes(&self) -> &'a [u8] {
106        self.bytes
107    }
108
109    /// The name of the signed extension.
110    pub fn name(&self) -> &'a str {
111        self.identifier
112    }
113
114    /// The type id of the signed extension.
115    pub fn type_id(&self) -> u32 {
116        self.ty_id
117    }
118
119    /// Signed Extension as a [`scale_value::Value`]
120    pub fn value(&self) -> Result<Value<u32>, Error> {
121        let value = scale_value::scale::decode_as_type(
122            &mut &self.bytes[..],
123            self.ty_id,
124            self.metadata.types(),
125        )?;
126        Ok(value)
127    }
128
129    /// Decodes the bytes of this Signed Extension into its associated `Decoded` type.
130    /// Returns `Ok(None)` if the data we have doesn't match the Signed Extension we're asking to
131    /// decode with.
132    pub fn as_signed_extension<S: TransactionExtension<T>>(
133        &self,
134    ) -> Result<Option<S::Decoded>, Error> {
135        if !S::matches(self.identifier, self.ty_id, self.metadata.types()) {
136            return Ok(None);
137        }
138        self.as_type::<S::Decoded>().map(Some)
139    }
140
141    fn as_type<E: DecodeAsType>(&self) -> Result<E, Error> {
142        let value = E::decode_as_type(&mut &self.bytes[..], self.ty_id, self.metadata.types())?;
143        Ok(value)
144    }
145}