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