1use crate::{
2 size::InMemorySize,
3 transaction::signed::{RecoveryError, SignedTransaction},
4};
5use alloc::vec::Vec;
6use alloy_consensus::{transaction::SignerRecoverable, EthereumTxEnvelope, Transaction};
7use alloy_eips::{
8 eip2718::{Eip2718Error, Eip2718Result, IsTyped2718},
9 eip2930::AccessList,
10 eip7702::SignedAuthorization,
11 Decodable2718, Encodable2718, Typed2718,
12};
13use alloy_primitives::{ChainId, TxHash};
14use alloy_rlp::{BufMut, Decodable, Encodable, Result as RlpResult};
15use revm_primitives::{Address, Bytes, TxKind, B256, U256};
16
17macro_rules! delegate {
18 ($self:expr => $tx:ident.$method:ident($($arg:expr),*)) => {
19 match $self {
20 Self::BuiltIn($tx) => $tx.$method($($arg),*),
21 Self::Other($tx) => $tx.$method($($arg),*),
22 }
23 };
24}
25
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
35#[derive(Debug, Clone, Hash, Eq, PartialEq)]
36pub enum Extended<BuiltIn, Other> {
37 BuiltIn(BuiltIn),
39 Other(Other),
41}
42
43impl<B, T> Transaction for Extended<B, T>
44where
45 B: Transaction,
46 T: Transaction,
47{
48 fn chain_id(&self) -> Option<ChainId> {
49 delegate!(self => tx.chain_id())
50 }
51
52 fn nonce(&self) -> u64 {
53 delegate!(self => tx.nonce())
54 }
55
56 fn gas_limit(&self) -> u64 {
57 delegate!(self => tx.gas_limit())
58 }
59
60 fn gas_price(&self) -> Option<u128> {
61 delegate!(self => tx.gas_price())
62 }
63
64 fn max_fee_per_gas(&self) -> u128 {
65 delegate!(self => tx.max_fee_per_gas())
66 }
67
68 fn max_priority_fee_per_gas(&self) -> Option<u128> {
69 delegate!(self => tx.max_priority_fee_per_gas())
70 }
71
72 fn max_fee_per_blob_gas(&self) -> Option<u128> {
73 delegate!(self => tx.max_fee_per_blob_gas())
74 }
75
76 fn priority_fee_or_price(&self) -> u128 {
77 delegate!(self => tx.priority_fee_or_price())
78 }
79
80 fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
81 delegate!(self => tx.effective_gas_price(base_fee))
82 }
83
84 fn is_dynamic_fee(&self) -> bool {
85 delegate!(self => tx.is_dynamic_fee())
86 }
87
88 fn kind(&self) -> TxKind {
89 delegate!(self => tx.kind())
90 }
91
92 fn is_create(&self) -> bool {
93 match self {
94 Self::BuiltIn(tx) => tx.is_create(),
95 Self::Other(_tx) => false,
96 }
97 }
98
99 fn value(&self) -> U256 {
100 delegate!(self => tx.value())
101 }
102
103 fn input(&self) -> &Bytes {
104 delegate!(self => tx.input())
105 }
106
107 fn access_list(&self) -> Option<&AccessList> {
108 delegate!(self => tx.access_list())
109 }
110
111 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
112 delegate!(self => tx.blob_versioned_hashes())
113 }
114
115 fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
116 delegate!(self => tx.authorization_list())
117 }
118}
119
120impl<B, T> IsTyped2718 for Extended<B, T>
121where
122 B: IsTyped2718,
123 T: IsTyped2718,
124{
125 fn is_type(type_id: u8) -> bool {
126 B::is_type(type_id) || T::is_type(type_id)
127 }
128}
129
130impl<B, T> InMemorySize for Extended<B, T>
131where
132 B: InMemorySize,
133 T: InMemorySize,
134{
135 fn size(&self) -> usize {
136 delegate!(self => tx.size())
137 }
138}
139
140impl<B, T> SignerRecoverable for Extended<B, T>
141where
142 B: SignedTransaction + IsTyped2718,
143 T: SignedTransaction,
144{
145 fn recover_signer(&self) -> Result<Address, RecoveryError> {
146 delegate!(self => tx.recover_signer())
147 }
148
149 fn recover_signer_unchecked(&self) -> Result<Address, RecoveryError> {
150 delegate!(self => tx.recover_signer_unchecked())
151 }
152
153 fn recover_unchecked_with_buf(&self, buf: &mut Vec<u8>) -> Result<Address, RecoveryError> {
154 delegate!(self => tx.recover_unchecked_with_buf(buf))
155 }
156}
157
158impl<B, T> SignedTransaction for Extended<B, T>
159where
160 B: SignedTransaction + IsTyped2718,
161 T: SignedTransaction,
162{
163 fn tx_hash(&self) -> &TxHash {
164 match self {
165 Self::BuiltIn(tx) => tx.tx_hash(),
166 Self::Other(tx) => tx.tx_hash(),
167 }
168 }
169}
170
171impl<B, T> Typed2718 for Extended<B, T>
172where
173 B: Typed2718,
174 T: Typed2718,
175{
176 fn ty(&self) -> u8 {
177 match self {
178 Self::BuiltIn(tx) => tx.ty(),
179 Self::Other(tx) => tx.ty(),
180 }
181 }
182}
183
184impl<B, T> Decodable2718 for Extended<B, T>
185where
186 B: Decodable2718 + IsTyped2718,
187 T: Decodable2718,
188{
189 fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> {
190 if B::is_type(ty) {
191 let envelope = B::typed_decode(ty, buf)?;
192 Ok(Self::BuiltIn(envelope))
193 } else {
194 let other = T::typed_decode(ty, buf)?;
195 Ok(Self::Other(other))
196 }
197 }
198 fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
199 if buf.is_empty() {
200 return Err(Eip2718Error::RlpError(alloy_rlp::Error::InputTooShort));
201 }
202 B::fallback_decode(buf).map(Self::BuiltIn)
203 }
204}
205
206impl<B, T> Encodable2718 for Extended<B, T>
207where
208 B: Encodable2718,
209 T: Encodable2718,
210{
211 fn encode_2718_len(&self) -> usize {
212 match self {
213 Self::BuiltIn(envelope) => envelope.encode_2718_len(),
214 Self::Other(tx) => tx.encode_2718_len(),
215 }
216 }
217
218 fn encode_2718(&self, out: &mut dyn BufMut) {
219 match self {
220 Self::BuiltIn(envelope) => envelope.encode_2718(out),
221 Self::Other(tx) => tx.encode_2718(out),
222 }
223 }
224}
225
226impl<B, T> Encodable for Extended<B, T>
227where
228 B: Encodable,
229 T: Encodable,
230{
231 fn encode(&self, out: &mut dyn BufMut) {
232 match self {
233 Self::BuiltIn(envelope) => envelope.encode(out),
234 Self::Other(tx) => tx.encode(out),
235 }
236 }
237
238 fn length(&self) -> usize {
239 match self {
240 Self::BuiltIn(envelope) => envelope.length(),
241 Self::Other(tx) => tx.length(),
242 }
243 }
244}
245
246impl<B, T> Decodable for Extended<B, T>
247where
248 B: Decodable,
249 T: Decodable,
250{
251 fn decode(buf: &mut &[u8]) -> RlpResult<Self> {
252 let original = *buf;
253
254 match B::decode(buf) {
255 Ok(tx) => Ok(Self::BuiltIn(tx)),
256 Err(_) => {
257 *buf = original;
258 T::decode(buf).map(Self::Other)
259 }
260 }
261 }
262}
263
264impl<Eip4844, Tx> From<EthereumTxEnvelope<Eip4844>> for Extended<EthereumTxEnvelope<Eip4844>, Tx> {
265 fn from(value: EthereumTxEnvelope<Eip4844>) -> Self {
266 Self::BuiltIn(value)
267 }
268}
269
270#[cfg(feature = "op")]
271mod op {
272 use crate::Extended;
273 use alloy_consensus::error::ValueError;
274 use alloy_primitives::{Sealed, Signature, B256};
275 use op_alloy_consensus::{OpPooledTransaction, OpTransaction, OpTxEnvelope, TxDeposit};
276
277 impl<B, T> OpTransaction for Extended<B, T>
278 where
279 B: OpTransaction,
280 T: OpTransaction,
281 {
282 fn is_deposit(&self) -> bool {
283 match self {
284 Self::BuiltIn(b) => b.is_deposit(),
285 Self::Other(t) => t.is_deposit(),
286 }
287 }
288
289 fn as_deposit(&self) -> Option<&Sealed<TxDeposit>> {
290 match self {
291 Self::BuiltIn(b) => b.as_deposit(),
292 Self::Other(t) => t.as_deposit(),
293 }
294 }
295 }
296
297 impl<Tx> TryFrom<Extended<OpTxEnvelope, Tx>> for Extended<OpPooledTransaction, Tx> {
298 type Error = <OpPooledTransaction as TryFrom<OpTxEnvelope>>::Error;
299
300 fn try_from(value: Extended<OpTxEnvelope, Tx>) -> Result<Self, Self::Error> {
301 match value {
302 Extended::BuiltIn(tx) => {
303 let converted_tx: OpPooledTransaction = tx.try_into()?;
304 Ok(Self::BuiltIn(converted_tx))
305 }
306 Extended::Other(tx) => Ok(Self::Other(tx)),
307 }
308 }
309 }
310
311 impl<Tx> From<OpPooledTransaction> for Extended<OpTxEnvelope, Tx> {
312 fn from(tx: OpPooledTransaction) -> Self {
313 Self::BuiltIn(tx.into())
314 }
315 }
316
317 impl<Tx> From<Extended<OpPooledTransaction, Tx>> for Extended<OpTxEnvelope, Tx> {
318 fn from(tx: Extended<OpPooledTransaction, Tx>) -> Self {
319 match tx {
320 Extended::BuiltIn(tx) => Self::BuiltIn(tx.into()),
321 Extended::Other(tx) => Self::Other(tx),
322 }
323 }
324 }
325
326 impl<Tx> TryFrom<Extended<OpTxEnvelope, Tx>> for OpPooledTransaction {
327 type Error = ValueError<OpTxEnvelope>;
328
329 fn try_from(_tx: Extended<OpTxEnvelope, Tx>) -> Result<Self, Self::Error> {
330 match _tx {
331 Extended::BuiltIn(inner) => inner.try_into(),
332 Extended::Other(_tx) => Err(ValueError::new(
333 OpTxEnvelope::Legacy(alloy_consensus::Signed::new_unchecked(
334 alloy_consensus::TxLegacy::default(),
335 Signature::decode_rlp_vrs(&mut &[0u8; 65][..], |_| Ok(false)).unwrap(),
336 B256::default(),
337 )),
338 "Cannot convert custom transaction to OpPooledTransaction",
339 )),
340 }
341 }
342 }
343
344 impl<Tx> From<OpTxEnvelope> for Extended<OpTxEnvelope, Tx> {
345 fn from(value: OpTxEnvelope) -> Self {
346 Self::BuiltIn(value)
347 }
348 }
349}
350
351#[cfg(feature = "serde-bincode-compat")]
352mod serde_bincode_compat {
353 use super::*;
354 use crate::serde_bincode_compat::SerdeBincodeCompat;
355
356 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
357 #[derive(Debug)]
358 pub enum ExtendedRepr<'a, B: SerdeBincodeCompat, T: SerdeBincodeCompat> {
359 BuiltIn(B::BincodeRepr<'a>),
360 Other(T::BincodeRepr<'a>),
361 }
362
363 impl<B, T> SerdeBincodeCompat for Extended<B, T>
364 where
365 B: SerdeBincodeCompat + core::fmt::Debug,
366 T: SerdeBincodeCompat + core::fmt::Debug,
367 {
368 type BincodeRepr<'a> = ExtendedRepr<'a, B, T>;
369
370 fn as_repr(&self) -> Self::BincodeRepr<'_> {
371 match self {
372 Self::BuiltIn(tx) => ExtendedRepr::BuiltIn(tx.as_repr()),
373 Self::Other(tx) => ExtendedRepr::Other(tx.as_repr()),
374 }
375 }
376
377 fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
378 match repr {
379 ExtendedRepr::BuiltIn(tx_repr) => Self::BuiltIn(B::from_repr(tx_repr)),
380 ExtendedRepr::Other(tx_repr) => Self::Other(T::from_repr(tx_repr)),
381 }
382 }
383 }
384}
385
386#[cfg(feature = "reth-codec")]
387use alloy_primitives::bytes::Buf;
388
389#[cfg(feature = "reth-codec")]
390impl<B, T> reth_codecs::Compact for Extended<B, T>
391where
392 B: Transaction + IsTyped2718 + reth_codecs::Compact,
393 T: Transaction + reth_codecs::Compact,
394{
395 fn to_compact<Buf>(&self, buf: &mut Buf) -> usize
396 where
397 Buf: alloy_rlp::bytes::BufMut + AsMut<[u8]>,
398 {
399 buf.put_u8(self.ty());
400 match self {
401 Self::BuiltIn(tx) => tx.to_compact(buf),
402 Self::Other(tx) => tx.to_compact(buf),
403 }
404 }
405
406 fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) {
407 let type_byte = buf.get_u8();
408
409 if <B as IsTyped2718>::is_type(type_byte) {
410 let (tx, remaining) = B::from_compact(buf, len);
411 return (Self::BuiltIn(tx), remaining);
412 }
413
414 let (tx, remaining) = T::from_compact(buf, len);
415 (Self::Other(tx), remaining)
416 }
417}