1use std::{
2 cmp::min,
3 fmt::Display,
4 num::NonZeroUsize,
5 sync::{LazyLock, Mutex},
6};
7
8use crate::utils::keccak;
9use bytes::Bytes;
10use ethereum_types::{Address, H256, U256};
11use ethrex_crypto::{Crypto, CryptoError, NativeCrypto};
12use lru::LruCache;
13pub use mempool::MempoolTransaction;
14
15const MAX_SIGNER_CACHE_ENTRIES: usize = 100_000;
16
17pub static GLOBAL_SIGNER_CACHE: LazyLock<Mutex<LruCache<H256, Address>>> = LazyLock::new(|| {
28 Mutex::new(LruCache::new(
29 NonZeroUsize::new(MAX_SIGNER_CACHE_ENTRIES).expect("MAX_SIGNER_CACHE_ENTRIES is non-zero"),
30 ))
31});
32use rkyv::{Archive, Deserialize as RDeserialize, Serialize as RSerialize};
33use serde::{Serialize, ser::SerializeStruct};
34pub use serde_impl::{
35 AccessListEntry, AuthorizationTupleEntry, FrameEntry, GenericTransaction,
36 GenericTransactionError,
37};
38
39pub const EIP1559_DEFAULT_SERIALIZED_LENGTH: usize = 15;
41
42use ethrex_rlp::{
43 constants::RLP_NULL,
44 decode::{RLPDecode, decode_bytes, decode_rlp_item},
45 encode::{PayloadRLPEncode, RLPEncode},
46 error::RLPDecodeError,
47 structs::{Decoder, Encoder},
48};
49
50#[cfg(all(feature = "zisk", target_arch = "riscv64"))]
51use super::unsync_cell::OnceCell;
52use crate::types::{
53 AccessList, AuthorizationList, BlobsBundle, constants::VERSIONED_HASH_VERSION_KZG,
54};
55#[cfg(not(all(feature = "zisk", target_arch = "riscv64")))]
56use once_cell::sync::OnceCell;
57
58#[derive(Clone, Debug, PartialEq, Eq, Serialize, RSerialize, RDeserialize, Archive)]
67#[serde(untagged)]
68pub enum Transaction {
69 LegacyTransaction(LegacyTransaction),
70 EIP2930Transaction(EIP2930Transaction),
71 EIP1559Transaction(EIP1559Transaction),
72 EIP4844Transaction(EIP4844Transaction),
73 EIP7702Transaction(EIP7702Transaction),
74 PrivilegedL2Transaction(PrivilegedL2Transaction),
75 FeeTokenTransaction(FeeTokenTransaction),
76 FrameTransaction(FrameTransaction),
77}
78
79#[derive(Clone, Debug, PartialEq, Eq)]
83pub enum P2PTransaction {
84 LegacyTransaction(LegacyTransaction),
85 EIP2930Transaction(EIP2930Transaction),
86 EIP1559Transaction(EIP1559Transaction),
87 EIP4844TransactionWithBlobs(WrappedEIP4844Transaction),
88 EIP7702Transaction(EIP7702Transaction),
89 FeeTokenTransaction(FeeTokenTransaction),
90 FrameTransaction(FrameTransaction),
91}
92
93impl TryInto<Transaction> for P2PTransaction {
94 type Error = String;
95
96 fn try_into(self) -> Result<Transaction, Self::Error> {
97 match self {
98 P2PTransaction::LegacyTransaction(itx) => Ok(Transaction::LegacyTransaction(itx)),
99 P2PTransaction::EIP2930Transaction(itx) => Ok(Transaction::EIP2930Transaction(itx)),
100 P2PTransaction::EIP1559Transaction(itx) => Ok(Transaction::EIP1559Transaction(itx)),
101 P2PTransaction::EIP7702Transaction(itx) => Ok(Transaction::EIP7702Transaction(itx)),
102 P2PTransaction::FrameTransaction(itx) => Ok(Transaction::FrameTransaction(itx)),
103 _ => Err("Can't convert blob p2p transaction into regular transaction. Blob bundle would be lost.".to_string()),
104 }
105 }
106}
107
108impl RLPEncode for P2PTransaction {
109 fn encode(&self, buf: &mut dyn bytes::BufMut) {
110 match self {
111 P2PTransaction::LegacyTransaction(t) => t.encode(buf),
112 tx => <[u8] as RLPEncode>::encode(&tx.encode_canonical_to_vec(), buf),
113 };
114 }
115}
116
117impl RLPDecode for P2PTransaction {
118 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
119 let (is_list, payload, remainder) = decode_rlp_item(rlp)?;
120 if !is_list {
121 let tx_type = *payload.first().ok_or(RLPDecodeError::InvalidLength)?;
122 let tx_encoding = payload.get(1..).ok_or(RLPDecodeError::InvalidLength)?;
123 let tx = match EnvelopeTxType::from_type_byte(tx_type)? {
126 EnvelopeTxType::EIP2930 => {
127 P2PTransaction::EIP2930Transaction(EIP2930Transaction::decode(tx_encoding)?)
128 }
129 EnvelopeTxType::EIP1559 => {
130 P2PTransaction::EIP1559Transaction(EIP1559Transaction::decode(tx_encoding)?)
131 }
132 EnvelopeTxType::EIP4844 => P2PTransaction::EIP4844TransactionWithBlobs(
133 WrappedEIP4844Transaction::decode(tx_encoding)?,
134 ),
135 EnvelopeTxType::EIP7702 => {
136 P2PTransaction::EIP7702Transaction(EIP7702Transaction::decode(tx_encoding)?)
137 }
138 EnvelopeTxType::Frame => {
139 P2PTransaction::FrameTransaction(FrameTransaction::decode(tx_encoding)?)
140 }
141 EnvelopeTxType::FeeToken => {
142 P2PTransaction::FeeTokenTransaction(FeeTokenTransaction::decode(tx_encoding)?)
143 }
144 EnvelopeTxType::Privileged => {
146 return Err(RLPDecodeError::Custom(
147 "privileged transactions are not sent over p2p".to_string(),
148 ));
149 }
150 };
151 Ok((tx, remainder))
152 } else {
153 LegacyTransaction::decode_unfinished(rlp)
155 .map(|(tx, rem)| (P2PTransaction::LegacyTransaction(tx), rem))
156 }
157 }
158}
159
160#[derive(Clone, Debug, PartialEq, Eq)]
161pub struct WrappedEIP4844Transaction {
162 pub tx: EIP4844Transaction,
163 pub wrapper_version: Option<u8>,
164 pub blobs_bundle: BlobsBundle,
165}
166
167impl RLPEncode for WrappedEIP4844Transaction {
168 fn encode(&self, buf: &mut dyn bytes::BufMut) {
169 let encoder = Encoder::new(buf);
170 encoder
171 .encode_field(&self.tx)
172 .encode_optional_field(&self.wrapper_version)
173 .encode_field(&self.blobs_bundle.blobs)
174 .encode_field(&self.blobs_bundle.commitments)
175 .encode_field(&self.blobs_bundle.proofs)
176 .finish();
177 }
178}
179
180impl RLPDecode for WrappedEIP4844Transaction {
181 fn decode_unfinished(rlp: &[u8]) -> Result<(WrappedEIP4844Transaction, &[u8]), RLPDecodeError> {
182 let decoder = Decoder::new(rlp)?;
183 let Ok((tx, decoder)) = decoder.decode_field("tx") else {
184 let (tx, rest) = EIP4844Transaction::decode_unfinished(rlp)?;
186 return Ok((
187 WrappedEIP4844Transaction {
188 tx,
189 wrapper_version: None,
190 blobs_bundle: BlobsBundle::empty(),
192 },
193 rest,
194 ));
195 };
196
197 let (wrapper_version, decoder) = decoder.decode_optional_field();
198 let (blobs, decoder) = decoder.decode_field("blobs")?;
199 let (commitments, decoder) = decoder.decode_field("commitments")?;
200 let (proofs, decoder) = decoder.decode_field("proofs")?;
201
202 let wrapped = WrappedEIP4844Transaction {
203 tx,
204 wrapper_version,
205 blobs_bundle: BlobsBundle {
206 blobs,
207 commitments,
208 proofs,
209 version: wrapper_version.unwrap_or_default(),
210 },
211 };
212 Ok((wrapped, decoder.finish()?))
213 }
214}
215
216#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
217pub struct LegacyTransaction {
218 pub nonce: u64,
219 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
220 pub gas_price: U256,
221 pub gas: u64,
222 pub to: TxKind,
225 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
226 pub value: U256,
227 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
228 pub data: Bytes,
229 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
230 pub v: U256,
231 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
232 pub r: U256,
233 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
234 pub s: U256,
235 #[rkyv(with=rkyv::with::Skip)]
236 pub inner_hash: OnceCell<H256>,
237 #[rkyv(with=rkyv::with::Skip)]
238 pub sender_cache: OnceCell<Address>,
239}
240
241#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
242pub struct EIP2930Transaction {
243 pub chain_id: u64,
244 pub nonce: u64,
245 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
246 pub gas_price: U256,
247 pub gas_limit: u64,
248 pub to: TxKind,
249 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
250 pub value: U256,
251 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
252 pub data: Bytes,
253 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
254 pub access_list: AccessList,
255 pub signature_y_parity: bool,
256 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
257 pub signature_r: U256,
258 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
259 pub signature_s: U256,
260 #[rkyv(with=rkyv::with::Skip)]
261 pub inner_hash: OnceCell<H256>,
262 #[rkyv(with=rkyv::with::Skip)]
263 pub sender_cache: OnceCell<Address>,
264 #[rkyv(with=rkyv::with::Skip)]
265 pub cached_canonical: OnceCell<Vec<u8>>,
266}
267
268#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
269pub struct EIP1559Transaction {
270 pub chain_id: u64,
271 pub nonce: u64,
272 pub max_priority_fee_per_gas: u64,
273 pub max_fee_per_gas: u64,
274 pub gas_limit: u64,
275 pub to: TxKind,
276 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
277 pub value: U256,
278 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
279 pub data: Bytes,
280 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
281 pub access_list: AccessList,
282 pub signature_y_parity: bool,
283 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
284 pub signature_r: U256,
285 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
286 pub signature_s: U256,
287 #[rkyv(with=rkyv::with::Skip)]
288 pub inner_hash: OnceCell<H256>,
289 #[rkyv(with=rkyv::with::Skip)]
290 pub sender_cache: OnceCell<Address>,
291 #[rkyv(with=rkyv::with::Skip)]
292 pub cached_canonical: OnceCell<Vec<u8>>,
293}
294
295#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
296pub struct EIP4844Transaction {
297 pub chain_id: u64,
298 pub nonce: u64,
299 pub max_priority_fee_per_gas: u64,
300 pub max_fee_per_gas: u64,
301 pub gas: u64,
302 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
303 pub to: Address,
304 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
305 pub value: U256,
306 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
307 pub data: Bytes,
308 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
309 pub access_list: AccessList,
310 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
311 pub max_fee_per_blob_gas: U256,
312 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H256Wrapper>)]
313 pub blob_versioned_hashes: Vec<H256>,
314 pub signature_y_parity: bool,
315 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
316 pub signature_r: U256,
317 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
318 pub signature_s: U256,
319 #[rkyv(with=rkyv::with::Skip)]
320 pub inner_hash: OnceCell<H256>,
321 #[rkyv(with=rkyv::with::Skip)]
322 pub sender_cache: OnceCell<Address>,
323 #[rkyv(with=rkyv::with::Skip)]
324 pub cached_canonical: OnceCell<Vec<u8>>,
325}
326
327#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
328pub struct EIP7702Transaction {
329 pub chain_id: u64,
330 pub nonce: u64,
331 pub max_priority_fee_per_gas: u64,
332 pub max_fee_per_gas: u64,
333 pub gas_limit: u64,
334 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
335 pub to: Address,
336 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
337 pub value: U256,
338 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
339 pub data: Bytes,
340 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
341 pub access_list: AccessList,
342 pub authorization_list: AuthorizationList,
343 pub signature_y_parity: bool,
344 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
345 pub signature_r: U256,
346 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
347 pub signature_s: U256,
348 #[rkyv(with=rkyv::with::Skip)]
349 pub inner_hash: OnceCell<H256>,
350 #[rkyv(with=rkyv::with::Skip)]
351 pub sender_cache: OnceCell<Address>,
352 #[rkyv(with=rkyv::with::Skip)]
353 pub cached_canonical: OnceCell<Vec<u8>>,
354}
355#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
356pub struct PrivilegedL2Transaction {
357 pub chain_id: u64,
358 pub nonce: u64,
359 pub max_priority_fee_per_gas: u64,
360 pub max_fee_per_gas: u64,
361 pub gas_limit: u64,
362 pub to: TxKind,
363 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
364 pub value: U256,
365 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
366 pub data: Bytes,
367 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
368 pub access_list: AccessList,
369 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
370 pub from: Address,
371 #[rkyv(with=rkyv::with::Skip)]
372 pub inner_hash: OnceCell<H256>,
373 #[rkyv(with=rkyv::with::Skip)]
374 pub sender_cache: OnceCell<Address>,
375 #[rkyv(with=rkyv::with::Skip)]
376 pub cached_canonical: OnceCell<Vec<u8>>,
377}
378
379#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
380pub enum TxType {
381 #[default]
382 Legacy = 0x00,
383 EIP2930 = 0x01,
384 EIP1559 = 0x02,
385 EIP4844 = 0x03,
386 EIP7702 = 0x04,
387 Frame = 0x06,
388 FeeToken = 0x7d,
389 Privileged = 0x7e,
392}
393
394#[derive(Clone, Copy, Debug, PartialEq, Eq)]
400pub enum EnvelopeTxType {
401 EIP2930,
402 EIP1559,
403 EIP4844,
404 EIP7702,
405 Frame,
406 FeeToken,
407 Privileged,
408}
409
410impl EnvelopeTxType {
411 pub fn from_type_byte(byte: u8) -> Result<Self, RLPDecodeError> {
419 TxType::from_u8(byte)
420 .and_then(TxType::as_envelope)
421 .ok_or_else(|| RLPDecodeError::Custom(format!("Invalid transaction type: {byte}")))
422 }
423}
424
425impl From<TxType> for u8 {
426 fn from(val: TxType) -> Self {
427 match val {
428 TxType::Legacy => 0x00,
429 TxType::EIP2930 => 0x01,
430 TxType::EIP1559 => 0x02,
431 TxType::EIP4844 => 0x03,
432 TxType::EIP7702 => 0x04,
433 TxType::Frame => 0x06,
434 TxType::FeeToken => 0x7d,
435 TxType::Privileged => 0x7e,
436 }
437 }
438}
439
440impl Display for TxType {
441 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
442 match self {
443 TxType::Legacy => write!(f, "Legacy"),
444 TxType::EIP2930 => write!(f, "EIP2930"),
445 TxType::EIP1559 => write!(f, "EIP1559"),
446 TxType::EIP4844 => write!(f, "EIP4844"),
447 TxType::EIP7702 => write!(f, "EIP7702"),
448 TxType::Frame => write!(f, "Frame"),
449 TxType::Privileged => write!(f, "Privileged"),
450 TxType::FeeToken => write!(f, "FeeToken"),
451 }
452 }
453}
454
455impl Transaction {
456 pub fn tx_type(&self) -> TxType {
457 match self {
458 Transaction::LegacyTransaction(_) => TxType::Legacy,
459 Transaction::EIP2930Transaction(_) => TxType::EIP2930,
460 Transaction::EIP1559Transaction(_) => TxType::EIP1559,
461 Transaction::EIP4844Transaction(_) => TxType::EIP4844,
462 Transaction::EIP7702Transaction(_) => TxType::EIP7702,
463 Transaction::FeeTokenTransaction(_) => TxType::FeeToken,
464 Transaction::PrivilegedL2Transaction(_) => TxType::Privileged,
465 Transaction::FrameTransaction(_) => TxType::Frame,
466 }
467 }
468
469 fn calc_effective_gas_price(&self, base_fee_per_gas: Option<u64>) -> Option<U256> {
470 let base_fee = base_fee_per_gas?;
471 let max_fee = self.max_fee_per_gas()?;
472 if max_fee < base_fee {
473 return None;
475 }
476
477 let priority_fee_per_gas = min(self.max_priority_fee()?, max_fee.saturating_sub(base_fee));
478 Some(U256::from(priority_fee_per_gas) + U256::from(base_fee))
479 }
480
481 pub fn effective_gas_price(&self, base_fee_per_gas: Option<u64>) -> Option<U256> {
482 match self.tx_type() {
483 TxType::Legacy => Some(self.gas_price()),
484 TxType::EIP2930 => Some(self.gas_price()),
485 TxType::EIP1559 => self.calc_effective_gas_price(base_fee_per_gas),
486 TxType::EIP4844 => self.calc_effective_gas_price(base_fee_per_gas),
487 TxType::EIP7702 => self.calc_effective_gas_price(base_fee_per_gas),
488 TxType::Frame => self.calc_effective_gas_price(base_fee_per_gas),
489 TxType::FeeToken => self.calc_effective_gas_price(base_fee_per_gas),
490 TxType::Privileged => Some(self.gas_price()),
491 }
492 }
493
494 pub fn cost_without_base_fee(&self) -> Option<U256> {
495 let price = match self.tx_type() {
496 TxType::Legacy => self.gas_price(),
497 TxType::EIP2930 => self.gas_price(),
498 TxType::EIP1559 => U256::from(self.max_fee_per_gas()?),
499 TxType::EIP4844 => U256::from(self.max_fee_per_gas()?),
500 TxType::EIP7702 => U256::from(self.max_fee_per_gas()?),
501 TxType::Frame => U256::from(self.max_fee_per_gas()?),
502 TxType::FeeToken => U256::from(self.max_fee_per_gas()?),
503 TxType::Privileged => self.gas_price(),
504 };
505
506 let base = U256::saturating_add(
507 U256::saturating_mul(price, self.gas_limit().into()),
508 self.value(),
509 );
510
511 if let Transaction::EIP4844Transaction(tx) = self {
515 let blob_gas = U256::from(crate::constants::GAS_PER_BLOB)
516 .saturating_mul(U256::from(tx.blob_versioned_hashes.len() as u64));
517 let blob_cost = blob_gas.saturating_mul(tx.max_fee_per_blob_gas);
518 return Some(base.saturating_add(blob_cost));
519 }
520
521 Some(base)
522 }
523
524 pub fn fee_token(&self) -> Option<Address> {
525 if let Transaction::FeeTokenTransaction(tx) = self {
526 Some(tx.fee_token)
527 } else {
528 None
529 }
530 }
531
532 fn cached_canonical_cell(&self) -> Option<&OnceCell<Vec<u8>>> {
535 match self {
536 Transaction::LegacyTransaction(_) => None,
537 Transaction::EIP2930Transaction(t) => Some(&t.cached_canonical),
538 Transaction::EIP1559Transaction(t) => Some(&t.cached_canonical),
539 Transaction::EIP4844Transaction(t) => Some(&t.cached_canonical),
540 Transaction::EIP7702Transaction(t) => Some(&t.cached_canonical),
541 Transaction::PrivilegedL2Transaction(t) => Some(&t.cached_canonical),
542 Transaction::FeeTokenTransaction(t) => Some(&t.cached_canonical),
543 Transaction::FrameTransaction(t) => Some(&t.cached_canonical),
544 }
545 }
546}
547
548impl RLPEncode for Transaction {
549 fn encode(&self, buf: &mut dyn bytes::BufMut) {
554 match self {
555 Transaction::LegacyTransaction(t) => t.encode(buf),
556 _ => {
557 let canonical = self.encode_canonical_to_vec();
558 <[u8] as RLPEncode>::encode(canonical.as_slice(), buf)
559 }
560 };
561 }
562}
563
564impl RLPDecode for Transaction {
565 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
570 let (is_list, payload, remainder) = decode_rlp_item(rlp)?;
571 if !is_list {
572 let tx_type = *payload.first().ok_or(RLPDecodeError::InvalidLength)?;
573 let tx_encoding = payload.get(1..).ok_or(RLPDecodeError::InvalidLength)?;
574 let tx = match EnvelopeTxType::from_type_byte(tx_type)? {
578 EnvelopeTxType::EIP2930 => {
579 Transaction::EIP2930Transaction(EIP2930Transaction::decode(tx_encoding)?)
580 }
581 EnvelopeTxType::EIP1559 => {
582 Transaction::EIP1559Transaction(EIP1559Transaction::decode(tx_encoding)?)
583 }
584 EnvelopeTxType::EIP4844 => {
585 Transaction::EIP4844Transaction(EIP4844Transaction::decode(tx_encoding)?)
586 }
587 EnvelopeTxType::EIP7702 => {
588 Transaction::EIP7702Transaction(EIP7702Transaction::decode(tx_encoding)?)
589 }
590 EnvelopeTxType::Frame => {
591 Transaction::FrameTransaction(FrameTransaction::decode(tx_encoding)?)
592 }
593 EnvelopeTxType::FeeToken => {
594 Transaction::FeeTokenTransaction(FeeTokenTransaction::decode(tx_encoding)?)
595 }
596 EnvelopeTxType::Privileged => Transaction::PrivilegedL2Transaction(
597 PrivilegedL2Transaction::decode(tx_encoding)?,
598 ),
599 };
600 Ok((tx, remainder))
601 } else {
602 LegacyTransaction::decode_unfinished(rlp)
604 .map(|(tx, rem)| (Transaction::LegacyTransaction(tx), rem))
605 }
606 }
607}
608
609#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
611pub enum TxKind {
612 Call(#[rkyv(with=crate::rkyv_utils::H160Wrapper)] Address),
613 #[default]
614 Create,
615}
616
617impl RLPEncode for TxKind {
618 fn encode(&self, buf: &mut dyn bytes::BufMut) {
619 match self {
620 Self::Call(address) => address.encode(buf),
621 Self::Create => buf.put_u8(RLP_NULL),
622 }
623 }
624}
625
626impl RLPDecode for TxKind {
627 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
628 let first_byte = rlp.first().ok_or(RLPDecodeError::InvalidLength)?;
629 if *first_byte == RLP_NULL {
630 return Ok((Self::Create, &rlp[1..]));
631 }
632 Address::decode_unfinished(rlp).map(|(t, rest)| (Self::Call(t), rest))
633 }
634}
635
636impl RLPEncode for LegacyTransaction {
637 fn encode(&self, buf: &mut dyn bytes::BufMut) {
638 Encoder::new(buf)
639 .encode_field(&self.nonce)
640 .encode_field(&self.gas_price)
641 .encode_field(&self.gas)
642 .encode_field(&self.to)
643 .encode_field(&self.value)
644 .encode_field(&self.data)
645 .encode_field(&self.v)
646 .encode_field(&self.r)
647 .encode_field(&self.s)
648 .finish();
649 }
650}
651
652impl RLPEncode for EIP2930Transaction {
653 fn encode(&self, buf: &mut dyn bytes::BufMut) {
654 Encoder::new(buf)
655 .encode_field(&self.chain_id)
656 .encode_field(&self.nonce)
657 .encode_field(&self.gas_price)
658 .encode_field(&self.gas_limit)
659 .encode_field(&self.to)
660 .encode_field(&self.value)
661 .encode_field(&self.data)
662 .encode_field(&self.access_list)
663 .encode_field(&self.signature_y_parity)
664 .encode_field(&self.signature_r)
665 .encode_field(&self.signature_s)
666 .finish()
667 }
668}
669
670impl RLPEncode for EIP1559Transaction {
671 fn encode(&self, buf: &mut dyn bytes::BufMut) {
672 Encoder::new(buf)
673 .encode_field(&self.chain_id)
674 .encode_field(&self.nonce)
675 .encode_field(&self.max_priority_fee_per_gas)
676 .encode_field(&self.max_fee_per_gas)
677 .encode_field(&self.gas_limit)
678 .encode_field(&self.to)
679 .encode_field(&self.value)
680 .encode_field(&self.data)
681 .encode_field(&self.access_list)
682 .encode_field(&self.signature_y_parity)
683 .encode_field(&self.signature_r)
684 .encode_field(&self.signature_s)
685 .finish()
686 }
687}
688
689impl RLPEncode for EIP4844Transaction {
690 fn encode(&self, buf: &mut dyn bytes::BufMut) {
691 Encoder::new(buf)
692 .encode_field(&self.chain_id)
693 .encode_field(&self.nonce)
694 .encode_field(&self.max_priority_fee_per_gas)
695 .encode_field(&self.max_fee_per_gas)
696 .encode_field(&self.gas)
697 .encode_field(&self.to)
698 .encode_field(&self.value)
699 .encode_field(&self.data)
700 .encode_field(&self.access_list)
701 .encode_field(&self.max_fee_per_blob_gas)
702 .encode_field(&self.blob_versioned_hashes)
703 .encode_field(&self.signature_y_parity)
704 .encode_field(&self.signature_r)
705 .encode_field(&self.signature_s)
706 .finish()
707 }
708}
709
710impl RLPEncode for EIP7702Transaction {
711 fn encode(&self, buf: &mut dyn bytes::BufMut) {
712 Encoder::new(buf)
713 .encode_field(&self.chain_id)
714 .encode_field(&self.nonce)
715 .encode_field(&self.max_priority_fee_per_gas)
716 .encode_field(&self.max_fee_per_gas)
717 .encode_field(&self.gas_limit)
718 .encode_field(&self.to)
719 .encode_field(&self.value)
720 .encode_field(&self.data)
721 .encode_field(&self.access_list)
722 .encode_field(&self.authorization_list)
723 .encode_field(&self.signature_y_parity)
724 .encode_field(&self.signature_r)
725 .encode_field(&self.signature_s)
726 .finish()
727 }
728}
729
730impl RLPEncode for PrivilegedL2Transaction {
731 fn encode(&self, buf: &mut dyn bytes::BufMut) {
732 Encoder::new(buf)
733 .encode_field(&self.chain_id)
734 .encode_field(&self.nonce)
735 .encode_field(&self.max_priority_fee_per_gas)
736 .encode_field(&self.max_fee_per_gas)
737 .encode_field(&self.gas_limit)
738 .encode_field(&self.to)
739 .encode_field(&self.value)
740 .encode_field(&self.data)
741 .encode_field(&self.access_list)
742 .encode_field(&self.from)
743 .finish()
744 }
745}
746
747impl RLPEncode for FeeTokenTransaction {
748 fn encode(&self, buf: &mut dyn bytes::BufMut) {
749 Encoder::new(buf)
750 .encode_field(&self.chain_id)
751 .encode_field(&self.nonce)
752 .encode_field(&self.max_priority_fee_per_gas)
753 .encode_field(&self.max_fee_per_gas)
754 .encode_field(&self.gas_limit)
755 .encode_field(&self.to)
756 .encode_field(&self.value)
757 .encode_field(&self.data)
758 .encode_field(&self.access_list)
759 .encode_field(&self.fee_token)
760 .encode_field(&self.signature_y_parity)
761 .encode_field(&self.signature_r)
762 .encode_field(&self.signature_s)
763 .finish()
764 }
765}
766
767impl PayloadRLPEncode for Transaction {
768 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
769 match self {
770 Transaction::LegacyTransaction(tx) => tx.encode_payload(buf),
771 Transaction::EIP1559Transaction(tx) => tx.encode_payload(buf),
772 Transaction::EIP2930Transaction(tx) => tx.encode_payload(buf),
773 Transaction::EIP4844Transaction(tx) => tx.encode_payload(buf),
774 Transaction::EIP7702Transaction(tx) => tx.encode_payload(buf),
775 Transaction::PrivilegedL2Transaction(tx) => tx.encode_payload(buf),
776 Transaction::FeeTokenTransaction(tx) => tx.encode_payload(buf),
777 Transaction::FrameTransaction(tx) => tx.encode_payload(buf),
778 }
779 }
780}
781
782impl PayloadRLPEncode for LegacyTransaction {
783 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
784 Encoder::new(buf)
785 .encode_field(&self.nonce)
786 .encode_field(&self.gas_price)
787 .encode_field(&self.gas)
788 .encode_field(&self.to)
789 .encode_field(&self.value)
790 .encode_field(&self.data)
791 .finish();
792 }
793}
794
795impl PayloadRLPEncode for EIP1559Transaction {
796 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
797 Encoder::new(buf)
798 .encode_field(&self.chain_id)
799 .encode_field(&self.nonce)
800 .encode_field(&self.max_priority_fee_per_gas)
801 .encode_field(&self.max_fee_per_gas)
802 .encode_field(&self.gas_limit)
803 .encode_field(&self.to)
804 .encode_field(&self.value)
805 .encode_field(&self.data)
806 .encode_field(&self.access_list)
807 .finish();
808 }
809}
810
811impl PayloadRLPEncode for EIP2930Transaction {
812 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
813 Encoder::new(buf)
814 .encode_field(&self.chain_id)
815 .encode_field(&self.nonce)
816 .encode_field(&self.gas_price)
817 .encode_field(&self.gas_limit)
818 .encode_field(&self.to)
819 .encode_field(&self.value)
820 .encode_field(&self.data)
821 .encode_field(&self.access_list)
822 .finish();
823 }
824}
825
826impl PayloadRLPEncode for EIP4844Transaction {
827 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
828 Encoder::new(buf)
829 .encode_field(&self.chain_id)
830 .encode_field(&self.nonce)
831 .encode_field(&self.max_priority_fee_per_gas)
832 .encode_field(&self.max_fee_per_gas)
833 .encode_field(&self.gas)
834 .encode_field(&self.to)
835 .encode_field(&self.value)
836 .encode_field(&self.data)
837 .encode_field(&self.access_list)
838 .encode_field(&self.max_fee_per_blob_gas)
839 .encode_field(&self.blob_versioned_hashes)
840 .finish();
841 }
842}
843
844impl PayloadRLPEncode for EIP7702Transaction {
845 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
846 Encoder::new(buf)
847 .encode_field(&self.chain_id)
848 .encode_field(&self.nonce)
849 .encode_field(&self.max_priority_fee_per_gas)
850 .encode_field(&self.max_fee_per_gas)
851 .encode_field(&self.gas_limit)
852 .encode_field(&self.to)
853 .encode_field(&self.value)
854 .encode_field(&self.data)
855 .encode_field(&self.access_list)
856 .encode_field(&self.authorization_list)
857 .finish();
858 }
859}
860
861impl PayloadRLPEncode for PrivilegedL2Transaction {
862 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
863 Encoder::new(buf)
864 .encode_field(&self.chain_id)
865 .encode_field(&self.nonce)
866 .encode_field(&self.max_priority_fee_per_gas)
867 .encode_field(&self.max_fee_per_gas)
868 .encode_field(&self.gas_limit)
869 .encode_field(&self.to)
870 .encode_field(&self.value)
871 .encode_field(&self.data)
872 .encode_field(&self.access_list)
873 .encode_field(&self.from)
874 .finish();
875 }
876}
877
878impl PayloadRLPEncode for FeeTokenTransaction {
879 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
880 Encoder::new(buf)
881 .encode_field(&self.chain_id)
882 .encode_field(&self.nonce)
883 .encode_field(&self.max_priority_fee_per_gas)
884 .encode_field(&self.max_fee_per_gas)
885 .encode_field(&self.gas_limit)
886 .encode_field(&self.to)
887 .encode_field(&self.value)
888 .encode_field(&self.data)
889 .encode_field(&self.access_list)
890 .encode_field(&self.fee_token)
891 .finish();
892 }
893}
894
895fn decode_nonce_field(decoder: Decoder) -> Result<(u64, Decoder), RLPDecodeError> {
905 let wrap =
909 |err| RLPDecodeError::Custom(format!("Error decoding field 'nonce' of type u64: {err}"));
910 let (item, rest) = decoder.get_encoded_item_ref().map_err(wrap)?;
911 if let Ok((content, _)) = decode_bytes(item)
923 && content.len() > 8
924 && content.first() != Some(&0)
925 {
926 return Err(RLPDecodeError::Custom("Nonce is max".into()));
927 }
928 let (nonce, _) = u64::decode_unfinished(item).map_err(wrap)?;
929 Ok((nonce, rest))
930}
931
932impl RLPDecode for LegacyTransaction {
933 fn decode_unfinished(rlp: &[u8]) -> Result<(LegacyTransaction, &[u8]), RLPDecodeError> {
934 let decoder = Decoder::new(rlp)?;
935 let (nonce, decoder) = decode_nonce_field(decoder)?;
936 let (gas_price, decoder) = decoder.decode_field("gas_price")?;
937 let (gas, decoder) = decoder.decode_field("gas")?;
938 let (to, decoder) = decoder.decode_field("to")?;
939 let (value, decoder) = decoder.decode_field("value")?;
940 let (data, decoder) = decoder.decode_field("data")?;
941 let (v, decoder) = decoder.decode_field("v")?;
942 let (r, decoder) = decoder.decode_field("r")?;
943 let (s, decoder) = decoder.decode_field("s")?;
944 let inner_hash = OnceCell::new();
945 let sender_cache = OnceCell::new();
946
947 let tx = LegacyTransaction {
948 nonce,
949 gas_price,
950 gas,
951 to,
952 value,
953 data,
954 v,
955 r,
956 s,
957 inner_hash,
958 sender_cache,
959 };
960 Ok((tx, decoder.finish()?))
961 }
962}
963
964impl RLPDecode for EIP2930Transaction {
965 fn decode_unfinished(rlp: &[u8]) -> Result<(EIP2930Transaction, &[u8]), RLPDecodeError> {
966 let decoder = Decoder::new(rlp)?;
967 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
968 let (nonce, decoder) = decode_nonce_field(decoder)?;
969 let (gas_price, decoder) = decoder.decode_field("gas_price")?;
970 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
971 let (to, decoder) = decoder.decode_field("to")?;
972 let (value, decoder) = decoder.decode_field("value")?;
973 let (data, decoder) = decoder.decode_field("data")?;
974 let (access_list, decoder) = decoder.decode_field("access_list")?;
975 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
976 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
977 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
978 let inner_hash = OnceCell::new();
979 let sender_cache = OnceCell::new();
980 let cached_canonical = OnceCell::new();
981
982 let tx = EIP2930Transaction {
983 chain_id,
984 nonce,
985 gas_price,
986 gas_limit,
987 to,
988 value,
989 data,
990 access_list,
991 signature_y_parity,
992 signature_r,
993 signature_s,
994 inner_hash,
995 sender_cache,
996 cached_canonical,
997 };
998 Ok((tx, decoder.finish()?))
999 }
1000}
1001
1002impl RLPDecode for EIP1559Transaction {
1003 fn decode_unfinished(rlp: &[u8]) -> Result<(EIP1559Transaction, &[u8]), RLPDecodeError> {
1004 let decoder = Decoder::new(rlp)?;
1005 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1006 let (nonce, decoder) = decode_nonce_field(decoder)?;
1007 let (max_priority_fee_per_gas, decoder) =
1008 decoder.decode_field("max_priority_fee_per_gas")?;
1009 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1010 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
1011 let (to, decoder) = decoder.decode_field("to")?;
1012 let (value, decoder) = decoder.decode_field("value")?;
1013 let (data, decoder) = decoder.decode_field("data")?;
1014 let (access_list, decoder) = decoder.decode_field("access_list")?;
1015 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
1016 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
1017 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
1018 let inner_hash = OnceCell::new();
1019 let sender_cache = OnceCell::new();
1020 let cached_canonical = OnceCell::new();
1021
1022 let tx = EIP1559Transaction {
1023 chain_id,
1024 nonce,
1025 max_priority_fee_per_gas,
1026 max_fee_per_gas,
1027 gas_limit,
1028 to,
1029 value,
1030 data,
1031 access_list,
1032 signature_y_parity,
1033 signature_r,
1034 signature_s,
1035 inner_hash,
1036 sender_cache,
1037 cached_canonical,
1038 };
1039 Ok((tx, decoder.finish()?))
1040 }
1041}
1042
1043impl RLPDecode for EIP4844Transaction {
1044 fn decode_unfinished(rlp: &[u8]) -> Result<(EIP4844Transaction, &[u8]), RLPDecodeError> {
1045 let decoder = Decoder::new(rlp)?;
1046 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1047 let (nonce, decoder) = decode_nonce_field(decoder)?;
1048 let (max_priority_fee_per_gas, decoder) =
1049 decoder.decode_field("max_priority_fee_per_gas")?;
1050 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1051 let (gas, decoder) = decoder.decode_field("gas")?;
1052 let (to, decoder) = decoder.decode_field("to")?;
1053 let (value, decoder) = decoder.decode_field("value")?;
1054 let (data, decoder) = decoder.decode_field("data")?;
1055 let (access_list, decoder) = decoder.decode_field("access_list")?;
1056 let (max_fee_per_blob_gas, decoder) = decoder.decode_field("max_fee_per_blob_gas")?;
1057 let (blob_versioned_hashes, decoder) = decoder.decode_field("blob_versioned_hashes")?;
1058 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
1059 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
1060 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
1061 let inner_hash = OnceCell::new();
1062 let sender_cache = OnceCell::new();
1063 let cached_canonical = OnceCell::new();
1064
1065 let tx = EIP4844Transaction {
1066 chain_id,
1067 nonce,
1068 max_priority_fee_per_gas,
1069 max_fee_per_gas,
1070 gas,
1071 to,
1072 value,
1073 data,
1074 access_list,
1075 max_fee_per_blob_gas,
1076 blob_versioned_hashes,
1077 signature_y_parity,
1078 signature_r,
1079 signature_s,
1080 inner_hash,
1081 sender_cache,
1082 cached_canonical,
1083 };
1084 Ok((tx, decoder.finish()?))
1085 }
1086}
1087
1088impl RLPDecode for EIP7702Transaction {
1089 fn decode_unfinished(rlp: &[u8]) -> Result<(EIP7702Transaction, &[u8]), RLPDecodeError> {
1090 let decoder = Decoder::new(rlp)?;
1091 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1092 let (nonce, decoder) = decode_nonce_field(decoder)?;
1093 let (max_priority_fee_per_gas, decoder) =
1094 decoder.decode_field("max_priority_fee_per_gas")?;
1095 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1096 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
1097 let (to, decoder) = decoder.decode_field("to")?;
1098 let (value, decoder) = decoder.decode_field("value")?;
1099 let (data, decoder) = decoder.decode_field("data")?;
1100 let (access_list, decoder) = decoder.decode_field("access_list")?;
1101 let (authorization_list, decoder) = decoder.decode_field("authorization_list")?;
1102 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
1103 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
1104 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
1105 let inner_hash = OnceCell::new();
1106 let sender_cache = OnceCell::new();
1107 let cached_canonical = OnceCell::new();
1108
1109 let tx = EIP7702Transaction {
1110 chain_id,
1111 nonce,
1112 max_priority_fee_per_gas,
1113 max_fee_per_gas,
1114 gas_limit,
1115 to,
1116 value,
1117 data,
1118 access_list,
1119 authorization_list,
1120 signature_y_parity,
1121 signature_r,
1122 signature_s,
1123 inner_hash,
1124 sender_cache,
1125 cached_canonical,
1126 };
1127 Ok((tx, decoder.finish()?))
1128 }
1129}
1130
1131impl RLPDecode for PrivilegedL2Transaction {
1132 fn decode_unfinished(rlp: &[u8]) -> Result<(PrivilegedL2Transaction, &[u8]), RLPDecodeError> {
1133 let decoder = Decoder::new(rlp)?;
1134 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1135 let (nonce, decoder) = decode_nonce_field(decoder)?;
1136 let (max_priority_fee_per_gas, decoder) =
1137 decoder.decode_field("max_priority_fee_per_gas")?;
1138 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1139 let (gas_limit, decoder) = decoder.decode_field::<u64>("gas_limit")?;
1140 let (to, decoder) = decoder.decode_field("to")?;
1141 let (value, decoder) = decoder.decode_field("value")?;
1142 let (data, decoder) = decoder.decode_field("data")?;
1143 let (access_list, decoder) = decoder.decode_field("access_list")?;
1144 let (from, decoder) = decoder.decode_field("from")?;
1145 let inner_hash = OnceCell::new();
1146 let sender_cache = OnceCell::new();
1147 let cached_canonical = OnceCell::new();
1148
1149 let tx = PrivilegedL2Transaction {
1150 chain_id,
1151 nonce,
1152 max_priority_fee_per_gas,
1153 max_fee_per_gas,
1154 gas_limit,
1155 to,
1156 value,
1157 data,
1158 access_list,
1159 from,
1160 inner_hash,
1161 sender_cache,
1162 cached_canonical,
1163 };
1164 Ok((tx, decoder.finish()?))
1165 }
1166}
1167
1168impl RLPDecode for FeeTokenTransaction {
1169 fn decode_unfinished(rlp: &[u8]) -> Result<(FeeTokenTransaction, &[u8]), RLPDecodeError> {
1170 let decoder = Decoder::new(rlp)?;
1171 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1172 let (nonce, decoder) = decode_nonce_field(decoder)?;
1173 let (max_priority_fee_per_gas, decoder) =
1174 decoder.decode_field("max_priority_fee_per_gas")?;
1175 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1176 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
1177 let (to, decoder) = decoder.decode_field("to")?;
1178 let (value, decoder) = decoder.decode_field("value")?;
1179 let (data, decoder) = decoder.decode_field("data")?;
1180 let (access_list, decoder) = decoder.decode_field("access_list")?;
1181 let (fee_token, decoder) = decoder.decode_field("fee_token")?;
1182 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
1183 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
1184 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
1185 let inner_hash = OnceCell::new();
1186 let sender_cache = OnceCell::new();
1187 let cached_canonical = OnceCell::new();
1188
1189 let tx = FeeTokenTransaction {
1190 chain_id,
1191 nonce,
1192 max_priority_fee_per_gas,
1193 max_fee_per_gas,
1194 gas_limit,
1195 to,
1196 value,
1197 data,
1198 access_list,
1199 fee_token,
1200 signature_y_parity,
1201 signature_r,
1202 signature_s,
1203 inner_hash,
1204 sender_cache,
1205 cached_canonical,
1206 };
1207 Ok((tx, decoder.finish()?))
1208 }
1209}
1210
1211pub type SigningPayload = (Vec<u8>, [u8; 65]);
1214
1215impl Transaction {
1216 pub fn sender(&self, crypto: &dyn Crypto) -> Result<Address, CryptoError> {
1217 if let Transaction::FrameTransaction(tx) = self {
1219 return Ok(tx.sender);
1220 }
1221 let sender_cache = match self {
1222 Transaction::LegacyTransaction(tx) => &tx.sender_cache,
1223 Transaction::EIP2930Transaction(tx) => &tx.sender_cache,
1224 Transaction::EIP1559Transaction(tx) => &tx.sender_cache,
1225 Transaction::EIP4844Transaction(tx) => &tx.sender_cache,
1226 Transaction::EIP7702Transaction(tx) => &tx.sender_cache,
1227 Transaction::PrivilegedL2Transaction(tx) => &tx.sender_cache,
1228 Transaction::FeeTokenTransaction(tx) => &tx.sender_cache,
1229 Transaction::FrameTransaction(_) => unreachable!(),
1230 };
1231 sender_cache
1232 .get_or_try_init(|| {
1233 let tx_hash = self.hash(crypto);
1234 let mut cache = GLOBAL_SIGNER_CACHE
1236 .lock()
1237 .unwrap_or_else(|e| e.into_inner());
1238 if let Some(&addr) = cache.get(&tx_hash) {
1239 return Ok(addr);
1240 }
1241 drop(cache);
1242 let sender = self.compute_sender(crypto)?;
1244 GLOBAL_SIGNER_CACHE
1246 .lock()
1247 .unwrap_or_else(|e| e.into_inner())
1248 .put(tx_hash, sender);
1249 Ok(sender)
1250 })
1251 .copied()
1252 }
1253
1254 pub fn signing_payload(&self) -> Result<Option<SigningPayload>, CryptoError> {
1263 let (buf, sig) = match self {
1264 Transaction::LegacyTransaction(tx) => {
1265 let v = u64::try_from(tx.v).map_err(|_| CryptoError::InvalidSignature)?;
1266 let signature_y_parity = match self.chain_id() {
1271 Some(chain_id) => match v.checked_sub(35 + chain_id * 2) {
1272 Some(0) => false,
1273 Some(1) => true,
1274 _ => return Err(CryptoError::InvalidSignature),
1275 },
1276 None => match v {
1277 27 => false,
1278 28 => true,
1279 _ => return Err(CryptoError::InvalidSignature),
1280 },
1281 };
1282 let mut buf = vec![];
1283 match self.chain_id() {
1284 None => Encoder::new(&mut buf)
1285 .encode_field(&tx.nonce)
1286 .encode_field(&tx.gas_price)
1287 .encode_field(&tx.gas)
1288 .encode_field(&tx.to)
1289 .encode_field(&tx.value)
1290 .encode_field(&tx.data)
1291 .finish(),
1292 Some(chain_id) => Encoder::new(&mut buf)
1293 .encode_field(&tx.nonce)
1294 .encode_field(&tx.gas_price)
1295 .encode_field(&tx.gas)
1296 .encode_field(&tx.to)
1297 .encode_field(&tx.value)
1298 .encode_field(&tx.data)
1299 .encode_field(&chain_id)
1300 .encode_field(&0u8)
1301 .encode_field(&0u8)
1302 .finish(),
1303 }
1304 let mut sig = [0u8; 65];
1305 sig[..32].copy_from_slice(&tx.r.to_big_endian());
1306 sig[32..64].copy_from_slice(&tx.s.to_big_endian());
1307 sig[64] = signature_y_parity as u8;
1308 (buf, sig)
1309 }
1310 Transaction::EIP2930Transaction(tx) => {
1311 let mut buf = vec![self.tx_type() as u8];
1312 Encoder::new(&mut buf)
1313 .encode_field(&tx.chain_id)
1314 .encode_field(&tx.nonce)
1315 .encode_field(&tx.gas_price)
1316 .encode_field(&tx.gas_limit)
1317 .encode_field(&tx.to)
1318 .encode_field(&tx.value)
1319 .encode_field(&tx.data)
1320 .encode_field(&tx.access_list)
1321 .finish();
1322 let mut sig = [0u8; 65];
1323 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1324 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1325 sig[64] = tx.signature_y_parity as u8;
1326 (buf, sig)
1327 }
1328 Transaction::EIP1559Transaction(tx) => {
1329 let mut buf = vec![self.tx_type() as u8];
1330 Encoder::new(&mut buf)
1331 .encode_field(&tx.chain_id)
1332 .encode_field(&tx.nonce)
1333 .encode_field(&tx.max_priority_fee_per_gas)
1334 .encode_field(&tx.max_fee_per_gas)
1335 .encode_field(&tx.gas_limit)
1336 .encode_field(&tx.to)
1337 .encode_field(&tx.value)
1338 .encode_field(&tx.data)
1339 .encode_field(&tx.access_list)
1340 .finish();
1341 let mut sig = [0u8; 65];
1342 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1343 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1344 sig[64] = tx.signature_y_parity as u8;
1345 (buf, sig)
1346 }
1347 Transaction::EIP4844Transaction(tx) => {
1348 let mut buf = vec![self.tx_type() as u8];
1349 Encoder::new(&mut buf)
1350 .encode_field(&tx.chain_id)
1351 .encode_field(&tx.nonce)
1352 .encode_field(&tx.max_priority_fee_per_gas)
1353 .encode_field(&tx.max_fee_per_gas)
1354 .encode_field(&tx.gas)
1355 .encode_field(&tx.to)
1356 .encode_field(&tx.value)
1357 .encode_field(&tx.data)
1358 .encode_field(&tx.access_list)
1359 .encode_field(&tx.max_fee_per_blob_gas)
1360 .encode_field(&tx.blob_versioned_hashes)
1361 .finish();
1362 let mut sig = [0u8; 65];
1363 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1364 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1365 sig[64] = tx.signature_y_parity as u8;
1366 (buf, sig)
1367 }
1368 Transaction::EIP7702Transaction(tx) => {
1369 let mut buf = vec![self.tx_type() as u8];
1370 Encoder::new(&mut buf)
1371 .encode_field(&tx.chain_id)
1372 .encode_field(&tx.nonce)
1373 .encode_field(&tx.max_priority_fee_per_gas)
1374 .encode_field(&tx.max_fee_per_gas)
1375 .encode_field(&tx.gas_limit)
1376 .encode_field(&tx.to)
1377 .encode_field(&tx.value)
1378 .encode_field(&tx.data)
1379 .encode_field(&tx.access_list)
1380 .encode_field(&tx.authorization_list)
1381 .finish();
1382 let mut sig = [0u8; 65];
1383 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1384 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1385 sig[64] = tx.signature_y_parity as u8;
1386 (buf, sig)
1387 }
1388 Transaction::PrivilegedL2Transaction(_) | Transaction::FrameTransaction(_) => {
1390 return Ok(None);
1391 }
1392 Transaction::FeeTokenTransaction(tx) => {
1393 let mut buf = vec![self.tx_type() as u8];
1394 Encoder::new(&mut buf)
1395 .encode_field(&tx.chain_id)
1396 .encode_field(&tx.nonce)
1397 .encode_field(&tx.max_priority_fee_per_gas)
1398 .encode_field(&tx.max_fee_per_gas)
1399 .encode_field(&tx.gas_limit)
1400 .encode_field(&tx.to)
1401 .encode_field(&tx.value)
1402 .encode_field(&tx.data)
1403 .encode_field(&tx.access_list)
1404 .encode_field(&tx.fee_token)
1405 .finish();
1406 let mut sig = [0u8; 65];
1407 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1408 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1409 sig[64] = tx.signature_y_parity as u8;
1410 (buf, sig)
1411 }
1412 };
1413 Ok(Some((buf, sig)))
1414 }
1415
1416 fn compute_sender(&self, crypto: &dyn Crypto) -> Result<Address, CryptoError> {
1417 match self {
1418 Transaction::PrivilegedL2Transaction(tx) => return Ok(tx.from),
1419 Transaction::FrameTransaction(tx) => return Ok(tx.sender),
1420 _ => {}
1421 }
1422 let Some((buf, sig)) = self.signing_payload()? else {
1423 return Err(CryptoError::InvalidSignature);
1425 };
1426 let msg = crypto.keccak256(&buf);
1427 crypto.recover_signer(&sig, &msg)
1428 }
1429
1430 #[cfg(feature = "secp256k1")]
1437 pub fn public_key(&self, crypto: &dyn Crypto) -> Result<Option<[u8; 65]>, CryptoError> {
1438 let Some((buf, sig)) = self.signing_payload()? else {
1439 return Ok(None);
1440 };
1441 let msg = crypto.keccak256(&buf);
1442 crypto.recover_public_key(&sig, &msg).map(Some)
1443 }
1444
1445 pub fn gas_limit(&self) -> u64 {
1446 match self {
1447 Transaction::LegacyTransaction(tx) => tx.gas,
1448 Transaction::EIP2930Transaction(tx) => tx.gas_limit,
1449 Transaction::EIP1559Transaction(tx) => tx.gas_limit,
1450 Transaction::EIP7702Transaction(tx) => tx.gas_limit,
1451 Transaction::EIP4844Transaction(tx) => tx.gas,
1452 Transaction::PrivilegedL2Transaction(tx) => tx.gas_limit,
1453 Transaction::FeeTokenTransaction(tx) => tx.gas_limit,
1454 Transaction::FrameTransaction(tx) => tx.max_gas(),
1455 }
1456 }
1457
1458 pub fn gas_price(&self) -> U256 {
1460 match self {
1461 Transaction::LegacyTransaction(tx) => tx.gas_price,
1462 Transaction::EIP2930Transaction(tx) => tx.gas_price,
1463 Transaction::EIP1559Transaction(tx) => U256::from(tx.max_fee_per_gas),
1464 Transaction::EIP7702Transaction(tx) => U256::from(tx.max_fee_per_gas),
1465 Transaction::EIP4844Transaction(tx) => U256::from(tx.max_fee_per_gas),
1466 Transaction::PrivilegedL2Transaction(tx) => U256::from(tx.max_fee_per_gas),
1467 Transaction::FeeTokenTransaction(tx) => U256::from(tx.max_fee_per_gas),
1468 Transaction::FrameTransaction(tx) => U256::from(tx.max_fee_per_gas),
1469 }
1470 }
1471
1472 pub fn to(&self) -> TxKind {
1473 match self {
1474 Transaction::LegacyTransaction(tx) => tx.to.clone(),
1475 Transaction::EIP2930Transaction(tx) => tx.to.clone(),
1476 Transaction::EIP1559Transaction(tx) => tx.to.clone(),
1477 Transaction::EIP4844Transaction(tx) => TxKind::Call(tx.to),
1478 Transaction::EIP7702Transaction(tx) => TxKind::Call(tx.to),
1479 Transaction::PrivilegedL2Transaction(tx) => tx.to.clone(),
1480 Transaction::FeeTokenTransaction(tx) => tx.to.clone(),
1481 Transaction::FrameTransaction(tx) => TxKind::Call(tx.sender),
1482 }
1483 }
1484
1485 pub fn value(&self) -> U256 {
1486 match self {
1487 Transaction::LegacyTransaction(tx) => tx.value,
1488 Transaction::EIP2930Transaction(tx) => tx.value,
1489 Transaction::EIP1559Transaction(tx) => tx.value,
1490 Transaction::EIP4844Transaction(tx) => tx.value,
1491 Transaction::EIP7702Transaction(tx) => tx.value,
1492 Transaction::PrivilegedL2Transaction(tx) => tx.value,
1493 Transaction::FeeTokenTransaction(tx) => tx.value,
1494 Transaction::FrameTransaction(_) => U256::zero(),
1495 }
1496 }
1497
1498 pub fn max_priority_fee(&self) -> Option<u64> {
1499 match self {
1500 Transaction::LegacyTransaction(_tx) => None,
1501 Transaction::EIP2930Transaction(_tx) => None,
1502 Transaction::EIP1559Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1503 Transaction::EIP4844Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1504 Transaction::EIP7702Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1505 Transaction::PrivilegedL2Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1506 Transaction::FeeTokenTransaction(tx) => Some(tx.max_priority_fee_per_gas),
1507 Transaction::FrameTransaction(tx) => Some(tx.max_priority_fee_per_gas),
1508 }
1509 }
1510
1511 pub fn chain_id(&self) -> Option<u64> {
1512 match self {
1513 Transaction::LegacyTransaction(tx) => derive_legacy_chain_id(tx.v),
1514 Transaction::EIP2930Transaction(tx) => Some(tx.chain_id),
1515 Transaction::EIP1559Transaction(tx) => Some(tx.chain_id),
1516 Transaction::EIP4844Transaction(tx) => Some(tx.chain_id),
1517 Transaction::EIP7702Transaction(tx) => Some(tx.chain_id),
1518 Transaction::PrivilegedL2Transaction(tx) => Some(tx.chain_id),
1519 Transaction::FeeTokenTransaction(tx) => Some(tx.chain_id),
1520 Transaction::FrameTransaction(tx) => Some(tx.chain_id),
1521 }
1522 }
1523
1524 pub fn access_list(&self) -> &AccessList {
1525 static EMPTY_ACCESS_LIST: AccessList = Vec::new();
1526 match self {
1527 Transaction::LegacyTransaction(_tx) => &EMPTY_ACCESS_LIST,
1528 Transaction::EIP2930Transaction(tx) => &tx.access_list,
1529 Transaction::EIP1559Transaction(tx) => &tx.access_list,
1530 Transaction::EIP4844Transaction(tx) => &tx.access_list,
1531 Transaction::EIP7702Transaction(tx) => &tx.access_list,
1532 Transaction::PrivilegedL2Transaction(tx) => &tx.access_list,
1533 Transaction::FeeTokenTransaction(tx) => &tx.access_list,
1534 Transaction::FrameTransaction(_) => &EMPTY_ACCESS_LIST,
1535 }
1536 }
1537 pub fn authorization_list(&self) -> Option<&AuthorizationList> {
1538 match self {
1539 Transaction::LegacyTransaction(_) => None,
1540 Transaction::EIP2930Transaction(_) => None,
1541 Transaction::EIP1559Transaction(_) => None,
1542 Transaction::EIP4844Transaction(_) => None,
1543 Transaction::EIP7702Transaction(tx) => Some(&tx.authorization_list),
1544 Transaction::PrivilegedL2Transaction(_) => None,
1545 Transaction::FeeTokenTransaction(_) => None,
1546 Transaction::FrameTransaction(_) => None,
1547 }
1548 }
1549
1550 pub fn nonce(&self) -> u64 {
1551 match self {
1552 Transaction::LegacyTransaction(tx) => tx.nonce,
1553 Transaction::EIP2930Transaction(tx) => tx.nonce,
1554 Transaction::EIP1559Transaction(tx) => tx.nonce,
1555 Transaction::EIP4844Transaction(tx) => tx.nonce,
1556 Transaction::EIP7702Transaction(tx) => tx.nonce,
1557 Transaction::PrivilegedL2Transaction(tx) => tx.nonce,
1558 Transaction::FeeTokenTransaction(tx) => tx.nonce,
1559 Transaction::FrameTransaction(tx) => tx.nonce,
1560 }
1561 }
1562
1563 pub fn data(&self) -> &Bytes {
1564 match self {
1565 Transaction::LegacyTransaction(tx) => &tx.data,
1566 Transaction::EIP2930Transaction(tx) => &tx.data,
1567 Transaction::EIP1559Transaction(tx) => &tx.data,
1568 Transaction::EIP4844Transaction(tx) => &tx.data,
1569 Transaction::EIP7702Transaction(tx) => &tx.data,
1570 Transaction::PrivilegedL2Transaction(tx) => &tx.data,
1571 Transaction::FeeTokenTransaction(tx) => &tx.data,
1572 Transaction::FrameTransaction(_) => {
1573 static EMPTY_DATA: Bytes = Bytes::new();
1574 &EMPTY_DATA
1575 }
1576 }
1577 }
1578
1579 pub fn blob_versioned_hashes(&self) -> Vec<H256> {
1580 match self {
1581 Transaction::LegacyTransaction(_) => Vec::new(),
1582 Transaction::EIP2930Transaction(_) => Vec::new(),
1583 Transaction::EIP1559Transaction(_) => Vec::new(),
1584 Transaction::EIP4844Transaction(tx) => tx.blob_versioned_hashes.clone(),
1585 Transaction::EIP7702Transaction(_) => Vec::new(),
1586 Transaction::PrivilegedL2Transaction(_) => Vec::new(),
1587 Transaction::FeeTokenTransaction(_) => Vec::new(),
1588 Transaction::FrameTransaction(tx) => tx.blob_versioned_hashes.clone(),
1589 }
1590 }
1591
1592 pub fn max_fee_per_blob_gas(&self) -> Option<U256> {
1593 match self {
1594 Transaction::LegacyTransaction(_) => None,
1595 Transaction::EIP2930Transaction(_) => None,
1596 Transaction::EIP1559Transaction(_) => None,
1597 Transaction::EIP4844Transaction(tx) => Some(tx.max_fee_per_blob_gas),
1598 Transaction::EIP7702Transaction(_) => None,
1599 Transaction::PrivilegedL2Transaction(_) => None,
1600 Transaction::FeeTokenTransaction(_) => None,
1601 Transaction::FrameTransaction(tx) => {
1602 if tx.blob_versioned_hashes.is_empty() {
1603 None
1604 } else {
1605 Some(tx.max_fee_per_blob_gas)
1606 }
1607 }
1608 }
1609 }
1610
1611 pub fn is_blob_carrying(&self) -> bool {
1618 self.max_fee_per_blob_gas().is_some()
1619 }
1620
1621 pub fn is_contract_creation(&self) -> bool {
1622 match &self {
1623 Transaction::LegacyTransaction(t) => matches!(t.to, TxKind::Create),
1624 Transaction::EIP2930Transaction(t) => matches!(t.to, TxKind::Create),
1625 Transaction::EIP1559Transaction(t) => matches!(t.to, TxKind::Create),
1626 Transaction::EIP4844Transaction(_) => false,
1627 Transaction::EIP7702Transaction(_) => false,
1628 Transaction::PrivilegedL2Transaction(t) => matches!(t.to, TxKind::Create),
1629 Transaction::FeeTokenTransaction(t) => matches!(t.to, TxKind::Create),
1630 Transaction::FrameTransaction(_) => false,
1631 }
1632 }
1633
1634 pub fn is_privileged(&self) -> bool {
1635 matches!(self, Transaction::PrivilegedL2Transaction(_))
1636 }
1637
1638 pub fn max_fee_per_gas(&self) -> Option<u64> {
1639 match self {
1640 Transaction::LegacyTransaction(_tx) => None,
1641 Transaction::EIP2930Transaction(_tx) => None,
1642 Transaction::EIP1559Transaction(tx) => Some(tx.max_fee_per_gas),
1643 Transaction::EIP4844Transaction(tx) => Some(tx.max_fee_per_gas),
1644 Transaction::EIP7702Transaction(tx) => Some(tx.max_fee_per_gas),
1645 Transaction::PrivilegedL2Transaction(tx) => Some(tx.max_fee_per_gas),
1646 Transaction::FeeTokenTransaction(tx) => Some(tx.max_fee_per_gas),
1647 Transaction::FrameTransaction(tx) => Some(tx.max_fee_per_gas),
1648 }
1649 }
1650
1651 fn compute_hash(&self, crypto: &dyn Crypto) -> H256 {
1652 if let Transaction::PrivilegedL2Transaction(tx) = self {
1653 return tx.get_privileged_hash().unwrap_or_default();
1654 }
1655 H256(crypto.keccak256(&self.encode_canonical_to_vec()))
1656 }
1657
1658 pub fn hash(&self, crypto: &dyn Crypto) -> H256 {
1659 let inner_hash = match self {
1660 Transaction::LegacyTransaction(tx) => &tx.inner_hash,
1661 Transaction::EIP2930Transaction(tx) => &tx.inner_hash,
1662 Transaction::EIP1559Transaction(tx) => &tx.inner_hash,
1663 Transaction::EIP4844Transaction(tx) => &tx.inner_hash,
1664 Transaction::EIP7702Transaction(tx) => &tx.inner_hash,
1665 Transaction::PrivilegedL2Transaction(tx) => &tx.inner_hash,
1666 Transaction::FeeTokenTransaction(tx) => &tx.inner_hash,
1667 Transaction::FrameTransaction(tx) => &tx.inner_hash,
1668 };
1669
1670 *inner_hash.get_or_init(|| self.compute_hash(crypto))
1671 }
1672
1673 pub fn gas_tip_cap(&self) -> U256 {
1674 self.max_priority_fee()
1675 .map(U256::from)
1676 .unwrap_or_else(|| self.gas_price())
1677 }
1678
1679 pub fn gas_fee_cap(&self) -> U256 {
1680 self.max_fee_per_gas()
1681 .map(U256::from)
1682 .unwrap_or_else(|| self.gas_price())
1683 }
1684
1685 pub fn effective_gas_tip(&self, base_fee: Option<u64>) -> Option<U256> {
1689 let tip_cap = self.gas_tip_cap();
1690 let Some(base_fee) = base_fee else {
1691 return Some(tip_cap);
1692 };
1693 let base_fee = U256::from(base_fee);
1694 let fee_cap = self.gas_fee_cap();
1695 let tip = fee_cap.checked_sub(base_fee)?;
1696 Some(min(tip, tip_cap))
1697 }
1698
1699 pub fn protected(&self) -> bool {
1702 match self {
1703 Transaction::LegacyTransaction(tx) if tx.v.bits() <= 8 => {
1704 let v = tx.v.low_u64();
1705 v != 27 && v != 28 && v != 1 && v != 0
1706 }
1707 _ => true,
1708 }
1709 }
1710}
1711
1712fn derive_legacy_chain_id(v: U256) -> Option<u64> {
1713 let v = u64::try_from(v).ok()?;
1714 if v < 35 { None } else { Some((v - 35) / 2) }
1719}
1720
1721impl TxType {
1722 pub fn from_u8(value: u8) -> Option<Self> {
1723 match value {
1724 0x00 => Some(Self::Legacy),
1725 0x01 => Some(Self::EIP2930),
1726 0x02 => Some(Self::EIP1559),
1727 0x03 => Some(Self::EIP4844),
1728 0x04 => Some(Self::EIP7702),
1729 0x06 => Some(Self::Frame),
1730 0x7d => Some(Self::FeeToken),
1731 0x7e => Some(Self::Privileged),
1732 _ => None,
1733 }
1734 }
1735
1736 pub fn as_envelope(self) -> Option<EnvelopeTxType> {
1741 match self {
1742 TxType::EIP2930 => Some(EnvelopeTxType::EIP2930),
1743 TxType::EIP1559 => Some(EnvelopeTxType::EIP1559),
1744 TxType::EIP4844 => Some(EnvelopeTxType::EIP4844),
1745 TxType::EIP7702 => Some(EnvelopeTxType::EIP7702),
1746 TxType::Frame => Some(EnvelopeTxType::Frame),
1747 TxType::FeeToken => Some(EnvelopeTxType::FeeToken),
1748 TxType::Privileged => Some(EnvelopeTxType::Privileged),
1749 TxType::Legacy => None,
1750 }
1751 }
1752
1753 pub fn is_l2_only(self) -> bool {
1761 match self {
1762 Self::Legacy
1763 | Self::EIP2930
1764 | Self::EIP1559
1765 | Self::EIP4844
1766 | Self::EIP7702
1767 | Self::Frame => false,
1768 Self::FeeToken | Self::Privileged => true,
1769 }
1770 }
1771}
1772
1773impl PrivilegedL2Transaction {
1774 pub fn get_privileged_hash(&self) -> Option<H256> {
1778 let to = match self.to {
1780 TxKind::Call(to) => to,
1781 _ => return None,
1782 };
1783
1784 let value = self.value.to_big_endian();
1785
1786 let u256_nonce = U256::from(self.nonce);
1789 let nonce = u256_nonce.to_big_endian();
1790
1791 Some(crate::utils::keccak(
1792 [
1793 U256::from(self.chain_id).to_big_endian().as_ref(),
1794 self.from.as_bytes(),
1795 to.as_bytes(),
1796 &nonce,
1797 &value,
1798 &U256::from(self.gas_limit).to_big_endian(),
1799 keccak(&self.data).as_bytes(),
1800 ]
1801 .concat(),
1802 ))
1803 }
1804}
1805
1806#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1807pub struct FeeTokenTransaction {
1808 pub chain_id: u64,
1809 pub nonce: u64,
1810 pub max_priority_fee_per_gas: u64,
1811 pub max_fee_per_gas: u64,
1812 pub gas_limit: u64,
1813 pub to: TxKind,
1814 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1815 pub value: U256,
1816 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1817 pub data: Bytes,
1818 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
1819 pub access_list: AccessList,
1820 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
1821 pub fee_token: Address,
1822 pub signature_y_parity: bool,
1823 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1824 pub signature_r: U256,
1825 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1826 pub signature_s: U256,
1827 #[rkyv(with=rkyv::with::Skip)]
1828 pub inner_hash: OnceCell<H256>,
1829 #[rkyv(with=rkyv::with::Skip)]
1830 pub sender_cache: OnceCell<Address>,
1831 #[rkyv(with=rkyv::with::Skip)]
1832 pub cached_canonical: OnceCell<Vec<u8>>,
1833}
1834
1835#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1837#[repr(u8)]
1838pub enum FrameMode {
1839 #[default]
1840 Default = 0,
1841 Verify = 1,
1842 Sender = 2,
1843}
1844
1845impl FrameMode {
1846 pub fn from_u8(val: u8) -> Option<Self> {
1849 match val {
1850 0 => Some(FrameMode::Default),
1851 1 => Some(FrameMode::Verify),
1852 2 => Some(FrameMode::Sender),
1853 _ => None,
1854 }
1855 }
1856}
1857
1858impl From<FrameMode> for u8 {
1859 fn from(mode: FrameMode) -> u8 {
1860 match mode {
1861 FrameMode::Default => 0,
1862 FrameMode::Verify => 1,
1863 FrameMode::Sender => 2,
1864 }
1865 }
1866}
1867
1868#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1874pub struct Frame {
1875 pub mode: u8,
1876 pub flags: u8,
1877 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H160Wrapper>)]
1878 pub target: Option<Address>,
1879 pub gas_limit: u64,
1880 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1884 pub value: U256,
1885 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1886 pub data: Bytes,
1887}
1888
1889impl Frame {
1890 pub fn execution_mode(&self) -> FrameMode {
1892 FrameMode::from_u8(self.mode).unwrap_or_default()
1893 }
1894
1895 pub fn scope_restriction(&self) -> u8 {
1897 self.flags & 0x03
1898 }
1899
1900 pub fn is_atomic_batch(&self) -> bool {
1902 (self.flags >> 2) & 1 == 1
1903 }
1904
1905 pub fn is_expiry_verifier(&self) -> bool {
1908 self.execution_mode() == FrameMode::Verify
1909 && self.target == Some(frame_tx_expiry_verifier())
1910 }
1911}
1912
1913impl RLPEncode for Frame {
1914 fn encode(&self, buf: &mut dyn bytes::BufMut) {
1915 let target_kind = match self.target {
1917 Some(addr) => TxKind::Call(addr),
1918 None => TxKind::Create, };
1920 Encoder::new(buf)
1921 .encode_field(&(self.mode as u64))
1922 .encode_field(&(self.flags as u64))
1923 .encode_field(&target_kind)
1924 .encode_field(&self.gas_limit)
1925 .encode_field(&self.value)
1926 .encode_field(&self.data)
1927 .finish();
1928 }
1929}
1930
1931impl RLPDecode for Frame {
1932 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
1933 let decoder = Decoder::new(rlp)?;
1934 let (mode_u64, decoder): (u64, _) = decoder.decode_field("mode")?;
1935 let mode = u8::try_from(mode_u64)
1936 .map_err(|_| RLPDecodeError::Custom(format!("Frame mode too large: {mode_u64}")))?;
1937 let (flags_u64, decoder): (u64, _) = decoder.decode_field("flags")?;
1938 let flags = u8::try_from(flags_u64)
1939 .map_err(|_| RLPDecodeError::Custom(format!("Frame flags too large: {flags_u64}")))?;
1940 let (target_kind, decoder): (TxKind, _) = decoder.decode_field("target")?;
1941 let target = match target_kind {
1942 TxKind::Call(addr) => Some(addr),
1943 TxKind::Create => None,
1944 };
1945 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
1946 let (value, decoder): (U256, _) = decoder.decode_field("value")?;
1947 let (data, decoder) = decoder.decode_field("data")?;
1948 let frame = Frame {
1949 mode,
1950 flags,
1951 target,
1952 gas_limit,
1953 value,
1954 data,
1955 };
1956 Ok((frame, decoder.finish()?))
1957 }
1958}
1959
1960#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1968pub struct FrameSignature {
1969 pub scheme: u8,
1970 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H160Wrapper>)]
1971 pub signer: Option<Address>,
1972 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1973 pub msg: Bytes,
1974 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1975 pub signature: Bytes,
1976}
1977
1978impl RLPEncode for FrameSignature {
1979 fn encode(&self, buf: &mut dyn bytes::BufMut) {
1980 let signer_kind = match self.signer {
1983 Some(addr) => TxKind::Call(addr),
1984 None => TxKind::Create,
1985 };
1986 Encoder::new(buf)
1987 .encode_field(&self.scheme)
1988 .encode_field(&signer_kind)
1989 .encode_field(&self.msg)
1990 .encode_field(&self.signature)
1991 .finish();
1992 }
1993}
1994
1995impl RLPDecode for FrameSignature {
1996 fn decode_unfinished(rlp: &[u8]) -> Result<(FrameSignature, &[u8]), RLPDecodeError> {
1997 let decoder = Decoder::new(rlp)?;
1998 let (scheme, decoder) = decoder.decode_field("scheme")?;
1999 let (signer_kind, decoder): (TxKind, _) = decoder.decode_field("signer")?;
2000 let signer = match signer_kind {
2001 TxKind::Call(addr) => Some(addr),
2002 TxKind::Create => None,
2003 };
2004 let (msg, decoder) = decoder.decode_field("msg")?;
2005 let (signature, decoder) = decoder.decode_field("signature")?;
2006 Ok((
2007 FrameSignature {
2008 scheme,
2009 signer,
2010 msg,
2011 signature,
2012 },
2013 decoder.finish()?,
2014 ))
2015 }
2016}
2017
2018#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
2022pub struct FrameTransaction {
2023 pub chain_id: u64,
2024 pub nonce: u64,
2025 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
2026 pub sender: Address,
2027 pub frames: Vec<Frame>,
2028 pub signatures: Vec<FrameSignature>,
2031 pub max_priority_fee_per_gas: u64,
2032 pub max_fee_per_gas: u64,
2033 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
2034 pub max_fee_per_blob_gas: U256,
2035 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H256Wrapper>)]
2036 pub blob_versioned_hashes: Vec<H256>,
2037 #[rkyv(with=rkyv::with::Skip)]
2038 pub inner_hash: OnceCell<H256>,
2039 #[rkyv(with=rkyv::with::Skip)]
2040 pub cached_canonical: OnceCell<Vec<u8>>,
2041}
2042
2043pub const FRAME_TX_INTRINSIC_COST: u64 = 15000;
2045pub const FRAME_TX_PER_FRAME_COST: u64 = 475;
2047pub const FRAME_TX_ENTRY_POINT_U64: u64 = 0xaa;
2049pub const FRAME_TX_MAX_FRAMES: usize = 64;
2051const FRAME_TX_STANDARD_TOKEN_COST: u64 = 4;
2055const FRAME_TX_TOTAL_COST_FLOOR_PER_TOKEN: u64 = 16;
2056pub const FRAME_SIG_SCHEME_ARBITRARY: u8 = 0;
2058pub const FRAME_SIG_SCHEME_SECP256K1: u8 = 1;
2059pub const FRAME_SIG_SCHEME_P256: u8 = 2;
2060pub const FRAME_TX_MAX_VERIFY_GAS: u64 = 100_000;
2066pub const APPROVE_PAYMENT: u8 = 0x1;
2069pub const APPROVE_EXECUTION: u8 = 0x2;
2070pub const APPROVE_EXECUTION_AND_PAYMENT: u8 = 0x3;
2071pub const FRAME_TX_MAX_PENDING_NONCANONICAL_PAYMASTER: u8 = 1;
2076
2077pub fn frame_tx_entry_point() -> Address {
2080 Address::from_low_u64_be(FRAME_TX_ENTRY_POINT_U64)
2081}
2082
2083pub const FRAME_TX_EXPIRY_VERIFIER_U64: u64 = 0x8141;
2085pub const FRAME_TX_EXPIRY_DATA_LENGTH: usize = 8;
2087
2088pub fn frame_tx_expiry_verifier() -> Address {
2090 Address::from_low_u64_be(FRAME_TX_EXPIRY_VERIFIER_U64)
2091}
2092
2093impl FrameTransaction {
2094 pub fn compute_sig_hash(&self) -> H256 {
2100 let mut buf = vec![TxType::Frame as u8];
2101 let elided_signatures: Vec<FrameSignature> = self
2102 .signatures
2103 .iter()
2104 .map(|s| {
2105 if s.msg.is_empty() {
2106 FrameSignature {
2107 scheme: s.scheme,
2108 signer: s.signer,
2109 msg: s.msg.clone(),
2110 signature: Bytes::new(),
2111 }
2112 } else {
2113 s.clone()
2114 }
2115 })
2116 .collect();
2117 Encoder::new(&mut buf)
2119 .encode_field(&self.chain_id)
2120 .encode_field(&self.nonce)
2121 .encode_field(&self.sender)
2122 .encode_field(&self.frames)
2123 .encode_field(&elided_signatures)
2124 .encode_field(&self.max_priority_fee_per_gas)
2125 .encode_field(&self.max_fee_per_gas)
2126 .encode_field(&self.max_fee_per_blob_gas)
2127 .encode_field(&self.blob_versioned_hashes)
2128 .finish();
2129 keccak(&buf)
2130 }
2131
2132 pub fn signature_verification_cost(&self) -> u64 {
2136 self.signatures
2137 .iter()
2138 .map(|s| match s.scheme {
2139 FRAME_SIG_SCHEME_ARBITRARY => 100u64,
2140 FRAME_SIG_SCHEME_SECP256K1 => 2800u64,
2141 FRAME_SIG_SCHEME_P256 => 6700u64,
2142 _ => 0,
2143 })
2144 .sum()
2145 }
2146
2147 fn data_fields(&self) -> impl Iterator<Item = &[u8]> {
2151 const EMPTY: &[u8] = &[];
2152 self.frames
2153 .iter()
2154 .map(|f| f.data.as_ref())
2155 .chain(self.signatures.iter().flat_map(|s| {
2156 [
2157 s.signer.as_ref().map_or(EMPTY, |a| a.as_bytes()),
2158 s.msg.as_ref(),
2159 s.signature.as_ref(),
2160 ]
2161 }))
2162 }
2163
2164 pub fn data_cost(&self) -> u64 {
2167 self.data_fields().flatten().fold(0u64, |acc, byte| {
2168 acc.saturating_add(if *byte == 0 { 4 } else { 16 })
2169 })
2170 }
2171
2172 pub fn calldata_tokens(&self) -> u64 {
2177 self.data_fields()
2178 .fold(0u64, |acc, field| acc.saturating_add(field.len() as u64))
2179 .saturating_mul(FRAME_TX_STANDARD_TOKEN_COST)
2180 }
2181
2182 pub fn calldata_floor_gas(&self) -> u64 {
2185 self.calldata_tokens()
2186 .saturating_mul(FRAME_TX_TOTAL_COST_FLOOR_PER_TOKEN)
2187 }
2188
2189 pub fn mandatory_gas(&self) -> u64 {
2192 FRAME_TX_INTRINSIC_COST
2193 .saturating_add((self.frames.len() as u64).saturating_mul(FRAME_TX_PER_FRAME_COST))
2194 .saturating_add(self.signature_verification_cost())
2195 }
2196
2197 pub fn standard_gas_limit(&self) -> u64 {
2200 self.mandatory_gas()
2201 .saturating_add(self.data_cost())
2202 .saturating_add(
2203 self.frames
2204 .iter()
2205 .map(|f| f.gas_limit)
2206 .fold(0u64, |acc, g| acc.saturating_add(g)),
2207 )
2208 }
2209
2210 pub fn calldata_floor_total(&self) -> u64 {
2214 self.mandatory_gas()
2215 .saturating_add(self.calldata_floor_gas())
2216 }
2217
2218 pub fn max_gas(&self) -> u64 {
2223 self.standard_gas_limit().max(self.calldata_floor_total())
2224 }
2225
2226 pub fn expiry_deadline(&self) -> Option<u64> {
2229 self.frames
2230 .iter()
2231 .find(|f| f.is_expiry_verifier())
2232 .and_then(|f| {
2233 let bytes: [u8; FRAME_TX_EXPIRY_DATA_LENGTH] = f.data.as_ref().try_into().ok()?;
2234 Some(u64::from_be_bytes(bytes))
2235 })
2236 }
2237
2238 pub fn validate_static_constraints(&self) -> Result<(), String> {
2241 if self.sender == Address::zero() {
2243 return Err("tx.sender must not be zero address".to_string());
2244 }
2245 if self.frames.is_empty() || self.frames.len() > FRAME_TX_MAX_FRAMES {
2246 return Err(format!(
2247 "Frame count must be between 1 and {FRAME_TX_MAX_FRAMES}"
2248 ));
2249 }
2250 for (i, hash) in self.blob_versioned_hashes.iter().enumerate() {
2253 if hash.0.first() != Some(&VERSIONED_HASH_VERSION_KZG) {
2254 return Err(format!("Blob versioned hash {i}: wrong version byte"));
2255 }
2256 }
2257 if self.blob_versioned_hashes.is_empty() && !self.max_fee_per_blob_gas.is_zero() {
2258 return Err(
2259 "max_fee_per_blob_gas must be zero when the transaction carries no blobs"
2260 .to_string(),
2261 );
2262 }
2263 for (i, sig) in self.signatures.iter().enumerate() {
2266 match sig.scheme {
2267 FRAME_SIG_SCHEME_ARBITRARY => {
2270 if sig.signer.is_some() {
2271 return Err(format!(
2272 "Signature {i}: ARBITRARY signatures must not name a signer"
2273 ));
2274 }
2275 }
2276 FRAME_SIG_SCHEME_SECP256K1 | FRAME_SIG_SCHEME_P256 => {}
2277 other => {
2278 return Err(format!("Signature {i}: unsupported scheme {other}"));
2279 }
2280 }
2281 match sig.msg.len() {
2282 0 => {}
2283 32 => {
2284 if sig.msg.iter().all(|&b| b == 0) {
2285 return Err(format!(
2286 "Signature {i}: explicit msg must not be zero digest"
2287 ));
2288 }
2289 }
2290 other => {
2291 return Err(format!(
2292 "Signature {i}: msg must be empty or 32 bytes, got {other}"
2293 ));
2294 }
2295 }
2296 }
2297 let mut total_frame_gas: u128 = 0;
2300 let mut expiry_frame_count: usize = 0;
2301 for (i, frame) in self.frames.iter().enumerate() {
2302 if frame.mode >= 3 {
2304 return Err(format!("Frame {i}: reserved execution mode {}", frame.mode));
2305 }
2306 if frame.flags >= 8 {
2308 return Err(format!(
2309 "Frame {i}: reserved flag bits must be zero (flags={:#04x})",
2310 frame.flags
2311 ));
2312 }
2313 if frame.is_expiry_verifier() {
2318 expiry_frame_count = expiry_frame_count.saturating_add(1);
2319 if expiry_frame_count > 1 {
2320 return Err(format!("Frame {i}: more than one expiry verifier frame"));
2321 }
2322 if frame.flags != 0 {
2323 return Err(format!(
2324 "Frame {i}: expiry verifier frame must have flags == 0"
2325 ));
2326 }
2327 if frame.data.len() != FRAME_TX_EXPIRY_DATA_LENGTH {
2328 return Err(format!(
2329 "Frame {i}: expiry verifier frame data must be {FRAME_TX_EXPIRY_DATA_LENGTH} bytes"
2330 ));
2331 }
2332 }
2333 if frame.mode != FrameMode::Sender as u8 && !frame.value.is_zero() {
2337 return Err(format!(
2338 "Frame {i}: non-zero value only allowed in SENDER mode (mode={}, value={})",
2339 frame.mode, frame.value
2340 ));
2341 }
2342 if frame.gas_limit > i64::MAX as u64 {
2350 return Err(format!(
2351 "Frame {i}: gas_limit {} exceeds 2**63-1",
2352 frame.gas_limit
2353 ));
2354 }
2355 total_frame_gas = total_frame_gas
2356 .checked_add(frame.gas_limit as u128)
2357 .ok_or_else(|| format!("Frame {i}: cumulative gas_limit overflow"))?;
2358 if total_frame_gas > i64::MAX as u128 {
2359 return Err(format!(
2360 "Frame {i}: cumulative frame gas_limit {} exceeds 2**63-1",
2361 total_frame_gas
2362 ));
2363 }
2364 if frame.flags & APPROVE_EXECUTION != 0
2367 && frame.target.is_some_and(|target| target != self.sender)
2368 {
2369 return Err(format!(
2370 "Frame {i}: APPROVE_EXECUTION requires an empty target or tx.sender"
2371 ));
2372 }
2373 if frame.is_atomic_batch() {
2378 if frame.mode == FrameMode::Verify as u8 {
2379 return Err(format!("Frame {i}: atomic batch flag on a VERIFY frame"));
2380 }
2381 match self.frames.get(i.saturating_add(1)) {
2382 None => return Err(format!("Frame {i}: atomic batch flag on last frame")),
2383 Some(next) if next.mode == FrameMode::Verify as u8 => {
2384 return Err(format!(
2385 "Frame {i}: atomic batch flag followed by a VERIFY frame"
2386 ));
2387 }
2388 Some(_) => {}
2389 }
2390 }
2391 }
2392 Ok(())
2393 }
2394
2395 pub fn validation_prefix(&self) -> Result<ValidationPrefix, FrameValidationError> {
2411 let non_expiry: Vec<usize> = self
2413 .frames
2414 .iter()
2415 .enumerate()
2416 .filter(|(_, f)| !f.is_expiry_verifier())
2417 .map(|(i, _)| i)
2418 .collect();
2419
2420 let frame = |pos: usize| -> Option<&Frame> {
2422 non_expiry.get(pos).and_then(|&idx| self.frames.get(idx))
2423 };
2424
2425 let is_default = |pos: usize| -> bool {
2426 frame(pos).is_some_and(|f| f.execution_mode() == FrameMode::Default)
2427 };
2428 let is_verify = |pos: usize| -> bool {
2429 frame(pos).is_some_and(|f| f.execution_mode() == FrameMode::Verify)
2430 };
2431 let scope_of = |pos: usize| -> u8 { frame(pos).map_or(0, |f| f.scope_restriction()) };
2432
2433 if non_expiry.is_empty() {
2434 return Err(FrameValidationError::UnrecognizedPrefix);
2435 }
2436
2437 if is_default(0)
2441 && is_verify(1)
2442 && scope_of(1) == APPROVE_EXECUTION
2443 && is_verify(2)
2444 && scope_of(2) == APPROVE_PAYMENT
2445 {
2446 return Ok(ValidationPrefix {
2447 shape: PrefixShape::DeployOnlyVerifyPay,
2448 frame_indices: vec![non_expiry[0], non_expiry[1], non_expiry[2]],
2449 deploy_index: Some(non_expiry[0]),
2450 pay_index: Some(non_expiry[2]),
2451 });
2452 }
2453
2454 if is_default(0) && is_verify(1) && scope_of(1) == APPROVE_EXECUTION_AND_PAYMENT {
2456 return Ok(ValidationPrefix {
2457 shape: PrefixShape::DeploySelfVerify,
2458 frame_indices: vec![non_expiry[0], non_expiry[1]],
2459 deploy_index: Some(non_expiry[0]),
2460 pay_index: Some(non_expiry[1]),
2461 });
2462 }
2463
2464 if is_verify(0)
2466 && scope_of(0) == APPROVE_EXECUTION
2467 && is_verify(1)
2468 && scope_of(1) == APPROVE_PAYMENT
2469 {
2470 return Ok(ValidationPrefix {
2471 shape: PrefixShape::OnlyVerifyPay,
2472 frame_indices: vec![non_expiry[0], non_expiry[1]],
2473 deploy_index: None,
2474 pay_index: Some(non_expiry[1]),
2475 });
2476 }
2477
2478 if is_verify(0) && scope_of(0) == APPROVE_EXECUTION_AND_PAYMENT {
2480 return Ok(ValidationPrefix {
2481 shape: PrefixShape::SelfVerify,
2482 frame_indices: vec![non_expiry[0]],
2483 deploy_index: None,
2484 pay_index: Some(non_expiry[0]),
2485 });
2486 }
2487
2488 Err(FrameValidationError::UnrecognizedPrefix)
2489 }
2490
2491 pub fn validate_prefix_structure(
2505 &self,
2506 prefix: &ValidationPrefix,
2507 ) -> Result<(), FrameValidationError> {
2508 let mut deploy_count = 0usize;
2509
2510 for &idx in &prefix.frame_indices {
2511 let frame = &self.frames[idx];
2512
2513 if frame.is_atomic_batch() {
2514 return Err(FrameValidationError::AtomicBatchInPrefix { frame_index: idx });
2515 }
2516
2517 match prefix.deploy_index {
2518 Some(deploy_idx) if deploy_idx == idx => {
2519 deploy_count += 1;
2521 if deploy_count > 1 {
2522 return Err(FrameValidationError::MultipleDeploys { frame_index: idx });
2523 }
2524 if prefix.frame_indices.first() != Some(&idx) {
2529 return Err(FrameValidationError::DeployNotFirst { frame_index: idx });
2530 }
2531 if frame.execution_mode() != FrameMode::Default {
2532 return Err(FrameValidationError::DeployNotDefaultMode {
2533 frame_index: idx,
2534 });
2535 }
2536 }
2537 _ => {
2538 if frame.execution_mode() != FrameMode::Verify {
2540 return Err(FrameValidationError::VerifyFrameNotVerifyMode {
2541 frame_index: idx,
2542 });
2543 }
2544
2545 let target_ok = match frame.target {
2547 None => true,
2548 Some(addr) => addr == self.sender,
2549 };
2550 if !target_ok {
2551 return Err(FrameValidationError::VerifyTargetNotSender {
2552 frame_index: idx,
2553 });
2554 }
2555
2556 let expected_scope = match prefix.shape {
2558 PrefixShape::SelfVerify | PrefixShape::DeploySelfVerify => {
2559 APPROVE_EXECUTION_AND_PAYMENT
2560 }
2561 PrefixShape::OnlyVerifyPay | PrefixShape::DeployOnlyVerifyPay => {
2562 if prefix.pay_index == Some(idx) {
2564 APPROVE_PAYMENT
2565 } else {
2566 APPROVE_EXECUTION
2567 }
2568 }
2569 };
2570 if frame.scope_restriction() != expected_scope {
2571 return Err(FrameValidationError::WrongScopeRestriction {
2572 frame_index: idx,
2573 expected: expected_scope,
2574 actual: frame.scope_restriction(),
2575 });
2576 }
2577 }
2578 }
2579 }
2580
2581 let prefix_gas: u64 = prefix
2583 .frame_indices
2584 .iter()
2585 .map(|&i| self.frames[i].gas_limit)
2586 .fold(0u64, |acc, g| acc.saturating_add(g));
2587 let total_verify_gas = prefix_gas.saturating_add(self.signature_verification_cost());
2588 if total_verify_gas > FRAME_TX_MAX_VERIFY_GAS {
2589 return Err(FrameValidationError::VerifyGasBudgetExceeded {
2590 actual: total_verify_gas,
2591 limit: FRAME_TX_MAX_VERIFY_GAS,
2592 });
2593 }
2594
2595 Ok(())
2596 }
2597}
2598
2599#[derive(Clone, Debug, PartialEq, Eq)]
2601pub enum PrefixShape {
2602 SelfVerify,
2604 DeploySelfVerify,
2606 OnlyVerifyPay,
2608 DeployOnlyVerifyPay,
2610}
2611
2612#[derive(Clone, Debug, PartialEq, Eq)]
2614pub struct ValidationPrefix {
2615 pub shape: PrefixShape,
2617 pub frame_indices: Vec<usize>,
2620 pub deploy_index: Option<usize>,
2622 pub pay_index: Option<usize>,
2624}
2625
2626#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
2629pub enum FrameValidationError {
2630 #[error("frame transaction validation prefix does not match any recognized shape")]
2631 UnrecognizedPrefix,
2632 #[error("frame {frame_index}: prefix has more than one DEFAULT deploy frame")]
2633 MultipleDeploys { frame_index: usize },
2634 #[error("frame {frame_index}: deploy frame must be at index 0")]
2635 DeployNotFirst { frame_index: usize },
2636 #[error("frame {frame_index}: deploy frame must use DEFAULT execution mode")]
2637 DeployNotDefaultMode { frame_index: usize },
2638 #[error("frame {frame_index}: prefix VERIFY frame does not use VERIFY execution mode")]
2639 VerifyFrameNotVerifyMode { frame_index: usize },
2640 #[error("frame {frame_index}: VERIFY frame target is not tx.sender")]
2641 VerifyTargetNotSender { frame_index: usize },
2642 #[error("frame {frame_index}: scope restriction is {actual:#04x}, expected {expected:#04x}")]
2643 WrongScopeRestriction {
2644 frame_index: usize,
2645 expected: u8,
2646 actual: u8,
2647 },
2648 #[error("frame {frame_index}: prefix frame has atomic-batch flag set")]
2649 AtomicBatchInPrefix { frame_index: usize },
2650 #[error("prefix gas budget exceeded: {actual} > {limit} (MAX_VERIFY_GAS)")]
2651 VerifyGasBudgetExceeded { actual: u64, limit: u64 },
2652}
2653
2654impl RLPEncode for FrameTransaction {
2655 fn encode(&self, buf: &mut dyn bytes::BufMut) {
2656 Encoder::new(buf)
2657 .encode_field(&self.chain_id)
2658 .encode_field(&self.nonce)
2659 .encode_field(&self.sender)
2660 .encode_field(&self.frames)
2661 .encode_field(&self.signatures)
2662 .encode_field(&self.max_priority_fee_per_gas)
2663 .encode_field(&self.max_fee_per_gas)
2664 .encode_field(&self.max_fee_per_blob_gas)
2665 .encode_field(&self.blob_versioned_hashes)
2666 .finish();
2667 }
2668}
2669
2670impl RLPDecode for FrameTransaction {
2671 fn decode_unfinished(rlp: &[u8]) -> Result<(FrameTransaction, &[u8]), RLPDecodeError> {
2672 let decoder = Decoder::new(rlp)?;
2673 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
2674 let (nonce, decoder) = decode_nonce_field(decoder)?;
2675 let (sender, decoder) = decoder.decode_field("sender")?;
2676 let (frames, decoder) = decoder.decode_field("frames")?;
2677 let (signatures, decoder) = decoder.decode_field("signatures")?;
2678 let (max_priority_fee_per_gas, decoder) =
2679 decoder.decode_field("max_priority_fee_per_gas")?;
2680 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
2681 let (max_fee_per_blob_gas, decoder) = decoder.decode_field("max_fee_per_blob_gas")?;
2682 let (blob_versioned_hashes, decoder) = decoder.decode_field("blob_versioned_hashes")?;
2683 let tx = FrameTransaction {
2684 chain_id,
2685 nonce,
2686 sender,
2687 frames,
2688 signatures,
2689 max_priority_fee_per_gas,
2690 max_fee_per_gas,
2691 max_fee_per_blob_gas,
2692 blob_versioned_hashes,
2693 inner_hash: OnceCell::new(),
2694 cached_canonical: OnceCell::new(),
2695 };
2696 Ok((tx, decoder.finish()?))
2697 }
2698}
2699
2700impl PayloadRLPEncode for FrameTransaction {
2701 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
2702 buf.put_u8(TxType::Frame as u8);
2703 self.encode(buf);
2704 }
2705}
2706
2707mod canonic_encoding {
2713 use super::*;
2714
2715 impl Transaction {
2716 pub fn decode_canonical(bytes: &[u8]) -> Result<Self, RLPDecodeError> {
2722 match bytes.first() {
2724 Some(tx_type) if *tx_type < 0x7f => {
2726 let tx_bytes = &bytes[1..];
2730 match EnvelopeTxType::from_type_byte(*tx_type)? {
2731 EnvelopeTxType::EIP2930 => EIP2930Transaction::decode(tx_bytes)
2732 .map(Transaction::EIP2930Transaction),
2733 EnvelopeTxType::EIP1559 => EIP1559Transaction::decode(tx_bytes)
2734 .map(Transaction::EIP1559Transaction),
2735 EnvelopeTxType::EIP4844 => EIP4844Transaction::decode(tx_bytes)
2736 .map(Transaction::EIP4844Transaction),
2737 EnvelopeTxType::EIP7702 => EIP7702Transaction::decode(tx_bytes)
2738 .map(Transaction::EIP7702Transaction),
2739 EnvelopeTxType::Frame => {
2740 FrameTransaction::decode(tx_bytes).map(Transaction::FrameTransaction)
2741 }
2742 EnvelopeTxType::FeeToken => FeeTokenTransaction::decode(tx_bytes)
2743 .map(Transaction::FeeTokenTransaction),
2744 EnvelopeTxType::Privileged => PrivilegedL2Transaction::decode(tx_bytes)
2745 .map(Transaction::PrivilegedL2Transaction),
2746 }
2747 }
2748 _ => LegacyTransaction::decode(bytes).map(Transaction::LegacyTransaction),
2750 }
2751 }
2752
2753 pub fn encode_canonical(&self, buf: &mut dyn bytes::BufMut) {
2759 match self {
2760 Transaction::LegacyTransaction(_) => {}
2762 _ => buf.put_u8(self.tx_type() as u8),
2763 }
2764 match self {
2765 Transaction::LegacyTransaction(t) => t.encode(buf),
2766 Transaction::EIP2930Transaction(t) => t.encode(buf),
2767 Transaction::EIP1559Transaction(t) => t.encode(buf),
2768 Transaction::EIP4844Transaction(t) => t.encode(buf),
2769 Transaction::EIP7702Transaction(t) => t.encode(buf),
2770 Transaction::FeeTokenTransaction(t) => t.encode(buf),
2771 Transaction::PrivilegedL2Transaction(t) => t.encode(buf),
2772 Transaction::FrameTransaction(t) => t.encode(buf),
2773 };
2774 }
2775
2776 pub fn encode_canonical_to_vec(&self) -> Vec<u8> {
2782 if let Some(cell) = self.cached_canonical_cell() {
2783 return cell
2784 .get_or_init(|| {
2785 let mut buf = Vec::new();
2786 self.encode_canonical(&mut buf);
2787 buf
2788 })
2789 .clone();
2790 }
2791 let mut buf = Vec::new();
2792 self.encode_canonical(&mut buf);
2793 buf
2794 }
2795
2796 pub fn encode_canonical_len(&self) -> usize {
2801 let prefix_len = match self {
2802 Transaction::LegacyTransaction(_) => 0,
2803 _ => 1,
2804 };
2805 let inner_len = match self {
2806 Transaction::LegacyTransaction(t) => t.length(),
2807 Transaction::EIP2930Transaction(t) => t.length(),
2808 Transaction::EIP1559Transaction(t) => t.length(),
2809 Transaction::EIP4844Transaction(t) => t.length(),
2810 Transaction::EIP7702Transaction(t) => t.length(),
2811 Transaction::FeeTokenTransaction(t) => t.length(),
2812 Transaction::PrivilegedL2Transaction(t) => t.length(),
2813 Transaction::FrameTransaction(t) => t.length(),
2814 };
2815 prefix_len + inner_len
2816 }
2817 }
2818
2819 impl P2PTransaction {
2820 pub fn tx_type(&self) -> TxType {
2821 match self {
2822 P2PTransaction::LegacyTransaction(_) => TxType::Legacy,
2823 P2PTransaction::EIP2930Transaction(_) => TxType::EIP2930,
2824 P2PTransaction::EIP1559Transaction(_) => TxType::EIP1559,
2825 P2PTransaction::EIP4844TransactionWithBlobs(_) => TxType::EIP4844,
2826 P2PTransaction::EIP7702Transaction(_) => TxType::EIP7702,
2827 P2PTransaction::FeeTokenTransaction(_) => TxType::FeeToken,
2828 P2PTransaction::FrameTransaction(_) => TxType::Frame,
2829 }
2830 }
2831
2832 pub fn encode_canonical(&self, buf: &mut dyn bytes::BufMut) {
2833 match self {
2834 P2PTransaction::LegacyTransaction(_) => {}
2836 _ => buf.put_u8(self.tx_type() as u8),
2837 }
2838 match self {
2839 P2PTransaction::LegacyTransaction(t) => t.encode(buf),
2840 P2PTransaction::EIP2930Transaction(t) => t.encode(buf),
2841 P2PTransaction::EIP1559Transaction(t) => t.encode(buf),
2842 P2PTransaction::EIP4844TransactionWithBlobs(t) => t.encode(buf),
2843 P2PTransaction::EIP7702Transaction(t) => t.encode(buf),
2844 P2PTransaction::FeeTokenTransaction(t) => t.encode(buf),
2845 P2PTransaction::FrameTransaction(t) => t.encode(buf),
2846 };
2847 }
2848
2849 pub fn encode_canonical_to_vec(&self) -> Vec<u8> {
2850 let mut buf = Vec::new();
2851 self.encode_canonical(&mut buf);
2852 buf
2853 }
2854
2855 pub fn encode_canonical_len(&self) -> usize {
2860 let prefix_len = match self {
2861 P2PTransaction::LegacyTransaction(_) => 0,
2862 _ => 1,
2863 };
2864 let inner_len = match self {
2865 P2PTransaction::LegacyTransaction(t) => t.length(),
2866 P2PTransaction::EIP2930Transaction(t) => t.length(),
2867 P2PTransaction::EIP1559Transaction(t) => t.length(),
2868 P2PTransaction::EIP4844TransactionWithBlobs(t) => t.length(),
2869 P2PTransaction::EIP7702Transaction(t) => t.length(),
2870 P2PTransaction::FeeTokenTransaction(t) => t.length(),
2871 P2PTransaction::FrameTransaction(t) => t.length(),
2872 };
2873 prefix_len + inner_len
2874 }
2875
2876 pub fn compute_hash(&self) -> H256 {
2877 match self {
2878 P2PTransaction::LegacyTransaction(t) => {
2879 Transaction::LegacyTransaction(t.clone()).compute_hash(&NativeCrypto)
2880 }
2881 P2PTransaction::EIP2930Transaction(t) => {
2882 Transaction::EIP2930Transaction(t.clone()).compute_hash(&NativeCrypto)
2883 }
2884 P2PTransaction::EIP1559Transaction(t) => {
2885 Transaction::EIP1559Transaction(t.clone()).compute_hash(&NativeCrypto)
2886 }
2887 P2PTransaction::EIP4844TransactionWithBlobs(t) => {
2888 Transaction::EIP4844Transaction(t.tx.clone()).compute_hash(&NativeCrypto)
2889 }
2890 P2PTransaction::EIP7702Transaction(t) => {
2891 Transaction::EIP7702Transaction(t.clone()).compute_hash(&NativeCrypto)
2892 }
2893 P2PTransaction::FeeTokenTransaction(t) => {
2894 Transaction::FeeTokenTransaction(t.clone()).compute_hash(&NativeCrypto)
2895 }
2896 P2PTransaction::FrameTransaction(t) => {
2897 Transaction::FrameTransaction(t.clone()).compute_hash(&NativeCrypto)
2898 }
2899 }
2900 }
2901 }
2902}
2903
2904mod serde_impl {
2908 use ethereum_types::H160;
2909 use serde::Deserialize;
2910 use serde::{Deserializer, de::Error};
2911 use serde_json::Value;
2912 use std::{collections::HashMap, str::FromStr};
2913
2914 #[cfg(feature = "c-kzg")]
2915 use crate::types::BYTES_PER_BLOB;
2916 use crate::types::{AccessListItem, AuthorizationTuple, BlobsBundleError};
2917
2918 use super::*;
2919
2920 impl Serialize for TxKind {
2921 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2922 where
2923 S: serde::Serializer,
2924 {
2925 match self {
2926 TxKind::Call(address) => serializer.serialize_str(&format!("{address:#x}")),
2927 TxKind::Create => serializer.serialize_none(),
2928 }
2929 }
2930 }
2931
2932 impl<'de> Deserialize<'de> for TxKind {
2933 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2934 where
2935 D: serde::Deserializer<'de>,
2936 {
2937 let str_option = Option::<String>::deserialize(deserializer)?;
2938 match str_option {
2939 Some(str) if !str.is_empty() => Ok(TxKind::Call(
2940 Address::from_str(str.trim_start_matches("0x")).map_err(|_| {
2941 serde::de::Error::custom(format!("Failed to deserialize hex value {str}"))
2942 })?,
2943 )),
2944 _ => Ok(TxKind::Create),
2945 }
2946 }
2947 }
2948
2949 impl Serialize for TxType {
2950 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2951 where
2952 S: serde::Serializer,
2953 {
2954 serializer.serialize_str(&format!("{:#x}", *self as u8))
2955 }
2956 }
2957
2958 impl<'de> Deserialize<'de> for TxType {
2959 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2960 where
2961 D: serde::Deserializer<'de>,
2962 {
2963 let str = String::deserialize(deserializer)?;
2964 let tx_num = u8::from_str_radix(str.trim_start_matches("0x"), 16).map_err(|_| {
2965 serde::de::Error::custom(format!("Failed to deserialize hex value {str}"))
2966 })?;
2967 TxType::from_u8(tx_num).ok_or_else(|| {
2968 serde::de::Error::custom(format!("Invalid transaction type {tx_num}"))
2969 })
2970 }
2971 }
2972
2973 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
2974 #[serde(rename_all = "camelCase")]
2975 pub struct AccessListEntry {
2976 pub address: Address,
2977 pub storage_keys: Vec<H256>,
2978 }
2979
2980 impl From<&AccessListItem> for AccessListEntry {
2981 fn from(value: &AccessListItem) -> AccessListEntry {
2982 AccessListEntry {
2983 address: value.0,
2984 storage_keys: value.1.clone(),
2985 }
2986 }
2987 }
2988
2989 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
2990 #[serde(rename_all = "camelCase")]
2991 pub struct AuthorizationTupleEntry {
2992 pub chain_id: U256,
2993 pub address: Address,
2994 #[serde(default, with = "crate::serde_utils::u64::hex_str")]
2995 pub nonce: u64,
2996 pub y_parity: U256,
2997 pub r: U256,
2998 pub s: U256,
2999 }
3000
3001 impl From<&AuthorizationTuple> for AuthorizationTupleEntry {
3002 fn from(value: &AuthorizationTuple) -> AuthorizationTupleEntry {
3003 AuthorizationTupleEntry {
3004 chain_id: value.chain_id,
3005 address: value.address,
3006 nonce: value.nonce,
3007 y_parity: value.y_parity,
3008 r: value.r_signature,
3009 s: value.s_signature,
3010 }
3011 }
3012 }
3013
3014 impl From<AuthorizationTupleEntry> for AuthorizationTuple {
3015 fn from(entry: AuthorizationTupleEntry) -> AuthorizationTuple {
3016 AuthorizationTuple {
3017 chain_id: entry.chain_id,
3018 address: entry.address,
3019 nonce: entry.nonce,
3020 y_parity: entry.y_parity,
3021 r_signature: entry.r,
3022 s_signature: entry.s,
3023 }
3024 }
3025 }
3026
3027 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
3028 #[serde(rename_all = "camelCase")]
3029 pub struct FrameEntry {
3030 #[serde(with = "crate::serde_utils::u64::hex_str")]
3031 pub mode: u64,
3032 #[serde(with = "crate::serde_utils::u64::hex_str")]
3033 pub flags: u64,
3034 pub to: Option<Address>,
3035 #[serde(with = "crate::serde_utils::u64::hex_str")]
3036 pub gas_limit: u64,
3037 #[serde(
3038 default,
3039 serialize_with = "serialize_u256_hex",
3040 deserialize_with = "crate::serde_utils::u256::deser_hex_str"
3041 )]
3042 pub value: U256,
3043 #[serde(with = "crate::serde_utils::bytes")]
3044 pub data: Bytes,
3045 }
3046
3047 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
3049 #[serde(rename_all = "camelCase")]
3050 pub struct SignatureEntry {
3051 #[serde(with = "crate::serde_utils::u64::hex_str")]
3052 pub scheme: u64,
3053 pub signer: Option<Address>,
3054 #[serde(with = "crate::serde_utils::bytes")]
3055 pub msg: Bytes,
3056 #[serde(with = "crate::serde_utils::bytes")]
3057 pub signature: Bytes,
3058 }
3059
3060 impl From<&FrameSignature> for SignatureEntry {
3061 fn from(value: &FrameSignature) -> SignatureEntry {
3062 SignatureEntry {
3063 scheme: value.scheme as u64,
3064 signer: value.signer,
3065 msg: value.msg.clone(),
3066 signature: value.signature.clone(),
3067 }
3068 }
3069 }
3070
3071 fn serialize_u256_hex<S: serde::Serializer>(value: &U256, s: S) -> Result<S::Ok, S::Error> {
3072 s.serialize_str(&format!("{value:#x}"))
3073 }
3074
3075 impl From<&Frame> for FrameEntry {
3076 fn from(value: &Frame) -> FrameEntry {
3077 FrameEntry {
3078 mode: value.mode as u64,
3079 flags: value.flags as u64,
3080 to: value.target,
3081 gas_limit: value.gas_limit,
3082 value: value.value,
3083 data: value.data.clone(),
3084 }
3085 }
3086 }
3087
3088 impl From<FrameEntry> for Frame {
3089 fn from(entry: FrameEntry) -> Frame {
3090 Frame {
3091 mode: entry.mode as u8,
3092 flags: entry.flags as u8,
3093 target: entry.to,
3094 gas_limit: entry.gas_limit,
3095 value: entry.value,
3096 data: entry.data,
3097 }
3098 }
3099 }
3100
3101 impl Serialize for LegacyTransaction {
3102 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3103 where
3104 S: serde::Serializer,
3105 {
3106 let mut struct_serializer = serializer.serialize_struct("LegacyTransaction", 11)?;
3107 struct_serializer.serialize_field("type", &TxType::Legacy)?;
3108 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3109 struct_serializer.serialize_field("to", &self.to)?;
3110 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas))?;
3111 struct_serializer.serialize_field("value", &self.value)?;
3112 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3113 struct_serializer.serialize_field("gasPrice", &format!("{:#x}", self.gas_price))?;
3114 struct_serializer.serialize_field(
3115 "chainId",
3116 &format!("{:#x}", derive_legacy_chain_id(self.v).unwrap_or_default()),
3117 )?;
3118 struct_serializer.serialize_field("v", &self.v)?;
3119 struct_serializer.serialize_field("r", &self.r)?;
3120 struct_serializer.serialize_field("s", &self.s)?;
3121 struct_serializer.end()
3122 }
3123 }
3124
3125 impl Serialize for EIP2930Transaction {
3126 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3127 where
3128 S: serde::Serializer,
3129 {
3130 let mut struct_serializer = serializer.serialize_struct("Eip2930Transaction", 12)?;
3131 struct_serializer.serialize_field("type", &TxType::EIP2930)?;
3132 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3133 struct_serializer.serialize_field("to", &self.to)?;
3134 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3135 struct_serializer.serialize_field("value", &self.value)?;
3136 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3137 struct_serializer.serialize_field("gasPrice", &format!("{:#x}", self.gas_price))?;
3138 struct_serializer.serialize_field(
3139 "accessList",
3140 &self
3141 .access_list
3142 .iter()
3143 .map(AccessListEntry::from)
3144 .collect::<Vec<_>>(),
3145 )?;
3146 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3147 struct_serializer
3148 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3149 struct_serializer
3150 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
3152 struct_serializer.serialize_field("s", &self.signature_s)?;
3153 struct_serializer.end()
3154 }
3155 }
3156
3157 impl Serialize for EIP1559Transaction {
3158 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3159 where
3160 S: serde::Serializer,
3161 {
3162 let mut struct_serializer = serializer.serialize_struct("Eip1559Transaction", 14)?;
3163 struct_serializer.serialize_field("type", &TxType::EIP1559)?;
3164 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3165 struct_serializer.serialize_field("to", &self.to)?;
3166 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3167 struct_serializer.serialize_field("value", &self.value)?;
3168 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3169 struct_serializer.serialize_field(
3170 "maxPriorityFeePerGas",
3171 &format!("{:#x}", self.max_priority_fee_per_gas),
3172 )?;
3173 struct_serializer
3174 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3175 struct_serializer
3176 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3177 struct_serializer.serialize_field(
3178 "accessList",
3179 &self
3180 .access_list
3181 .iter()
3182 .map(AccessListEntry::from)
3183 .collect::<Vec<_>>(),
3184 )?;
3185 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3186 struct_serializer
3187 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3188 struct_serializer
3189 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
3191 struct_serializer.serialize_field("s", &self.signature_s)?;
3192 struct_serializer.end()
3193 }
3194 }
3195
3196 impl Serialize for EIP4844Transaction {
3197 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3198 where
3199 S: serde::Serializer,
3200 {
3201 let mut struct_serializer = serializer.serialize_struct("Eip4844Transaction", 15)?;
3202 struct_serializer.serialize_field("type", &TxType::EIP4844)?;
3203 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3204 struct_serializer.serialize_field("to", &self.to)?;
3205 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas))?;
3206 struct_serializer.serialize_field("value", &self.value)?;
3207 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3208 struct_serializer.serialize_field(
3209 "maxPriorityFeePerGas",
3210 &format!("{:#x}", self.max_priority_fee_per_gas),
3211 )?;
3212 struct_serializer
3213 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3214 struct_serializer
3215 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3216 struct_serializer.serialize_field(
3217 "maxFeePerBlobGas",
3218 &format!("{:#x}", self.max_fee_per_blob_gas),
3219 )?;
3220 struct_serializer.serialize_field(
3221 "accessList",
3222 &self
3223 .access_list
3224 .iter()
3225 .map(AccessListEntry::from)
3226 .collect::<Vec<_>>(),
3227 )?;
3228 struct_serializer
3229 .serialize_field("blobVersionedHashes", &self.blob_versioned_hashes)?;
3230 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3231 struct_serializer
3232 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3233 struct_serializer
3234 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
3236 struct_serializer.serialize_field("s", &self.signature_s)?;
3237 struct_serializer.end()
3238 }
3239 }
3240
3241 impl Serialize for EIP7702Transaction {
3242 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3243 where
3244 S: serde::Serializer,
3245 {
3246 let mut struct_serializer = serializer.serialize_struct("Eip7702Transaction", 15)?;
3247 struct_serializer.serialize_field("type", &TxType::EIP7702)?;
3248 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3249 struct_serializer.serialize_field("to", &self.to)?;
3250 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3251 struct_serializer.serialize_field("value", &self.value)?;
3252 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3253 struct_serializer.serialize_field(
3254 "maxPriorityFeePerGas",
3255 &format!("{:#x}", self.max_priority_fee_per_gas),
3256 )?;
3257 struct_serializer
3258 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3259 struct_serializer
3260 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3261 struct_serializer.serialize_field(
3262 "accessList",
3263 &self
3264 .access_list
3265 .iter()
3266 .map(AccessListEntry::from)
3267 .collect::<Vec<_>>(),
3268 )?;
3269 struct_serializer.serialize_field(
3270 "authorizationList",
3271 &self
3272 .authorization_list
3273 .iter()
3274 .map(AuthorizationTupleEntry::from)
3275 .collect::<Vec<_>>(),
3276 )?;
3277 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3278 struct_serializer
3279 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3280 struct_serializer
3281 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
3283 struct_serializer.serialize_field("s", &self.signature_s)?;
3284 struct_serializer.end()
3285 }
3286 }
3287
3288 impl Serialize for PrivilegedL2Transaction {
3289 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3290 where
3291 S: serde::Serializer,
3292 {
3293 let mut struct_serializer = serializer.serialize_struct("Eip1559Transaction", 14)?;
3294 struct_serializer.serialize_field("type", &TxType::Privileged)?;
3295 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3296 struct_serializer.serialize_field("to", &self.to)?;
3297 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3298 struct_serializer.serialize_field("value", &self.value)?;
3299 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3300 struct_serializer.serialize_field(
3301 "maxPriorityFeePerGas",
3302 &format!("{:#x}", self.max_priority_fee_per_gas),
3303 )?;
3304 struct_serializer
3305 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3306 struct_serializer
3307 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3308 struct_serializer.serialize_field(
3309 "accessList",
3310 &self
3311 .access_list
3312 .iter()
3313 .map(AccessListEntry::from)
3314 .collect::<Vec<_>>(),
3315 )?;
3316 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3317 struct_serializer.serialize_field("sender", &self.from)?;
3318 struct_serializer.end()
3319 }
3320 }
3321
3322 impl Serialize for FeeTokenTransaction {
3323 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3324 where
3325 S: serde::Serializer,
3326 {
3327 let mut struct_serializer = serializer.serialize_struct("FeeTokenTransaction", 15)?;
3328 struct_serializer.serialize_field("type", &TxType::FeeToken)?;
3329 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3330 struct_serializer.serialize_field("to", &self.to)?;
3331 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3332 struct_serializer.serialize_field("value", &self.value)?;
3333 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3334 struct_serializer.serialize_field(
3335 "maxPriorityFeePerGas",
3336 &format!("{:#x}", self.max_priority_fee_per_gas),
3337 )?;
3338 struct_serializer
3339 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3340 struct_serializer
3341 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3342 struct_serializer.serialize_field(
3343 "accessList",
3344 &self
3345 .access_list
3346 .iter()
3347 .map(AccessListEntry::from)
3348 .collect::<Vec<_>>(),
3349 )?;
3350 struct_serializer.serialize_field("feeToken", &format!("{:#x}", self.fee_token))?;
3351 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3352 struct_serializer
3353 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3354 struct_serializer
3355 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?;
3356 struct_serializer.serialize_field("r", &self.signature_r)?;
3357 struct_serializer.serialize_field("s", &self.signature_s)?;
3358 struct_serializer.end()
3359 }
3360 }
3361
3362 impl Serialize for FrameTransaction {
3363 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3364 where
3365 S: serde::Serializer,
3366 {
3367 let mut s = serializer.serialize_struct("FrameTransaction", 10)?;
3368 s.serialize_field("type", &TxType::Frame)?;
3369 s.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3370 s.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3371 s.serialize_field("sender", &format!("{:#x}", self.sender))?;
3372 s.serialize_field(
3373 "frames",
3374 &self.frames.iter().map(FrameEntry::from).collect::<Vec<_>>(),
3375 )?;
3376 s.serialize_field(
3377 "signatures",
3378 &self
3379 .signatures
3380 .iter()
3381 .map(SignatureEntry::from)
3382 .collect::<Vec<_>>(),
3383 )?;
3384 s.serialize_field(
3385 "maxPriorityFeePerGas",
3386 &format!("{:#x}", self.max_priority_fee_per_gas),
3387 )?;
3388 s.serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3389 s.serialize_field("maxFeePerBlobGas", &self.max_fee_per_blob_gas)?;
3390 s.serialize_field("blobVersionedHashes", &self.blob_versioned_hashes)?;
3391 s.end()
3392 }
3393 }
3394
3395 impl<'de> Deserialize<'de> for Transaction {
3396 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3397 where
3398 D: serde::Deserializer<'de>,
3399 {
3400 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3401 let tx_type =
3402 serde_json::from_value::<TxType>(map.remove("type").unwrap_or(Value::default()))
3403 .unwrap_or_else(|_| {
3404 if map.contains_key("tx_type") {
3405 return TxType::Privileged;
3406 }
3407 TxType::EIP1559
3408 });
3409
3410 let iter = map.into_iter();
3411 match tx_type {
3412 TxType::Legacy => {
3413 LegacyTransaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3414 .map(Transaction::LegacyTransaction)
3415 .map_err(|e| {
3416 serde::de::Error::custom(format!("Couldn't Deserialize Legacy {e}"))
3417 })
3418 }
3419 TxType::EIP2930 => {
3420 EIP2930Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3421 .map(Transaction::EIP2930Transaction)
3422 .map_err(|e| {
3423 serde::de::Error::custom(format!("Couldn't Deserialize EIP2930 {e}"))
3424 })
3425 }
3426 TxType::EIP1559 => {
3427 EIP1559Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3428 .map(Transaction::EIP1559Transaction)
3429 .map_err(|e| {
3430 serde::de::Error::custom(format!("Couldn't Deserialize EIP1559 {e}"))
3431 })
3432 }
3433 TxType::EIP4844 => {
3434 EIP4844Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3435 .map(Transaction::EIP4844Transaction)
3436 .map_err(|e| {
3437 serde::de::Error::custom(format!("Couldn't Deserialize EIP4844 {e}"))
3438 })
3439 }
3440 TxType::EIP7702 => {
3441 EIP7702Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3442 .map(Transaction::EIP7702Transaction)
3443 .map_err(|e| {
3444 serde::de::Error::custom(format!("Couldn't Deserialize EIP7702 {e}"))
3445 })
3446 }
3447 TxType::Privileged => PrivilegedL2Transaction::deserialize(
3448 serde::de::value::MapDeserializer::new(iter),
3449 )
3450 .map(Transaction::PrivilegedL2Transaction)
3451 .map_err(|e| {
3452 serde::de::Error::custom(format!("Couldn't Deserialize Privileged: {e}"))
3453 }),
3454 TxType::FeeToken => {
3455 FeeTokenTransaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3456 .map(Transaction::FeeTokenTransaction)
3457 .map_err(|e| {
3458 serde::de::Error::custom(format!("Couldn't Deserialize FeeToken {e}"))
3459 })
3460 }
3461 TxType::Frame => Err(serde::de::Error::custom(
3462 "Frame transaction JSON deserialization not supported",
3463 )),
3464 }
3465 }
3466 }
3467
3468 fn deserialize_input_field(
3469 map: &mut std::collections::HashMap<String, Value>,
3470 ) -> Result<Bytes, serde_json::Error> {
3471 let data_str: String = serde_json::from_value(
3472 map.remove("input")
3473 .ok_or_else(|| serde::de::Error::missing_field("input"))?,
3474 )
3475 .map_err(serde::de::Error::custom)?;
3476 if let Some(stripped) = data_str.strip_prefix("0x") {
3477 match hex::decode(stripped) {
3478 Ok(decoded_bytes) => Ok(Bytes::from(decoded_bytes)),
3479 Err(_) => Err(serde::de::Error::custom(
3480 "Invalid hex format in 'input' field",
3481 ))?,
3482 }
3483 } else {
3484 Err(serde::de::Error::custom(
3485 "'input' field must start with '0x'",
3486 ))?
3487 }
3488 }
3489
3490 fn deserialize_field<'de, T, D>(
3491 map: &mut HashMap<String, serde_json::Value>,
3492 key: &str,
3493 ) -> Result<T, D::Error>
3494 where
3495 D: serde::Deserializer<'de>,
3496 T: serde::de::DeserializeOwned,
3497 {
3498 map.remove(key)
3499 .ok_or_else(|| D::Error::custom(format!("Missing field: {key}")))
3500 .and_then(|value| {
3501 serde_json::from_value(value).map_err(|err| D::Error::custom(err.to_string()))
3502 })
3503 }
3504
3505 fn deserialize_u64_field<'de, D>(
3506 map: &mut HashMap<String, serde_json::Value>,
3507 key: &str,
3508 ) -> Result<u64, D::Error>
3509 where
3510 D: serde::Deserializer<'de>,
3511 {
3512 let value = deserialize_field::<U256, D>(map, key)?;
3513 u64::try_from(value).map_err(|_| D::Error::custom(format!("{key} value overflows u64")))
3514 }
3515
3516 impl<'de> Deserialize<'de> for LegacyTransaction {
3517 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3518 where
3519 D: serde::Deserializer<'de>,
3520 {
3521 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3522
3523 Ok(LegacyTransaction {
3524 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3525 gas_price: deserialize_field::<U256, D>(&mut map, "gasPrice")?,
3526 gas: deserialize_u64_field::<D>(&mut map, "gas")?,
3527 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3528 value: deserialize_field::<U256, D>(&mut map, "value")?,
3529 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3530 v: deserialize_field::<U256, D>(&mut map, "v")?,
3531 r: deserialize_field::<U256, D>(&mut map, "r")?,
3532 s: deserialize_field::<U256, D>(&mut map, "s")?,
3533 ..Default::default()
3534 })
3535 }
3536 }
3537
3538 impl<'de> Deserialize<'de> for EIP2930Transaction {
3539 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3540 where
3541 D: serde::Deserializer<'de>,
3542 {
3543 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3544
3545 Ok(EIP2930Transaction {
3546 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3547 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3548 gas_price: deserialize_field::<U256, D>(&mut map, "gasPrice")?,
3549 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3550 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3551 value: deserialize_field::<U256, D>(&mut map, "value")?,
3552 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3553 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3554 .into_iter()
3555 .map(|v| (v.address, v.storage_keys))
3556 .collect::<Vec<_>>(),
3557 signature_y_parity: u8::from_str_radix(
3558 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3559 16,
3560 )
3561 .map_err(serde::de::Error::custom)?
3562 != 0,
3563 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3564 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3565 ..Default::default()
3566 })
3567 }
3568 }
3569
3570 impl<'de> Deserialize<'de> for EIP1559Transaction {
3571 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3572 where
3573 D: serde::Deserializer<'de>,
3574 {
3575 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3576 Ok(EIP1559Transaction {
3577 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3578 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3579 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3580 &mut map,
3581 "maxPriorityFeePerGas",
3582 )?,
3583 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3584 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3585 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3586 value: deserialize_field::<U256, D>(&mut map, "value")?,
3587 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3588 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3589 .into_iter()
3590 .map(|v| (v.address, v.storage_keys))
3591 .collect::<Vec<_>>(),
3592 signature_y_parity: u8::from_str_radix(
3593 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3594 16,
3595 )
3596 .map_err(serde::de::Error::custom)?
3597 != 0,
3598 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3599 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3600 ..Default::default()
3601 })
3602 }
3603 }
3604
3605 impl<'de> Deserialize<'de> for EIP4844Transaction {
3606 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3607 where
3608 D: serde::Deserializer<'de>,
3609 {
3610 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3611
3612 Ok(EIP4844Transaction {
3613 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3614 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3615 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3616 &mut map,
3617 "maxPriorityFeePerGas",
3618 )?,
3619 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3620 gas: deserialize_u64_field::<D>(&mut map, "gas")?,
3621 to: deserialize_field::<Address, D>(&mut map, "to")?,
3622 value: deserialize_field::<U256, D>(&mut map, "value")?,
3623 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3624 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3625 .into_iter()
3626 .map(|v| (v.address, v.storage_keys))
3627 .collect::<Vec<_>>(),
3628 max_fee_per_blob_gas: deserialize_field::<U256, D>(&mut map, "maxFeePerBlobGas")?,
3629 blob_versioned_hashes: deserialize_field::<Vec<H256>, D>(
3630 &mut map,
3631 "blobVersionedHashes",
3632 )?,
3633 signature_y_parity: u8::from_str_radix(
3634 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3635 16,
3636 )
3637 .map_err(serde::de::Error::custom)?
3638 != 0,
3639 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3640 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3641 ..Default::default()
3642 })
3643 }
3644 }
3645
3646 impl<'de> Deserialize<'de> for EIP7702Transaction {
3647 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3648 where
3649 D: serde::Deserializer<'de>,
3650 {
3651 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3652
3653 Ok(EIP7702Transaction {
3654 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3655 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3656 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3657 &mut map,
3658 "maxPriorityFeePerGas",
3659 )?,
3660 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3661 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3662 to: deserialize_field::<Address, D>(&mut map, "to")?,
3663 value: deserialize_field::<U256, D>(&mut map, "value")?,
3664 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3665 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3666 .into_iter()
3667 .map(|v| (v.address, v.storage_keys))
3668 .collect::<Vec<_>>(),
3669 authorization_list: deserialize_field::<Vec<AuthorizationTupleEntry>, D>(
3670 &mut map,
3671 "authorizationList",
3672 )?
3673 .into_iter()
3674 .map(AuthorizationTuple::from)
3675 .collect::<Vec<_>>(),
3676 signature_y_parity: u8::from_str_radix(
3677 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3678 16,
3679 )
3680 .map_err(serde::de::Error::custom)?
3681 != 0,
3682 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3683 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3684 ..Default::default()
3685 })
3686 }
3687 }
3688
3689 impl<'de> Deserialize<'de> for PrivilegedL2Transaction {
3690 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3691 where
3692 D: serde::Deserializer<'de>,
3693 {
3694 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3695
3696 Ok(PrivilegedL2Transaction {
3697 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3698 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3699 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3700 &mut map,
3701 "maxPriorityFeePerGas",
3702 )?,
3703 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3704 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3705 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3706 value: deserialize_field::<U256, D>(&mut map, "value")?,
3707 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3708 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3709 .into_iter()
3710 .map(|v| (v.address, v.storage_keys))
3711 .collect::<Vec<_>>(),
3712 from: deserialize_field::<Address, D>(&mut map, "sender")?,
3713 ..Default::default()
3714 })
3715 }
3716 }
3717
3718 impl<'de> Deserialize<'de> for FeeTokenTransaction {
3719 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3720 where
3721 D: serde::Deserializer<'de>,
3722 {
3723 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3724
3725 Ok(FeeTokenTransaction {
3726 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3727 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3728 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3729 &mut map,
3730 "maxPriorityFeePerGas",
3731 )?,
3732 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3733 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3734 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3735 value: deserialize_field::<U256, D>(&mut map, "value")?,
3736 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3737 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3738 .into_iter()
3739 .map(|v| (v.address, v.storage_keys))
3740 .collect::<Vec<_>>(),
3741 fee_token: deserialize_field::<Address, D>(&mut map, "feeToken")?,
3742 signature_y_parity: u8::from_str_radix(
3743 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3744 16,
3745 )
3746 .map_err(serde::de::Error::custom)?
3747 != 0,
3748 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3749 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3750 ..Default::default()
3751 })
3752 }
3753 }
3754
3755 #[derive(Debug, thiserror::Error)]
3756 pub enum GenericTransactionError {
3757 #[error("Invalid transaction type: {0}")]
3758 InvalidTxType(TxType),
3759 #[error("Blob bundle error: {0}")]
3760 BlobBundleError(#[from] BlobsBundleError),
3761 #[error("Missing field: {0}")]
3762 MissingField(String),
3763 #[error("Invalid field: {0}")]
3764 InvalidField(String),
3765 }
3766
3767 #[derive(Deserialize, Debug, PartialEq, Clone, Default)]
3770 #[serde(rename_all = "camelCase")]
3771 pub struct GenericTransaction {
3772 #[serde(default)]
3773 pub r#type: TxType,
3774 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3775 pub nonce: Option<u64>,
3776 pub to: TxKind,
3777 #[serde(default)]
3778 pub from: Address,
3779 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3780 pub gas: Option<u64>,
3781 #[serde(default)]
3782 pub value: U256,
3783 #[serde(default)]
3784 pub gas_price: U256,
3785 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3786 pub max_priority_fee_per_gas: Option<u64>,
3787 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3788 pub max_fee_per_gas: Option<u64>,
3789 pub max_fee_per_blob_gas: Option<U256>,
3790 #[serde(default)]
3791 pub access_list: Vec<AccessListEntry>,
3792 #[serde(default)]
3793 pub fee_token: Option<Address>,
3794 #[serde(default)]
3795 pub authorization_list: Option<Vec<AuthorizationTupleEntry>>,
3796 #[serde(default)]
3797 pub blob_versioned_hashes: Vec<H256>,
3798 pub wrapper_version: Option<u8>,
3799 #[serde(default, with = "crate::serde_utils::bytes::vec")]
3800 pub blobs: Vec<Bytes>,
3801 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3802 pub chain_id: Option<u64>,
3803 #[serde(
3805 flatten,
3806 rename = "input_or_data",
3807 deserialize_with = "deserialize_input",
3808 serialize_with = "crate::serde_utils::bytes::serialize"
3809 )]
3810 pub input: Bytes,
3811 }
3812 pub fn deserialize_input<'de, D>(deserializer: D) -> Result<Bytes, D::Error>
3814 where
3815 D: Deserializer<'de>,
3816 {
3817 let variables = HashMap::<String, Value>::deserialize(deserializer)?;
3820 let data = variables.get("data");
3821 let input = variables.get("input");
3822 let value = match (data, input) {
3823 (None, None) => return Ok(Bytes::new()),
3825 (None, Some(val)) => val,
3826 (Some(val), None) => val,
3827 (Some(val_a), Some(val_b)) => {
3828 if val_a == val_b {
3829 val_a
3830 } else {
3831 return Err(D::Error::custom(
3832 "Transaction has both `data` and `input` fields with different values",
3833 ));
3834 }
3835 }
3836 };
3837 let value = String::deserialize(value).map_err(D::Error::custom)?;
3838 let bytes = hex::decode(value.trim_start_matches("0x"))
3839 .map_err(|e| D::Error::custom(e.to_string()))?;
3840 Ok(Bytes::from(bytes))
3841 }
3842
3843 impl From<EIP1559Transaction> for GenericTransaction {
3844 fn from(value: EIP1559Transaction) -> Self {
3845 Self {
3846 r#type: TxType::EIP1559,
3847 nonce: Some(value.nonce),
3848 to: value.to,
3849 gas: Some(value.gas_limit),
3850 value: value.value,
3851 input: value.data.clone(),
3852 gas_price: U256::from(value.max_fee_per_gas),
3853 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
3854 max_fee_per_gas: Some(value.max_fee_per_gas),
3855 max_fee_per_blob_gas: None,
3856 access_list: value
3857 .access_list
3858 .iter()
3859 .map(AccessListEntry::from)
3860 .collect(),
3861 fee_token: None,
3862 authorization_list: None,
3863 blob_versioned_hashes: vec![],
3864 blobs: vec![],
3865 wrapper_version: None,
3866 chain_id: Some(value.chain_id),
3867 from: Address::default(),
3868 }
3869 }
3870 }
3871
3872 impl TryFrom<GenericTransaction> for EIP1559Transaction {
3873 type Error = GenericTransactionError;
3874
3875 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3876 if value.r#type != TxType::EIP1559 {
3877 return Err(GenericTransactionError::InvalidTxType(value.r#type));
3878 }
3879
3880 Ok(Self {
3881 nonce: value.nonce.unwrap_or_default(),
3882 to: value.to,
3883 gas_limit: value.gas.unwrap_or_default(),
3884 value: value.value,
3885 data: value.input.clone(),
3886 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
3887 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
3888 u64::try_from(value.gas_price).map_err(|_| {
3889 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
3890 })?,
3891 ),
3892 access_list: value
3893 .access_list
3894 .into_iter()
3895 .map(|v| (v.address, v.storage_keys))
3896 .collect::<Vec<_>>(),
3897 chain_id: value.chain_id.unwrap_or_default(),
3898 ..Default::default()
3899 })
3900 }
3901 }
3902
3903 impl From<EIP4844Transaction> for GenericTransaction {
3904 fn from(value: EIP4844Transaction) -> Self {
3905 Self {
3906 r#type: TxType::EIP4844,
3907 nonce: Some(value.nonce),
3908 to: TxKind::Call(value.to),
3909 gas: Some(value.gas),
3910 value: value.value,
3911 input: value.data.clone(),
3912 gas_price: U256::from(value.max_fee_per_gas),
3913 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
3914 max_fee_per_gas: Some(value.max_fee_per_gas),
3915 max_fee_per_blob_gas: Some(value.max_fee_per_blob_gas),
3916 access_list: value
3917 .access_list
3918 .iter()
3919 .map(AccessListEntry::from)
3920 .collect(),
3921 fee_token: None,
3922 authorization_list: None,
3923 blob_versioned_hashes: value.blob_versioned_hashes,
3924 blobs: vec![],
3925 wrapper_version: None,
3926 chain_id: Some(value.chain_id),
3927 from: Address::default(),
3928 }
3929 }
3930 }
3931
3932 #[cfg(feature = "c-kzg")]
3933 impl TryFrom<GenericTransaction> for WrappedEIP4844Transaction {
3934 type Error = GenericTransactionError;
3935
3936 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3937 let blobs = value
3938 .blobs
3939 .iter()
3940 .map(|bytes| {
3941 let slice = bytes.as_ref();
3942 let mut blob = [0u8; BYTES_PER_BLOB];
3943 blob.copy_from_slice(slice);
3944 blob
3945 })
3946 .collect();
3947
3948 let wrapper_version = value.wrapper_version;
3949 Ok(Self {
3950 tx: value.try_into()?,
3951 wrapper_version,
3952 blobs_bundle: BlobsBundle::create_from_blobs(&blobs, wrapper_version)?,
3953 })
3954 }
3955 }
3956
3957 impl TryFrom<GenericTransaction> for EIP4844Transaction {
3958 type Error = GenericTransactionError;
3959
3960 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3961 if value.r#type != TxType::EIP4844 {
3962 return Err(GenericTransactionError::InvalidTxType(value.r#type));
3963 }
3964 Ok(Self {
3965 nonce: value.nonce.unwrap_or_default(),
3966 to: match value.to {
3967 TxKind::Call(to) => to,
3968 _ => H160::default(),
3969 },
3970 gas: value.gas.unwrap_or_default(),
3971 value: value.value,
3972 data: value.input.clone(),
3973 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
3974 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
3975 u64::try_from(value.gas_price).map_err(|_| {
3976 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
3977 })?,
3978 ),
3979 max_fee_per_blob_gas: value.max_fee_per_blob_gas.unwrap_or_default(),
3980 access_list: value
3981 .access_list
3982 .into_iter()
3983 .map(|v| (v.address, v.storage_keys))
3984 .collect::<Vec<_>>(),
3985 blob_versioned_hashes: value.blob_versioned_hashes,
3986 chain_id: value.chain_id.unwrap_or_default(),
3987 ..Default::default()
3988 })
3989 }
3990 }
3991
3992 impl From<EIP7702Transaction> for GenericTransaction {
3993 fn from(value: EIP7702Transaction) -> Self {
3994 Self {
3995 r#type: TxType::EIP7702,
3996 nonce: Some(value.nonce),
3997 to: TxKind::Call(value.to),
3998 gas: Some(value.gas_limit),
3999 value: value.value,
4000 input: value.data.clone(),
4001 gas_price: U256::from(value.max_fee_per_gas),
4002 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
4003 max_fee_per_gas: Some(value.max_fee_per_gas),
4004 max_fee_per_blob_gas: None,
4005 access_list: value
4006 .access_list
4007 .iter()
4008 .map(AccessListEntry::from)
4009 .collect(),
4010 fee_token: None,
4011 authorization_list: Some(
4012 value
4013 .authorization_list
4014 .iter()
4015 .map(AuthorizationTupleEntry::from)
4016 .collect(),
4017 ),
4018 blob_versioned_hashes: vec![],
4019 blobs: vec![],
4020 wrapper_version: None,
4021 chain_id: Some(value.chain_id),
4022 from: Address::default(),
4023 }
4024 }
4025 }
4026
4027 impl TryFrom<GenericTransaction> for EIP7702Transaction {
4028 type Error = GenericTransactionError;
4029
4030 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
4031 if value.r#type != TxType::EIP7702 {
4032 return Err(GenericTransactionError::InvalidTxType(value.r#type));
4033 }
4034 let TxKind::Call(to) = value.to else {
4035 return Err(GenericTransactionError::MissingField("to".to_owned()));
4036 };
4037 Ok(Self {
4038 chain_id: value.chain_id.unwrap_or_default(),
4039 nonce: value.nonce.unwrap_or_default(),
4040 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
4041 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
4042 u64::try_from(value.gas_price).map_err(|_| {
4043 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
4044 })?,
4045 ),
4046 gas_limit: value.gas.unwrap_or_default(),
4047 to,
4048 value: value.value,
4049 data: value.input,
4050 access_list: value
4051 .access_list
4052 .into_iter()
4053 .map(|v| (v.address, v.storage_keys))
4054 .collect::<Vec<_>>(),
4055 authorization_list: value
4056 .authorization_list
4057 .unwrap_or_default()
4058 .into_iter()
4059 .map(AuthorizationTuple::from)
4060 .collect(),
4061 ..Default::default()
4062 })
4063 }
4064 }
4065
4066 impl From<PrivilegedL2Transaction> for GenericTransaction {
4067 fn from(value: PrivilegedL2Transaction) -> Self {
4068 Self {
4069 r#type: TxType::Privileged,
4070 nonce: Some(value.nonce),
4071 to: value.to,
4072 gas: Some(value.gas_limit),
4073 value: value.value,
4074 input: value.data.clone(),
4075 gas_price: U256::from(value.max_fee_per_gas),
4076 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
4077 max_fee_per_gas: Some(value.max_fee_per_gas),
4078 max_fee_per_blob_gas: None,
4079 access_list: value
4080 .access_list
4081 .iter()
4082 .map(AccessListEntry::from)
4083 .collect(),
4084 fee_token: None,
4085 authorization_list: None,
4086 blob_versioned_hashes: vec![],
4087 blobs: vec![],
4088 wrapper_version: None,
4089 chain_id: Some(value.chain_id),
4090 from: value.from,
4091 }
4092 }
4093 }
4094
4095 impl TryFrom<GenericTransaction> for PrivilegedL2Transaction {
4096 type Error = GenericTransactionError;
4097
4098 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
4099 if value.r#type != TxType::Privileged {
4100 return Err(GenericTransactionError::InvalidTxType(value.r#type));
4101 }
4102 Ok(Self {
4103 nonce: value.nonce.unwrap_or_default(),
4104 to: value.to,
4105 gas_limit: value.gas.unwrap_or_default(),
4106 value: value.value,
4107 data: value.input.clone(),
4108 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
4109 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
4110 u64::try_from(value.gas_price).map_err(|_| {
4111 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
4112 })?,
4113 ),
4114 access_list: value
4115 .access_list
4116 .into_iter()
4117 .map(|v| (v.address, v.storage_keys))
4118 .collect::<Vec<_>>(),
4119 chain_id: value.chain_id.unwrap_or_default(),
4120 from: value.from,
4121 ..Default::default()
4122 })
4123 }
4124 }
4125
4126 impl From<FeeTokenTransaction> for GenericTransaction {
4127 fn from(value: FeeTokenTransaction) -> Self {
4128 Self {
4129 r#type: TxType::FeeToken,
4130 nonce: Some(value.nonce),
4131 to: value.to,
4132 gas: Some(value.gas_limit),
4133 value: value.value,
4134 input: value.data.clone(),
4135 gas_price: U256::from(value.max_fee_per_gas),
4136 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
4137 max_fee_per_gas: Some(value.max_fee_per_gas),
4138 max_fee_per_blob_gas: None,
4139 access_list: value
4140 .access_list
4141 .iter()
4142 .map(AccessListEntry::from)
4143 .collect(),
4144 fee_token: Some(value.fee_token),
4145 authorization_list: None,
4146 blob_versioned_hashes: vec![],
4147 blobs: vec![],
4148 chain_id: Some(value.chain_id),
4149 from: Address::default(),
4150 wrapper_version: None,
4151 }
4152 }
4153 }
4154
4155 impl TryFrom<GenericTransaction> for FeeTokenTransaction {
4156 type Error = GenericTransactionError;
4157
4158 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
4159 if value.r#type != TxType::FeeToken {
4160 return Err(GenericTransactionError::InvalidTxType(value.r#type));
4161 }
4162
4163 Ok(Self {
4164 nonce: value.nonce.unwrap_or_default(),
4165 to: value.to,
4166 gas_limit: value.gas.unwrap_or_default(),
4167 value: value.value,
4168 data: value.input.clone(),
4169 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
4170 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
4171 u64::try_from(value.gas_price).map_err(|_| {
4172 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
4173 })?,
4174 ),
4175 access_list: value
4176 .access_list
4177 .into_iter()
4178 .map(|v| (v.address, v.storage_keys))
4179 .collect::<Vec<_>>(),
4180 fee_token: value
4181 .fee_token
4182 .ok_or(GenericTransactionError::MissingField(
4183 "fee token".to_owned(),
4184 ))?,
4185 chain_id: value.chain_id.unwrap_or_default(),
4186 ..Default::default()
4187 })
4188 }
4189 }
4190
4191 impl From<LegacyTransaction> for GenericTransaction {
4192 fn from(value: LegacyTransaction) -> Self {
4193 Self {
4194 r#type: TxType::Legacy,
4195 nonce: Some(value.nonce),
4196 to: value.to,
4197 from: Address::default(),
4198 gas: Some(value.gas),
4199 value: value.value,
4200 gas_price: value.gas_price,
4201 max_priority_fee_per_gas: None,
4202 max_fee_per_gas: None,
4203 max_fee_per_blob_gas: None,
4204 access_list: vec![],
4205 fee_token: None,
4206 authorization_list: None,
4207 blob_versioned_hashes: vec![],
4208 blobs: vec![],
4209 wrapper_version: None,
4210 chain_id: None,
4211 input: value.data,
4212 }
4213 }
4214 }
4215
4216 impl From<EIP2930Transaction> for GenericTransaction {
4217 fn from(value: EIP2930Transaction) -> Self {
4218 Self {
4219 r#type: TxType::EIP2930,
4220 nonce: Some(value.nonce),
4221 to: value.to,
4222 from: Address::default(),
4223 gas: Some(value.gas_limit),
4224 value: value.value,
4225 gas_price: value.gas_price,
4226 max_priority_fee_per_gas: None,
4227 max_fee_per_gas: None,
4228 max_fee_per_blob_gas: None,
4229 access_list: value
4230 .access_list
4231 .into_iter()
4232 .map(|(address, storage_keys)| AccessListEntry {
4233 address,
4234 storage_keys,
4235 })
4236 .collect(),
4237 fee_token: None,
4238 authorization_list: None,
4239 blob_versioned_hashes: vec![],
4240 blobs: vec![],
4241 wrapper_version: None,
4242 chain_id: Some(value.chain_id),
4243 input: value.data,
4244 }
4245 }
4246 }
4247
4248 impl From<Transaction> for GenericTransaction {
4249 fn from(value: Transaction) -> Self {
4250 match value {
4251 Transaction::LegacyTransaction(tx) => tx.into(),
4252 Transaction::EIP2930Transaction(tx) => tx.into(),
4253 Transaction::EIP1559Transaction(tx) => tx.into(),
4254 Transaction::EIP4844Transaction(tx) => tx.into(),
4255 Transaction::EIP7702Transaction(tx) => tx.into(),
4256 Transaction::PrivilegedL2Transaction(tx) => tx.into(),
4257 Transaction::FeeTokenTransaction(tx) => tx.into(),
4258 Transaction::FrameTransaction(tx) => tx.into(),
4259 }
4260 }
4261 }
4262
4263 impl From<FrameTransaction> for GenericTransaction {
4264 fn from(value: FrameTransaction) -> Self {
4265 Self {
4266 r#type: TxType::Frame,
4267 nonce: Some(value.nonce),
4268 to: TxKind::Call(value.sender),
4269 from: value.sender,
4270 gas: Some(value.max_gas()),
4271 value: U256::zero(),
4272 gas_price: value.max_fee_per_gas.into(),
4273 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
4274 max_fee_per_gas: Some(value.max_fee_per_gas),
4275 max_fee_per_blob_gas: if value.blob_versioned_hashes.is_empty() {
4276 None
4277 } else {
4278 Some(value.max_fee_per_blob_gas)
4279 },
4280 access_list: vec![],
4281 fee_token: None,
4282 authorization_list: None,
4283 blob_versioned_hashes: value.blob_versioned_hashes,
4284 blobs: vec![],
4285 wrapper_version: None,
4286 chain_id: Some(value.chain_id),
4287 input: Bytes::new(),
4288 }
4289 }
4290 }
4291}
4292
4293mod mempool {
4294 use super::*;
4295 use std::{
4296 cmp::Ordering,
4297 sync::Arc,
4298 time::{SystemTime, UNIX_EPOCH},
4299 };
4300
4301 #[derive(Debug, Clone, PartialEq, Eq)]
4302 pub struct MempoolTransaction {
4303 timestamp: u128,
4305 sender: Address,
4306 inner: Arc<Transaction>,
4307 }
4308
4309 impl MempoolTransaction {
4310 pub fn new(tx: Transaction, sender: Address) -> Self {
4311 Self {
4312 timestamp: SystemTime::now()
4313 .duration_since(UNIX_EPOCH)
4314 .expect("Invalid system time")
4315 .as_micros(),
4316 sender,
4317 inner: Arc::new(tx),
4318 }
4319 }
4320 pub fn time(&self) -> u128 {
4321 self.timestamp
4322 }
4323
4324 pub fn sender(&self) -> Address {
4325 self.sender
4326 }
4327
4328 pub fn transaction(&self) -> &Transaction {
4329 &self.inner
4330 }
4331 }
4332
4333 impl RLPEncode for MempoolTransaction {
4334 fn encode(&self, buf: &mut dyn bytes::BufMut) {
4335 Encoder::new(buf)
4336 .encode_field(&self.timestamp)
4337 .encode_field(&*self.inner)
4338 .finish();
4339 }
4340 }
4341
4342 impl RLPDecode for MempoolTransaction {
4343 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
4344 let decoder = Decoder::new(rlp)?;
4345 let (timestamp, decoder) = decoder.decode_field("timestamp")?;
4346 let (sender, decoder) = decoder.decode_field("sender")?;
4347 let (inner, decoder) = decoder.decode_field("inner")?;
4348 Ok((
4349 Self {
4350 timestamp,
4351 sender,
4352 inner: Arc::new(inner),
4353 },
4354 decoder.finish()?,
4355 ))
4356 }
4357 }
4358
4359 impl std::ops::Deref for MempoolTransaction {
4360 type Target = Transaction;
4361
4362 fn deref(&self) -> &Self::Target {
4363 &self.inner
4364 }
4365 }
4366
4367 impl Ord for MempoolTransaction {
4369 fn cmp(&self, other: &Self) -> Ordering {
4370 match (self.tx_type(), other.tx_type()) {
4371 (TxType::Privileged, TxType::Privileged) => {
4372 return self.nonce().cmp(&other.nonce());
4373 }
4374 (TxType::Privileged, _) => return Ordering::Less,
4375 (_, TxType::Privileged) => return Ordering::Greater,
4376 _ => (),
4377 };
4378 match self.nonce().cmp(&other.nonce()) {
4379 Ordering::Equal => other.time().cmp(&self.time()),
4380 ordering => ordering,
4381 }
4382 }
4383 }
4384
4385 impl PartialOrd for MempoolTransaction {
4386 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
4387 Some(self.cmp(other))
4388 }
4389 }
4390}
4391
4392#[cfg(test)]
4393mod tests {
4394
4395 use super::*;
4396 use crate::types::{
4397 AuthorizationTuple, BlockBody, Receipt, compute_receipts_root, compute_transactions_root,
4398 };
4399 use ethereum_types::H160;
4400 use hex_literal::hex;
4401 use serde_impl::{AccessListEntry, GenericTransaction};
4402 use std::str::FromStr;
4403
4404 #[test]
4405 fn is_blob_carrying_covers_frame_txs_with_blob_hashes() {
4406 let plain_frame = Transaction::FrameTransaction(FrameTransaction::default());
4412 assert!(!plain_frame.is_blob_carrying());
4413
4414 let blob_frame = Transaction::FrameTransaction(FrameTransaction {
4415 max_fee_per_blob_gas: U256::from(7u64),
4416 blob_versioned_hashes: vec![H256::from_low_u64_be(1)],
4417 ..Default::default()
4418 });
4419 assert!(blob_frame.is_blob_carrying());
4420
4421 let blob_tx = Transaction::EIP4844Transaction(EIP4844Transaction {
4422 blob_versioned_hashes: vec![H256::from_low_u64_be(1)],
4423 ..Default::default()
4424 });
4425 assert!(blob_tx.is_blob_carrying());
4426
4427 let plain = Transaction::EIP1559Transaction(EIP1559Transaction::default());
4428 assert!(!plain.is_blob_carrying());
4429 }
4430
4431 #[test]
4432 fn nonce_over_u64_max_maps_to_nonce_is_max() {
4433 let rlp = hex!("ca89010000000000000000");
4436 let decoder = Decoder::new(&rlp).unwrap();
4437 let err = decode_nonce_field(decoder).unwrap_err();
4438 assert!(
4439 err.to_string().contains("Nonce is max"),
4440 "unexpected error: {err}"
4441 );
4442 }
4443
4444 #[test]
4445 fn nonce_at_u64_max_decodes() {
4446 let rlp = hex!("c988ffffffffffffffff");
4448 let decoder = Decoder::new(&rlp).unwrap();
4449 let (nonce, _) = decode_nonce_field(decoder).unwrap();
4450 assert_eq!(nonce, u64::MAX);
4451 }
4452
4453 #[test]
4454 fn legacy_chain_id_handles_out_of_range_v_without_underflow() {
4455 let legacy_chain_id = |v: u64| {
4461 Transaction::LegacyTransaction(LegacyTransaction {
4462 v: U256::from(v),
4463 ..Default::default()
4464 })
4465 .chain_id()
4466 };
4467 for v in [0u64, 1, 26, 27, 28, 34] {
4468 assert_eq!(legacy_chain_id(v), None, "v={v} must yield no chain id");
4469 }
4470 assert_eq!(legacy_chain_id(35), Some(0));
4472 assert_eq!(legacy_chain_id(36), Some(0));
4473 assert_eq!(legacy_chain_id(37), Some(1));
4474 }
4475
4476 #[test]
4477 fn test_compute_transactions_root() {
4478 let mut body = BlockBody::empty();
4479 let tx = LegacyTransaction {
4480 nonce: 0,
4481 gas_price: U256::from(0x0a),
4482 gas: 0x05f5e100,
4483 to: TxKind::Call(hex!("1000000000000000000000000000000000000000").into()),
4484 value: 0.into(),
4485 data: Default::default(),
4486 v: U256::from(0x1b),
4487 r: U256::from_big_endian(&hex!(
4488 "7e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37"
4489 )),
4490 s: U256::from_big_endian(&hex!(
4491 "5f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509b"
4492 )),
4493 ..Default::default()
4494 };
4495 body.transactions.push(Transaction::LegacyTransaction(tx));
4496 let expected_root =
4497 hex!("8151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcb");
4498 let result = compute_transactions_root(&body.transactions, ðrex_crypto::NativeCrypto);
4499
4500 assert_eq!(result, expected_root.into());
4501 }
4502 #[test]
4503 fn test_compute_hash() {
4504 let tx_eip2930 = EIP2930Transaction {
4506 chain_id: 3503995874084926u64,
4507 nonce: 7,
4508 gas_price: U256::from(0x2dbf1f9a_u64),
4509 gas_limit: 0x186A0,
4510 to: TxKind::Call(hex!("7dcd17433742f4c0ca53122ab541d0ba67fc27df").into()),
4511 value: 2.into(),
4512 data: Bytes::from(&b"\xdbS\x06$\x8e\x03\x13\xe7emit"[..]),
4513 access_list: vec![(
4514 hex!("7dcd17433742f4c0ca53122ab541d0ba67fc27df").into(),
4515 vec![
4516 hex!("0000000000000000000000000000000000000000000000000000000000000000").into(),
4517 hex!("a3d07a7d68fbd49ec2f8e6befdd86c885f86c272819f6f345f365dec35ae6707").into(),
4518 ],
4519 )],
4520 signature_y_parity: false,
4521 signature_r: U256::from_dec_str(
4522 "75813812796588349127366022588733264074091236448495248199152066031778895768879",
4523 )
4524 .unwrap(),
4525 signature_s: U256::from_dec_str(
4526 "25476208226281085290728123165613764315157904411823916642262684106502155457829",
4527 )
4528 .unwrap(),
4529 ..Default::default()
4530 };
4531 let tx = Transaction::EIP2930Transaction(tx_eip2930);
4532
4533 let expected_hash =
4534 hex!("a0762610d794acddd2dca15fb7c437ada3611c886f3bea675d53d8da8a6c41b2");
4535 let hash = tx.compute_hash(&NativeCrypto);
4536 assert_eq!(hash, expected_hash.into());
4537 }
4538
4539 #[test]
4540 fn test_compute_receipts_root() {
4541 let tx_type = TxType::Legacy;
4544 let succeeded = true;
4545 let cumulative_gas_used = 0x5208;
4546 let logs = vec![];
4547 let receipt = Receipt::new(tx_type, succeeded, cumulative_gas_used, logs);
4548
4549 let result = compute_receipts_root(&[receipt], ðrex_crypto::NativeCrypto);
4550 let expected_root =
4551 hex!("056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2");
4552 assert_eq!(result, expected_root.into());
4553 }
4554
4555 #[test]
4556 fn legacy_tx_rlp_decode() {
4557 let encoded_tx = "f86d80843baa0c4082f618946177843db3138ae69679a54b95cf345ed759450d870aa87bee538000808360306ba0151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65da064c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4";
4558 let encoded_tx_bytes = hex::decode(encoded_tx).unwrap();
4559 let tx = LegacyTransaction::decode(&encoded_tx_bytes).unwrap();
4560 let expected_tx = LegacyTransaction {
4561 nonce: 0,
4562 gas_price: U256::from(1001000000u64),
4563 gas: 63000,
4564 to: TxKind::Call(Address::from_slice(
4565 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4566 )),
4567 value: 3000000000000000_u64.into(),
4568 data: Bytes::new(),
4569 r: U256::from_str_radix(
4570 "151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65d",
4571 16,
4572 )
4573 .unwrap(),
4574 s: U256::from_str_radix(
4575 "64c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4",
4576 16,
4577 )
4578 .unwrap(),
4579 v: 6303851.into(),
4580 ..Default::default()
4581 };
4582 assert_eq!(tx, expected_tx);
4583 }
4584
4585 #[test]
4586 fn eip1559_tx_rlp_decode() {
4587 let encoded_tx = "f86c8330182480114e82f618946177843db3138ae69679a54b95cf345ed759450d870aa87bee53800080c080a0151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65da064c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4";
4588 let encoded_tx_bytes = hex::decode(encoded_tx).unwrap();
4589 let tx = EIP1559Transaction::decode(&encoded_tx_bytes).unwrap();
4590 let expected_tx = EIP1559Transaction {
4591 nonce: 0,
4592 max_fee_per_gas: 78,
4593 max_priority_fee_per_gas: 17,
4594 to: TxKind::Call(Address::from_slice(
4595 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4596 )),
4597 value: 3000000000000000_u64.into(),
4598 data: Bytes::new(),
4599 signature_r: U256::from_str_radix(
4600 "151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65d",
4601 16,
4602 )
4603 .unwrap(),
4604 signature_s: U256::from_str_radix(
4605 "64c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4",
4606 16,
4607 )
4608 .unwrap(),
4609 signature_y_parity: false,
4610 chain_id: 3151908,
4611 gas_limit: 63000,
4612 access_list: vec![],
4613 ..Default::default()
4614 };
4615 assert_eq!(tx, expected_tx);
4616 }
4617
4618 #[test]
4619 fn deserialize_tx_kind() {
4620 let tx_kind_create = r#""""#;
4621 let tx_kind_call = r#""0x6177843db3138ae69679A54b95cf345ED759450d""#;
4622 let deserialized_tx_kind_create = TxKind::Create;
4623 let deserialized_tx_kind_call = TxKind::Call(Address::from_slice(
4624 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4625 ));
4626 assert_eq!(
4627 deserialized_tx_kind_create,
4628 serde_json::from_str(tx_kind_create).unwrap()
4629 );
4630 assert_eq!(
4631 deserialized_tx_kind_call,
4632 serde_json::from_str(tx_kind_call).unwrap()
4633 )
4634 }
4635
4636 #[test]
4637 fn deserialize_tx_type() {
4638 let tx_type_eip2930 = r#""0x01""#;
4639 let tx_type_eip1559 = r#""0x02""#;
4640 let deserialized_tx_type_eip2930 = TxType::EIP2930;
4641 let deserialized_tx_type_eip1559 = TxType::EIP1559;
4642 assert_eq!(
4643 deserialized_tx_type_eip2930,
4644 serde_json::from_str(tx_type_eip2930).unwrap()
4645 );
4646 assert_eq!(
4647 deserialized_tx_type_eip1559,
4648 serde_json::from_str(tx_type_eip1559).unwrap()
4649 )
4650 }
4651
4652 #[test]
4653 fn deserialize_generic_transaction() {
4654 let generic_transaction = r#"{
4655 "type":"0x01",
4656 "nonce":"0x02",
4657 "to":"",
4658 "from":"0x6177843db3138ae69679A54b95cf345ED759450d",
4659 "gas":"0x5208",
4660 "value":"0x01",
4661 "input":"0x010203040506",
4662 "gasPrice":"0x07",
4663 "accessList": [
4664 {
4665 "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
4666 "storageKeys": [
4667 "0x000000000000000000000000000000000000000000000000000000000000000c",
4668 "0x000000000000000000000000000000000000000000000000000000000000200b"
4669 ]
4670 }
4671 ]
4672 }"#;
4673 let deserialized_generic_transaction = GenericTransaction {
4674 r#type: TxType::EIP2930,
4675 nonce: Some(2),
4676 to: TxKind::Create,
4677 from: Address::from_slice(
4678 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4679 ),
4680 gas: Some(0x5208),
4681 value: U256::from(1),
4682 input: Bytes::from(hex::decode("010203040506").unwrap()),
4683 gas_price: U256::from(7),
4684 max_priority_fee_per_gas: Default::default(),
4685 max_fee_per_gas: Default::default(),
4686 max_fee_per_blob_gas: Default::default(),
4687 access_list: vec![AccessListEntry {
4688 address: Address::from_slice(
4689 &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
4690 ),
4691 storage_keys: vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
4692 }],
4693 fee_token: None,
4694 blob_versioned_hashes: Default::default(),
4695 blobs: Default::default(),
4696 wrapper_version: None,
4697 chain_id: Default::default(),
4698 authorization_list: None,
4699 };
4700 assert_eq!(
4701 deserialized_generic_transaction,
4702 serde_json::from_str(generic_transaction).unwrap()
4703 )
4704 }
4705
4706 #[test]
4707 fn deserialize_generic_transaction_with_data_and_input_fields() {
4708 let generic_transaction = r#"{
4709 "type":"0x01",
4710 "nonce":"0x02",
4711 "to":"",
4712 "from":"0x6177843db3138ae69679A54b95cf345ED759450d",
4713 "gas":"0x5208",
4714 "value":"0x01",
4715 "input":"0x010203040506",
4716 "data":"0x010203040506",
4717 "gasPrice":"0x07",
4718 "accessList": [
4719 {
4720 "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
4721 "storageKeys": [
4722 "0x000000000000000000000000000000000000000000000000000000000000000c",
4723 "0x000000000000000000000000000000000000000000000000000000000000200b"
4724 ]
4725 }
4726 ]
4727 }"#;
4728 let deserialized_generic_transaction = GenericTransaction {
4729 r#type: TxType::EIP2930,
4730 nonce: Some(2),
4731 to: TxKind::Create,
4732 from: Address::from_slice(
4733 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4734 ),
4735 gas: Some(0x5208),
4736 value: U256::from(1),
4737 input: Bytes::from(hex::decode("010203040506").unwrap()),
4738 gas_price: U256::from(7),
4739 max_priority_fee_per_gas: Default::default(),
4740 max_fee_per_gas: Default::default(),
4741 max_fee_per_blob_gas: Default::default(),
4742 access_list: vec![AccessListEntry {
4743 address: Address::from_slice(
4744 &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
4745 ),
4746 storage_keys: vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
4747 }],
4748 fee_token: None,
4749 blob_versioned_hashes: Default::default(),
4750 blobs: Default::default(),
4751 wrapper_version: None,
4752 chain_id: Default::default(),
4753 authorization_list: None,
4754 };
4755 assert_eq!(
4756 deserialized_generic_transaction,
4757 serde_json::from_str(generic_transaction).unwrap()
4758 )
4759 }
4760
4761 #[test]
4762 fn deserialize_eip4844_transaction() {
4763 let eip4844_transaction = r#"{
4764 "chainId":"0x01",
4765 "nonce":"0x02",
4766 "maxPriorityFeePerGas":"0x01",
4767 "maxFeePerGas":"0x01",
4768 "gas":"0x5208",
4769 "to":"0x6177843db3138ae69679A54b95cf345ED759450d",
4770 "value":"0x01",
4771 "input":"0x3033",
4772 "accessList": [
4773 {
4774 "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
4775 "storageKeys": [
4776 "0x000000000000000000000000000000000000000000000000000000000000000c",
4777 "0x000000000000000000000000000000000000000000000000000000000000200b"
4778 ]
4779 }
4780 ],
4781 "maxFeePerBlobGas":"0x03",
4782 "blobVersionedHashes": [
4783 "0x0000000000000000000000000000000000000000000000000000000000000001",
4784 "0x0000000000000000000000000000000000000000000000000000000000000002"
4785 ],
4786 "yParity":"0x0",
4787 "r": "0x01",
4788 "s": "0x02"
4789 }"#;
4790 let deserialized_eip4844_transaction = EIP4844Transaction {
4791 chain_id: 0x01,
4792 nonce: 0x02,
4793 to: Address::from_slice(
4794 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4795 ),
4796 max_priority_fee_per_gas: 1,
4797 max_fee_per_gas: 1,
4798 max_fee_per_blob_gas: U256::from(0x03),
4799 gas: 0x5208,
4800 value: U256::from(0x01),
4801 data: Bytes::from_static(b"03"),
4803 access_list: vec![(
4804 Address::from_slice(
4805 &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
4806 ),
4807 vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
4808 )],
4809 blob_versioned_hashes: vec![H256::from_low_u64_be(1), H256::from_low_u64_be(2)],
4810 signature_y_parity: false,
4811 signature_r: U256::from(0x01),
4812 signature_s: U256::from(0x02),
4813 ..Default::default()
4814 };
4815
4816 assert_eq!(
4817 deserialized_eip4844_transaction,
4818 serde_json::from_str(eip4844_transaction).unwrap()
4819 )
4820 }
4821
4822 #[test]
4823 fn serialize_deserialize_transaction() {
4824 let eip1559 = EIP1559Transaction {
4825 chain_id: 65536999,
4826 nonce: 1,
4827 max_priority_fee_per_gas: 1000,
4828 max_fee_per_gas: 2000,
4829 gas_limit: 21000,
4830 to: TxKind::Call(H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B0").unwrap()),
4831 value: U256::from(100000),
4832 data: Bytes::from_static(b"03"),
4833 access_list: vec![(
4834 H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B3").unwrap(),
4835 vec![H256::zero()],
4836 )],
4837 signature_y_parity: true,
4838 signature_r: U256::one(),
4839 signature_s: U256::zero(),
4840 ..Default::default()
4841 };
4842 let tx_to_serialize = Transaction::EIP1559Transaction(eip1559.clone());
4843 let serialized = serde_json::to_string(&tx_to_serialize).expect("Failed to serialize");
4844
4845 let deserialized_tx: Transaction =
4846 serde_json::from_str(&serialized).expect("Failed to deserialize");
4847
4848 assert!(deserialized_tx.tx_type() == TxType::EIP1559);
4849
4850 if let Transaction::EIP1559Transaction(tx) = deserialized_tx {
4851 assert_eq!(tx, eip1559);
4852 }
4853 }
4854
4855 #[test]
4856 fn serialize_deserialize_eip7702transaction() {
4857 let eip7702 = EIP7702Transaction {
4858 chain_id: 65536999,
4859 nonce: 1,
4860 max_priority_fee_per_gas: 1000,
4861 max_fee_per_gas: 2000,
4862 gas_limit: 21000,
4863 to: Address::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B0").unwrap(),
4864 value: U256::from(100000),
4865 data: Bytes::from_static(b"03"),
4866 access_list: vec![],
4867 signature_y_parity: true,
4868 signature_r: U256::one(),
4869 signature_s: U256::zero(),
4870 authorization_list: vec![AuthorizationTuple {
4871 chain_id: U256::from(65536999),
4872 address: H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B1").unwrap(),
4873 nonce: 2,
4874 y_parity: U256::one(),
4875 r_signature: U256::from(22),
4876 s_signature: U256::from(37),
4877 }],
4878 ..Default::default()
4879 };
4880 let tx_to_serialize = Transaction::EIP7702Transaction(eip7702.clone());
4881 let serialized = serde_json::to_string(&tx_to_serialize).expect("Failed to serialize");
4882
4883 let deserialized_tx: Transaction =
4884 serde_json::from_str(&serialized).expect("Failed to deserialize");
4885
4886 assert!(deserialized_tx.tx_type() == TxType::EIP7702);
4887
4888 if let Transaction::EIP7702Transaction(tx) = deserialized_tx {
4889 assert_eq!(tx, eip7702);
4890 }
4891 }
4892
4893 #[test]
4894 fn serialize_deserialize_privileged_l2_transaction() -> Result<(), RLPDecodeError> {
4895 let privileged_l2 = PrivilegedL2Transaction {
4896 chain_id: 65536999,
4897 nonce: 0,
4898 max_priority_fee_per_gas: 875000000,
4899 max_fee_per_gas: 875000000,
4900 gas_limit: 42000u64,
4901 to: TxKind::Call(
4902 Address::from_str("0x8943545177806ed17b9f23f0a21ee5948ecaa776").unwrap(),
4903 ),
4904 value: U256::from(500000000000000000000000000u128),
4905 data: Bytes::new(),
4906 access_list: vec![],
4907 from: Address::from_str("0x8943545177806ed17b9f23f0a21ee5948ecaa776").unwrap(),
4908 ..Default::default()
4909 };
4910
4911 let encoded = PrivilegedL2Transaction::encode_to_vec(&privileged_l2);
4912 println!("encoded length: {}", encoded.len());
4913 assert_eq!(encoded.len(), privileged_l2.length());
4914
4915 let deserialized_tx = PrivilegedL2Transaction::decode(&encoded)?;
4916
4917 assert_eq!(deserialized_tx, privileged_l2);
4918
4919 Ok(())
4920 }
4921
4922 #[test]
4923 fn test_legacy_transaction_into_generic() {
4924 let legacy_tx = LegacyTransaction {
4925 nonce: 1,
4926 gas_price: U256::from(20_000_000_000u64),
4927 gas: 21000,
4928 to: TxKind::Call(
4929 Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
4930 ),
4931 value: U256::from(1_000_000_000_000_000_000u64),
4932 data: Bytes::default(),
4933 v: U256::from(27),
4934 r: U256::from(1),
4935 s: U256::from(1),
4936 ..Default::default()
4937 };
4938
4939 let generic_tx: GenericTransaction = legacy_tx.into();
4940 assert_eq!(generic_tx.r#type, TxType::Legacy);
4941 assert_eq!(generic_tx.nonce, Some(1));
4942 assert_eq!(generic_tx.gas_price, U256::from(20_000_000_000u64));
4943 assert_eq!(generic_tx.gas, Some(21000));
4944 assert_eq!(generic_tx.max_priority_fee_per_gas, None);
4945 assert_eq!(generic_tx.max_fee_per_gas, None);
4946 assert_eq!(generic_tx.access_list.len(), 0);
4947 assert_eq!(generic_tx.chain_id, None);
4948 }
4949
4950 #[test]
4951 fn test_eip2930_transaction_into_generic() {
4952 let access_list = vec![(
4953 Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
4954 vec![
4955 H256::from_str(
4956 "0x1234567890123456789012345678901234567890123456789012345678901234",
4957 )
4958 .unwrap(),
4959 ],
4960 )];
4961
4962 let eip2930_tx = EIP2930Transaction {
4963 chain_id: 1,
4964 nonce: 1,
4965 gas_price: U256::from(20_000_000_000u64),
4966 gas_limit: 21000,
4967 to: TxKind::Call(
4968 Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
4969 ),
4970 value: U256::from(1_000_000_000_000_000_000u64),
4971 data: Bytes::default(),
4972 access_list: access_list.clone(),
4973 signature_y_parity: false,
4974 signature_r: U256::from(1),
4975 signature_s: U256::from(1),
4976 ..Default::default()
4977 };
4978
4979 let generic_tx: GenericTransaction = eip2930_tx.into();
4980 assert_eq!(generic_tx.r#type, TxType::EIP2930);
4981 assert_eq!(generic_tx.nonce, Some(1));
4982 assert_eq!(generic_tx.gas_price, U256::from(20_000_000_000u64));
4983 assert_eq!(generic_tx.gas, Some(21000));
4984 assert_eq!(generic_tx.max_priority_fee_per_gas, None);
4985 assert_eq!(generic_tx.max_fee_per_gas, None);
4986 assert_eq!(generic_tx.chain_id, Some(1));
4987 assert_eq!(generic_tx.access_list.len(), 1);
4988 assert_eq!(generic_tx.access_list[0].address, access_list[0].0);
4989 assert_eq!(generic_tx.access_list[0].storage_keys, access_list[0].1);
4990 }
4991
4992 #[test]
4993 fn recover_address_rejects_high_s_signatures() {
4994 use ethrex_crypto::NativeCrypto;
4995 use k256::ecdsa::SigningKey;
4996
4997 let crypto = NativeCrypto;
4998
4999 let private_key = hex!("4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318");
5002 let signing_key = SigningKey::from_bytes(&private_key.into()).expect("Valid private key");
5003
5004 let msg = b"Test message for high-s signature rejection";
5006 let payload = keccak(msg).to_fixed_bytes();
5008
5009 let (signature, recovery_id) = signing_key
5012 .sign_prehash_recoverable(&payload)
5013 .expect("Signing failed");
5014
5015 let mut sig_bytes = [0u8; 65];
5017 sig_bytes[..64].copy_from_slice(&signature.to_bytes());
5018 sig_bytes[64] = recovery_id.to_byte();
5019
5020 let uncompressed_pub = signing_key.verifying_key().to_encoded_point(false);
5023 let pub_hash = ethrex_crypto::keccak::keccak_hash(&uncompressed_pub.as_bytes()[1..]);
5024 let expected_address = Address::from_slice(&pub_hash[12..]);
5025
5026 let recovered = crypto
5027 .recover_signer(&sig_bytes, &payload)
5028 .expect("Valid low-s signature should recover successfully");
5029 assert_eq!(recovered, expected_address, "Recovered address mismatch");
5030
5031 let n = U256::from_big_endian(&hex!(
5034 "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"
5035 ));
5036 let s = U256::from_big_endian(&sig_bytes[32..64]);
5037
5038 let half_n = n / 2;
5040 assert!(
5041 s <= half_n,
5042 "Generated signature was not low-s, cannot test high-s rejection"
5043 );
5044
5045 let s_high = n - s;
5047
5048 let mut sig_high_bytes = sig_bytes;
5049 sig_high_bytes[32..64].copy_from_slice(&s_high.to_big_endian());
5051 sig_high_bytes[64] ^= 1;
5053
5054 assert!(
5057 crypto.recover_signer(&sig_high_bytes, &payload).is_err(),
5058 "High-s signature should be rejected (EIP-2 compliance)"
5059 );
5060 }
5061
5062 #[test]
5063 fn encode_decode_low_size_tx() {
5064 let tx = Transaction::EIP2930Transaction(EIP2930Transaction::default());
5065 let tx_to_encode = tx.clone();
5068 let encoded = tx_to_encode.encode_to_vec();
5069 let decoded_tx = Transaction::decode(&encoded).unwrap();
5070 assert_eq!(tx, decoded_tx);
5071 }
5072
5073 #[test]
5074 fn test_eip1559_simple_transfer_size() {
5075 let tx = Transaction::EIP1559Transaction(EIP1559Transaction::default());
5076 assert_eq!(tx.encode_to_vec().len(), EIP1559_DEFAULT_SERIALIZED_LENGTH);
5077 }
5078
5079 fn make_test_frame_tx() -> FrameTransaction {
5082 FrameTransaction {
5083 chain_id: 1,
5084 nonce: 42,
5085 sender: Address::from_low_u64_be(0xABCD),
5086 frames: vec![
5087 Frame {
5088 mode: FrameMode::Verify as u8,
5089 flags: 0x03, target: Some(Address::from_low_u64_be(0xABCD)),
5091 gas_limit: 100_000,
5092 value: U256::zero(),
5093 data: Bytes::from_static(b"verify_data"),
5094 },
5095 Frame {
5096 mode: FrameMode::Sender as u8,
5097 flags: 0x00,
5098 target: Some(Address::from_low_u64_be(0x1234)),
5099 gas_limit: 200_000,
5100 value: U256::zero(),
5101 data: Bytes::from_static(b"call_data"),
5102 },
5103 ],
5104 signatures: vec![FrameSignature {
5105 scheme: FRAME_SIG_SCHEME_SECP256K1,
5106 signer: Some(Address::from_low_u64_be(0xABCD)),
5107 msg: Bytes::new(),
5108 signature: Bytes::from(vec![0u8; 65]),
5109 }],
5110 max_priority_fee_per_gas: 1_000_000_000,
5111 max_fee_per_gas: 30_000_000_000,
5112 max_fee_per_blob_gas: U256::zero(),
5113 blob_versioned_hashes: vec![],
5114 inner_hash: OnceCell::new(),
5115 cached_canonical: OnceCell::new(),
5116 }
5117 }
5118
5119 #[test]
5120 fn atomic_batch_flag_valid_on_default_and_sender_frames() {
5121 let mut tx = make_test_frame_tx();
5124 tx.frames = vec![
5125 Frame {
5126 mode: FrameMode::Default as u8,
5127 flags: 0x04, target: Some(Address::from_low_u64_be(0xB0B)),
5129 gas_limit: 21_000,
5130 value: U256::zero(),
5131 data: Bytes::new(),
5132 },
5133 Frame {
5134 mode: FrameMode::Sender as u8,
5135 flags: 0x04, target: Some(Address::from_low_u64_be(0xB0B)),
5137 gas_limit: 21_000,
5138 value: U256::zero(),
5139 data: Bytes::new(),
5140 },
5141 Frame {
5142 mode: FrameMode::Sender as u8,
5143 flags: 0x00, target: Some(Address::from_low_u64_be(0xCAFE)),
5145 gas_limit: 21_000,
5146 value: U256::zero(),
5147 data: Bytes::new(),
5148 },
5149 ];
5150 assert!(tx.validate_static_constraints().is_ok());
5151 }
5152
5153 #[test]
5154 fn atomic_batch_flag_on_last_frame_still_invalid() {
5155 let mut tx = make_test_frame_tx();
5156 tx.frames = vec![Frame {
5157 mode: FrameMode::Sender as u8,
5158 flags: 0x04,
5159 target: Some(Address::from_low_u64_be(0xCAFE)),
5160 gas_limit: 21_000,
5161 value: U256::zero(),
5162 data: Bytes::new(),
5163 }];
5164 let err = tx.validate_static_constraints().unwrap_err();
5165 assert!(err.contains("atomic batch flag on last frame"), "{err}");
5166 }
5167
5168 #[test]
5169 fn test_frame_rlp_roundtrip() {
5170 let frame = Frame {
5171 mode: FrameMode::Verify as u8,
5172 flags: 0x03,
5173 target: Some(Address::from_low_u64_be(0x1234)),
5174 gas_limit: 50_000,
5175 value: U256::zero(),
5176 data: Bytes::from_static(b"hello"),
5177 };
5178 let encoded = frame.encode_to_vec();
5179 let decoded = Frame::decode(&encoded).unwrap();
5180 assert_eq!(frame, decoded);
5181 }
5182
5183 #[test]
5184 fn test_frame_null_target_rlp_roundtrip() {
5185 let frame = Frame {
5186 mode: FrameMode::Default as u8,
5187 flags: 0x00,
5188 target: None,
5189 gas_limit: 10_000,
5190 value: U256::zero(),
5191 data: Bytes::from_static(b"deploy"),
5192 };
5193 let encoded = frame.encode_to_vec();
5194 let decoded = Frame::decode(&encoded).unwrap();
5195 assert_eq!(frame, decoded);
5196 }
5197
5198 #[test]
5199 fn test_frame_mode_with_flags_rlp_roundtrip() {
5200 for mode_val in [0u8, 1, 2] {
5202 let frame = Frame {
5203 mode: mode_val,
5204 flags: if mode_val == 1 { 0x03 } else { 0x00 },
5205 target: Some(Address::from_low_u64_be(0x1234)),
5206 gas_limit: 50_000,
5207 value: U256::zero(),
5208 data: Bytes::new(),
5209 };
5210 let encoded = frame.encode_to_vec();
5211 let decoded = Frame::decode(&encoded).unwrap();
5212 assert_eq!(frame, decoded);
5213 }
5214 let frame = Frame {
5216 mode: 2, flags: 0x01 | 0x04, target: Some(Address::from_low_u64_be(0x1234)),
5219 gas_limit: 50_000,
5220 value: U256::zero(),
5221 data: Bytes::new(),
5222 };
5223 assert_eq!(frame.execution_mode(), FrameMode::Sender);
5224 assert_eq!(frame.scope_restriction(), 1);
5225 assert!(frame.is_atomic_batch());
5226 let encoded = frame.encode_to_vec();
5227 let decoded = Frame::decode(&encoded).unwrap();
5228 assert_eq!(frame, decoded);
5229 }
5230
5231 #[test]
5232 fn frame_rlp_roundtrip_preserves_value() {
5233 let frame = Frame {
5234 mode: FrameMode::Sender as u8,
5235 flags: 0x00,
5236 target: Some(Address::from_low_u64_be(0xCAFE)),
5237 gas_limit: 100_000,
5238 value: U256::from(1_000_000_000_000_000u64), data: Bytes::from_static(b"hello"),
5240 };
5241 let encoded = frame.encode_to_vec();
5242 let decoded = Frame::decode(&encoded).unwrap();
5243 assert_eq!(frame, decoded);
5244 assert_eq!(decoded.value, U256::from(1_000_000_000_000_000u64));
5245 }
5246
5247 #[test]
5248 fn test_frame_transaction_rlp_roundtrip() {
5249 let tx = make_test_frame_tx();
5250 let encoded = tx.encode_to_vec();
5251 let decoded = FrameTransaction::decode(&encoded).unwrap();
5252 assert_eq!(tx, decoded);
5253 }
5254
5255 #[test]
5256 fn test_frame_transaction_canonical_roundtrip() {
5257 let tx = Transaction::FrameTransaction(make_test_frame_tx());
5258 let encoded = tx.encode_canonical_to_vec();
5259 let decoded = Transaction::decode_canonical(&encoded).unwrap();
5260 assert_eq!(
5262 tx.hash(ðrex_crypto::NativeCrypto),
5263 decoded.hash(ðrex_crypto::NativeCrypto)
5264 );
5265 assert_eq!(tx.tx_type(), decoded.tx_type());
5267 assert_eq!(tx.nonce(), decoded.nonce());
5268 assert_eq!(tx.chain_id(), decoded.chain_id());
5269 }
5270
5271 #[test]
5272 fn test_frame_transaction_sig_hash_covers_all_frame_data() {
5273 let tx = make_test_frame_tx();
5275 let hash1 = tx.compute_sig_hash();
5276
5277 let mut tx2 = tx.clone();
5279 tx2.frames[0].data = Bytes::from_static(b"completely_different_verify_data");
5280 let hash2 = tx2.compute_sig_hash();
5281 assert_ne!(hash1, hash2);
5282
5283 let mut tx3 = tx;
5285 tx3.frames[1].data = Bytes::from_static(b"different_call_data");
5286 let hash3 = tx3.compute_sig_hash();
5287 assert_ne!(hash1, hash3);
5288 }
5289
5290 #[test]
5291 fn test_frame_transaction_accessor_methods() {
5292 let frame_tx = make_test_frame_tx();
5293 let tx = Transaction::FrameTransaction(frame_tx);
5294
5295 assert_eq!(tx.tx_type(), TxType::Frame);
5296 assert_eq!(tx.nonce(), 42);
5297 assert_eq!(tx.chain_id(), Some(1));
5298 assert_eq!(tx.to(), TxKind::Call(Address::from_low_u64_be(0xABCD)));
5299 assert_eq!(tx.value(), U256::zero());
5300 assert_eq!(tx.data(), &Bytes::new());
5301 assert!(tx.access_list().is_empty());
5302 assert!(tx.authorization_list().is_none());
5303 assert_eq!(tx.max_priority_fee(), Some(1_000_000_000));
5304 assert_eq!(tx.max_fee_per_gas(), Some(30_000_000_000));
5305 assert_eq!(tx.max_fee_per_blob_gas(), None); assert!(!tx.is_contract_creation());
5307 assert_eq!(
5309 tx.sender(ðrex_crypto::NativeCrypto).unwrap(),
5310 Address::from_low_u64_be(0xABCD)
5311 );
5312 assert!(tx.gas_limit() >= 300_000); }
5315
5316 #[test]
5317 fn test_frame_transaction_hash_uses_oncecell() {
5318 let tx = Transaction::FrameTransaction(make_test_frame_tx());
5319 let h1 = tx.hash(ðrex_crypto::NativeCrypto);
5320 let h2 = tx.hash(ðrex_crypto::NativeCrypto);
5321 assert_eq!(h1, h2);
5322 }
5323
5324 #[test]
5325 fn frame_transaction_rlp_roundtrip_preserves_fields() {
5326 let tx = make_test_frame_tx();
5327 let mut buf = Vec::new();
5328 tx.encode(&mut buf);
5329 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5330 assert!(rest.is_empty());
5331 assert_eq!(decoded.chain_id, tx.chain_id);
5332 assert_eq!(decoded.nonce, tx.nonce);
5333 assert_eq!(decoded.sender, tx.sender);
5334 }
5335
5336 #[test]
5337 fn frame_transaction_variant_is_exposed_on_transaction_enum() {
5338 let tx = Transaction::FrameTransaction(make_test_frame_tx());
5339 assert!(matches!(tx, Transaction::FrameTransaction(_)));
5340 }
5341
5342 #[test]
5343 fn frame_transaction_serializes_signatures_as_array() {
5344 let tx = make_test_frame_tx();
5346 let json = serde_json::to_value(&tx).unwrap();
5347 let sigs = json
5348 .get("signatures")
5349 .expect("signatures field present")
5350 .as_array()
5351 .expect("signatures serialized as an array");
5352 assert_eq!(sigs.len(), tx.signatures.len());
5353 assert_eq!(sigs[0].get("scheme").unwrap(), "0x1");
5354 assert!(sigs[0].get("signer").is_some());
5355 assert!(sigs[0].get("signature").is_some());
5356 assert!(sigs[0].get("msg").is_some());
5357 }
5358
5359 fn make_frame_tx_with_gas_limits(limits: Vec<u64>) -> FrameTransaction {
5360 let frames = limits
5361 .into_iter()
5362 .map(|gl| Frame {
5363 mode: FrameMode::Sender as u8,
5364 flags: 0x00,
5365 target: Some(Address::from_low_u64_be(0x1234)),
5366 gas_limit: gl,
5367 value: U256::zero(),
5368 data: Bytes::new(),
5369 })
5370 .collect();
5371 FrameTransaction {
5372 chain_id: 1,
5373 nonce: 0,
5374 sender: Address::from_low_u64_be(0xABCD),
5375 frames,
5376 signatures: vec![],
5377 max_priority_fee_per_gas: 1_000_000_000,
5378 max_fee_per_gas: 30_000_000_000,
5379 max_fee_per_blob_gas: U256::zero(),
5380 blob_versioned_hashes: vec![],
5381 inner_hash: OnceCell::new(),
5382 cached_canonical: OnceCell::new(),
5383 }
5384 }
5385
5386 #[test]
5387 fn per_frame_gas_limit_above_i64_max_is_rejected() {
5388 let tx = make_frame_tx_with_gas_limits(vec![(i64::MAX as u64) + 1]);
5389 let err = tx.validate_static_constraints().unwrap_err();
5390 assert!(err.contains("exceeds 2**63-1"), "unexpected error: {err}");
5391 }
5392
5393 #[test]
5394 fn cumulative_frame_gas_limit_above_i64_max_is_rejected() {
5395 let half = (i64::MAX as u64) / 2 + 1;
5396 let tx = make_frame_tx_with_gas_limits(vec![half, half]);
5397 let err = tx.validate_static_constraints().unwrap_err();
5398 assert!(err.contains("cumulative"), "unexpected error: {err}");
5399 }
5400
5401 #[test]
5402 fn cumulative_frame_gas_limit_equal_to_i64_max_is_accepted() {
5403 let a = (i64::MAX as u64) / 2;
5404 let b = i64::MAX as u64 - a;
5405 let tx = make_frame_tx_with_gas_limits(vec![a, b]);
5406 tx.validate_static_constraints()
5407 .expect("exact i64::MAX total should be accepted");
5408 }
5409
5410 #[test]
5411 fn empty_frames_list_is_rejected_by_count_check_not_gas_check() {
5412 let tx = make_frame_tx_with_gas_limits(vec![]);
5415 let err = tx.validate_static_constraints().unwrap_err();
5416 assert!(err.contains("between 1 and"), "unexpected error: {err}");
5417 }
5418
5419 #[test]
5420 fn sig_hash_covers_frame_value() {
5421 let tx = make_test_frame_tx();
5424 let baseline = tx.compute_sig_hash();
5425
5426 let mut with_sender_value = tx.clone();
5427 with_sender_value.frames[1].value = U256::from(1u64);
5428 assert_ne!(
5429 baseline,
5430 with_sender_value.compute_sig_hash(),
5431 "sig_hash must change when a SENDER frame's value changes"
5432 );
5433
5434 let mut with_verify_value = tx.clone();
5435 with_verify_value.frames[0].value = U256::from(1u64);
5436 assert_ne!(
5437 baseline,
5438 with_verify_value.compute_sig_hash(),
5439 "sig_hash must change when a VERIFY frame's value changes"
5440 );
5441
5442 let mut with_verify_data = tx;
5444 with_verify_data.frames[0].data = Bytes::from_static(b"different_verify_data");
5445 assert_ne!(
5446 baseline,
5447 with_verify_data.compute_sig_hash(),
5448 "VERIFY.data is now covered by sig_hash (no longer elided)"
5449 );
5450 }
5451
5452 #[test]
5453 fn validate_static_constraints_rejects_nonzero_value_on_non_sender_frames() {
5454 let verify_tx = FrameTransaction {
5456 chain_id: 1,
5457 nonce: 0,
5458 sender: Address::from_low_u64_be(0xABCD),
5459 frames: vec![Frame {
5460 mode: FrameMode::Verify as u8,
5461 flags: 0x01,
5462 target: None,
5463 gas_limit: 50_000,
5464 value: U256::from(1u64),
5465 data: Bytes::new(),
5466 }],
5467 signatures: vec![],
5468 max_priority_fee_per_gas: 1_000_000_000,
5469 max_fee_per_gas: 30_000_000_000,
5470 max_fee_per_blob_gas: U256::zero(),
5471 blob_versioned_hashes: vec![],
5472 inner_hash: OnceCell::new(),
5473 cached_canonical: OnceCell::new(),
5474 };
5475 let err = verify_tx.validate_static_constraints().unwrap_err();
5476 assert!(
5477 err.contains("non-zero value only allowed in SENDER mode"),
5478 "unexpected error for VERIFY: {err}"
5479 );
5480
5481 let default_tx = FrameTransaction {
5483 frames: vec![Frame {
5484 mode: FrameMode::Default as u8,
5485 flags: 0x00,
5486 target: Some(Address::from_low_u64_be(0x1234)),
5487 gas_limit: 50_000,
5488 value: U256::from(1u64),
5489 data: Bytes::new(),
5490 }],
5491 ..verify_tx
5492 };
5493 let err = default_tx.validate_static_constraints().unwrap_err();
5494 assert!(
5495 err.contains("non-zero value only allowed in SENDER mode"),
5496 "unexpected error for DEFAULT: {err}"
5497 );
5498
5499 let sender_tx = FrameTransaction {
5501 frames: vec![Frame {
5502 mode: FrameMode::Sender as u8,
5503 flags: 0x00,
5504 target: Some(Address::from_low_u64_be(0x1234)),
5505 gas_limit: 50_000,
5506 value: U256::from(1u64),
5507 data: Bytes::new(),
5508 }],
5509 ..default_tx
5510 };
5511 sender_tx
5512 .validate_static_constraints()
5513 .expect("SENDER frames may carry non-zero value");
5514 }
5515
5516 fn chain_config_with_hegota(hegota_time: Option<u64>) -> crate::types::ChainConfig {
5519 crate::types::ChainConfig {
5520 hegota_time,
5521 ..Default::default()
5522 }
5523 }
5524
5525 #[test]
5526 fn test_frame_tx_pre_fork_chain_config_rejects() {
5527 let cfg = chain_config_with_hegota(None);
5528 assert!(!cfg.is_hegota_activated(0));
5529 assert!(!cfg.is_hegota_activated(u64::MAX));
5530 }
5531
5532 #[test]
5533 fn test_frame_tx_post_fork_admits() {
5534 let cfg = chain_config_with_hegota(Some(1000));
5535 assert!(cfg.is_hegota_activated(2000));
5536 }
5537
5538 #[test]
5539 fn test_frame_tx_fork_boundary_admits() {
5540 let activation_time = 1_700_000_000u64;
5541 let cfg = chain_config_with_hegota(Some(activation_time));
5542 assert!(cfg.is_hegota_activated(activation_time));
5543 assert!(!cfg.is_hegota_activated(activation_time - 1));
5544 }
5545
5546 #[test]
5547 fn test_frame_tx_devnet_fork_epoch_zero_admits() {
5548 let cfg = chain_config_with_hegota(Some(0));
5549 assert!(cfg.is_hegota_activated(0));
5550 assert!(cfg.is_hegota_activated(1));
5551 assert!(cfg.is_hegota_activated(u64::MAX));
5552 }
5553
5554 #[test]
5555 fn test_frame_tx_pre_fork_rlp_roundtrip_still_decodes() {
5556 let tx = make_test_frame_tx();
5559 let mut buf = Vec::new();
5560 tx.encode(&mut buf);
5561 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5562 assert!(rest.is_empty());
5563 assert_eq!(decoded.chain_id, tx.chain_id);
5564 assert_eq!(decoded.nonce, tx.nonce);
5565 assert_eq!(decoded.sender, tx.sender);
5566 }
5567
5568 fn expiry_frame(deadline: u64) -> Frame {
5571 Frame {
5572 mode: FrameMode::Verify as u8,
5573 flags: 0x00,
5574 target: Some(frame_tx_expiry_verifier()),
5575 gas_limit: 30_000,
5576 value: U256::zero(),
5577 data: Bytes::copy_from_slice(&deadline.to_be_bytes()),
5578 }
5579 }
5580
5581 #[test]
5582 fn expiry_verifier_frame_passes_static_validation() {
5583 let mut tx = make_test_frame_tx();
5584 tx.frames.insert(0, expiry_frame(1_700_000_000));
5585 assert!(tx.validate_static_constraints().is_ok());
5586 }
5587
5588 #[test]
5589 fn expiry_verifier_frame_rejects_bad_data_length() {
5590 let mut tx = make_test_frame_tx();
5591 let mut f = expiry_frame(0);
5592 f.data = Bytes::from_static(b"short");
5593 tx.frames.insert(0, f);
5594 let err = tx.validate_static_constraints().unwrap_err();
5595 assert!(err.contains("8 bytes"), "{err}");
5596 }
5597
5598 #[test]
5599 fn expiry_verifier_frame_rejects_nonzero_flags() {
5600 let mut tx = make_test_frame_tx();
5601 let mut f = expiry_frame(0);
5602 f.flags = 0x01;
5603 tx.frames.insert(0, f);
5604 let err = tx.validate_static_constraints().unwrap_err();
5605 assert!(err.contains("flags == 0"), "{err}");
5606 }
5607
5608 #[test]
5609 fn at_most_one_expiry_verifier_frame() {
5610 let mut tx = make_test_frame_tx();
5611 tx.frames.insert(0, expiry_frame(1));
5612 tx.frames.insert(0, expiry_frame(2));
5613 let err = tx.validate_static_constraints().unwrap_err();
5614 assert!(err.contains("more than one expiry"), "{err}");
5615 }
5616
5617 #[test]
5618 fn verify_frame_with_zero_scope_is_now_statically_valid() {
5619 let mut tx = make_test_frame_tx();
5624 tx.frames[0].flags = 0x00;
5625 assert!(tx.validate_static_constraints().is_ok());
5626 }
5627
5628 #[test]
5629 fn sig_hash_covers_expiry_deadline_and_all_verify_data() {
5630 let mut tx_a = make_test_frame_tx();
5632 tx_a.frames.insert(0, expiry_frame(100));
5633 let mut tx_b = make_test_frame_tx();
5634 tx_b.frames.insert(0, expiry_frame(200));
5635 assert_ne!(tx_a.compute_sig_hash(), tx_b.compute_sig_hash());
5637
5638 let tx_c = make_test_frame_tx();
5640 let mut tx_d = make_test_frame_tx();
5641 tx_d.frames[0].data = Bytes::from_static(b"different_verify_data");
5642 assert_ne!(tx_c.compute_sig_hash(), tx_d.compute_sig_hash());
5643 }
5644
5645 #[test]
5646 fn expiry_deadline_parses_or_none() {
5647 let mut tx = make_test_frame_tx();
5648 assert_eq!(tx.expiry_deadline(), None);
5649 tx.frames.insert(0, expiry_frame(1_700_000_123));
5650 assert_eq!(tx.expiry_deadline(), Some(1_700_000_123));
5651 }
5652
5653 #[test]
5654 fn signature_rlp_roundtrip() {
5655 let sig = FrameSignature {
5656 scheme: FRAME_SIG_SCHEME_P256,
5657 signer: Some(Address::from_low_u64_be(0x1234)),
5658 msg: Bytes::from(vec![7u8; 32]),
5659 signature: Bytes::from(vec![9u8; 128]),
5660 };
5661 let mut buf = Vec::new();
5662 sig.encode(&mut buf);
5663 let (decoded, rest) = FrameSignature::decode_unfinished(&buf).unwrap();
5664 assert!(rest.is_empty());
5665 assert_eq!(decoded, sig);
5666 }
5667
5668 #[test]
5669 fn frame_tx_with_signatures_rlp_roundtrip() {
5670 let tx = make_test_frame_tx();
5671 assert!(!tx.signatures.is_empty());
5672 let mut buf = Vec::new();
5673 tx.encode(&mut buf);
5674 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5675 assert!(rest.is_empty());
5676 assert_eq!(decoded.signatures, tx.signatures);
5677 assert_eq!(decoded.frames, tx.frames);
5678 assert_eq!(decoded.nonce, tx.nonce);
5679 }
5680
5681 #[test]
5682 fn p2p_frame_transaction_rlp_roundtrip() {
5683 let ft = make_test_frame_tx();
5686 assert!(!ft.signatures.is_empty());
5687 assert!(!ft.frames.is_empty());
5688 let original = P2PTransaction::FrameTransaction(ft);
5689
5690 let encoded = original.encode_to_vec();
5691 let (decoded, rest) = P2PTransaction::decode_unfinished(&encoded).unwrap();
5692 assert!(rest.is_empty());
5693 assert_eq!(decoded, original);
5694 assert_eq!(decoded.tx_type(), TxType::Frame);
5695
5696 let as_tx: Transaction = decoded.try_into().unwrap();
5699 assert!(matches!(as_tx, Transaction::FrameTransaction(_)));
5700 }
5701
5702 #[test]
5703 fn static_validation_rejects_unknown_scheme() {
5704 let mut tx = make_test_frame_tx();
5705 tx.signatures[0].scheme = 3;
5706 assert!(
5707 tx.validate_static_constraints()
5708 .unwrap_err()
5709 .contains("unsupported scheme"),
5710 );
5711 }
5712
5713 #[test]
5714 fn static_validation_accepts_arbitrary_with_empty_signer() {
5715 let mut tx = make_test_frame_tx();
5716 tx.signatures[0].scheme = FRAME_SIG_SCHEME_ARBITRARY;
5718 tx.signatures[0].signer = None;
5719 assert!(tx.validate_static_constraints().is_ok());
5720 assert_eq!(tx.signature_verification_cost(), 100);
5721 }
5722
5723 #[test]
5724 fn static_validation_rejects_arbitrary_with_signer() {
5725 let mut tx = make_test_frame_tx();
5726 tx.signatures[0].scheme = FRAME_SIG_SCHEME_ARBITRARY;
5727 tx.signatures[0].signer = Some(Address::from_low_u64_be(0xABCD));
5728 assert!(
5729 tx.validate_static_constraints()
5730 .unwrap_err()
5731 .contains("ARBITRARY signatures must not name a signer"),
5732 );
5733 }
5734
5735 #[test]
5736 fn static_validation_rejects_bad_msg_length() {
5737 let mut tx = make_test_frame_tx();
5738 tx.signatures[0].msg = Bytes::from(vec![1u8; 16]);
5739 assert!(
5740 tx.validate_static_constraints()
5741 .unwrap_err()
5742 .contains("32 bytes"),
5743 );
5744 }
5745
5746 #[test]
5747 fn static_validation_rejects_zero_explicit_msg() {
5748 let mut tx = make_test_frame_tx();
5749 tx.signatures[0].msg = Bytes::from(vec![0u8; 32]);
5750 assert!(
5751 tx.validate_static_constraints()
5752 .unwrap_err()
5753 .contains("zero digest"),
5754 );
5755 }
5756
5757 #[test]
5758 fn sig_hash_elides_empty_msg_signature_bytes() {
5759 let mut a = make_test_frame_tx();
5760 let mut b = make_test_frame_tx();
5761 assert!(a.signatures[0].msg.is_empty());
5763 a.signatures[0].signature = Bytes::from(vec![1u8; 65]);
5764 b.signatures[0].signature = Bytes::from(vec![2u8; 65]);
5765 assert_eq!(a.compute_sig_hash(), b.compute_sig_hash());
5766 }
5767
5768 #[test]
5769 fn sig_hash_covers_frame_data_now() {
5770 let mut a = make_test_frame_tx();
5771 let mut b = make_test_frame_tx();
5772 a.frames[0].data = Bytes::from_static(b"aaaa");
5774 b.frames[0].data = Bytes::from_static(b"bbbb");
5775 assert_ne!(a.compute_sig_hash(), b.compute_sig_hash());
5776 }
5777
5778 #[test]
5779 fn sig_hash_covers_explicit_msg_signature_bytes() {
5780 let mut a = make_test_frame_tx();
5781 let mut b = make_test_frame_tx();
5782 a.signatures[0].msg = Bytes::from(vec![9u8; 32]);
5783 b.signatures[0].msg = Bytes::from(vec![9u8; 32]);
5784 a.signatures[0].signature = Bytes::from(vec![1u8; 65]);
5785 b.signatures[0].signature = Bytes::from(vec![2u8; 65]);
5786 assert_ne!(a.compute_sig_hash(), b.compute_sig_hash());
5788 }
5789
5790 #[test]
5791 fn max_gas_includes_signature_costs() {
5792 let mut tx = make_test_frame_tx();
5793 let base = tx.max_gas();
5794 tx.signatures.push(FrameSignature {
5796 scheme: FRAME_SIG_SCHEME_P256,
5797 signer: Some(Address::from_low_u64_be(1)),
5798 msg: Bytes::new(),
5799 signature: Bytes::from(vec![0u8; 128]),
5800 });
5801 assert!(tx.max_gas() >= base + 6700);
5802 assert_eq!(tx.signature_verification_cost(), 2800 + 6700);
5803 }
5804
5805 #[test]
5806 fn golden_frame_tx_rlp_and_sig_hash() {
5807 let tx = FrameTransaction {
5812 chain_id: 1,
5813 nonce: 7,
5814 sender: Address::from_low_u64_be(0xABCD),
5815 frames: vec![
5816 Frame {
5817 mode: 1,
5818 flags: 3,
5819 target: None,
5820 gas_limit: 0x5208,
5821 value: U256::zero(),
5822 data: Bytes::from_static(&[0x11, 0x22]),
5823 },
5824 Frame {
5825 mode: 2,
5826 flags: 0,
5827 target: Some(Address::from_low_u64_be(0x1234)),
5828 gas_limit: 0x9c40,
5829 value: U256::zero(),
5830 data: Bytes::new(),
5831 },
5832 ],
5833 signatures: vec![FrameSignature {
5834 scheme: FRAME_SIG_SCHEME_SECP256K1,
5835 signer: Some(Address::from_low_u64_be(0xABCD)),
5836 msg: Bytes::new(),
5837 signature: Bytes::from(vec![0x01u8; 65]),
5838 }],
5839 max_priority_fee_per_gas: 0x3b9aca00,
5840 max_fee_per_gas: 0x6fc23ac00,
5841 max_fee_per_blob_gas: U256::zero(),
5842 blob_versioned_hashes: vec![],
5843 inner_hash: OnceCell::new(),
5844 cached_canonical: OnceCell::new(),
5845 };
5846
5847 let mut buf = Vec::new();
5848 tx.encode(&mut buf);
5849 let rlp_hex = hex::encode(&buf);
5850 assert_eq!(
5852 rlp_hex,
5853 "f8ab010794000000000000000000000000000000000000abcde8ca01038082520880821122dc0280940000000000000000000000000000000000001234829c408080f85cf85a0194000000000000000000000000000000000000abcd80b8410101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101843b9aca008506fc23ac0080c0"
5854 );
5855
5856 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5858 assert!(rest.is_empty());
5859 assert_eq!(decoded, tx);
5860
5861 let sig_hash = tx.compute_sig_hash();
5862 assert_eq!(
5864 format!("{:#x}", sig_hash),
5865 "0xe7dc3f33413fc69c09f9c690be154ded294954e497aeea6ce0010ba513f2f26d",
5866 );
5867
5868 let mut tx2 = tx.clone();
5870 tx2.signatures[0].signature = Bytes::from(vec![0x02u8; 65]);
5871 assert_eq!(
5872 tx.compute_sig_hash(),
5873 tx2.compute_sig_hash(),
5874 "sig_hash must be independent of empty-msg signature bytes",
5875 );
5876 }
5877
5878 #[test]
5879 fn test_cost_without_base_fee_eip4844_includes_blob_gas() {
5880 use crate::constants::GAS_PER_BLOB;
5884
5885 let max_fee_per_gas: u64 = 100;
5886 let gas: u64 = 21_000;
5887 let value = U256::from(7u64);
5888 let max_fee_per_blob_gas = U256::from(50u64);
5889 let blob_count: usize = 1;
5890
5891 let tx = Transaction::EIP4844Transaction(EIP4844Transaction {
5892 max_fee_per_gas,
5893 gas,
5894 value,
5895 max_fee_per_blob_gas,
5896 blob_versioned_hashes: vec![H256::zero(); blob_count],
5897 ..Default::default()
5898 });
5899
5900 let got = tx.cost_without_base_fee().expect("cost is computable");
5901
5902 let gas_cost = U256::from(max_fee_per_gas) * U256::from(gas);
5903 let blob_gas = U256::from(GAS_PER_BLOB) * U256::from(blob_count as u64);
5904 let blob_cost = blob_gas * max_fee_per_blob_gas;
5905 let expected = gas_cost + blob_cost + value;
5906
5907 assert_eq!(
5908 got, expected,
5909 "blob-gas term missing from cost_without_base_fee() for EIP-4844"
5910 );
5911 }
5912}