1use crate::{EthereumTxEnvelope, Transaction};
4use alloy_eips::{
5 eip2718::{Eip2718Error, Eip2718Result, IsTyped2718},
6 eip2930::AccessList,
7 eip7702::SignedAuthorization,
8 Decodable2718, Encodable2718, Typed2718,
9};
10use alloy_primitives::{Bytes, ChainId, TxKind, B256, U256};
11use alloy_rlp::{BufMut, Decodable, Encodable, Result as RlpResult};
12
13macro_rules! delegate {
14 ($self:expr => $tx:ident.$method:ident($($arg:expr),*)) => {
15 match $self {
16 Self::BuiltIn($tx) => $tx.$method($($arg),*),
17 Self::Other($tx) => $tx.$method($($arg),*),
18 }
19 };
20}
21
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31#[derive(Debug, Clone, Hash, Eq, PartialEq)]
32pub enum Extended<BuiltIn, Other> {
33 BuiltIn(BuiltIn),
35 Other(Other),
37}
38
39impl<B, T> Extended<B, T> {
40 pub fn convert_builtin<U>(self) -> Extended<U, T>
42 where
43 U: From<B>,
44 {
45 self.map_builtin(U::from)
46 }
47
48 pub fn try_convert_builtin<U>(self) -> Result<Extended<U, T>, U::Error>
51 where
52 U: TryFrom<B>,
53 {
54 self.try_map_builtin(U::try_from)
55 }
56
57 pub fn convert_other<U>(self) -> Extended<B, U>
59 where
60 U: From<T>,
61 {
62 self.map_other(U::from)
63 }
64
65 pub fn try_convert_other<U>(self) -> Result<Extended<B, U>, U::Error>
68 where
69 U: TryFrom<T>,
70 {
71 self.try_map_other(U::try_from)
72 }
73
74 pub fn map_builtin<U>(self, f: impl FnOnce(B) -> U) -> Extended<U, T> {
77 match self {
78 Self::BuiltIn(tx) => Extended::BuiltIn(f(tx)),
79 Self::Other(tx) => Extended::Other(tx),
80 }
81 }
82
83 pub fn try_map_builtin<U, E>(
87 self,
88 f: impl FnOnce(B) -> Result<U, E>,
89 ) -> Result<Extended<U, T>, E> {
90 match self {
91 Self::BuiltIn(tx) => f(tx).map(Extended::BuiltIn),
92 Self::Other(tx) => Ok(Extended::Other(tx)),
93 }
94 }
95
96 pub fn map_other<U>(self, f: impl FnOnce(T) -> U) -> Extended<B, U> {
99 match self {
100 Self::BuiltIn(tx) => Extended::BuiltIn(tx),
101 Self::Other(tx) => Extended::Other(f(tx)),
102 }
103 }
104
105 pub fn try_map_other<U, F>(
109 self,
110 f: impl FnOnce(T) -> Result<U, F>,
111 ) -> Result<Extended<B, U>, F> {
112 match self {
113 Self::BuiltIn(tx) => Ok(Extended::BuiltIn(tx)),
114 Self::Other(tx) => f(tx).map(Extended::Other),
115 }
116 }
117}
118
119impl<B, T> Transaction for Extended<B, T>
120where
121 B: Transaction,
122 T: Transaction,
123{
124 fn chain_id(&self) -> Option<ChainId> {
125 delegate!(self => tx.chain_id())
126 }
127
128 fn nonce(&self) -> u64 {
129 delegate!(self => tx.nonce())
130 }
131
132 fn gas_limit(&self) -> u64 {
133 delegate!(self => tx.gas_limit())
134 }
135
136 fn gas_price(&self) -> Option<u128> {
137 delegate!(self => tx.gas_price())
138 }
139
140 fn max_fee_per_gas(&self) -> u128 {
141 delegate!(self => tx.max_fee_per_gas())
142 }
143
144 fn max_priority_fee_per_gas(&self) -> Option<u128> {
145 delegate!(self => tx.max_priority_fee_per_gas())
146 }
147
148 fn max_fee_per_blob_gas(&self) -> Option<u128> {
149 delegate!(self => tx.max_fee_per_blob_gas())
150 }
151
152 fn priority_fee_or_price(&self) -> u128 {
153 delegate!(self => tx.priority_fee_or_price())
154 }
155
156 fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
157 delegate!(self => tx.effective_gas_price(base_fee))
158 }
159
160 fn is_dynamic_fee(&self) -> bool {
161 delegate!(self => tx.is_dynamic_fee())
162 }
163
164 fn kind(&self) -> TxKind {
165 delegate!(self => tx.kind())
166 }
167
168 fn is_create(&self) -> bool {
169 match self {
170 Self::BuiltIn(tx) => tx.is_create(),
171 Self::Other(_tx) => false,
172 }
173 }
174
175 fn value(&self) -> U256 {
176 delegate!(self => tx.value())
177 }
178
179 fn input(&self) -> &Bytes {
180 delegate!(self => tx.input())
181 }
182
183 fn access_list(&self) -> Option<&AccessList> {
184 delegate!(self => tx.access_list())
185 }
186
187 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
188 delegate!(self => tx.blob_versioned_hashes())
189 }
190
191 fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
192 delegate!(self => tx.authorization_list())
193 }
194}
195
196impl<B, T> IsTyped2718 for Extended<B, T>
197where
198 B: IsTyped2718,
199 T: IsTyped2718,
200{
201 fn is_type(type_id: u8) -> bool {
202 B::is_type(type_id) || T::is_type(type_id)
203 }
204}
205
206impl<B, T> Typed2718 for Extended<B, T>
207where
208 B: Typed2718,
209 T: Typed2718,
210{
211 fn ty(&self) -> u8 {
212 match self {
213 Self::BuiltIn(tx) => tx.ty(),
214 Self::Other(tx) => tx.ty(),
215 }
216 }
217}
218
219impl<B, T> Decodable2718 for Extended<B, T>
220where
221 B: Decodable2718 + IsTyped2718,
222 T: Decodable2718,
223{
224 fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> {
225 if B::is_type(ty) {
226 let envelope = B::typed_decode(ty, buf)?;
227 Ok(Self::BuiltIn(envelope))
228 } else {
229 let other = T::typed_decode(ty, buf)?;
230 Ok(Self::Other(other))
231 }
232 }
233 fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
234 if buf.is_empty() {
235 return Err(Eip2718Error::RlpError(alloy_rlp::Error::InputTooShort));
236 }
237 B::fallback_decode(buf).map(Self::BuiltIn)
238 }
239}
240
241impl<B, T> Encodable2718 for Extended<B, T>
242where
243 B: Encodable2718,
244 T: Encodable2718,
245{
246 fn encode_2718_len(&self) -> usize {
247 match self {
248 Self::BuiltIn(envelope) => envelope.encode_2718_len(),
249 Self::Other(tx) => tx.encode_2718_len(),
250 }
251 }
252
253 fn encode_2718(&self, out: &mut dyn BufMut) {
254 match self {
255 Self::BuiltIn(envelope) => envelope.encode_2718(out),
256 Self::Other(tx) => tx.encode_2718(out),
257 }
258 }
259}
260
261impl<B, T> Encodable for Extended<B, T>
262where
263 B: Encodable,
264 T: Encodable,
265{
266 fn encode(&self, out: &mut dyn BufMut) {
267 match self {
268 Self::BuiltIn(envelope) => envelope.encode(out),
269 Self::Other(tx) => tx.encode(out),
270 }
271 }
272
273 fn length(&self) -> usize {
274 match self {
275 Self::BuiltIn(envelope) => envelope.length(),
276 Self::Other(tx) => tx.length(),
277 }
278 }
279}
280
281impl<B, T> Decodable for Extended<B, T>
282where
283 B: Decodable,
284 T: Decodable,
285{
286 fn decode(buf: &mut &[u8]) -> RlpResult<Self> {
287 let original = *buf;
288
289 B::decode(buf).map_or_else(
290 |_| {
291 *buf = original;
292 T::decode(buf).map(Self::Other)
293 },
294 |tx| Ok(Self::BuiltIn(tx)),
295 )
296 }
297}
298
299impl<Eip4844, Tx> From<EthereumTxEnvelope<Eip4844>> for Extended<EthereumTxEnvelope<Eip4844>, Tx> {
300 fn from(value: EthereumTxEnvelope<Eip4844>) -> Self {
301 Self::BuiltIn(value)
302 }
303}