Skip to main content

signet_bundle/send/
decoded.rs

1use alloy::{
2    consensus::{transaction::Recovered, Transaction, TxEnvelope},
3    primitives::{Address, TxHash, U256},
4    serde::OtherFields,
5};
6
7/// Transaction requirement info for a single transaction.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct TxRequirement {
10    /// Signer address
11    pub signer: Address,
12    /// Nonce
13    pub nonce: u64,
14    /// Max spend (max_fee_per_gas * gas_limit) + value
15    pub balance: U256,
16}
17
18/// Version of [`SignetEthBundle`] with decoded transactions.
19///
20/// [`SignetEthBundle`]: crate::send::bundle::SignetEthBundle
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct RecoveredBundle {
23    /// Transactions in this bundle.
24    pub(crate) txs: Vec<Recovered<TxEnvelope>>,
25
26    /// Host transactions to be included in the host bundle.
27    pub(crate) host_txs: Vec<Recovered<TxEnvelope>>,
28
29    /// Block number for which this bundle is valid
30    pub(crate) block_number: u64,
31
32    /// unix timestamp when this bundle becomes active
33    pub(crate) min_timestamp: Option<u64>,
34
35    /// unix timestamp how long this bundle stays valid
36    pub(crate) max_timestamp: Option<u64>,
37
38    /// list of hashes of possibly reverting txs
39    pub(crate) reverting_tx_hashes: Vec<TxHash>,
40
41    /// UUID that can be used to cancel/replace this bundle
42    pub(crate) replacement_uuid: Option<String>,
43
44    /// A list of tx hashes that are allowed to be discarded
45    pub(crate) dropping_tx_hashes: Vec<TxHash>,
46
47    /// The percent that should be refunded to refund recipient
48    pub(crate) refund_percent: Option<u8>,
49
50    /// The address that receives the refund
51    pub(crate) refund_recipient: Option<Address>,
52
53    /// A list of tx hashes used to determine the refund
54    pub(crate) refund_tx_hashes: Vec<TxHash>,
55
56    /// Additional fields that are specific to the builder
57    pub(crate) extra_fields: OtherFields,
58}
59
60impl RecoveredBundle {
61    /// Instantiator. Generally recommend instantiating via conversion from
62    /// [`SignetEthBundle`] via [`SignetEthBundle::try_into_recovered`] or
63    /// [`SignetEthBundle::try_to_recovered`]. This allows instantiating empty
64    /// bundles, which are otherwise disallowed and is used for testing.
65    ///
66    /// [`SignetEthBundle`]: crate::send::bundle::SignetEthBundle
67    /// [`SignetEthBundle::try_into_recovered`]: crate::send::bundle::SignetEthBundle::try_into_recovered
68    /// [`SignetEthBundle::try_to_recovered`]: crate::send::bundle::SignetEthBundle::try_to_recovered
69    #[doc(hidden)]
70    #[allow(clippy::too_many_arguments)]
71    pub const fn new_unchecked(
72        txs: Vec<Recovered<TxEnvelope>>,
73        host_txs: Vec<Recovered<TxEnvelope>>,
74        block_number: u64,
75        min_timestamp: Option<u64>,
76        max_timestamp: Option<u64>,
77        reverting_tx_hashes: Vec<TxHash>,
78        replacement_uuid: Option<String>,
79        dropping_tx_hashes: Vec<TxHash>,
80        refund_percent: Option<u8>,
81        refund_recipient: Option<Address>,
82        refund_tx_hashes: Vec<TxHash>,
83        extra_fields: OtherFields,
84    ) -> Self {
85        Self {
86            txs,
87            host_txs,
88            block_number,
89            min_timestamp,
90            max_timestamp,
91            reverting_tx_hashes,
92            replacement_uuid,
93            dropping_tx_hashes,
94            refund_percent,
95            refund_recipient,
96            refund_tx_hashes,
97            extra_fields,
98        }
99    }
100
101    /// Get the transactions.
102    pub const fn txs(&self) -> &[Recovered<TxEnvelope>] {
103        self.txs.as_slice()
104    }
105
106    /// Get the host transactions.
107    pub const fn host_txs(&self) -> &[Recovered<TxEnvelope>] {
108        self.host_txs.as_slice()
109    }
110
111    /// Get an iterator draining the transactions.
112    pub fn drain_txns(&mut self) -> impl Iterator<Item = Recovered<TxEnvelope>> + '_ {
113        self.txs.drain(..)
114    }
115
116    /// Get an iterator draining the host transactions.
117    pub fn drain_host_txns(&mut self) -> impl Iterator<Item = Recovered<TxEnvelope>> + '_ {
118        self.host_txs.drain(..)
119    }
120
121    /// Get an iterator over the transaction requirements:
122    /// - signer address
123    /// - nonce
124    /// - min_balance ((max_fee_per_gas * gas_limit) + value)
125    pub fn tx_reqs(&self) -> impl Iterator<Item = TxRequirement> + '_ {
126        self.txs.iter().map(|tx| {
127            let balance = U256::from(tx.max_fee_per_gas() * tx.gas_limit() as u128) + tx.value();
128            TxRequirement { signer: tx.signer(), nonce: tx.nonce(), balance }
129        })
130    }
131
132    /// Get an iterator over the host transaction requirements:
133    /// - signer address
134    /// - nonce
135    /// - min_balance ((max_fee_per_gas * gas_limit) + value)
136    pub fn host_tx_reqs(&self) -> impl Iterator<Item = TxRequirement> + '_ {
137        self.host_txs.iter().map(|tx| {
138            let balance = U256::from(tx.max_fee_per_gas() * tx.gas_limit() as u128) + tx.value();
139            TxRequirement { signer: tx.signer(), nonce: tx.nonce(), balance }
140        })
141    }
142
143    /// Getter for block_number, a standard bundle prop.
144    pub const fn block_number(&self) -> u64 {
145        self.block_number
146    }
147
148    /// Get the valid timestamp range for this bundle.
149    pub const fn valid_timestamp_range(&self) -> std::ops::RangeInclusive<u64> {
150        let min = if let Some(min) = self.min_timestamp { min } else { 0 };
151        let max = if let Some(max) = self.max_timestamp { max } else { u64::MAX };
152        min..=max
153    }
154
155    /// Getter for min_timestamp, a standard bundle prop.
156    pub const fn raw_min_timestamp(&self) -> Option<u64> {
157        self.min_timestamp
158    }
159
160    /// Getter for [`Self::raw_min_timestamp`], with default of 0.
161    pub const fn min_timestamp(&self) -> u64 {
162        if let Some(min) = self.min_timestamp {
163            min
164        } else {
165            0
166        }
167    }
168
169    /// Getter for max_timestamp, a standard bundle prop.
170    pub const fn raw_max_timestamp(&self) -> Option<u64> {
171        self.max_timestamp
172    }
173
174    /// Getter for [`Self::raw_max_timestamp`], with default of `u64::MAX`.
175    pub const fn max_timestamp(&self) -> u64 {
176        if let Some(max) = self.max_timestamp {
177            max
178        } else {
179            u64::MAX
180        }
181    }
182
183    /// Getter for reverting_tx_hashes, a standard bundle prop.
184    pub const fn reverting_tx_hashes(&self) -> &[TxHash] {
185        self.reverting_tx_hashes.as_slice()
186    }
187
188    /// Getter for replacement_uuid, a standard bundle prop.
189    pub const fn replacement_uuid(&self) -> Option<&str> {
190        if let Some(ref uuid) = self.replacement_uuid {
191            Some(uuid.as_str())
192        } else {
193            None
194        }
195    }
196
197    /// Getter for dropping_tx_hashes, a standard bundle prop.
198    pub const fn dropping_tx_hashes(&self) -> &[TxHash] {
199        self.dropping_tx_hashes.as_slice()
200    }
201
202    /// Getter for refund_percent, a standard bundle prop.
203    pub const fn refund_percent(&self) -> Option<u8> {
204        self.refund_percent
205    }
206
207    /// Getter for refund_recipient, a standard bundle prop.
208    pub const fn refund_recipient(&self) -> Option<Address> {
209        self.refund_recipient
210    }
211
212    /// Getter for refund_tx_hashes, a standard bundle prop.
213    pub const fn refund_tx_hashes(&self) -> &[TxHash] {
214        self.refund_tx_hashes.as_slice()
215    }
216
217    /// Getter for extra_fields, a standard bundle prop.
218    pub const fn extra_fields(&self) -> &OtherFields {
219        &self.extra_fields
220    }
221
222    /// Checks if the bundle is valid at a given timestamp.
223    pub fn is_valid_at_timestamp(&self, timestamp: u64) -> bool {
224        let min_timestamp = self.min_timestamp.unwrap_or(0);
225        let max_timestamp = self.max_timestamp.unwrap_or(u64::MAX);
226
227        (min_timestamp..=max_timestamp).contains(&timestamp)
228    }
229
230    /// Checks if the bundle is valid at a given block number.
231    pub const fn is_valid_at_block_number(&self, block_number: u64) -> bool {
232        self.block_number == block_number
233    }
234}