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
154impl<B, T> SignedTransaction for Extended<B, T>
155where
156 B: SignedTransaction + IsTyped2718,
157 T: SignedTransaction,
158{
159 fn tx_hash(&self) -> &TxHash {
160 match self {
161 Self::BuiltIn(tx) => tx.tx_hash(),
162 Self::Other(tx) => tx.tx_hash(),
163 }
164 }
165
166 fn recover_signer_unchecked_with_buf(
167 &self,
168 buf: &mut Vec<u8>,
169 ) -> Result<Address, RecoveryError> {
170 delegate!(self => tx.recover_signer_unchecked_with_buf(buf))
171 }
172}
173
174impl<B, T> Typed2718 for Extended<B, T>
175where
176 B: Typed2718,
177 T: Typed2718,
178{
179 fn ty(&self) -> u8 {
180 match self {
181 Self::BuiltIn(tx) => tx.ty(),
182 Self::Other(tx) => tx.ty(),
183 }
184 }
185}
186
187impl<B, T> Decodable2718 for Extended<B, T>
188where
189 B: Decodable2718 + IsTyped2718,
190 T: Decodable2718,
191{
192 fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> {
193 if B::is_type(ty) {
194 let envelope = B::typed_decode(ty, buf)?;
195 Ok(Self::BuiltIn(envelope))
196 } else {
197 let other = T::typed_decode(ty, buf)?;
198 Ok(Self::Other(other))
199 }
200 }
201 fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
202 if buf.is_empty() {
203 return Err(Eip2718Error::RlpError(alloy_rlp::Error::InputTooShort));
204 }
205 B::fallback_decode(buf).map(Self::BuiltIn)
206 }
207}
208
209impl<B, T> Encodable2718 for Extended<B, T>
210where
211 B: Encodable2718,
212 T: Encodable2718,
213{
214 fn encode_2718_len(&self) -> usize {
215 match self {
216 Self::BuiltIn(envelope) => envelope.encode_2718_len(),
217 Self::Other(tx) => tx.encode_2718_len(),
218 }
219 }
220
221 fn encode_2718(&self, out: &mut dyn BufMut) {
222 match self {
223 Self::BuiltIn(envelope) => envelope.encode_2718(out),
224 Self::Other(tx) => tx.encode_2718(out),
225 }
226 }
227}
228
229impl<B, T> Encodable for Extended<B, T>
230where
231 B: Encodable,
232 T: Encodable,
233{
234 fn encode(&self, out: &mut dyn BufMut) {
235 match self {
236 Self::BuiltIn(envelope) => envelope.encode(out),
237 Self::Other(tx) => tx.encode(out),
238 }
239 }
240
241 fn length(&self) -> usize {
242 match self {
243 Self::BuiltIn(envelope) => envelope.length(),
244 Self::Other(tx) => tx.length(),
245 }
246 }
247}
248
249impl<B, T> Decodable for Extended<B, T>
250where
251 B: Decodable,
252 T: Decodable,
253{
254 fn decode(buf: &mut &[u8]) -> RlpResult<Self> {
255 let original = *buf;
256
257 match B::decode(buf) {
258 Ok(tx) => Ok(Self::BuiltIn(tx)),
259 Err(_) => {
260 *buf = original;
261 T::decode(buf).map(Self::Other)
262 }
263 }
264 }
265}
266
267impl<Eip4844, Tx> From<EthereumTxEnvelope<Eip4844>> for Extended<EthereumTxEnvelope<Eip4844>, Tx> {
268 fn from(value: EthereumTxEnvelope<Eip4844>) -> Self {
269 Self::BuiltIn(value)
270 }
271}
272
273#[cfg(feature = "op")]
274mod op {
275 use crate::Extended;
276 use alloy_consensus::error::ValueError;
277 use alloy_primitives::{Signature, B256};
278 use op_alloy_consensus::{OpPooledTransaction, OpTxEnvelope};
279
280 impl<Tx> TryFrom<Extended<OpTxEnvelope, Tx>> for Extended<OpPooledTransaction, Tx> {
281 type Error = OpTxEnvelope;
282
283 fn try_from(value: Extended<OpTxEnvelope, Tx>) -> Result<Self, Self::Error> {
284 match value {
285 Extended::BuiltIn(tx) => {
286 let converted_tx: OpPooledTransaction =
287 tx.clone().try_into().map_err(|_| tx)?;
288 Ok(Self::BuiltIn(converted_tx))
289 }
290 Extended::Other(tx) => Ok(Self::Other(tx)),
291 }
292 }
293 }
294
295 impl<Tx> From<OpPooledTransaction> for Extended<OpTxEnvelope, Tx> {
296 fn from(tx: OpPooledTransaction) -> Self {
297 Self::BuiltIn(tx.into())
298 }
299 }
300
301 impl<Tx> TryFrom<Extended<OpTxEnvelope, Tx>> for OpPooledTransaction {
302 type Error = ValueError<OpTxEnvelope>;
303
304 fn try_from(_tx: Extended<OpTxEnvelope, Tx>) -> Result<Self, Self::Error> {
305 match _tx {
306 Extended::BuiltIn(inner) => inner.try_into(),
307 Extended::Other(_tx) => Err(ValueError::new(
308 OpTxEnvelope::Legacy(alloy_consensus::Signed::new_unchecked(
309 alloy_consensus::TxLegacy::default(),
310 Signature::decode_rlp_vrs(&mut &[0u8; 65][..], |_| Ok(false)).unwrap(),
311 B256::default(),
312 )),
313 "Cannot convert custom transaction to OpPooledTransaction",
314 )),
315 }
316 }
317 }
318
319 impl<Tx> From<OpTxEnvelope> for Extended<OpTxEnvelope, Tx> {
320 fn from(value: OpTxEnvelope) -> Self {
321 Self::BuiltIn(value)
322 }
323 }
324}
325
326#[cfg(feature = "serde-bincode-compat")]
327mod serde_bincode_compat {
328 use super::*;
329 use crate::serde_bincode_compat::SerdeBincodeCompat;
330
331 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
332 #[derive(Debug)]
333 pub enum ExtendedTxEnvelopeRepr<'a, B: SerdeBincodeCompat, T: SerdeBincodeCompat> {
334 BuiltIn(B::BincodeRepr<'a>),
335 Other(T::BincodeRepr<'a>),
336 }
337
338 impl<B, T> SerdeBincodeCompat for Extended<B, T>
339 where
340 B: SerdeBincodeCompat + core::fmt::Debug,
341 T: SerdeBincodeCompat + core::fmt::Debug,
342 {
343 type BincodeRepr<'a> = ExtendedTxEnvelopeRepr<'a, B, T>;
344
345 fn as_repr(&self) -> Self::BincodeRepr<'_> {
346 match self {
347 Self::BuiltIn(tx) => ExtendedTxEnvelopeRepr::BuiltIn(tx.as_repr()),
348 Self::Other(tx) => ExtendedTxEnvelopeRepr::Other(tx.as_repr()),
349 }
350 }
351
352 fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
353 match repr {
354 ExtendedTxEnvelopeRepr::BuiltIn(tx_repr) => Self::BuiltIn(B::from_repr(tx_repr)),
355 ExtendedTxEnvelopeRepr::Other(tx_repr) => Self::Other(T::from_repr(tx_repr)),
356 }
357 }
358 }
359}
360
361#[cfg(feature = "reth-codec")]
362use alloy_primitives::bytes::Buf;
363
364#[cfg(feature = "reth-codec")]
365impl<B, T> reth_codecs::Compact for Extended<B, T>
366where
367 B: Transaction + IsTyped2718 + reth_codecs::Compact,
368 T: Transaction + reth_codecs::Compact,
369{
370 fn to_compact<Buf>(&self, buf: &mut Buf) -> usize
371 where
372 Buf: alloy_rlp::bytes::BufMut + AsMut<[u8]>,
373 {
374 buf.put_u8(self.ty());
375 match self {
376 Self::BuiltIn(tx) => tx.to_compact(buf),
377 Self::Other(tx) => tx.to_compact(buf),
378 }
379 }
380
381 fn from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) {
382 let type_byte = buf.get_u8();
383
384 if <B as IsTyped2718>::is_type(type_byte) {
385 let (tx, remaining) = B::from_compact(buf, len);
386 return (Self::BuiltIn(tx), remaining);
387 }
388
389 let (tx, remaining) = T::from_compact(buf, len);
390 (Self::Other(tx), remaining)
391 }
392}