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 pub fn gas_limit(&self) -> u64 {
1431 match self {
1432 Transaction::LegacyTransaction(tx) => tx.gas,
1433 Transaction::EIP2930Transaction(tx) => tx.gas_limit,
1434 Transaction::EIP1559Transaction(tx) => tx.gas_limit,
1435 Transaction::EIP7702Transaction(tx) => tx.gas_limit,
1436 Transaction::EIP4844Transaction(tx) => tx.gas,
1437 Transaction::PrivilegedL2Transaction(tx) => tx.gas_limit,
1438 Transaction::FeeTokenTransaction(tx) => tx.gas_limit,
1439 Transaction::FrameTransaction(tx) => tx.max_gas(),
1440 }
1441 }
1442
1443 pub fn gas_price(&self) -> U256 {
1445 match self {
1446 Transaction::LegacyTransaction(tx) => tx.gas_price,
1447 Transaction::EIP2930Transaction(tx) => tx.gas_price,
1448 Transaction::EIP1559Transaction(tx) => U256::from(tx.max_fee_per_gas),
1449 Transaction::EIP7702Transaction(tx) => U256::from(tx.max_fee_per_gas),
1450 Transaction::EIP4844Transaction(tx) => U256::from(tx.max_fee_per_gas),
1451 Transaction::PrivilegedL2Transaction(tx) => U256::from(tx.max_fee_per_gas),
1452 Transaction::FeeTokenTransaction(tx) => U256::from(tx.max_fee_per_gas),
1453 Transaction::FrameTransaction(tx) => U256::from(tx.max_fee_per_gas),
1454 }
1455 }
1456
1457 pub fn to(&self) -> TxKind {
1458 match self {
1459 Transaction::LegacyTransaction(tx) => tx.to.clone(),
1460 Transaction::EIP2930Transaction(tx) => tx.to.clone(),
1461 Transaction::EIP1559Transaction(tx) => tx.to.clone(),
1462 Transaction::EIP4844Transaction(tx) => TxKind::Call(tx.to),
1463 Transaction::EIP7702Transaction(tx) => TxKind::Call(tx.to),
1464 Transaction::PrivilegedL2Transaction(tx) => tx.to.clone(),
1465 Transaction::FeeTokenTransaction(tx) => tx.to.clone(),
1466 Transaction::FrameTransaction(tx) => TxKind::Call(tx.sender),
1467 }
1468 }
1469
1470 pub fn value(&self) -> U256 {
1471 match self {
1472 Transaction::LegacyTransaction(tx) => tx.value,
1473 Transaction::EIP2930Transaction(tx) => tx.value,
1474 Transaction::EIP1559Transaction(tx) => tx.value,
1475 Transaction::EIP4844Transaction(tx) => tx.value,
1476 Transaction::EIP7702Transaction(tx) => tx.value,
1477 Transaction::PrivilegedL2Transaction(tx) => tx.value,
1478 Transaction::FeeTokenTransaction(tx) => tx.value,
1479 Transaction::FrameTransaction(_) => U256::zero(),
1480 }
1481 }
1482
1483 pub fn max_priority_fee(&self) -> Option<u64> {
1484 match self {
1485 Transaction::LegacyTransaction(_tx) => None,
1486 Transaction::EIP2930Transaction(_tx) => None,
1487 Transaction::EIP1559Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1488 Transaction::EIP4844Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1489 Transaction::EIP7702Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1490 Transaction::PrivilegedL2Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1491 Transaction::FeeTokenTransaction(tx) => Some(tx.max_priority_fee_per_gas),
1492 Transaction::FrameTransaction(tx) => Some(tx.max_priority_fee_per_gas),
1493 }
1494 }
1495
1496 pub fn chain_id(&self) -> Option<u64> {
1497 match self {
1498 Transaction::LegacyTransaction(tx) => derive_legacy_chain_id(tx.v),
1499 Transaction::EIP2930Transaction(tx) => Some(tx.chain_id),
1500 Transaction::EIP1559Transaction(tx) => Some(tx.chain_id),
1501 Transaction::EIP4844Transaction(tx) => Some(tx.chain_id),
1502 Transaction::EIP7702Transaction(tx) => Some(tx.chain_id),
1503 Transaction::PrivilegedL2Transaction(tx) => Some(tx.chain_id),
1504 Transaction::FeeTokenTransaction(tx) => Some(tx.chain_id),
1505 Transaction::FrameTransaction(tx) => Some(tx.chain_id),
1506 }
1507 }
1508
1509 pub fn access_list(&self) -> &AccessList {
1510 static EMPTY_ACCESS_LIST: AccessList = Vec::new();
1511 match self {
1512 Transaction::LegacyTransaction(_tx) => &EMPTY_ACCESS_LIST,
1513 Transaction::EIP2930Transaction(tx) => &tx.access_list,
1514 Transaction::EIP1559Transaction(tx) => &tx.access_list,
1515 Transaction::EIP4844Transaction(tx) => &tx.access_list,
1516 Transaction::EIP7702Transaction(tx) => &tx.access_list,
1517 Transaction::PrivilegedL2Transaction(tx) => &tx.access_list,
1518 Transaction::FeeTokenTransaction(tx) => &tx.access_list,
1519 Transaction::FrameTransaction(_) => &EMPTY_ACCESS_LIST,
1520 }
1521 }
1522 pub fn authorization_list(&self) -> Option<&AuthorizationList> {
1523 match self {
1524 Transaction::LegacyTransaction(_) => None,
1525 Transaction::EIP2930Transaction(_) => None,
1526 Transaction::EIP1559Transaction(_) => None,
1527 Transaction::EIP4844Transaction(_) => None,
1528 Transaction::EIP7702Transaction(tx) => Some(&tx.authorization_list),
1529 Transaction::PrivilegedL2Transaction(_) => None,
1530 Transaction::FeeTokenTransaction(_) => None,
1531 Transaction::FrameTransaction(_) => None,
1532 }
1533 }
1534
1535 pub fn nonce(&self) -> u64 {
1536 match self {
1537 Transaction::LegacyTransaction(tx) => tx.nonce,
1538 Transaction::EIP2930Transaction(tx) => tx.nonce,
1539 Transaction::EIP1559Transaction(tx) => tx.nonce,
1540 Transaction::EIP4844Transaction(tx) => tx.nonce,
1541 Transaction::EIP7702Transaction(tx) => tx.nonce,
1542 Transaction::PrivilegedL2Transaction(tx) => tx.nonce,
1543 Transaction::FeeTokenTransaction(tx) => tx.nonce,
1544 Transaction::FrameTransaction(tx) => tx.nonce,
1545 }
1546 }
1547
1548 pub fn data(&self) -> &Bytes {
1549 match self {
1550 Transaction::LegacyTransaction(tx) => &tx.data,
1551 Transaction::EIP2930Transaction(tx) => &tx.data,
1552 Transaction::EIP1559Transaction(tx) => &tx.data,
1553 Transaction::EIP4844Transaction(tx) => &tx.data,
1554 Transaction::EIP7702Transaction(tx) => &tx.data,
1555 Transaction::PrivilegedL2Transaction(tx) => &tx.data,
1556 Transaction::FeeTokenTransaction(tx) => &tx.data,
1557 Transaction::FrameTransaction(_) => {
1558 static EMPTY_DATA: Bytes = Bytes::new();
1559 &EMPTY_DATA
1560 }
1561 }
1562 }
1563
1564 pub fn blob_versioned_hashes(&self) -> Vec<H256> {
1565 match self {
1566 Transaction::LegacyTransaction(_) => Vec::new(),
1567 Transaction::EIP2930Transaction(_) => Vec::new(),
1568 Transaction::EIP1559Transaction(_) => Vec::new(),
1569 Transaction::EIP4844Transaction(tx) => tx.blob_versioned_hashes.clone(),
1570 Transaction::EIP7702Transaction(_) => Vec::new(),
1571 Transaction::PrivilegedL2Transaction(_) => Vec::new(),
1572 Transaction::FeeTokenTransaction(_) => Vec::new(),
1573 Transaction::FrameTransaction(tx) => tx.blob_versioned_hashes.clone(),
1574 }
1575 }
1576
1577 pub fn max_fee_per_blob_gas(&self) -> Option<U256> {
1578 match self {
1579 Transaction::LegacyTransaction(_) => None,
1580 Transaction::EIP2930Transaction(_) => None,
1581 Transaction::EIP1559Transaction(_) => None,
1582 Transaction::EIP4844Transaction(tx) => Some(tx.max_fee_per_blob_gas),
1583 Transaction::EIP7702Transaction(_) => None,
1584 Transaction::PrivilegedL2Transaction(_) => None,
1585 Transaction::FeeTokenTransaction(_) => None,
1586 Transaction::FrameTransaction(tx) => {
1587 if tx.blob_versioned_hashes.is_empty() {
1588 None
1589 } else {
1590 Some(tx.max_fee_per_blob_gas)
1591 }
1592 }
1593 }
1594 }
1595
1596 pub fn is_blob_carrying(&self) -> bool {
1603 self.max_fee_per_blob_gas().is_some()
1604 }
1605
1606 pub fn is_contract_creation(&self) -> bool {
1607 match &self {
1608 Transaction::LegacyTransaction(t) => matches!(t.to, TxKind::Create),
1609 Transaction::EIP2930Transaction(t) => matches!(t.to, TxKind::Create),
1610 Transaction::EIP1559Transaction(t) => matches!(t.to, TxKind::Create),
1611 Transaction::EIP4844Transaction(_) => false,
1612 Transaction::EIP7702Transaction(_) => false,
1613 Transaction::PrivilegedL2Transaction(t) => matches!(t.to, TxKind::Create),
1614 Transaction::FeeTokenTransaction(t) => matches!(t.to, TxKind::Create),
1615 Transaction::FrameTransaction(_) => false,
1616 }
1617 }
1618
1619 pub fn is_privileged(&self) -> bool {
1620 matches!(self, Transaction::PrivilegedL2Transaction(_))
1621 }
1622
1623 pub fn max_fee_per_gas(&self) -> Option<u64> {
1624 match self {
1625 Transaction::LegacyTransaction(_tx) => None,
1626 Transaction::EIP2930Transaction(_tx) => None,
1627 Transaction::EIP1559Transaction(tx) => Some(tx.max_fee_per_gas),
1628 Transaction::EIP4844Transaction(tx) => Some(tx.max_fee_per_gas),
1629 Transaction::EIP7702Transaction(tx) => Some(tx.max_fee_per_gas),
1630 Transaction::PrivilegedL2Transaction(tx) => Some(tx.max_fee_per_gas),
1631 Transaction::FeeTokenTransaction(tx) => Some(tx.max_fee_per_gas),
1632 Transaction::FrameTransaction(tx) => Some(tx.max_fee_per_gas),
1633 }
1634 }
1635
1636 fn compute_hash(&self, crypto: &dyn Crypto) -> H256 {
1637 if let Transaction::PrivilegedL2Transaction(tx) = self {
1638 return tx.get_privileged_hash().unwrap_or_default();
1639 }
1640 H256(crypto.keccak256(&self.encode_canonical_to_vec()))
1641 }
1642
1643 pub fn hash(&self, crypto: &dyn Crypto) -> H256 {
1644 let inner_hash = match self {
1645 Transaction::LegacyTransaction(tx) => &tx.inner_hash,
1646 Transaction::EIP2930Transaction(tx) => &tx.inner_hash,
1647 Transaction::EIP1559Transaction(tx) => &tx.inner_hash,
1648 Transaction::EIP4844Transaction(tx) => &tx.inner_hash,
1649 Transaction::EIP7702Transaction(tx) => &tx.inner_hash,
1650 Transaction::PrivilegedL2Transaction(tx) => &tx.inner_hash,
1651 Transaction::FeeTokenTransaction(tx) => &tx.inner_hash,
1652 Transaction::FrameTransaction(tx) => &tx.inner_hash,
1653 };
1654
1655 *inner_hash.get_or_init(|| self.compute_hash(crypto))
1656 }
1657
1658 pub fn gas_tip_cap(&self) -> U256 {
1659 self.max_priority_fee()
1660 .map(U256::from)
1661 .unwrap_or_else(|| self.gas_price())
1662 }
1663
1664 pub fn gas_fee_cap(&self) -> U256 {
1665 self.max_fee_per_gas()
1666 .map(U256::from)
1667 .unwrap_or_else(|| self.gas_price())
1668 }
1669
1670 pub fn effective_gas_tip(&self, base_fee: Option<u64>) -> Option<U256> {
1674 let tip_cap = self.gas_tip_cap();
1675 let Some(base_fee) = base_fee else {
1676 return Some(tip_cap);
1677 };
1678 let base_fee = U256::from(base_fee);
1679 let fee_cap = self.gas_fee_cap();
1680 let tip = fee_cap.checked_sub(base_fee)?;
1681 Some(min(tip, tip_cap))
1682 }
1683
1684 pub fn protected(&self) -> bool {
1687 match self {
1688 Transaction::LegacyTransaction(tx) if tx.v.bits() <= 8 => {
1689 let v = tx.v.low_u64();
1690 v != 27 && v != 28 && v != 1 && v != 0
1691 }
1692 _ => true,
1693 }
1694 }
1695}
1696
1697fn derive_legacy_chain_id(v: U256) -> Option<u64> {
1698 let v = u64::try_from(v).ok()?;
1699 if v < 35 { None } else { Some((v - 35) / 2) }
1704}
1705
1706impl TxType {
1707 pub fn from_u8(value: u8) -> Option<Self> {
1708 match value {
1709 0x00 => Some(Self::Legacy),
1710 0x01 => Some(Self::EIP2930),
1711 0x02 => Some(Self::EIP1559),
1712 0x03 => Some(Self::EIP4844),
1713 0x04 => Some(Self::EIP7702),
1714 0x06 => Some(Self::Frame),
1715 0x7d => Some(Self::FeeToken),
1716 0x7e => Some(Self::Privileged),
1717 _ => None,
1718 }
1719 }
1720
1721 pub fn as_envelope(self) -> Option<EnvelopeTxType> {
1726 match self {
1727 TxType::EIP2930 => Some(EnvelopeTxType::EIP2930),
1728 TxType::EIP1559 => Some(EnvelopeTxType::EIP1559),
1729 TxType::EIP4844 => Some(EnvelopeTxType::EIP4844),
1730 TxType::EIP7702 => Some(EnvelopeTxType::EIP7702),
1731 TxType::Frame => Some(EnvelopeTxType::Frame),
1732 TxType::FeeToken => Some(EnvelopeTxType::FeeToken),
1733 TxType::Privileged => Some(EnvelopeTxType::Privileged),
1734 TxType::Legacy => None,
1735 }
1736 }
1737
1738 pub fn is_l2_only(self) -> bool {
1746 match self {
1747 Self::Legacy
1748 | Self::EIP2930
1749 | Self::EIP1559
1750 | Self::EIP4844
1751 | Self::EIP7702
1752 | Self::Frame => false,
1753 Self::FeeToken | Self::Privileged => true,
1754 }
1755 }
1756}
1757
1758impl PrivilegedL2Transaction {
1759 pub fn get_privileged_hash(&self) -> Option<H256> {
1763 let to = match self.to {
1765 TxKind::Call(to) => to,
1766 _ => return None,
1767 };
1768
1769 let value = self.value.to_big_endian();
1770
1771 let u256_nonce = U256::from(self.nonce);
1774 let nonce = u256_nonce.to_big_endian();
1775
1776 Some(crate::utils::keccak(
1777 [
1778 U256::from(self.chain_id).to_big_endian().as_ref(),
1779 self.from.as_bytes(),
1780 to.as_bytes(),
1781 &nonce,
1782 &value,
1783 &U256::from(self.gas_limit).to_big_endian(),
1784 keccak(&self.data).as_bytes(),
1785 ]
1786 .concat(),
1787 ))
1788 }
1789}
1790
1791#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1792pub struct FeeTokenTransaction {
1793 pub chain_id: u64,
1794 pub nonce: u64,
1795 pub max_priority_fee_per_gas: u64,
1796 pub max_fee_per_gas: u64,
1797 pub gas_limit: u64,
1798 pub to: TxKind,
1799 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1800 pub value: U256,
1801 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1802 pub data: Bytes,
1803 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
1804 pub access_list: AccessList,
1805 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
1806 pub fee_token: Address,
1807 pub signature_y_parity: bool,
1808 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1809 pub signature_r: U256,
1810 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1811 pub signature_s: U256,
1812 #[rkyv(with=rkyv::with::Skip)]
1813 pub inner_hash: OnceCell<H256>,
1814 #[rkyv(with=rkyv::with::Skip)]
1815 pub sender_cache: OnceCell<Address>,
1816 #[rkyv(with=rkyv::with::Skip)]
1817 pub cached_canonical: OnceCell<Vec<u8>>,
1818}
1819
1820#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1822#[repr(u8)]
1823pub enum FrameMode {
1824 #[default]
1825 Default = 0,
1826 Verify = 1,
1827 Sender = 2,
1828}
1829
1830impl FrameMode {
1831 pub fn from_u8(val: u8) -> Option<Self> {
1834 match val {
1835 0 => Some(FrameMode::Default),
1836 1 => Some(FrameMode::Verify),
1837 2 => Some(FrameMode::Sender),
1838 _ => None,
1839 }
1840 }
1841}
1842
1843impl From<FrameMode> for u8 {
1844 fn from(mode: FrameMode) -> u8 {
1845 match mode {
1846 FrameMode::Default => 0,
1847 FrameMode::Verify => 1,
1848 FrameMode::Sender => 2,
1849 }
1850 }
1851}
1852
1853#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1859pub struct Frame {
1860 pub mode: u8,
1861 pub flags: u8,
1862 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H160Wrapper>)]
1863 pub target: Option<Address>,
1864 pub gas_limit: u64,
1865 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1869 pub value: U256,
1870 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1871 pub data: Bytes,
1872}
1873
1874impl Frame {
1875 pub fn execution_mode(&self) -> FrameMode {
1877 FrameMode::from_u8(self.mode).unwrap_or_default()
1878 }
1879
1880 pub fn scope_restriction(&self) -> u8 {
1882 self.flags & 0x03
1883 }
1884
1885 pub fn is_atomic_batch(&self) -> bool {
1887 (self.flags >> 2) & 1 == 1
1888 }
1889
1890 pub fn is_expiry_verifier(&self) -> bool {
1893 self.execution_mode() == FrameMode::Verify
1894 && self.target == Some(frame_tx_expiry_verifier())
1895 }
1896}
1897
1898impl RLPEncode for Frame {
1899 fn encode(&self, buf: &mut dyn bytes::BufMut) {
1900 let target_kind = match self.target {
1902 Some(addr) => TxKind::Call(addr),
1903 None => TxKind::Create, };
1905 Encoder::new(buf)
1906 .encode_field(&(self.mode as u64))
1907 .encode_field(&(self.flags as u64))
1908 .encode_field(&target_kind)
1909 .encode_field(&self.gas_limit)
1910 .encode_field(&self.value)
1911 .encode_field(&self.data)
1912 .finish();
1913 }
1914}
1915
1916impl RLPDecode for Frame {
1917 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
1918 let decoder = Decoder::new(rlp)?;
1919 let (mode_u64, decoder): (u64, _) = decoder.decode_field("mode")?;
1920 let mode = u8::try_from(mode_u64)
1921 .map_err(|_| RLPDecodeError::Custom(format!("Frame mode too large: {mode_u64}")))?;
1922 let (flags_u64, decoder): (u64, _) = decoder.decode_field("flags")?;
1923 let flags = u8::try_from(flags_u64)
1924 .map_err(|_| RLPDecodeError::Custom(format!("Frame flags too large: {flags_u64}")))?;
1925 let (target_kind, decoder): (TxKind, _) = decoder.decode_field("target")?;
1926 let target = match target_kind {
1927 TxKind::Call(addr) => Some(addr),
1928 TxKind::Create => None,
1929 };
1930 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
1931 let (value, decoder): (U256, _) = decoder.decode_field("value")?;
1932 let (data, decoder) = decoder.decode_field("data")?;
1933 let frame = Frame {
1934 mode,
1935 flags,
1936 target,
1937 gas_limit,
1938 value,
1939 data,
1940 };
1941 Ok((frame, decoder.finish()?))
1942 }
1943}
1944
1945#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1953pub struct FrameSignature {
1954 pub scheme: u8,
1955 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H160Wrapper>)]
1956 pub signer: Option<Address>,
1957 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1958 pub msg: Bytes,
1959 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1960 pub signature: Bytes,
1961}
1962
1963impl RLPEncode for FrameSignature {
1964 fn encode(&self, buf: &mut dyn bytes::BufMut) {
1965 let signer_kind = match self.signer {
1968 Some(addr) => TxKind::Call(addr),
1969 None => TxKind::Create,
1970 };
1971 Encoder::new(buf)
1972 .encode_field(&self.scheme)
1973 .encode_field(&signer_kind)
1974 .encode_field(&self.msg)
1975 .encode_field(&self.signature)
1976 .finish();
1977 }
1978}
1979
1980impl RLPDecode for FrameSignature {
1981 fn decode_unfinished(rlp: &[u8]) -> Result<(FrameSignature, &[u8]), RLPDecodeError> {
1982 let decoder = Decoder::new(rlp)?;
1983 let (scheme, decoder) = decoder.decode_field("scheme")?;
1984 let (signer_kind, decoder): (TxKind, _) = decoder.decode_field("signer")?;
1985 let signer = match signer_kind {
1986 TxKind::Call(addr) => Some(addr),
1987 TxKind::Create => None,
1988 };
1989 let (msg, decoder) = decoder.decode_field("msg")?;
1990 let (signature, decoder) = decoder.decode_field("signature")?;
1991 Ok((
1992 FrameSignature {
1993 scheme,
1994 signer,
1995 msg,
1996 signature,
1997 },
1998 decoder.finish()?,
1999 ))
2000 }
2001}
2002
2003#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
2007pub struct FrameTransaction {
2008 pub chain_id: u64,
2009 pub nonce: u64,
2010 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
2011 pub sender: Address,
2012 pub frames: Vec<Frame>,
2013 pub signatures: Vec<FrameSignature>,
2016 pub max_priority_fee_per_gas: u64,
2017 pub max_fee_per_gas: u64,
2018 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
2019 pub max_fee_per_blob_gas: U256,
2020 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H256Wrapper>)]
2021 pub blob_versioned_hashes: Vec<H256>,
2022 #[rkyv(with=rkyv::with::Skip)]
2023 pub inner_hash: OnceCell<H256>,
2024 #[rkyv(with=rkyv::with::Skip)]
2025 pub cached_canonical: OnceCell<Vec<u8>>,
2026}
2027
2028pub const FRAME_TX_INTRINSIC_COST: u64 = 15000;
2030pub const FRAME_TX_PER_FRAME_COST: u64 = 475;
2032pub const FRAME_TX_ENTRY_POINT_U64: u64 = 0xaa;
2034pub const FRAME_TX_MAX_FRAMES: usize = 64;
2036const FRAME_TX_STANDARD_TOKEN_COST: u64 = 4;
2040const FRAME_TX_TOTAL_COST_FLOOR_PER_TOKEN: u64 = 16;
2041pub const FRAME_SIG_SCHEME_ARBITRARY: u8 = 0;
2043pub const FRAME_SIG_SCHEME_SECP256K1: u8 = 1;
2044pub const FRAME_SIG_SCHEME_P256: u8 = 2;
2045pub const FRAME_TX_MAX_VERIFY_GAS: u64 = 100_000;
2051pub const APPROVE_PAYMENT: u8 = 0x1;
2054pub const APPROVE_EXECUTION: u8 = 0x2;
2055pub const APPROVE_EXECUTION_AND_PAYMENT: u8 = 0x3;
2056pub const FRAME_TX_MAX_PENDING_NONCANONICAL_PAYMASTER: u8 = 1;
2061
2062pub fn frame_tx_entry_point() -> Address {
2065 Address::from_low_u64_be(FRAME_TX_ENTRY_POINT_U64)
2066}
2067
2068pub const FRAME_TX_EXPIRY_VERIFIER_U64: u64 = 0x8141;
2070pub const FRAME_TX_EXPIRY_DATA_LENGTH: usize = 8;
2072
2073pub fn frame_tx_expiry_verifier() -> Address {
2075 Address::from_low_u64_be(FRAME_TX_EXPIRY_VERIFIER_U64)
2076}
2077
2078impl FrameTransaction {
2079 pub fn compute_sig_hash(&self) -> H256 {
2085 let mut buf = vec![TxType::Frame as u8];
2086 let elided_signatures: Vec<FrameSignature> = self
2087 .signatures
2088 .iter()
2089 .map(|s| {
2090 if s.msg.is_empty() {
2091 FrameSignature {
2092 scheme: s.scheme,
2093 signer: s.signer,
2094 msg: s.msg.clone(),
2095 signature: Bytes::new(),
2096 }
2097 } else {
2098 s.clone()
2099 }
2100 })
2101 .collect();
2102 Encoder::new(&mut buf)
2104 .encode_field(&self.chain_id)
2105 .encode_field(&self.nonce)
2106 .encode_field(&self.sender)
2107 .encode_field(&self.frames)
2108 .encode_field(&elided_signatures)
2109 .encode_field(&self.max_priority_fee_per_gas)
2110 .encode_field(&self.max_fee_per_gas)
2111 .encode_field(&self.max_fee_per_blob_gas)
2112 .encode_field(&self.blob_versioned_hashes)
2113 .finish();
2114 keccak(&buf)
2115 }
2116
2117 pub fn signature_verification_cost(&self) -> u64 {
2121 self.signatures
2122 .iter()
2123 .map(|s| match s.scheme {
2124 FRAME_SIG_SCHEME_ARBITRARY => 100u64,
2125 FRAME_SIG_SCHEME_SECP256K1 => 2800u64,
2126 FRAME_SIG_SCHEME_P256 => 6700u64,
2127 _ => 0,
2128 })
2129 .sum()
2130 }
2131
2132 fn data_fields(&self) -> impl Iterator<Item = &[u8]> {
2136 const EMPTY: &[u8] = &[];
2137 self.frames
2138 .iter()
2139 .map(|f| f.data.as_ref())
2140 .chain(self.signatures.iter().flat_map(|s| {
2141 [
2142 s.signer.as_ref().map_or(EMPTY, |a| a.as_bytes()),
2143 s.msg.as_ref(),
2144 s.signature.as_ref(),
2145 ]
2146 }))
2147 }
2148
2149 pub fn data_cost(&self) -> u64 {
2152 self.data_fields().flatten().fold(0u64, |acc, byte| {
2153 acc.saturating_add(if *byte == 0 { 4 } else { 16 })
2154 })
2155 }
2156
2157 pub fn calldata_tokens(&self) -> u64 {
2162 self.data_fields()
2163 .fold(0u64, |acc, field| acc.saturating_add(field.len() as u64))
2164 .saturating_mul(FRAME_TX_STANDARD_TOKEN_COST)
2165 }
2166
2167 pub fn calldata_floor_gas(&self) -> u64 {
2170 self.calldata_tokens()
2171 .saturating_mul(FRAME_TX_TOTAL_COST_FLOOR_PER_TOKEN)
2172 }
2173
2174 pub fn mandatory_gas(&self) -> u64 {
2177 FRAME_TX_INTRINSIC_COST
2178 .saturating_add((self.frames.len() as u64).saturating_mul(FRAME_TX_PER_FRAME_COST))
2179 .saturating_add(self.signature_verification_cost())
2180 }
2181
2182 pub fn standard_gas_limit(&self) -> u64 {
2185 self.mandatory_gas()
2186 .saturating_add(self.data_cost())
2187 .saturating_add(
2188 self.frames
2189 .iter()
2190 .map(|f| f.gas_limit)
2191 .fold(0u64, |acc, g| acc.saturating_add(g)),
2192 )
2193 }
2194
2195 pub fn calldata_floor_total(&self) -> u64 {
2199 self.mandatory_gas()
2200 .saturating_add(self.calldata_floor_gas())
2201 }
2202
2203 pub fn max_gas(&self) -> u64 {
2208 self.standard_gas_limit().max(self.calldata_floor_total())
2209 }
2210
2211 pub fn expiry_deadline(&self) -> Option<u64> {
2214 self.frames
2215 .iter()
2216 .find(|f| f.is_expiry_verifier())
2217 .and_then(|f| {
2218 let bytes: [u8; FRAME_TX_EXPIRY_DATA_LENGTH] = f.data.as_ref().try_into().ok()?;
2219 Some(u64::from_be_bytes(bytes))
2220 })
2221 }
2222
2223 pub fn validate_static_constraints(&self) -> Result<(), String> {
2226 if self.sender == Address::zero() {
2228 return Err("tx.sender must not be zero address".to_string());
2229 }
2230 if self.frames.is_empty() || self.frames.len() > FRAME_TX_MAX_FRAMES {
2231 return Err(format!(
2232 "Frame count must be between 1 and {FRAME_TX_MAX_FRAMES}"
2233 ));
2234 }
2235 for (i, hash) in self.blob_versioned_hashes.iter().enumerate() {
2238 if hash.0.first() != Some(&VERSIONED_HASH_VERSION_KZG) {
2239 return Err(format!("Blob versioned hash {i}: wrong version byte"));
2240 }
2241 }
2242 if self.blob_versioned_hashes.is_empty() && !self.max_fee_per_blob_gas.is_zero() {
2243 return Err(
2244 "max_fee_per_blob_gas must be zero when the transaction carries no blobs"
2245 .to_string(),
2246 );
2247 }
2248 for (i, sig) in self.signatures.iter().enumerate() {
2251 match sig.scheme {
2252 FRAME_SIG_SCHEME_ARBITRARY => {
2255 if sig.signer.is_some() {
2256 return Err(format!(
2257 "Signature {i}: ARBITRARY signatures must not name a signer"
2258 ));
2259 }
2260 }
2261 FRAME_SIG_SCHEME_SECP256K1 | FRAME_SIG_SCHEME_P256 => {}
2262 other => {
2263 return Err(format!("Signature {i}: unsupported scheme {other}"));
2264 }
2265 }
2266 match sig.msg.len() {
2267 0 => {}
2268 32 => {
2269 if sig.msg.iter().all(|&b| b == 0) {
2270 return Err(format!(
2271 "Signature {i}: explicit msg must not be zero digest"
2272 ));
2273 }
2274 }
2275 other => {
2276 return Err(format!(
2277 "Signature {i}: msg must be empty or 32 bytes, got {other}"
2278 ));
2279 }
2280 }
2281 }
2282 let mut total_frame_gas: u128 = 0;
2285 let mut expiry_frame_count: usize = 0;
2286 for (i, frame) in self.frames.iter().enumerate() {
2287 if frame.mode >= 3 {
2289 return Err(format!("Frame {i}: reserved execution mode {}", frame.mode));
2290 }
2291 if frame.flags >= 8 {
2293 return Err(format!(
2294 "Frame {i}: reserved flag bits must be zero (flags={:#04x})",
2295 frame.flags
2296 ));
2297 }
2298 if frame.is_expiry_verifier() {
2303 expiry_frame_count = expiry_frame_count.saturating_add(1);
2304 if expiry_frame_count > 1 {
2305 return Err(format!("Frame {i}: more than one expiry verifier frame"));
2306 }
2307 if frame.flags != 0 {
2308 return Err(format!(
2309 "Frame {i}: expiry verifier frame must have flags == 0"
2310 ));
2311 }
2312 if frame.data.len() != FRAME_TX_EXPIRY_DATA_LENGTH {
2313 return Err(format!(
2314 "Frame {i}: expiry verifier frame data must be {FRAME_TX_EXPIRY_DATA_LENGTH} bytes"
2315 ));
2316 }
2317 }
2318 if frame.mode != FrameMode::Sender as u8 && !frame.value.is_zero() {
2322 return Err(format!(
2323 "Frame {i}: non-zero value only allowed in SENDER mode (mode={}, value={})",
2324 frame.mode, frame.value
2325 ));
2326 }
2327 if frame.gas_limit > i64::MAX as u64 {
2335 return Err(format!(
2336 "Frame {i}: gas_limit {} exceeds 2**63-1",
2337 frame.gas_limit
2338 ));
2339 }
2340 total_frame_gas = total_frame_gas
2341 .checked_add(frame.gas_limit as u128)
2342 .ok_or_else(|| format!("Frame {i}: cumulative gas_limit overflow"))?;
2343 if total_frame_gas > i64::MAX as u128 {
2344 return Err(format!(
2345 "Frame {i}: cumulative frame gas_limit {} exceeds 2**63-1",
2346 total_frame_gas
2347 ));
2348 }
2349 if frame.flags & APPROVE_EXECUTION != 0
2352 && frame.target.is_some_and(|target| target != self.sender)
2353 {
2354 return Err(format!(
2355 "Frame {i}: APPROVE_EXECUTION requires an empty target or tx.sender"
2356 ));
2357 }
2358 if frame.is_atomic_batch() {
2363 if frame.mode == FrameMode::Verify as u8 {
2364 return Err(format!("Frame {i}: atomic batch flag on a VERIFY frame"));
2365 }
2366 match self.frames.get(i.saturating_add(1)) {
2367 None => return Err(format!("Frame {i}: atomic batch flag on last frame")),
2368 Some(next) if next.mode == FrameMode::Verify as u8 => {
2369 return Err(format!(
2370 "Frame {i}: atomic batch flag followed by a VERIFY frame"
2371 ));
2372 }
2373 Some(_) => {}
2374 }
2375 }
2376 }
2377 Ok(())
2378 }
2379
2380 pub fn validation_prefix(&self) -> Result<ValidationPrefix, FrameValidationError> {
2396 let non_expiry: Vec<usize> = self
2398 .frames
2399 .iter()
2400 .enumerate()
2401 .filter(|(_, f)| !f.is_expiry_verifier())
2402 .map(|(i, _)| i)
2403 .collect();
2404
2405 let frame = |pos: usize| -> Option<&Frame> {
2407 non_expiry.get(pos).and_then(|&idx| self.frames.get(idx))
2408 };
2409
2410 let is_default = |pos: usize| -> bool {
2411 frame(pos).is_some_and(|f| f.execution_mode() == FrameMode::Default)
2412 };
2413 let is_verify = |pos: usize| -> bool {
2414 frame(pos).is_some_and(|f| f.execution_mode() == FrameMode::Verify)
2415 };
2416 let scope_of = |pos: usize| -> u8 { frame(pos).map_or(0, |f| f.scope_restriction()) };
2417
2418 if non_expiry.is_empty() {
2419 return Err(FrameValidationError::UnrecognizedPrefix);
2420 }
2421
2422 if is_default(0)
2426 && is_verify(1)
2427 && scope_of(1) == APPROVE_EXECUTION
2428 && is_verify(2)
2429 && scope_of(2) == APPROVE_PAYMENT
2430 {
2431 return Ok(ValidationPrefix {
2432 shape: PrefixShape::DeployOnlyVerifyPay,
2433 frame_indices: vec![non_expiry[0], non_expiry[1], non_expiry[2]],
2434 deploy_index: Some(non_expiry[0]),
2435 pay_index: Some(non_expiry[2]),
2436 });
2437 }
2438
2439 if is_default(0) && is_verify(1) && scope_of(1) == APPROVE_EXECUTION_AND_PAYMENT {
2441 return Ok(ValidationPrefix {
2442 shape: PrefixShape::DeploySelfVerify,
2443 frame_indices: vec![non_expiry[0], non_expiry[1]],
2444 deploy_index: Some(non_expiry[0]),
2445 pay_index: Some(non_expiry[1]),
2446 });
2447 }
2448
2449 if is_verify(0)
2451 && scope_of(0) == APPROVE_EXECUTION
2452 && is_verify(1)
2453 && scope_of(1) == APPROVE_PAYMENT
2454 {
2455 return Ok(ValidationPrefix {
2456 shape: PrefixShape::OnlyVerifyPay,
2457 frame_indices: vec![non_expiry[0], non_expiry[1]],
2458 deploy_index: None,
2459 pay_index: Some(non_expiry[1]),
2460 });
2461 }
2462
2463 if is_verify(0) && scope_of(0) == APPROVE_EXECUTION_AND_PAYMENT {
2465 return Ok(ValidationPrefix {
2466 shape: PrefixShape::SelfVerify,
2467 frame_indices: vec![non_expiry[0]],
2468 deploy_index: None,
2469 pay_index: Some(non_expiry[0]),
2470 });
2471 }
2472
2473 Err(FrameValidationError::UnrecognizedPrefix)
2474 }
2475
2476 pub fn validate_prefix_structure(
2490 &self,
2491 prefix: &ValidationPrefix,
2492 ) -> Result<(), FrameValidationError> {
2493 let mut deploy_count = 0usize;
2494
2495 for &idx in &prefix.frame_indices {
2496 let frame = &self.frames[idx];
2497
2498 if frame.is_atomic_batch() {
2499 return Err(FrameValidationError::AtomicBatchInPrefix { frame_index: idx });
2500 }
2501
2502 match prefix.deploy_index {
2503 Some(deploy_idx) if deploy_idx == idx => {
2504 deploy_count += 1;
2506 if deploy_count > 1 {
2507 return Err(FrameValidationError::MultipleDeploys { frame_index: idx });
2508 }
2509 if prefix.frame_indices.first() != Some(&idx) {
2514 return Err(FrameValidationError::DeployNotFirst { frame_index: idx });
2515 }
2516 if frame.execution_mode() != FrameMode::Default {
2517 return Err(FrameValidationError::DeployNotDefaultMode {
2518 frame_index: idx,
2519 });
2520 }
2521 }
2522 _ => {
2523 if frame.execution_mode() != FrameMode::Verify {
2525 return Err(FrameValidationError::VerifyFrameNotVerifyMode {
2526 frame_index: idx,
2527 });
2528 }
2529
2530 let target_ok = match frame.target {
2532 None => true,
2533 Some(addr) => addr == self.sender,
2534 };
2535 if !target_ok {
2536 return Err(FrameValidationError::VerifyTargetNotSender {
2537 frame_index: idx,
2538 });
2539 }
2540
2541 let expected_scope = match prefix.shape {
2543 PrefixShape::SelfVerify | PrefixShape::DeploySelfVerify => {
2544 APPROVE_EXECUTION_AND_PAYMENT
2545 }
2546 PrefixShape::OnlyVerifyPay | PrefixShape::DeployOnlyVerifyPay => {
2547 if prefix.pay_index == Some(idx) {
2549 APPROVE_PAYMENT
2550 } else {
2551 APPROVE_EXECUTION
2552 }
2553 }
2554 };
2555 if frame.scope_restriction() != expected_scope {
2556 return Err(FrameValidationError::WrongScopeRestriction {
2557 frame_index: idx,
2558 expected: expected_scope,
2559 actual: frame.scope_restriction(),
2560 });
2561 }
2562 }
2563 }
2564 }
2565
2566 let prefix_gas: u64 = prefix
2568 .frame_indices
2569 .iter()
2570 .map(|&i| self.frames[i].gas_limit)
2571 .fold(0u64, |acc, g| acc.saturating_add(g));
2572 let total_verify_gas = prefix_gas.saturating_add(self.signature_verification_cost());
2573 if total_verify_gas > FRAME_TX_MAX_VERIFY_GAS {
2574 return Err(FrameValidationError::VerifyGasBudgetExceeded {
2575 actual: total_verify_gas,
2576 limit: FRAME_TX_MAX_VERIFY_GAS,
2577 });
2578 }
2579
2580 Ok(())
2581 }
2582}
2583
2584#[derive(Clone, Debug, PartialEq, Eq)]
2586pub enum PrefixShape {
2587 SelfVerify,
2589 DeploySelfVerify,
2591 OnlyVerifyPay,
2593 DeployOnlyVerifyPay,
2595}
2596
2597#[derive(Clone, Debug, PartialEq, Eq)]
2599pub struct ValidationPrefix {
2600 pub shape: PrefixShape,
2602 pub frame_indices: Vec<usize>,
2605 pub deploy_index: Option<usize>,
2607 pub pay_index: Option<usize>,
2609}
2610
2611#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
2614pub enum FrameValidationError {
2615 #[error("frame transaction validation prefix does not match any recognized shape")]
2616 UnrecognizedPrefix,
2617 #[error("frame {frame_index}: prefix has more than one DEFAULT deploy frame")]
2618 MultipleDeploys { frame_index: usize },
2619 #[error("frame {frame_index}: deploy frame must be at index 0")]
2620 DeployNotFirst { frame_index: usize },
2621 #[error("frame {frame_index}: deploy frame must use DEFAULT execution mode")]
2622 DeployNotDefaultMode { frame_index: usize },
2623 #[error("frame {frame_index}: prefix VERIFY frame does not use VERIFY execution mode")]
2624 VerifyFrameNotVerifyMode { frame_index: usize },
2625 #[error("frame {frame_index}: VERIFY frame target is not tx.sender")]
2626 VerifyTargetNotSender { frame_index: usize },
2627 #[error("frame {frame_index}: scope restriction is {actual:#04x}, expected {expected:#04x}")]
2628 WrongScopeRestriction {
2629 frame_index: usize,
2630 expected: u8,
2631 actual: u8,
2632 },
2633 #[error("frame {frame_index}: prefix frame has atomic-batch flag set")]
2634 AtomicBatchInPrefix { frame_index: usize },
2635 #[error("prefix gas budget exceeded: {actual} > {limit} (MAX_VERIFY_GAS)")]
2636 VerifyGasBudgetExceeded { actual: u64, limit: u64 },
2637}
2638
2639impl RLPEncode for FrameTransaction {
2640 fn encode(&self, buf: &mut dyn bytes::BufMut) {
2641 Encoder::new(buf)
2642 .encode_field(&self.chain_id)
2643 .encode_field(&self.nonce)
2644 .encode_field(&self.sender)
2645 .encode_field(&self.frames)
2646 .encode_field(&self.signatures)
2647 .encode_field(&self.max_priority_fee_per_gas)
2648 .encode_field(&self.max_fee_per_gas)
2649 .encode_field(&self.max_fee_per_blob_gas)
2650 .encode_field(&self.blob_versioned_hashes)
2651 .finish();
2652 }
2653}
2654
2655impl RLPDecode for FrameTransaction {
2656 fn decode_unfinished(rlp: &[u8]) -> Result<(FrameTransaction, &[u8]), RLPDecodeError> {
2657 let decoder = Decoder::new(rlp)?;
2658 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
2659 let (nonce, decoder) = decode_nonce_field(decoder)?;
2660 let (sender, decoder) = decoder.decode_field("sender")?;
2661 let (frames, decoder) = decoder.decode_field("frames")?;
2662 let (signatures, decoder) = decoder.decode_field("signatures")?;
2663 let (max_priority_fee_per_gas, decoder) =
2664 decoder.decode_field("max_priority_fee_per_gas")?;
2665 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
2666 let (max_fee_per_blob_gas, decoder) = decoder.decode_field("max_fee_per_blob_gas")?;
2667 let (blob_versioned_hashes, decoder) = decoder.decode_field("blob_versioned_hashes")?;
2668 let tx = FrameTransaction {
2669 chain_id,
2670 nonce,
2671 sender,
2672 frames,
2673 signatures,
2674 max_priority_fee_per_gas,
2675 max_fee_per_gas,
2676 max_fee_per_blob_gas,
2677 blob_versioned_hashes,
2678 inner_hash: OnceCell::new(),
2679 cached_canonical: OnceCell::new(),
2680 };
2681 Ok((tx, decoder.finish()?))
2682 }
2683}
2684
2685impl PayloadRLPEncode for FrameTransaction {
2686 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
2687 buf.put_u8(TxType::Frame as u8);
2688 self.encode(buf);
2689 }
2690}
2691
2692mod canonic_encoding {
2698 use super::*;
2699
2700 impl Transaction {
2701 pub fn decode_canonical(bytes: &[u8]) -> Result<Self, RLPDecodeError> {
2707 match bytes.first() {
2709 Some(tx_type) if *tx_type < 0x7f => {
2711 let tx_bytes = &bytes[1..];
2715 match EnvelopeTxType::from_type_byte(*tx_type)? {
2716 EnvelopeTxType::EIP2930 => EIP2930Transaction::decode(tx_bytes)
2717 .map(Transaction::EIP2930Transaction),
2718 EnvelopeTxType::EIP1559 => EIP1559Transaction::decode(tx_bytes)
2719 .map(Transaction::EIP1559Transaction),
2720 EnvelopeTxType::EIP4844 => EIP4844Transaction::decode(tx_bytes)
2721 .map(Transaction::EIP4844Transaction),
2722 EnvelopeTxType::EIP7702 => EIP7702Transaction::decode(tx_bytes)
2723 .map(Transaction::EIP7702Transaction),
2724 EnvelopeTxType::Frame => {
2725 FrameTransaction::decode(tx_bytes).map(Transaction::FrameTransaction)
2726 }
2727 EnvelopeTxType::FeeToken => FeeTokenTransaction::decode(tx_bytes)
2728 .map(Transaction::FeeTokenTransaction),
2729 EnvelopeTxType::Privileged => PrivilegedL2Transaction::decode(tx_bytes)
2730 .map(Transaction::PrivilegedL2Transaction),
2731 }
2732 }
2733 _ => LegacyTransaction::decode(bytes).map(Transaction::LegacyTransaction),
2735 }
2736 }
2737
2738 pub fn encode_canonical(&self, buf: &mut dyn bytes::BufMut) {
2744 match self {
2745 Transaction::LegacyTransaction(_) => {}
2747 _ => buf.put_u8(self.tx_type() as u8),
2748 }
2749 match self {
2750 Transaction::LegacyTransaction(t) => t.encode(buf),
2751 Transaction::EIP2930Transaction(t) => t.encode(buf),
2752 Transaction::EIP1559Transaction(t) => t.encode(buf),
2753 Transaction::EIP4844Transaction(t) => t.encode(buf),
2754 Transaction::EIP7702Transaction(t) => t.encode(buf),
2755 Transaction::FeeTokenTransaction(t) => t.encode(buf),
2756 Transaction::PrivilegedL2Transaction(t) => t.encode(buf),
2757 Transaction::FrameTransaction(t) => t.encode(buf),
2758 };
2759 }
2760
2761 pub fn encode_canonical_to_vec(&self) -> Vec<u8> {
2767 if let Some(cell) = self.cached_canonical_cell() {
2768 return cell
2769 .get_or_init(|| {
2770 let mut buf = Vec::new();
2771 self.encode_canonical(&mut buf);
2772 buf
2773 })
2774 .clone();
2775 }
2776 let mut buf = Vec::new();
2777 self.encode_canonical(&mut buf);
2778 buf
2779 }
2780
2781 pub fn encode_canonical_len(&self) -> usize {
2786 let prefix_len = match self {
2787 Transaction::LegacyTransaction(_) => 0,
2788 _ => 1,
2789 };
2790 let inner_len = match self {
2791 Transaction::LegacyTransaction(t) => t.length(),
2792 Transaction::EIP2930Transaction(t) => t.length(),
2793 Transaction::EIP1559Transaction(t) => t.length(),
2794 Transaction::EIP4844Transaction(t) => t.length(),
2795 Transaction::EIP7702Transaction(t) => t.length(),
2796 Transaction::FeeTokenTransaction(t) => t.length(),
2797 Transaction::PrivilegedL2Transaction(t) => t.length(),
2798 Transaction::FrameTransaction(t) => t.length(),
2799 };
2800 prefix_len + inner_len
2801 }
2802 }
2803
2804 impl P2PTransaction {
2805 pub fn tx_type(&self) -> TxType {
2806 match self {
2807 P2PTransaction::LegacyTransaction(_) => TxType::Legacy,
2808 P2PTransaction::EIP2930Transaction(_) => TxType::EIP2930,
2809 P2PTransaction::EIP1559Transaction(_) => TxType::EIP1559,
2810 P2PTransaction::EIP4844TransactionWithBlobs(_) => TxType::EIP4844,
2811 P2PTransaction::EIP7702Transaction(_) => TxType::EIP7702,
2812 P2PTransaction::FeeTokenTransaction(_) => TxType::FeeToken,
2813 P2PTransaction::FrameTransaction(_) => TxType::Frame,
2814 }
2815 }
2816
2817 pub fn encode_canonical(&self, buf: &mut dyn bytes::BufMut) {
2818 match self {
2819 P2PTransaction::LegacyTransaction(_) => {}
2821 _ => buf.put_u8(self.tx_type() as u8),
2822 }
2823 match self {
2824 P2PTransaction::LegacyTransaction(t) => t.encode(buf),
2825 P2PTransaction::EIP2930Transaction(t) => t.encode(buf),
2826 P2PTransaction::EIP1559Transaction(t) => t.encode(buf),
2827 P2PTransaction::EIP4844TransactionWithBlobs(t) => t.encode(buf),
2828 P2PTransaction::EIP7702Transaction(t) => t.encode(buf),
2829 P2PTransaction::FeeTokenTransaction(t) => t.encode(buf),
2830 P2PTransaction::FrameTransaction(t) => t.encode(buf),
2831 };
2832 }
2833
2834 pub fn encode_canonical_to_vec(&self) -> Vec<u8> {
2835 let mut buf = Vec::new();
2836 self.encode_canonical(&mut buf);
2837 buf
2838 }
2839
2840 pub fn encode_canonical_len(&self) -> usize {
2845 let prefix_len = match self {
2846 P2PTransaction::LegacyTransaction(_) => 0,
2847 _ => 1,
2848 };
2849 let inner_len = match self {
2850 P2PTransaction::LegacyTransaction(t) => t.length(),
2851 P2PTransaction::EIP2930Transaction(t) => t.length(),
2852 P2PTransaction::EIP1559Transaction(t) => t.length(),
2853 P2PTransaction::EIP4844TransactionWithBlobs(t) => t.length(),
2854 P2PTransaction::EIP7702Transaction(t) => t.length(),
2855 P2PTransaction::FeeTokenTransaction(t) => t.length(),
2856 P2PTransaction::FrameTransaction(t) => t.length(),
2857 };
2858 prefix_len + inner_len
2859 }
2860
2861 pub fn compute_hash(&self) -> H256 {
2862 match self {
2863 P2PTransaction::LegacyTransaction(t) => {
2864 Transaction::LegacyTransaction(t.clone()).compute_hash(&NativeCrypto)
2865 }
2866 P2PTransaction::EIP2930Transaction(t) => {
2867 Transaction::EIP2930Transaction(t.clone()).compute_hash(&NativeCrypto)
2868 }
2869 P2PTransaction::EIP1559Transaction(t) => {
2870 Transaction::EIP1559Transaction(t.clone()).compute_hash(&NativeCrypto)
2871 }
2872 P2PTransaction::EIP4844TransactionWithBlobs(t) => {
2873 Transaction::EIP4844Transaction(t.tx.clone()).compute_hash(&NativeCrypto)
2874 }
2875 P2PTransaction::EIP7702Transaction(t) => {
2876 Transaction::EIP7702Transaction(t.clone()).compute_hash(&NativeCrypto)
2877 }
2878 P2PTransaction::FeeTokenTransaction(t) => {
2879 Transaction::FeeTokenTransaction(t.clone()).compute_hash(&NativeCrypto)
2880 }
2881 P2PTransaction::FrameTransaction(t) => {
2882 Transaction::FrameTransaction(t.clone()).compute_hash(&NativeCrypto)
2883 }
2884 }
2885 }
2886 }
2887}
2888
2889mod serde_impl {
2893 use ethereum_types::H160;
2894 use serde::Deserialize;
2895 use serde::{Deserializer, de::Error};
2896 use serde_json::Value;
2897 use std::{collections::HashMap, str::FromStr};
2898
2899 #[cfg(feature = "c-kzg")]
2900 use crate::types::BYTES_PER_BLOB;
2901 use crate::types::{AccessListItem, AuthorizationTuple, BlobsBundleError};
2902
2903 use super::*;
2904
2905 impl Serialize for TxKind {
2906 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2907 where
2908 S: serde::Serializer,
2909 {
2910 match self {
2911 TxKind::Call(address) => serializer.serialize_str(&format!("{address:#x}")),
2912 TxKind::Create => serializer.serialize_none(),
2913 }
2914 }
2915 }
2916
2917 impl<'de> Deserialize<'de> for TxKind {
2918 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2919 where
2920 D: serde::Deserializer<'de>,
2921 {
2922 let str_option = Option::<String>::deserialize(deserializer)?;
2923 match str_option {
2924 Some(str) if !str.is_empty() => Ok(TxKind::Call(
2925 Address::from_str(str.trim_start_matches("0x")).map_err(|_| {
2926 serde::de::Error::custom(format!("Failed to deserialize hex value {str}"))
2927 })?,
2928 )),
2929 _ => Ok(TxKind::Create),
2930 }
2931 }
2932 }
2933
2934 impl Serialize for TxType {
2935 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2936 where
2937 S: serde::Serializer,
2938 {
2939 serializer.serialize_str(&format!("{:#x}", *self as u8))
2940 }
2941 }
2942
2943 impl<'de> Deserialize<'de> for TxType {
2944 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2945 where
2946 D: serde::Deserializer<'de>,
2947 {
2948 let str = String::deserialize(deserializer)?;
2949 let tx_num = u8::from_str_radix(str.trim_start_matches("0x"), 16).map_err(|_| {
2950 serde::de::Error::custom(format!("Failed to deserialize hex value {str}"))
2951 })?;
2952 TxType::from_u8(tx_num).ok_or_else(|| {
2953 serde::de::Error::custom(format!("Invalid transaction type {tx_num}"))
2954 })
2955 }
2956 }
2957
2958 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
2959 #[serde(rename_all = "camelCase")]
2960 pub struct AccessListEntry {
2961 pub address: Address,
2962 pub storage_keys: Vec<H256>,
2963 }
2964
2965 impl From<&AccessListItem> for AccessListEntry {
2966 fn from(value: &AccessListItem) -> AccessListEntry {
2967 AccessListEntry {
2968 address: value.0,
2969 storage_keys: value.1.clone(),
2970 }
2971 }
2972 }
2973
2974 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
2975 #[serde(rename_all = "camelCase")]
2976 pub struct AuthorizationTupleEntry {
2977 pub chain_id: U256,
2978 pub address: Address,
2979 #[serde(default, with = "crate::serde_utils::u64::hex_str")]
2980 pub nonce: u64,
2981 pub y_parity: U256,
2982 pub r: U256,
2983 pub s: U256,
2984 }
2985
2986 impl From<&AuthorizationTuple> for AuthorizationTupleEntry {
2987 fn from(value: &AuthorizationTuple) -> AuthorizationTupleEntry {
2988 AuthorizationTupleEntry {
2989 chain_id: value.chain_id,
2990 address: value.address,
2991 nonce: value.nonce,
2992 y_parity: value.y_parity,
2993 r: value.r_signature,
2994 s: value.s_signature,
2995 }
2996 }
2997 }
2998
2999 impl From<AuthorizationTupleEntry> for AuthorizationTuple {
3000 fn from(entry: AuthorizationTupleEntry) -> AuthorizationTuple {
3001 AuthorizationTuple {
3002 chain_id: entry.chain_id,
3003 address: entry.address,
3004 nonce: entry.nonce,
3005 y_parity: entry.y_parity,
3006 r_signature: entry.r,
3007 s_signature: entry.s,
3008 }
3009 }
3010 }
3011
3012 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
3013 #[serde(rename_all = "camelCase")]
3014 pub struct FrameEntry {
3015 #[serde(with = "crate::serde_utils::u64::hex_str")]
3016 pub mode: u64,
3017 #[serde(with = "crate::serde_utils::u64::hex_str")]
3018 pub flags: u64,
3019 pub to: Option<Address>,
3020 #[serde(with = "crate::serde_utils::u64::hex_str")]
3021 pub gas_limit: u64,
3022 #[serde(
3023 default,
3024 serialize_with = "serialize_u256_hex",
3025 deserialize_with = "crate::serde_utils::u256::deser_hex_str"
3026 )]
3027 pub value: U256,
3028 #[serde(with = "crate::serde_utils::bytes")]
3029 pub data: Bytes,
3030 }
3031
3032 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
3034 #[serde(rename_all = "camelCase")]
3035 pub struct SignatureEntry {
3036 #[serde(with = "crate::serde_utils::u64::hex_str")]
3037 pub scheme: u64,
3038 pub signer: Option<Address>,
3039 #[serde(with = "crate::serde_utils::bytes")]
3040 pub msg: Bytes,
3041 #[serde(with = "crate::serde_utils::bytes")]
3042 pub signature: Bytes,
3043 }
3044
3045 impl From<&FrameSignature> for SignatureEntry {
3046 fn from(value: &FrameSignature) -> SignatureEntry {
3047 SignatureEntry {
3048 scheme: value.scheme as u64,
3049 signer: value.signer,
3050 msg: value.msg.clone(),
3051 signature: value.signature.clone(),
3052 }
3053 }
3054 }
3055
3056 fn serialize_u256_hex<S: serde::Serializer>(value: &U256, s: S) -> Result<S::Ok, S::Error> {
3057 s.serialize_str(&format!("{value:#x}"))
3058 }
3059
3060 impl From<&Frame> for FrameEntry {
3061 fn from(value: &Frame) -> FrameEntry {
3062 FrameEntry {
3063 mode: value.mode as u64,
3064 flags: value.flags as u64,
3065 to: value.target,
3066 gas_limit: value.gas_limit,
3067 value: value.value,
3068 data: value.data.clone(),
3069 }
3070 }
3071 }
3072
3073 impl From<FrameEntry> for Frame {
3074 fn from(entry: FrameEntry) -> Frame {
3075 Frame {
3076 mode: entry.mode as u8,
3077 flags: entry.flags as u8,
3078 target: entry.to,
3079 gas_limit: entry.gas_limit,
3080 value: entry.value,
3081 data: entry.data,
3082 }
3083 }
3084 }
3085
3086 impl Serialize for LegacyTransaction {
3087 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3088 where
3089 S: serde::Serializer,
3090 {
3091 let mut struct_serializer = serializer.serialize_struct("LegacyTransaction", 11)?;
3092 struct_serializer.serialize_field("type", &TxType::Legacy)?;
3093 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3094 struct_serializer.serialize_field("to", &self.to)?;
3095 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas))?;
3096 struct_serializer.serialize_field("value", &self.value)?;
3097 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3098 struct_serializer.serialize_field("gasPrice", &format!("{:#x}", self.gas_price))?;
3099 struct_serializer.serialize_field(
3100 "chainId",
3101 &format!("{:#x}", derive_legacy_chain_id(self.v).unwrap_or_default()),
3102 )?;
3103 struct_serializer.serialize_field("v", &self.v)?;
3104 struct_serializer.serialize_field("r", &self.r)?;
3105 struct_serializer.serialize_field("s", &self.s)?;
3106 struct_serializer.end()
3107 }
3108 }
3109
3110 impl Serialize for EIP2930Transaction {
3111 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3112 where
3113 S: serde::Serializer,
3114 {
3115 let mut struct_serializer = serializer.serialize_struct("Eip2930Transaction", 12)?;
3116 struct_serializer.serialize_field("type", &TxType::EIP2930)?;
3117 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3118 struct_serializer.serialize_field("to", &self.to)?;
3119 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3120 struct_serializer.serialize_field("value", &self.value)?;
3121 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3122 struct_serializer.serialize_field("gasPrice", &format!("{:#x}", self.gas_price))?;
3123 struct_serializer.serialize_field(
3124 "accessList",
3125 &self
3126 .access_list
3127 .iter()
3128 .map(AccessListEntry::from)
3129 .collect::<Vec<_>>(),
3130 )?;
3131 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3132 struct_serializer
3133 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3134 struct_serializer
3135 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
3137 struct_serializer.serialize_field("s", &self.signature_s)?;
3138 struct_serializer.end()
3139 }
3140 }
3141
3142 impl Serialize for EIP1559Transaction {
3143 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3144 where
3145 S: serde::Serializer,
3146 {
3147 let mut struct_serializer = serializer.serialize_struct("Eip1559Transaction", 14)?;
3148 struct_serializer.serialize_field("type", &TxType::EIP1559)?;
3149 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3150 struct_serializer.serialize_field("to", &self.to)?;
3151 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3152 struct_serializer.serialize_field("value", &self.value)?;
3153 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3154 struct_serializer.serialize_field(
3155 "maxPriorityFeePerGas",
3156 &format!("{:#x}", self.max_priority_fee_per_gas),
3157 )?;
3158 struct_serializer
3159 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3160 struct_serializer
3161 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3162 struct_serializer.serialize_field(
3163 "accessList",
3164 &self
3165 .access_list
3166 .iter()
3167 .map(AccessListEntry::from)
3168 .collect::<Vec<_>>(),
3169 )?;
3170 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3171 struct_serializer
3172 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3173 struct_serializer
3174 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
3176 struct_serializer.serialize_field("s", &self.signature_s)?;
3177 struct_serializer.end()
3178 }
3179 }
3180
3181 impl Serialize for EIP4844Transaction {
3182 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3183 where
3184 S: serde::Serializer,
3185 {
3186 let mut struct_serializer = serializer.serialize_struct("Eip4844Transaction", 15)?;
3187 struct_serializer.serialize_field("type", &TxType::EIP4844)?;
3188 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3189 struct_serializer.serialize_field("to", &self.to)?;
3190 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas))?;
3191 struct_serializer.serialize_field("value", &self.value)?;
3192 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3193 struct_serializer.serialize_field(
3194 "maxPriorityFeePerGas",
3195 &format!("{:#x}", self.max_priority_fee_per_gas),
3196 )?;
3197 struct_serializer
3198 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3199 struct_serializer
3200 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3201 struct_serializer.serialize_field(
3202 "maxFeePerBlobGas",
3203 &format!("{:#x}", self.max_fee_per_blob_gas),
3204 )?;
3205 struct_serializer.serialize_field(
3206 "accessList",
3207 &self
3208 .access_list
3209 .iter()
3210 .map(AccessListEntry::from)
3211 .collect::<Vec<_>>(),
3212 )?;
3213 struct_serializer
3214 .serialize_field("blobVersionedHashes", &self.blob_versioned_hashes)?;
3215 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3216 struct_serializer
3217 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3218 struct_serializer
3219 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
3221 struct_serializer.serialize_field("s", &self.signature_s)?;
3222 struct_serializer.end()
3223 }
3224 }
3225
3226 impl Serialize for EIP7702Transaction {
3227 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3228 where
3229 S: serde::Serializer,
3230 {
3231 let mut struct_serializer = serializer.serialize_struct("Eip7702Transaction", 15)?;
3232 struct_serializer.serialize_field("type", &TxType::EIP7702)?;
3233 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3234 struct_serializer.serialize_field("to", &self.to)?;
3235 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3236 struct_serializer.serialize_field("value", &self.value)?;
3237 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3238 struct_serializer.serialize_field(
3239 "maxPriorityFeePerGas",
3240 &format!("{:#x}", self.max_priority_fee_per_gas),
3241 )?;
3242 struct_serializer
3243 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3244 struct_serializer
3245 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3246 struct_serializer.serialize_field(
3247 "accessList",
3248 &self
3249 .access_list
3250 .iter()
3251 .map(AccessListEntry::from)
3252 .collect::<Vec<_>>(),
3253 )?;
3254 struct_serializer.serialize_field(
3255 "authorizationList",
3256 &self
3257 .authorization_list
3258 .iter()
3259 .map(AuthorizationTupleEntry::from)
3260 .collect::<Vec<_>>(),
3261 )?;
3262 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3263 struct_serializer
3264 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3265 struct_serializer
3266 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
3268 struct_serializer.serialize_field("s", &self.signature_s)?;
3269 struct_serializer.end()
3270 }
3271 }
3272
3273 impl Serialize for PrivilegedL2Transaction {
3274 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3275 where
3276 S: serde::Serializer,
3277 {
3278 let mut struct_serializer = serializer.serialize_struct("Eip1559Transaction", 14)?;
3279 struct_serializer.serialize_field("type", &TxType::Privileged)?;
3280 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3281 struct_serializer.serialize_field("to", &self.to)?;
3282 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3283 struct_serializer.serialize_field("value", &self.value)?;
3284 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3285 struct_serializer.serialize_field(
3286 "maxPriorityFeePerGas",
3287 &format!("{:#x}", self.max_priority_fee_per_gas),
3288 )?;
3289 struct_serializer
3290 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3291 struct_serializer
3292 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3293 struct_serializer.serialize_field(
3294 "accessList",
3295 &self
3296 .access_list
3297 .iter()
3298 .map(AccessListEntry::from)
3299 .collect::<Vec<_>>(),
3300 )?;
3301 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3302 struct_serializer.serialize_field("sender", &self.from)?;
3303 struct_serializer.end()
3304 }
3305 }
3306
3307 impl Serialize for FeeTokenTransaction {
3308 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3309 where
3310 S: serde::Serializer,
3311 {
3312 let mut struct_serializer = serializer.serialize_struct("FeeTokenTransaction", 15)?;
3313 struct_serializer.serialize_field("type", &TxType::FeeToken)?;
3314 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3315 struct_serializer.serialize_field("to", &self.to)?;
3316 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3317 struct_serializer.serialize_field("value", &self.value)?;
3318 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3319 struct_serializer.serialize_field(
3320 "maxPriorityFeePerGas",
3321 &format!("{:#x}", self.max_priority_fee_per_gas),
3322 )?;
3323 struct_serializer
3324 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3325 struct_serializer
3326 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3327 struct_serializer.serialize_field(
3328 "accessList",
3329 &self
3330 .access_list
3331 .iter()
3332 .map(AccessListEntry::from)
3333 .collect::<Vec<_>>(),
3334 )?;
3335 struct_serializer.serialize_field("feeToken", &format!("{:#x}", self.fee_token))?;
3336 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3337 struct_serializer
3338 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3339 struct_serializer
3340 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?;
3341 struct_serializer.serialize_field("r", &self.signature_r)?;
3342 struct_serializer.serialize_field("s", &self.signature_s)?;
3343 struct_serializer.end()
3344 }
3345 }
3346
3347 impl Serialize for FrameTransaction {
3348 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3349 where
3350 S: serde::Serializer,
3351 {
3352 let mut s = serializer.serialize_struct("FrameTransaction", 10)?;
3353 s.serialize_field("type", &TxType::Frame)?;
3354 s.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3355 s.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3356 s.serialize_field("sender", &format!("{:#x}", self.sender))?;
3357 s.serialize_field(
3358 "frames",
3359 &self.frames.iter().map(FrameEntry::from).collect::<Vec<_>>(),
3360 )?;
3361 s.serialize_field(
3362 "signatures",
3363 &self
3364 .signatures
3365 .iter()
3366 .map(SignatureEntry::from)
3367 .collect::<Vec<_>>(),
3368 )?;
3369 s.serialize_field(
3370 "maxPriorityFeePerGas",
3371 &format!("{:#x}", self.max_priority_fee_per_gas),
3372 )?;
3373 s.serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3374 s.serialize_field("maxFeePerBlobGas", &self.max_fee_per_blob_gas)?;
3375 s.serialize_field("blobVersionedHashes", &self.blob_versioned_hashes)?;
3376 s.end()
3377 }
3378 }
3379
3380 impl<'de> Deserialize<'de> for Transaction {
3381 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3382 where
3383 D: serde::Deserializer<'de>,
3384 {
3385 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3386 let tx_type =
3387 serde_json::from_value::<TxType>(map.remove("type").unwrap_or(Value::default()))
3388 .unwrap_or_else(|_| {
3389 if map.contains_key("tx_type") {
3390 return TxType::Privileged;
3391 }
3392 TxType::EIP1559
3393 });
3394
3395 let iter = map.into_iter();
3396 match tx_type {
3397 TxType::Legacy => {
3398 LegacyTransaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3399 .map(Transaction::LegacyTransaction)
3400 .map_err(|e| {
3401 serde::de::Error::custom(format!("Couldn't Deserialize Legacy {e}"))
3402 })
3403 }
3404 TxType::EIP2930 => {
3405 EIP2930Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3406 .map(Transaction::EIP2930Transaction)
3407 .map_err(|e| {
3408 serde::de::Error::custom(format!("Couldn't Deserialize EIP2930 {e}"))
3409 })
3410 }
3411 TxType::EIP1559 => {
3412 EIP1559Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3413 .map(Transaction::EIP1559Transaction)
3414 .map_err(|e| {
3415 serde::de::Error::custom(format!("Couldn't Deserialize EIP1559 {e}"))
3416 })
3417 }
3418 TxType::EIP4844 => {
3419 EIP4844Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3420 .map(Transaction::EIP4844Transaction)
3421 .map_err(|e| {
3422 serde::de::Error::custom(format!("Couldn't Deserialize EIP4844 {e}"))
3423 })
3424 }
3425 TxType::EIP7702 => {
3426 EIP7702Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3427 .map(Transaction::EIP7702Transaction)
3428 .map_err(|e| {
3429 serde::de::Error::custom(format!("Couldn't Deserialize EIP7702 {e}"))
3430 })
3431 }
3432 TxType::Privileged => PrivilegedL2Transaction::deserialize(
3433 serde::de::value::MapDeserializer::new(iter),
3434 )
3435 .map(Transaction::PrivilegedL2Transaction)
3436 .map_err(|e| {
3437 serde::de::Error::custom(format!("Couldn't Deserialize Privileged: {e}"))
3438 }),
3439 TxType::FeeToken => {
3440 FeeTokenTransaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3441 .map(Transaction::FeeTokenTransaction)
3442 .map_err(|e| {
3443 serde::de::Error::custom(format!("Couldn't Deserialize FeeToken {e}"))
3444 })
3445 }
3446 TxType::Frame => Err(serde::de::Error::custom(
3447 "Frame transaction JSON deserialization not supported",
3448 )),
3449 }
3450 }
3451 }
3452
3453 fn deserialize_input_field(
3454 map: &mut std::collections::HashMap<String, Value>,
3455 ) -> Result<Bytes, serde_json::Error> {
3456 let data_str: String = serde_json::from_value(
3457 map.remove("input")
3458 .ok_or_else(|| serde::de::Error::missing_field("input"))?,
3459 )
3460 .map_err(serde::de::Error::custom)?;
3461 if let Some(stripped) = data_str.strip_prefix("0x") {
3462 match hex::decode(stripped) {
3463 Ok(decoded_bytes) => Ok(Bytes::from(decoded_bytes)),
3464 Err(_) => Err(serde::de::Error::custom(
3465 "Invalid hex format in 'input' field",
3466 ))?,
3467 }
3468 } else {
3469 Err(serde::de::Error::custom(
3470 "'input' field must start with '0x'",
3471 ))?
3472 }
3473 }
3474
3475 fn deserialize_field<'de, T, D>(
3476 map: &mut HashMap<String, serde_json::Value>,
3477 key: &str,
3478 ) -> Result<T, D::Error>
3479 where
3480 D: serde::Deserializer<'de>,
3481 T: serde::de::DeserializeOwned,
3482 {
3483 map.remove(key)
3484 .ok_or_else(|| D::Error::custom(format!("Missing field: {key}")))
3485 .and_then(|value| {
3486 serde_json::from_value(value).map_err(|err| D::Error::custom(err.to_string()))
3487 })
3488 }
3489
3490 fn deserialize_u64_field<'de, D>(
3491 map: &mut HashMap<String, serde_json::Value>,
3492 key: &str,
3493 ) -> Result<u64, D::Error>
3494 where
3495 D: serde::Deserializer<'de>,
3496 {
3497 let value = deserialize_field::<U256, D>(map, key)?;
3498 u64::try_from(value).map_err(|_| D::Error::custom(format!("{key} value overflows u64")))
3499 }
3500
3501 impl<'de> Deserialize<'de> for LegacyTransaction {
3502 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3503 where
3504 D: serde::Deserializer<'de>,
3505 {
3506 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3507
3508 Ok(LegacyTransaction {
3509 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3510 gas_price: deserialize_field::<U256, D>(&mut map, "gasPrice")?,
3511 gas: deserialize_u64_field::<D>(&mut map, "gas")?,
3512 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3513 value: deserialize_field::<U256, D>(&mut map, "value")?,
3514 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3515 v: deserialize_field::<U256, D>(&mut map, "v")?,
3516 r: deserialize_field::<U256, D>(&mut map, "r")?,
3517 s: deserialize_field::<U256, D>(&mut map, "s")?,
3518 ..Default::default()
3519 })
3520 }
3521 }
3522
3523 impl<'de> Deserialize<'de> for EIP2930Transaction {
3524 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3525 where
3526 D: serde::Deserializer<'de>,
3527 {
3528 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3529
3530 Ok(EIP2930Transaction {
3531 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3532 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3533 gas_price: deserialize_field::<U256, D>(&mut map, "gasPrice")?,
3534 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3535 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3536 value: deserialize_field::<U256, D>(&mut map, "value")?,
3537 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3538 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3539 .into_iter()
3540 .map(|v| (v.address, v.storage_keys))
3541 .collect::<Vec<_>>(),
3542 signature_y_parity: u8::from_str_radix(
3543 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3544 16,
3545 )
3546 .map_err(serde::de::Error::custom)?
3547 != 0,
3548 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3549 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3550 ..Default::default()
3551 })
3552 }
3553 }
3554
3555 impl<'de> Deserialize<'de> for EIP1559Transaction {
3556 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3557 where
3558 D: serde::Deserializer<'de>,
3559 {
3560 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3561 Ok(EIP1559Transaction {
3562 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3563 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3564 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3565 &mut map,
3566 "maxPriorityFeePerGas",
3567 )?,
3568 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3569 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3570 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3571 value: deserialize_field::<U256, D>(&mut map, "value")?,
3572 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3573 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3574 .into_iter()
3575 .map(|v| (v.address, v.storage_keys))
3576 .collect::<Vec<_>>(),
3577 signature_y_parity: u8::from_str_radix(
3578 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3579 16,
3580 )
3581 .map_err(serde::de::Error::custom)?
3582 != 0,
3583 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3584 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3585 ..Default::default()
3586 })
3587 }
3588 }
3589
3590 impl<'de> Deserialize<'de> for EIP4844Transaction {
3591 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3592 where
3593 D: serde::Deserializer<'de>,
3594 {
3595 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3596
3597 Ok(EIP4844Transaction {
3598 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3599 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3600 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3601 &mut map,
3602 "maxPriorityFeePerGas",
3603 )?,
3604 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3605 gas: deserialize_u64_field::<D>(&mut map, "gas")?,
3606 to: deserialize_field::<Address, D>(&mut map, "to")?,
3607 value: deserialize_field::<U256, D>(&mut map, "value")?,
3608 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3609 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3610 .into_iter()
3611 .map(|v| (v.address, v.storage_keys))
3612 .collect::<Vec<_>>(),
3613 max_fee_per_blob_gas: deserialize_field::<U256, D>(&mut map, "maxFeePerBlobGas")?,
3614 blob_versioned_hashes: deserialize_field::<Vec<H256>, D>(
3615 &mut map,
3616 "blobVersionedHashes",
3617 )?,
3618 signature_y_parity: u8::from_str_radix(
3619 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3620 16,
3621 )
3622 .map_err(serde::de::Error::custom)?
3623 != 0,
3624 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3625 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3626 ..Default::default()
3627 })
3628 }
3629 }
3630
3631 impl<'de> Deserialize<'de> for EIP7702Transaction {
3632 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3633 where
3634 D: serde::Deserializer<'de>,
3635 {
3636 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3637
3638 Ok(EIP7702Transaction {
3639 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3640 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3641 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3642 &mut map,
3643 "maxPriorityFeePerGas",
3644 )?,
3645 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3646 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3647 to: deserialize_field::<Address, D>(&mut map, "to")?,
3648 value: deserialize_field::<U256, D>(&mut map, "value")?,
3649 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3650 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3651 .into_iter()
3652 .map(|v| (v.address, v.storage_keys))
3653 .collect::<Vec<_>>(),
3654 authorization_list: deserialize_field::<Vec<AuthorizationTupleEntry>, D>(
3655 &mut map,
3656 "authorizationList",
3657 )?
3658 .into_iter()
3659 .map(AuthorizationTuple::from)
3660 .collect::<Vec<_>>(),
3661 signature_y_parity: u8::from_str_radix(
3662 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3663 16,
3664 )
3665 .map_err(serde::de::Error::custom)?
3666 != 0,
3667 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3668 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3669 ..Default::default()
3670 })
3671 }
3672 }
3673
3674 impl<'de> Deserialize<'de> for PrivilegedL2Transaction {
3675 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3676 where
3677 D: serde::Deserializer<'de>,
3678 {
3679 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3680
3681 Ok(PrivilegedL2Transaction {
3682 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3683 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3684 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3685 &mut map,
3686 "maxPriorityFeePerGas",
3687 )?,
3688 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3689 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3690 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3691 value: deserialize_field::<U256, D>(&mut map, "value")?,
3692 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3693 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3694 .into_iter()
3695 .map(|v| (v.address, v.storage_keys))
3696 .collect::<Vec<_>>(),
3697 from: deserialize_field::<Address, D>(&mut map, "sender")?,
3698 ..Default::default()
3699 })
3700 }
3701 }
3702
3703 impl<'de> Deserialize<'de> for FeeTokenTransaction {
3704 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3705 where
3706 D: serde::Deserializer<'de>,
3707 {
3708 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3709
3710 Ok(FeeTokenTransaction {
3711 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3712 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3713 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3714 &mut map,
3715 "maxPriorityFeePerGas",
3716 )?,
3717 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3718 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3719 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3720 value: deserialize_field::<U256, D>(&mut map, "value")?,
3721 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3722 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3723 .into_iter()
3724 .map(|v| (v.address, v.storage_keys))
3725 .collect::<Vec<_>>(),
3726 fee_token: deserialize_field::<Address, D>(&mut map, "feeToken")?,
3727 signature_y_parity: u8::from_str_radix(
3728 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3729 16,
3730 )
3731 .map_err(serde::de::Error::custom)?
3732 != 0,
3733 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3734 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3735 ..Default::default()
3736 })
3737 }
3738 }
3739
3740 #[derive(Debug, thiserror::Error)]
3741 pub enum GenericTransactionError {
3742 #[error("Invalid transaction type: {0}")]
3743 InvalidTxType(TxType),
3744 #[error("Blob bundle error: {0}")]
3745 BlobBundleError(#[from] BlobsBundleError),
3746 #[error("Missing field: {0}")]
3747 MissingField(String),
3748 #[error("Invalid field: {0}")]
3749 InvalidField(String),
3750 }
3751
3752 #[derive(Deserialize, Debug, PartialEq, Clone, Default)]
3755 #[serde(rename_all = "camelCase")]
3756 pub struct GenericTransaction {
3757 #[serde(default)]
3758 pub r#type: TxType,
3759 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3760 pub nonce: Option<u64>,
3761 pub to: TxKind,
3762 #[serde(default)]
3763 pub from: Address,
3764 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3765 pub gas: Option<u64>,
3766 #[serde(default)]
3767 pub value: U256,
3768 #[serde(default)]
3769 pub gas_price: U256,
3770 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3771 pub max_priority_fee_per_gas: Option<u64>,
3772 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3773 pub max_fee_per_gas: Option<u64>,
3774 pub max_fee_per_blob_gas: Option<U256>,
3775 #[serde(default)]
3776 pub access_list: Vec<AccessListEntry>,
3777 #[serde(default)]
3778 pub fee_token: Option<Address>,
3779 #[serde(default)]
3780 pub authorization_list: Option<Vec<AuthorizationTupleEntry>>,
3781 #[serde(default)]
3782 pub blob_versioned_hashes: Vec<H256>,
3783 pub wrapper_version: Option<u8>,
3784 #[serde(default, with = "crate::serde_utils::bytes::vec")]
3785 pub blobs: Vec<Bytes>,
3786 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3787 pub chain_id: Option<u64>,
3788 #[serde(
3790 flatten,
3791 rename = "input_or_data",
3792 deserialize_with = "deserialize_input",
3793 serialize_with = "crate::serde_utils::bytes::serialize"
3794 )]
3795 pub input: Bytes,
3796 }
3797 pub fn deserialize_input<'de, D>(deserializer: D) -> Result<Bytes, D::Error>
3799 where
3800 D: Deserializer<'de>,
3801 {
3802 let variables = HashMap::<String, Value>::deserialize(deserializer)?;
3805 let data = variables.get("data");
3806 let input = variables.get("input");
3807 let value = match (data, input) {
3808 (None, None) => return Ok(Bytes::new()),
3810 (None, Some(val)) => val,
3811 (Some(val), None) => val,
3812 (Some(val_a), Some(val_b)) => {
3813 if val_a == val_b {
3814 val_a
3815 } else {
3816 return Err(D::Error::custom(
3817 "Transaction has both `data` and `input` fields with different values",
3818 ));
3819 }
3820 }
3821 };
3822 let value = String::deserialize(value).map_err(D::Error::custom)?;
3823 let bytes = hex::decode(value.trim_start_matches("0x"))
3824 .map_err(|e| D::Error::custom(e.to_string()))?;
3825 Ok(Bytes::from(bytes))
3826 }
3827
3828 impl From<EIP1559Transaction> for GenericTransaction {
3829 fn from(value: EIP1559Transaction) -> Self {
3830 Self {
3831 r#type: TxType::EIP1559,
3832 nonce: Some(value.nonce),
3833 to: value.to,
3834 gas: Some(value.gas_limit),
3835 value: value.value,
3836 input: value.data.clone(),
3837 gas_price: U256::from(value.max_fee_per_gas),
3838 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
3839 max_fee_per_gas: Some(value.max_fee_per_gas),
3840 max_fee_per_blob_gas: None,
3841 access_list: value
3842 .access_list
3843 .iter()
3844 .map(AccessListEntry::from)
3845 .collect(),
3846 fee_token: None,
3847 authorization_list: None,
3848 blob_versioned_hashes: vec![],
3849 blobs: vec![],
3850 wrapper_version: None,
3851 chain_id: Some(value.chain_id),
3852 from: Address::default(),
3853 }
3854 }
3855 }
3856
3857 impl TryFrom<GenericTransaction> for EIP1559Transaction {
3858 type Error = GenericTransactionError;
3859
3860 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3861 if value.r#type != TxType::EIP1559 {
3862 return Err(GenericTransactionError::InvalidTxType(value.r#type));
3863 }
3864
3865 Ok(Self {
3866 nonce: value.nonce.unwrap_or_default(),
3867 to: value.to,
3868 gas_limit: value.gas.unwrap_or_default(),
3869 value: value.value,
3870 data: value.input.clone(),
3871 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
3872 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
3873 u64::try_from(value.gas_price).map_err(|_| {
3874 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
3875 })?,
3876 ),
3877 access_list: value
3878 .access_list
3879 .into_iter()
3880 .map(|v| (v.address, v.storage_keys))
3881 .collect::<Vec<_>>(),
3882 chain_id: value.chain_id.unwrap_or_default(),
3883 ..Default::default()
3884 })
3885 }
3886 }
3887
3888 impl From<EIP4844Transaction> for GenericTransaction {
3889 fn from(value: EIP4844Transaction) -> Self {
3890 Self {
3891 r#type: TxType::EIP4844,
3892 nonce: Some(value.nonce),
3893 to: TxKind::Call(value.to),
3894 gas: Some(value.gas),
3895 value: value.value,
3896 input: value.data.clone(),
3897 gas_price: U256::from(value.max_fee_per_gas),
3898 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
3899 max_fee_per_gas: Some(value.max_fee_per_gas),
3900 max_fee_per_blob_gas: Some(value.max_fee_per_blob_gas),
3901 access_list: value
3902 .access_list
3903 .iter()
3904 .map(AccessListEntry::from)
3905 .collect(),
3906 fee_token: None,
3907 authorization_list: None,
3908 blob_versioned_hashes: value.blob_versioned_hashes,
3909 blobs: vec![],
3910 wrapper_version: None,
3911 chain_id: Some(value.chain_id),
3912 from: Address::default(),
3913 }
3914 }
3915 }
3916
3917 #[cfg(feature = "c-kzg")]
3918 impl TryFrom<GenericTransaction> for WrappedEIP4844Transaction {
3919 type Error = GenericTransactionError;
3920
3921 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3922 let blobs = value
3923 .blobs
3924 .iter()
3925 .map(|bytes| {
3926 let slice = bytes.as_ref();
3927 let mut blob = [0u8; BYTES_PER_BLOB];
3928 blob.copy_from_slice(slice);
3929 blob
3930 })
3931 .collect();
3932
3933 let wrapper_version = value.wrapper_version;
3934 Ok(Self {
3935 tx: value.try_into()?,
3936 wrapper_version,
3937 blobs_bundle: BlobsBundle::create_from_blobs(&blobs, wrapper_version)?,
3938 })
3939 }
3940 }
3941
3942 impl TryFrom<GenericTransaction> for EIP4844Transaction {
3943 type Error = GenericTransactionError;
3944
3945 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3946 if value.r#type != TxType::EIP4844 {
3947 return Err(GenericTransactionError::InvalidTxType(value.r#type));
3948 }
3949 Ok(Self {
3950 nonce: value.nonce.unwrap_or_default(),
3951 to: match value.to {
3952 TxKind::Call(to) => to,
3953 _ => H160::default(),
3954 },
3955 gas: value.gas.unwrap_or_default(),
3956 value: value.value,
3957 data: value.input.clone(),
3958 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
3959 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
3960 u64::try_from(value.gas_price).map_err(|_| {
3961 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
3962 })?,
3963 ),
3964 max_fee_per_blob_gas: value.max_fee_per_blob_gas.unwrap_or_default(),
3965 access_list: value
3966 .access_list
3967 .into_iter()
3968 .map(|v| (v.address, v.storage_keys))
3969 .collect::<Vec<_>>(),
3970 blob_versioned_hashes: value.blob_versioned_hashes,
3971 chain_id: value.chain_id.unwrap_or_default(),
3972 ..Default::default()
3973 })
3974 }
3975 }
3976
3977 impl From<EIP7702Transaction> for GenericTransaction {
3978 fn from(value: EIP7702Transaction) -> Self {
3979 Self {
3980 r#type: TxType::EIP7702,
3981 nonce: Some(value.nonce),
3982 to: TxKind::Call(value.to),
3983 gas: Some(value.gas_limit),
3984 value: value.value,
3985 input: value.data.clone(),
3986 gas_price: U256::from(value.max_fee_per_gas),
3987 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
3988 max_fee_per_gas: Some(value.max_fee_per_gas),
3989 max_fee_per_blob_gas: None,
3990 access_list: value
3991 .access_list
3992 .iter()
3993 .map(AccessListEntry::from)
3994 .collect(),
3995 fee_token: None,
3996 authorization_list: Some(
3997 value
3998 .authorization_list
3999 .iter()
4000 .map(AuthorizationTupleEntry::from)
4001 .collect(),
4002 ),
4003 blob_versioned_hashes: vec![],
4004 blobs: vec![],
4005 wrapper_version: None,
4006 chain_id: Some(value.chain_id),
4007 from: Address::default(),
4008 }
4009 }
4010 }
4011
4012 impl TryFrom<GenericTransaction> for EIP7702Transaction {
4013 type Error = GenericTransactionError;
4014
4015 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
4016 if value.r#type != TxType::EIP7702 {
4017 return Err(GenericTransactionError::InvalidTxType(value.r#type));
4018 }
4019 let TxKind::Call(to) = value.to else {
4020 return Err(GenericTransactionError::MissingField("to".to_owned()));
4021 };
4022 Ok(Self {
4023 chain_id: value.chain_id.unwrap_or_default(),
4024 nonce: value.nonce.unwrap_or_default(),
4025 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
4026 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
4027 u64::try_from(value.gas_price).map_err(|_| {
4028 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
4029 })?,
4030 ),
4031 gas_limit: value.gas.unwrap_or_default(),
4032 to,
4033 value: value.value,
4034 data: value.input,
4035 access_list: value
4036 .access_list
4037 .into_iter()
4038 .map(|v| (v.address, v.storage_keys))
4039 .collect::<Vec<_>>(),
4040 authorization_list: value
4041 .authorization_list
4042 .unwrap_or_default()
4043 .into_iter()
4044 .map(AuthorizationTuple::from)
4045 .collect(),
4046 ..Default::default()
4047 })
4048 }
4049 }
4050
4051 impl From<PrivilegedL2Transaction> for GenericTransaction {
4052 fn from(value: PrivilegedL2Transaction) -> Self {
4053 Self {
4054 r#type: TxType::Privileged,
4055 nonce: Some(value.nonce),
4056 to: value.to,
4057 gas: Some(value.gas_limit),
4058 value: value.value,
4059 input: value.data.clone(),
4060 gas_price: U256::from(value.max_fee_per_gas),
4061 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
4062 max_fee_per_gas: Some(value.max_fee_per_gas),
4063 max_fee_per_blob_gas: None,
4064 access_list: value
4065 .access_list
4066 .iter()
4067 .map(AccessListEntry::from)
4068 .collect(),
4069 fee_token: None,
4070 authorization_list: None,
4071 blob_versioned_hashes: vec![],
4072 blobs: vec![],
4073 wrapper_version: None,
4074 chain_id: Some(value.chain_id),
4075 from: value.from,
4076 }
4077 }
4078 }
4079
4080 impl TryFrom<GenericTransaction> for PrivilegedL2Transaction {
4081 type Error = GenericTransactionError;
4082
4083 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
4084 if value.r#type != TxType::Privileged {
4085 return Err(GenericTransactionError::InvalidTxType(value.r#type));
4086 }
4087 Ok(Self {
4088 nonce: value.nonce.unwrap_or_default(),
4089 to: value.to,
4090 gas_limit: value.gas.unwrap_or_default(),
4091 value: value.value,
4092 data: value.input.clone(),
4093 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
4094 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
4095 u64::try_from(value.gas_price).map_err(|_| {
4096 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
4097 })?,
4098 ),
4099 access_list: value
4100 .access_list
4101 .into_iter()
4102 .map(|v| (v.address, v.storage_keys))
4103 .collect::<Vec<_>>(),
4104 chain_id: value.chain_id.unwrap_or_default(),
4105 from: value.from,
4106 ..Default::default()
4107 })
4108 }
4109 }
4110
4111 impl From<FeeTokenTransaction> for GenericTransaction {
4112 fn from(value: FeeTokenTransaction) -> Self {
4113 Self {
4114 r#type: TxType::FeeToken,
4115 nonce: Some(value.nonce),
4116 to: value.to,
4117 gas: Some(value.gas_limit),
4118 value: value.value,
4119 input: value.data.clone(),
4120 gas_price: U256::from(value.max_fee_per_gas),
4121 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
4122 max_fee_per_gas: Some(value.max_fee_per_gas),
4123 max_fee_per_blob_gas: None,
4124 access_list: value
4125 .access_list
4126 .iter()
4127 .map(AccessListEntry::from)
4128 .collect(),
4129 fee_token: Some(value.fee_token),
4130 authorization_list: None,
4131 blob_versioned_hashes: vec![],
4132 blobs: vec![],
4133 chain_id: Some(value.chain_id),
4134 from: Address::default(),
4135 wrapper_version: None,
4136 }
4137 }
4138 }
4139
4140 impl TryFrom<GenericTransaction> for FeeTokenTransaction {
4141 type Error = GenericTransactionError;
4142
4143 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
4144 if value.r#type != TxType::FeeToken {
4145 return Err(GenericTransactionError::InvalidTxType(value.r#type));
4146 }
4147
4148 Ok(Self {
4149 nonce: value.nonce.unwrap_or_default(),
4150 to: value.to,
4151 gas_limit: value.gas.unwrap_or_default(),
4152 value: value.value,
4153 data: value.input.clone(),
4154 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
4155 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
4156 u64::try_from(value.gas_price).map_err(|_| {
4157 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
4158 })?,
4159 ),
4160 access_list: value
4161 .access_list
4162 .into_iter()
4163 .map(|v| (v.address, v.storage_keys))
4164 .collect::<Vec<_>>(),
4165 fee_token: value
4166 .fee_token
4167 .ok_or(GenericTransactionError::MissingField(
4168 "fee token".to_owned(),
4169 ))?,
4170 chain_id: value.chain_id.unwrap_or_default(),
4171 ..Default::default()
4172 })
4173 }
4174 }
4175
4176 impl From<LegacyTransaction> for GenericTransaction {
4177 fn from(value: LegacyTransaction) -> Self {
4178 Self {
4179 r#type: TxType::Legacy,
4180 nonce: Some(value.nonce),
4181 to: value.to,
4182 from: Address::default(),
4183 gas: Some(value.gas),
4184 value: value.value,
4185 gas_price: value.gas_price,
4186 max_priority_fee_per_gas: None,
4187 max_fee_per_gas: None,
4188 max_fee_per_blob_gas: None,
4189 access_list: vec![],
4190 fee_token: None,
4191 authorization_list: None,
4192 blob_versioned_hashes: vec![],
4193 blobs: vec![],
4194 wrapper_version: None,
4195 chain_id: None,
4196 input: value.data,
4197 }
4198 }
4199 }
4200
4201 impl From<EIP2930Transaction> for GenericTransaction {
4202 fn from(value: EIP2930Transaction) -> Self {
4203 Self {
4204 r#type: TxType::EIP2930,
4205 nonce: Some(value.nonce),
4206 to: value.to,
4207 from: Address::default(),
4208 gas: Some(value.gas_limit),
4209 value: value.value,
4210 gas_price: value.gas_price,
4211 max_priority_fee_per_gas: None,
4212 max_fee_per_gas: None,
4213 max_fee_per_blob_gas: None,
4214 access_list: value
4215 .access_list
4216 .into_iter()
4217 .map(|(address, storage_keys)| AccessListEntry {
4218 address,
4219 storage_keys,
4220 })
4221 .collect(),
4222 fee_token: None,
4223 authorization_list: None,
4224 blob_versioned_hashes: vec![],
4225 blobs: vec![],
4226 wrapper_version: None,
4227 chain_id: Some(value.chain_id),
4228 input: value.data,
4229 }
4230 }
4231 }
4232
4233 impl From<Transaction> for GenericTransaction {
4234 fn from(value: Transaction) -> Self {
4235 match value {
4236 Transaction::LegacyTransaction(tx) => tx.into(),
4237 Transaction::EIP2930Transaction(tx) => tx.into(),
4238 Transaction::EIP1559Transaction(tx) => tx.into(),
4239 Transaction::EIP4844Transaction(tx) => tx.into(),
4240 Transaction::EIP7702Transaction(tx) => tx.into(),
4241 Transaction::PrivilegedL2Transaction(tx) => tx.into(),
4242 Transaction::FeeTokenTransaction(tx) => tx.into(),
4243 Transaction::FrameTransaction(tx) => tx.into(),
4244 }
4245 }
4246 }
4247
4248 impl From<FrameTransaction> for GenericTransaction {
4249 fn from(value: FrameTransaction) -> Self {
4250 Self {
4251 r#type: TxType::Frame,
4252 nonce: Some(value.nonce),
4253 to: TxKind::Call(value.sender),
4254 from: value.sender,
4255 gas: Some(value.max_gas()),
4256 value: U256::zero(),
4257 gas_price: value.max_fee_per_gas.into(),
4258 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
4259 max_fee_per_gas: Some(value.max_fee_per_gas),
4260 max_fee_per_blob_gas: if value.blob_versioned_hashes.is_empty() {
4261 None
4262 } else {
4263 Some(value.max_fee_per_blob_gas)
4264 },
4265 access_list: vec![],
4266 fee_token: None,
4267 authorization_list: None,
4268 blob_versioned_hashes: value.blob_versioned_hashes,
4269 blobs: vec![],
4270 wrapper_version: None,
4271 chain_id: Some(value.chain_id),
4272 input: Bytes::new(),
4273 }
4274 }
4275 }
4276}
4277
4278mod mempool {
4279 use super::*;
4280 use std::{
4281 cmp::Ordering,
4282 sync::Arc,
4283 time::{SystemTime, UNIX_EPOCH},
4284 };
4285
4286 #[derive(Debug, Clone, PartialEq, Eq)]
4287 pub struct MempoolTransaction {
4288 timestamp: u128,
4290 sender: Address,
4291 inner: Arc<Transaction>,
4292 }
4293
4294 impl MempoolTransaction {
4295 pub fn new(tx: Transaction, sender: Address) -> Self {
4296 Self {
4297 timestamp: SystemTime::now()
4298 .duration_since(UNIX_EPOCH)
4299 .expect("Invalid system time")
4300 .as_micros(),
4301 sender,
4302 inner: Arc::new(tx),
4303 }
4304 }
4305 pub fn time(&self) -> u128 {
4306 self.timestamp
4307 }
4308
4309 pub fn sender(&self) -> Address {
4310 self.sender
4311 }
4312
4313 pub fn transaction(&self) -> &Transaction {
4314 &self.inner
4315 }
4316 }
4317
4318 impl RLPEncode for MempoolTransaction {
4319 fn encode(&self, buf: &mut dyn bytes::BufMut) {
4320 Encoder::new(buf)
4321 .encode_field(&self.timestamp)
4322 .encode_field(&*self.inner)
4323 .finish();
4324 }
4325 }
4326
4327 impl RLPDecode for MempoolTransaction {
4328 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
4329 let decoder = Decoder::new(rlp)?;
4330 let (timestamp, decoder) = decoder.decode_field("timestamp")?;
4331 let (sender, decoder) = decoder.decode_field("sender")?;
4332 let (inner, decoder) = decoder.decode_field("inner")?;
4333 Ok((
4334 Self {
4335 timestamp,
4336 sender,
4337 inner: Arc::new(inner),
4338 },
4339 decoder.finish()?,
4340 ))
4341 }
4342 }
4343
4344 impl std::ops::Deref for MempoolTransaction {
4345 type Target = Transaction;
4346
4347 fn deref(&self) -> &Self::Target {
4348 &self.inner
4349 }
4350 }
4351
4352 impl Ord for MempoolTransaction {
4354 fn cmp(&self, other: &Self) -> Ordering {
4355 match (self.tx_type(), other.tx_type()) {
4356 (TxType::Privileged, TxType::Privileged) => {
4357 return self.nonce().cmp(&other.nonce());
4358 }
4359 (TxType::Privileged, _) => return Ordering::Less,
4360 (_, TxType::Privileged) => return Ordering::Greater,
4361 _ => (),
4362 };
4363 match self.nonce().cmp(&other.nonce()) {
4364 Ordering::Equal => other.time().cmp(&self.time()),
4365 ordering => ordering,
4366 }
4367 }
4368 }
4369
4370 impl PartialOrd for MempoolTransaction {
4371 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
4372 Some(self.cmp(other))
4373 }
4374 }
4375}
4376
4377#[cfg(test)]
4378mod tests {
4379
4380 use super::*;
4381 use crate::types::{
4382 AuthorizationTuple, BlockBody, Receipt, compute_receipts_root, compute_transactions_root,
4383 };
4384 use ethereum_types::H160;
4385 use hex_literal::hex;
4386 use serde_impl::{AccessListEntry, GenericTransaction};
4387 use std::str::FromStr;
4388
4389 #[test]
4390 fn is_blob_carrying_covers_frame_txs_with_blob_hashes() {
4391 let plain_frame = Transaction::FrameTransaction(FrameTransaction::default());
4397 assert!(!plain_frame.is_blob_carrying());
4398
4399 let blob_frame = Transaction::FrameTransaction(FrameTransaction {
4400 max_fee_per_blob_gas: U256::from(7u64),
4401 blob_versioned_hashes: vec![H256::from_low_u64_be(1)],
4402 ..Default::default()
4403 });
4404 assert!(blob_frame.is_blob_carrying());
4405
4406 let blob_tx = Transaction::EIP4844Transaction(EIP4844Transaction {
4407 blob_versioned_hashes: vec![H256::from_low_u64_be(1)],
4408 ..Default::default()
4409 });
4410 assert!(blob_tx.is_blob_carrying());
4411
4412 let plain = Transaction::EIP1559Transaction(EIP1559Transaction::default());
4413 assert!(!plain.is_blob_carrying());
4414 }
4415
4416 #[test]
4417 fn nonce_over_u64_max_maps_to_nonce_is_max() {
4418 let rlp = hex!("ca89010000000000000000");
4421 let decoder = Decoder::new(&rlp).unwrap();
4422 let err = decode_nonce_field(decoder).unwrap_err();
4423 assert!(
4424 err.to_string().contains("Nonce is max"),
4425 "unexpected error: {err}"
4426 );
4427 }
4428
4429 #[test]
4430 fn nonce_at_u64_max_decodes() {
4431 let rlp = hex!("c988ffffffffffffffff");
4433 let decoder = Decoder::new(&rlp).unwrap();
4434 let (nonce, _) = decode_nonce_field(decoder).unwrap();
4435 assert_eq!(nonce, u64::MAX);
4436 }
4437
4438 #[test]
4439 fn legacy_chain_id_handles_out_of_range_v_without_underflow() {
4440 let legacy_chain_id = |v: u64| {
4446 Transaction::LegacyTransaction(LegacyTransaction {
4447 v: U256::from(v),
4448 ..Default::default()
4449 })
4450 .chain_id()
4451 };
4452 for v in [0u64, 1, 26, 27, 28, 34] {
4453 assert_eq!(legacy_chain_id(v), None, "v={v} must yield no chain id");
4454 }
4455 assert_eq!(legacy_chain_id(35), Some(0));
4457 assert_eq!(legacy_chain_id(36), Some(0));
4458 assert_eq!(legacy_chain_id(37), Some(1));
4459 }
4460
4461 #[test]
4462 fn test_compute_transactions_root() {
4463 let mut body = BlockBody::empty();
4464 let tx = LegacyTransaction {
4465 nonce: 0,
4466 gas_price: U256::from(0x0a),
4467 gas: 0x05f5e100,
4468 to: TxKind::Call(hex!("1000000000000000000000000000000000000000").into()),
4469 value: 0.into(),
4470 data: Default::default(),
4471 v: U256::from(0x1b),
4472 r: U256::from_big_endian(&hex!(
4473 "7e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37"
4474 )),
4475 s: U256::from_big_endian(&hex!(
4476 "5f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509b"
4477 )),
4478 ..Default::default()
4479 };
4480 body.transactions.push(Transaction::LegacyTransaction(tx));
4481 let expected_root =
4482 hex!("8151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcb");
4483 let result = compute_transactions_root(&body.transactions, ðrex_crypto::NativeCrypto);
4484
4485 assert_eq!(result, expected_root.into());
4486 }
4487 #[test]
4488 fn test_compute_hash() {
4489 let tx_eip2930 = EIP2930Transaction {
4491 chain_id: 3503995874084926u64,
4492 nonce: 7,
4493 gas_price: U256::from(0x2dbf1f9a_u64),
4494 gas_limit: 0x186A0,
4495 to: TxKind::Call(hex!("7dcd17433742f4c0ca53122ab541d0ba67fc27df").into()),
4496 value: 2.into(),
4497 data: Bytes::from(&b"\xdbS\x06$\x8e\x03\x13\xe7emit"[..]),
4498 access_list: vec![(
4499 hex!("7dcd17433742f4c0ca53122ab541d0ba67fc27df").into(),
4500 vec![
4501 hex!("0000000000000000000000000000000000000000000000000000000000000000").into(),
4502 hex!("a3d07a7d68fbd49ec2f8e6befdd86c885f86c272819f6f345f365dec35ae6707").into(),
4503 ],
4504 )],
4505 signature_y_parity: false,
4506 signature_r: U256::from_dec_str(
4507 "75813812796588349127366022588733264074091236448495248199152066031778895768879",
4508 )
4509 .unwrap(),
4510 signature_s: U256::from_dec_str(
4511 "25476208226281085290728123165613764315157904411823916642262684106502155457829",
4512 )
4513 .unwrap(),
4514 ..Default::default()
4515 };
4516 let tx = Transaction::EIP2930Transaction(tx_eip2930);
4517
4518 let expected_hash =
4519 hex!("a0762610d794acddd2dca15fb7c437ada3611c886f3bea675d53d8da8a6c41b2");
4520 let hash = tx.compute_hash(&NativeCrypto);
4521 assert_eq!(hash, expected_hash.into());
4522 }
4523
4524 #[test]
4525 fn test_compute_receipts_root() {
4526 let tx_type = TxType::Legacy;
4529 let succeeded = true;
4530 let cumulative_gas_used = 0x5208;
4531 let logs = vec![];
4532 let receipt = Receipt::new(tx_type, succeeded, cumulative_gas_used, logs);
4533
4534 let result = compute_receipts_root(&[receipt], ðrex_crypto::NativeCrypto);
4535 let expected_root =
4536 hex!("056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2");
4537 assert_eq!(result, expected_root.into());
4538 }
4539
4540 #[test]
4541 fn legacy_tx_rlp_decode() {
4542 let encoded_tx = "f86d80843baa0c4082f618946177843db3138ae69679a54b95cf345ed759450d870aa87bee538000808360306ba0151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65da064c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4";
4543 let encoded_tx_bytes = hex::decode(encoded_tx).unwrap();
4544 let tx = LegacyTransaction::decode(&encoded_tx_bytes).unwrap();
4545 let expected_tx = LegacyTransaction {
4546 nonce: 0,
4547 gas_price: U256::from(1001000000u64),
4548 gas: 63000,
4549 to: TxKind::Call(Address::from_slice(
4550 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4551 )),
4552 value: 3000000000000000_u64.into(),
4553 data: Bytes::new(),
4554 r: U256::from_str_radix(
4555 "151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65d",
4556 16,
4557 )
4558 .unwrap(),
4559 s: U256::from_str_radix(
4560 "64c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4",
4561 16,
4562 )
4563 .unwrap(),
4564 v: 6303851.into(),
4565 ..Default::default()
4566 };
4567 assert_eq!(tx, expected_tx);
4568 }
4569
4570 #[test]
4571 fn eip1559_tx_rlp_decode() {
4572 let encoded_tx = "f86c8330182480114e82f618946177843db3138ae69679a54b95cf345ed759450d870aa87bee53800080c080a0151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65da064c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4";
4573 let encoded_tx_bytes = hex::decode(encoded_tx).unwrap();
4574 let tx = EIP1559Transaction::decode(&encoded_tx_bytes).unwrap();
4575 let expected_tx = EIP1559Transaction {
4576 nonce: 0,
4577 max_fee_per_gas: 78,
4578 max_priority_fee_per_gas: 17,
4579 to: TxKind::Call(Address::from_slice(
4580 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4581 )),
4582 value: 3000000000000000_u64.into(),
4583 data: Bytes::new(),
4584 signature_r: U256::from_str_radix(
4585 "151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65d",
4586 16,
4587 )
4588 .unwrap(),
4589 signature_s: U256::from_str_radix(
4590 "64c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4",
4591 16,
4592 )
4593 .unwrap(),
4594 signature_y_parity: false,
4595 chain_id: 3151908,
4596 gas_limit: 63000,
4597 access_list: vec![],
4598 ..Default::default()
4599 };
4600 assert_eq!(tx, expected_tx);
4601 }
4602
4603 #[test]
4604 fn deserialize_tx_kind() {
4605 let tx_kind_create = r#""""#;
4606 let tx_kind_call = r#""0x6177843db3138ae69679A54b95cf345ED759450d""#;
4607 let deserialized_tx_kind_create = TxKind::Create;
4608 let deserialized_tx_kind_call = TxKind::Call(Address::from_slice(
4609 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4610 ));
4611 assert_eq!(
4612 deserialized_tx_kind_create,
4613 serde_json::from_str(tx_kind_create).unwrap()
4614 );
4615 assert_eq!(
4616 deserialized_tx_kind_call,
4617 serde_json::from_str(tx_kind_call).unwrap()
4618 )
4619 }
4620
4621 #[test]
4622 fn deserialize_tx_type() {
4623 let tx_type_eip2930 = r#""0x01""#;
4624 let tx_type_eip1559 = r#""0x02""#;
4625 let deserialized_tx_type_eip2930 = TxType::EIP2930;
4626 let deserialized_tx_type_eip1559 = TxType::EIP1559;
4627 assert_eq!(
4628 deserialized_tx_type_eip2930,
4629 serde_json::from_str(tx_type_eip2930).unwrap()
4630 );
4631 assert_eq!(
4632 deserialized_tx_type_eip1559,
4633 serde_json::from_str(tx_type_eip1559).unwrap()
4634 )
4635 }
4636
4637 #[test]
4638 fn deserialize_generic_transaction() {
4639 let generic_transaction = r#"{
4640 "type":"0x01",
4641 "nonce":"0x02",
4642 "to":"",
4643 "from":"0x6177843db3138ae69679A54b95cf345ED759450d",
4644 "gas":"0x5208",
4645 "value":"0x01",
4646 "input":"0x010203040506",
4647 "gasPrice":"0x07",
4648 "accessList": [
4649 {
4650 "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
4651 "storageKeys": [
4652 "0x000000000000000000000000000000000000000000000000000000000000000c",
4653 "0x000000000000000000000000000000000000000000000000000000000000200b"
4654 ]
4655 }
4656 ]
4657 }"#;
4658 let deserialized_generic_transaction = GenericTransaction {
4659 r#type: TxType::EIP2930,
4660 nonce: Some(2),
4661 to: TxKind::Create,
4662 from: Address::from_slice(
4663 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4664 ),
4665 gas: Some(0x5208),
4666 value: U256::from(1),
4667 input: Bytes::from(hex::decode("010203040506").unwrap()),
4668 gas_price: U256::from(7),
4669 max_priority_fee_per_gas: Default::default(),
4670 max_fee_per_gas: Default::default(),
4671 max_fee_per_blob_gas: Default::default(),
4672 access_list: vec![AccessListEntry {
4673 address: Address::from_slice(
4674 &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
4675 ),
4676 storage_keys: vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
4677 }],
4678 fee_token: None,
4679 blob_versioned_hashes: Default::default(),
4680 blobs: Default::default(),
4681 wrapper_version: None,
4682 chain_id: Default::default(),
4683 authorization_list: None,
4684 };
4685 assert_eq!(
4686 deserialized_generic_transaction,
4687 serde_json::from_str(generic_transaction).unwrap()
4688 )
4689 }
4690
4691 #[test]
4692 fn deserialize_generic_transaction_with_data_and_input_fields() {
4693 let generic_transaction = r#"{
4694 "type":"0x01",
4695 "nonce":"0x02",
4696 "to":"",
4697 "from":"0x6177843db3138ae69679A54b95cf345ED759450d",
4698 "gas":"0x5208",
4699 "value":"0x01",
4700 "input":"0x010203040506",
4701 "data":"0x010203040506",
4702 "gasPrice":"0x07",
4703 "accessList": [
4704 {
4705 "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
4706 "storageKeys": [
4707 "0x000000000000000000000000000000000000000000000000000000000000000c",
4708 "0x000000000000000000000000000000000000000000000000000000000000200b"
4709 ]
4710 }
4711 ]
4712 }"#;
4713 let deserialized_generic_transaction = GenericTransaction {
4714 r#type: TxType::EIP2930,
4715 nonce: Some(2),
4716 to: TxKind::Create,
4717 from: Address::from_slice(
4718 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4719 ),
4720 gas: Some(0x5208),
4721 value: U256::from(1),
4722 input: Bytes::from(hex::decode("010203040506").unwrap()),
4723 gas_price: U256::from(7),
4724 max_priority_fee_per_gas: Default::default(),
4725 max_fee_per_gas: Default::default(),
4726 max_fee_per_blob_gas: Default::default(),
4727 access_list: vec![AccessListEntry {
4728 address: Address::from_slice(
4729 &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
4730 ),
4731 storage_keys: vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
4732 }],
4733 fee_token: None,
4734 blob_versioned_hashes: Default::default(),
4735 blobs: Default::default(),
4736 wrapper_version: None,
4737 chain_id: Default::default(),
4738 authorization_list: None,
4739 };
4740 assert_eq!(
4741 deserialized_generic_transaction,
4742 serde_json::from_str(generic_transaction).unwrap()
4743 )
4744 }
4745
4746 #[test]
4747 fn deserialize_eip4844_transaction() {
4748 let eip4844_transaction = r#"{
4749 "chainId":"0x01",
4750 "nonce":"0x02",
4751 "maxPriorityFeePerGas":"0x01",
4752 "maxFeePerGas":"0x01",
4753 "gas":"0x5208",
4754 "to":"0x6177843db3138ae69679A54b95cf345ED759450d",
4755 "value":"0x01",
4756 "input":"0x3033",
4757 "accessList": [
4758 {
4759 "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
4760 "storageKeys": [
4761 "0x000000000000000000000000000000000000000000000000000000000000000c",
4762 "0x000000000000000000000000000000000000000000000000000000000000200b"
4763 ]
4764 }
4765 ],
4766 "maxFeePerBlobGas":"0x03",
4767 "blobVersionedHashes": [
4768 "0x0000000000000000000000000000000000000000000000000000000000000001",
4769 "0x0000000000000000000000000000000000000000000000000000000000000002"
4770 ],
4771 "yParity":"0x0",
4772 "r": "0x01",
4773 "s": "0x02"
4774 }"#;
4775 let deserialized_eip4844_transaction = EIP4844Transaction {
4776 chain_id: 0x01,
4777 nonce: 0x02,
4778 to: Address::from_slice(
4779 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4780 ),
4781 max_priority_fee_per_gas: 1,
4782 max_fee_per_gas: 1,
4783 max_fee_per_blob_gas: U256::from(0x03),
4784 gas: 0x5208,
4785 value: U256::from(0x01),
4786 data: Bytes::from_static(b"03"),
4788 access_list: vec![(
4789 Address::from_slice(
4790 &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
4791 ),
4792 vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
4793 )],
4794 blob_versioned_hashes: vec![H256::from_low_u64_be(1), H256::from_low_u64_be(2)],
4795 signature_y_parity: false,
4796 signature_r: U256::from(0x01),
4797 signature_s: U256::from(0x02),
4798 ..Default::default()
4799 };
4800
4801 assert_eq!(
4802 deserialized_eip4844_transaction,
4803 serde_json::from_str(eip4844_transaction).unwrap()
4804 )
4805 }
4806
4807 #[test]
4808 fn serialize_deserialize_transaction() {
4809 let eip1559 = EIP1559Transaction {
4810 chain_id: 65536999,
4811 nonce: 1,
4812 max_priority_fee_per_gas: 1000,
4813 max_fee_per_gas: 2000,
4814 gas_limit: 21000,
4815 to: TxKind::Call(H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B0").unwrap()),
4816 value: U256::from(100000),
4817 data: Bytes::from_static(b"03"),
4818 access_list: vec![(
4819 H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B3").unwrap(),
4820 vec![H256::zero()],
4821 )],
4822 signature_y_parity: true,
4823 signature_r: U256::one(),
4824 signature_s: U256::zero(),
4825 ..Default::default()
4826 };
4827 let tx_to_serialize = Transaction::EIP1559Transaction(eip1559.clone());
4828 let serialized = serde_json::to_string(&tx_to_serialize).expect("Failed to serialize");
4829
4830 let deserialized_tx: Transaction =
4831 serde_json::from_str(&serialized).expect("Failed to deserialize");
4832
4833 assert!(deserialized_tx.tx_type() == TxType::EIP1559);
4834
4835 if let Transaction::EIP1559Transaction(tx) = deserialized_tx {
4836 assert_eq!(tx, eip1559);
4837 }
4838 }
4839
4840 #[test]
4841 fn serialize_deserialize_eip7702transaction() {
4842 let eip7702 = EIP7702Transaction {
4843 chain_id: 65536999,
4844 nonce: 1,
4845 max_priority_fee_per_gas: 1000,
4846 max_fee_per_gas: 2000,
4847 gas_limit: 21000,
4848 to: Address::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B0").unwrap(),
4849 value: U256::from(100000),
4850 data: Bytes::from_static(b"03"),
4851 access_list: vec![],
4852 signature_y_parity: true,
4853 signature_r: U256::one(),
4854 signature_s: U256::zero(),
4855 authorization_list: vec![AuthorizationTuple {
4856 chain_id: U256::from(65536999),
4857 address: H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B1").unwrap(),
4858 nonce: 2,
4859 y_parity: U256::one(),
4860 r_signature: U256::from(22),
4861 s_signature: U256::from(37),
4862 }],
4863 ..Default::default()
4864 };
4865 let tx_to_serialize = Transaction::EIP7702Transaction(eip7702.clone());
4866 let serialized = serde_json::to_string(&tx_to_serialize).expect("Failed to serialize");
4867
4868 let deserialized_tx: Transaction =
4869 serde_json::from_str(&serialized).expect("Failed to deserialize");
4870
4871 assert!(deserialized_tx.tx_type() == TxType::EIP7702);
4872
4873 if let Transaction::EIP7702Transaction(tx) = deserialized_tx {
4874 assert_eq!(tx, eip7702);
4875 }
4876 }
4877
4878 #[test]
4879 fn serialize_deserialize_privileged_l2_transaction() -> Result<(), RLPDecodeError> {
4880 let privileged_l2 = PrivilegedL2Transaction {
4881 chain_id: 65536999,
4882 nonce: 0,
4883 max_priority_fee_per_gas: 875000000,
4884 max_fee_per_gas: 875000000,
4885 gas_limit: 42000u64,
4886 to: TxKind::Call(
4887 Address::from_str("0x8943545177806ed17b9f23f0a21ee5948ecaa776").unwrap(),
4888 ),
4889 value: U256::from(500000000000000000000000000u128),
4890 data: Bytes::new(),
4891 access_list: vec![],
4892 from: Address::from_str("0x8943545177806ed17b9f23f0a21ee5948ecaa776").unwrap(),
4893 ..Default::default()
4894 };
4895
4896 let encoded = PrivilegedL2Transaction::encode_to_vec(&privileged_l2);
4897 println!("encoded length: {}", encoded.len());
4898 assert_eq!(encoded.len(), privileged_l2.length());
4899
4900 let deserialized_tx = PrivilegedL2Transaction::decode(&encoded)?;
4901
4902 assert_eq!(deserialized_tx, privileged_l2);
4903
4904 Ok(())
4905 }
4906
4907 #[test]
4908 fn test_legacy_transaction_into_generic() {
4909 let legacy_tx = LegacyTransaction {
4910 nonce: 1,
4911 gas_price: U256::from(20_000_000_000u64),
4912 gas: 21000,
4913 to: TxKind::Call(
4914 Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
4915 ),
4916 value: U256::from(1_000_000_000_000_000_000u64),
4917 data: Bytes::default(),
4918 v: U256::from(27),
4919 r: U256::from(1),
4920 s: U256::from(1),
4921 ..Default::default()
4922 };
4923
4924 let generic_tx: GenericTransaction = legacy_tx.into();
4925 assert_eq!(generic_tx.r#type, TxType::Legacy);
4926 assert_eq!(generic_tx.nonce, Some(1));
4927 assert_eq!(generic_tx.gas_price, U256::from(20_000_000_000u64));
4928 assert_eq!(generic_tx.gas, Some(21000));
4929 assert_eq!(generic_tx.max_priority_fee_per_gas, None);
4930 assert_eq!(generic_tx.max_fee_per_gas, None);
4931 assert_eq!(generic_tx.access_list.len(), 0);
4932 assert_eq!(generic_tx.chain_id, None);
4933 }
4934
4935 #[test]
4936 fn test_eip2930_transaction_into_generic() {
4937 let access_list = vec![(
4938 Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
4939 vec![
4940 H256::from_str(
4941 "0x1234567890123456789012345678901234567890123456789012345678901234",
4942 )
4943 .unwrap(),
4944 ],
4945 )];
4946
4947 let eip2930_tx = EIP2930Transaction {
4948 chain_id: 1,
4949 nonce: 1,
4950 gas_price: U256::from(20_000_000_000u64),
4951 gas_limit: 21000,
4952 to: TxKind::Call(
4953 Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
4954 ),
4955 value: U256::from(1_000_000_000_000_000_000u64),
4956 data: Bytes::default(),
4957 access_list: access_list.clone(),
4958 signature_y_parity: false,
4959 signature_r: U256::from(1),
4960 signature_s: U256::from(1),
4961 ..Default::default()
4962 };
4963
4964 let generic_tx: GenericTransaction = eip2930_tx.into();
4965 assert_eq!(generic_tx.r#type, TxType::EIP2930);
4966 assert_eq!(generic_tx.nonce, Some(1));
4967 assert_eq!(generic_tx.gas_price, U256::from(20_000_000_000u64));
4968 assert_eq!(generic_tx.gas, Some(21000));
4969 assert_eq!(generic_tx.max_priority_fee_per_gas, None);
4970 assert_eq!(generic_tx.max_fee_per_gas, None);
4971 assert_eq!(generic_tx.chain_id, Some(1));
4972 assert_eq!(generic_tx.access_list.len(), 1);
4973 assert_eq!(generic_tx.access_list[0].address, access_list[0].0);
4974 assert_eq!(generic_tx.access_list[0].storage_keys, access_list[0].1);
4975 }
4976
4977 #[test]
4978 fn recover_address_rejects_high_s_signatures() {
4979 use ethrex_crypto::NativeCrypto;
4980 use k256::ecdsa::SigningKey;
4981
4982 let crypto = NativeCrypto;
4983
4984 let private_key = hex!("4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318");
4987 let signing_key = SigningKey::from_bytes(&private_key.into()).expect("Valid private key");
4988
4989 let msg = b"Test message for high-s signature rejection";
4991 let payload = keccak(msg).to_fixed_bytes();
4993
4994 let (signature, recovery_id) = signing_key
4997 .sign_prehash_recoverable(&payload)
4998 .expect("Signing failed");
4999
5000 let mut sig_bytes = [0u8; 65];
5002 sig_bytes[..64].copy_from_slice(&signature.to_bytes());
5003 sig_bytes[64] = recovery_id.to_byte();
5004
5005 let uncompressed_pub = signing_key.verifying_key().to_encoded_point(false);
5008 let pub_hash = ethrex_crypto::keccak::keccak_hash(&uncompressed_pub.as_bytes()[1..]);
5009 let expected_address = Address::from_slice(&pub_hash[12..]);
5010
5011 let recovered = crypto
5012 .recover_signer(&sig_bytes, &payload)
5013 .expect("Valid low-s signature should recover successfully");
5014 assert_eq!(recovered, expected_address, "Recovered address mismatch");
5015
5016 let n = U256::from_big_endian(&hex!(
5019 "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"
5020 ));
5021 let s = U256::from_big_endian(&sig_bytes[32..64]);
5022
5023 let half_n = n / 2;
5025 assert!(
5026 s <= half_n,
5027 "Generated signature was not low-s, cannot test high-s rejection"
5028 );
5029
5030 let s_high = n - s;
5032
5033 let mut sig_high_bytes = sig_bytes;
5034 sig_high_bytes[32..64].copy_from_slice(&s_high.to_big_endian());
5036 sig_high_bytes[64] ^= 1;
5038
5039 assert!(
5042 crypto.recover_signer(&sig_high_bytes, &payload).is_err(),
5043 "High-s signature should be rejected (EIP-2 compliance)"
5044 );
5045 }
5046
5047 #[test]
5048 fn encode_decode_low_size_tx() {
5049 let tx = Transaction::EIP2930Transaction(EIP2930Transaction::default());
5050 let tx_to_encode = tx.clone();
5053 let encoded = tx_to_encode.encode_to_vec();
5054 let decoded_tx = Transaction::decode(&encoded).unwrap();
5055 assert_eq!(tx, decoded_tx);
5056 }
5057
5058 #[test]
5059 fn test_eip1559_simple_transfer_size() {
5060 let tx = Transaction::EIP1559Transaction(EIP1559Transaction::default());
5061 assert_eq!(tx.encode_to_vec().len(), EIP1559_DEFAULT_SERIALIZED_LENGTH);
5062 }
5063
5064 fn make_test_frame_tx() -> FrameTransaction {
5067 FrameTransaction {
5068 chain_id: 1,
5069 nonce: 42,
5070 sender: Address::from_low_u64_be(0xABCD),
5071 frames: vec![
5072 Frame {
5073 mode: FrameMode::Verify as u8,
5074 flags: 0x03, target: Some(Address::from_low_u64_be(0xABCD)),
5076 gas_limit: 100_000,
5077 value: U256::zero(),
5078 data: Bytes::from_static(b"verify_data"),
5079 },
5080 Frame {
5081 mode: FrameMode::Sender as u8,
5082 flags: 0x00,
5083 target: Some(Address::from_low_u64_be(0x1234)),
5084 gas_limit: 200_000,
5085 value: U256::zero(),
5086 data: Bytes::from_static(b"call_data"),
5087 },
5088 ],
5089 signatures: vec![FrameSignature {
5090 scheme: FRAME_SIG_SCHEME_SECP256K1,
5091 signer: Some(Address::from_low_u64_be(0xABCD)),
5092 msg: Bytes::new(),
5093 signature: Bytes::from(vec![0u8; 65]),
5094 }],
5095 max_priority_fee_per_gas: 1_000_000_000,
5096 max_fee_per_gas: 30_000_000_000,
5097 max_fee_per_blob_gas: U256::zero(),
5098 blob_versioned_hashes: vec![],
5099 inner_hash: OnceCell::new(),
5100 cached_canonical: OnceCell::new(),
5101 }
5102 }
5103
5104 #[test]
5105 fn atomic_batch_flag_valid_on_default_and_sender_frames() {
5106 let mut tx = make_test_frame_tx();
5109 tx.frames = vec![
5110 Frame {
5111 mode: FrameMode::Default as u8,
5112 flags: 0x04, target: Some(Address::from_low_u64_be(0xB0B)),
5114 gas_limit: 21_000,
5115 value: U256::zero(),
5116 data: Bytes::new(),
5117 },
5118 Frame {
5119 mode: FrameMode::Sender as u8,
5120 flags: 0x04, target: Some(Address::from_low_u64_be(0xB0B)),
5122 gas_limit: 21_000,
5123 value: U256::zero(),
5124 data: Bytes::new(),
5125 },
5126 Frame {
5127 mode: FrameMode::Sender as u8,
5128 flags: 0x00, target: Some(Address::from_low_u64_be(0xCAFE)),
5130 gas_limit: 21_000,
5131 value: U256::zero(),
5132 data: Bytes::new(),
5133 },
5134 ];
5135 assert!(tx.validate_static_constraints().is_ok());
5136 }
5137
5138 #[test]
5139 fn atomic_batch_flag_on_last_frame_still_invalid() {
5140 let mut tx = make_test_frame_tx();
5141 tx.frames = vec![Frame {
5142 mode: FrameMode::Sender as u8,
5143 flags: 0x04,
5144 target: Some(Address::from_low_u64_be(0xCAFE)),
5145 gas_limit: 21_000,
5146 value: U256::zero(),
5147 data: Bytes::new(),
5148 }];
5149 let err = tx.validate_static_constraints().unwrap_err();
5150 assert!(err.contains("atomic batch flag on last frame"), "{err}");
5151 }
5152
5153 #[test]
5154 fn test_frame_rlp_roundtrip() {
5155 let frame = Frame {
5156 mode: FrameMode::Verify as u8,
5157 flags: 0x03,
5158 target: Some(Address::from_low_u64_be(0x1234)),
5159 gas_limit: 50_000,
5160 value: U256::zero(),
5161 data: Bytes::from_static(b"hello"),
5162 };
5163 let encoded = frame.encode_to_vec();
5164 let decoded = Frame::decode(&encoded).unwrap();
5165 assert_eq!(frame, decoded);
5166 }
5167
5168 #[test]
5169 fn test_frame_null_target_rlp_roundtrip() {
5170 let frame = Frame {
5171 mode: FrameMode::Default as u8,
5172 flags: 0x00,
5173 target: None,
5174 gas_limit: 10_000,
5175 value: U256::zero(),
5176 data: Bytes::from_static(b"deploy"),
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_mode_with_flags_rlp_roundtrip() {
5185 for mode_val in [0u8, 1, 2] {
5187 let frame = Frame {
5188 mode: mode_val,
5189 flags: if mode_val == 1 { 0x03 } else { 0x00 },
5190 target: Some(Address::from_low_u64_be(0x1234)),
5191 gas_limit: 50_000,
5192 value: U256::zero(),
5193 data: Bytes::new(),
5194 };
5195 let encoded = frame.encode_to_vec();
5196 let decoded = Frame::decode(&encoded).unwrap();
5197 assert_eq!(frame, decoded);
5198 }
5199 let frame = Frame {
5201 mode: 2, flags: 0x01 | 0x04, target: Some(Address::from_low_u64_be(0x1234)),
5204 gas_limit: 50_000,
5205 value: U256::zero(),
5206 data: Bytes::new(),
5207 };
5208 assert_eq!(frame.execution_mode(), FrameMode::Sender);
5209 assert_eq!(frame.scope_restriction(), 1);
5210 assert!(frame.is_atomic_batch());
5211 let encoded = frame.encode_to_vec();
5212 let decoded = Frame::decode(&encoded).unwrap();
5213 assert_eq!(frame, decoded);
5214 }
5215
5216 #[test]
5217 fn frame_rlp_roundtrip_preserves_value() {
5218 let frame = Frame {
5219 mode: FrameMode::Sender as u8,
5220 flags: 0x00,
5221 target: Some(Address::from_low_u64_be(0xCAFE)),
5222 gas_limit: 100_000,
5223 value: U256::from(1_000_000_000_000_000u64), data: Bytes::from_static(b"hello"),
5225 };
5226 let encoded = frame.encode_to_vec();
5227 let decoded = Frame::decode(&encoded).unwrap();
5228 assert_eq!(frame, decoded);
5229 assert_eq!(decoded.value, U256::from(1_000_000_000_000_000u64));
5230 }
5231
5232 #[test]
5233 fn test_frame_transaction_rlp_roundtrip() {
5234 let tx = make_test_frame_tx();
5235 let encoded = tx.encode_to_vec();
5236 let decoded = FrameTransaction::decode(&encoded).unwrap();
5237 assert_eq!(tx, decoded);
5238 }
5239
5240 #[test]
5241 fn test_frame_transaction_canonical_roundtrip() {
5242 let tx = Transaction::FrameTransaction(make_test_frame_tx());
5243 let encoded = tx.encode_canonical_to_vec();
5244 let decoded = Transaction::decode_canonical(&encoded).unwrap();
5245 assert_eq!(
5247 tx.hash(ðrex_crypto::NativeCrypto),
5248 decoded.hash(ðrex_crypto::NativeCrypto)
5249 );
5250 assert_eq!(tx.tx_type(), decoded.tx_type());
5252 assert_eq!(tx.nonce(), decoded.nonce());
5253 assert_eq!(tx.chain_id(), decoded.chain_id());
5254 }
5255
5256 #[test]
5257 fn test_frame_transaction_sig_hash_covers_all_frame_data() {
5258 let tx = make_test_frame_tx();
5260 let hash1 = tx.compute_sig_hash();
5261
5262 let mut tx2 = tx.clone();
5264 tx2.frames[0].data = Bytes::from_static(b"completely_different_verify_data");
5265 let hash2 = tx2.compute_sig_hash();
5266 assert_ne!(hash1, hash2);
5267
5268 let mut tx3 = tx;
5270 tx3.frames[1].data = Bytes::from_static(b"different_call_data");
5271 let hash3 = tx3.compute_sig_hash();
5272 assert_ne!(hash1, hash3);
5273 }
5274
5275 #[test]
5276 fn test_frame_transaction_accessor_methods() {
5277 let frame_tx = make_test_frame_tx();
5278 let tx = Transaction::FrameTransaction(frame_tx);
5279
5280 assert_eq!(tx.tx_type(), TxType::Frame);
5281 assert_eq!(tx.nonce(), 42);
5282 assert_eq!(tx.chain_id(), Some(1));
5283 assert_eq!(tx.to(), TxKind::Call(Address::from_low_u64_be(0xABCD)));
5284 assert_eq!(tx.value(), U256::zero());
5285 assert_eq!(tx.data(), &Bytes::new());
5286 assert!(tx.access_list().is_empty());
5287 assert!(tx.authorization_list().is_none());
5288 assert_eq!(tx.max_priority_fee(), Some(1_000_000_000));
5289 assert_eq!(tx.max_fee_per_gas(), Some(30_000_000_000));
5290 assert_eq!(tx.max_fee_per_blob_gas(), None); assert!(!tx.is_contract_creation());
5292 assert_eq!(
5294 tx.sender(ðrex_crypto::NativeCrypto).unwrap(),
5295 Address::from_low_u64_be(0xABCD)
5296 );
5297 assert!(tx.gas_limit() >= 300_000); }
5300
5301 #[test]
5302 fn test_frame_transaction_hash_uses_oncecell() {
5303 let tx = Transaction::FrameTransaction(make_test_frame_tx());
5304 let h1 = tx.hash(ðrex_crypto::NativeCrypto);
5305 let h2 = tx.hash(ðrex_crypto::NativeCrypto);
5306 assert_eq!(h1, h2);
5307 }
5308
5309 #[test]
5310 fn frame_transaction_rlp_roundtrip_preserves_fields() {
5311 let tx = make_test_frame_tx();
5312 let mut buf = Vec::new();
5313 tx.encode(&mut buf);
5314 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5315 assert!(rest.is_empty());
5316 assert_eq!(decoded.chain_id, tx.chain_id);
5317 assert_eq!(decoded.nonce, tx.nonce);
5318 assert_eq!(decoded.sender, tx.sender);
5319 }
5320
5321 #[test]
5322 fn frame_transaction_variant_is_exposed_on_transaction_enum() {
5323 let tx = Transaction::FrameTransaction(make_test_frame_tx());
5324 assert!(matches!(tx, Transaction::FrameTransaction(_)));
5325 }
5326
5327 #[test]
5328 fn frame_transaction_serializes_signatures_as_array() {
5329 let tx = make_test_frame_tx();
5331 let json = serde_json::to_value(&tx).unwrap();
5332 let sigs = json
5333 .get("signatures")
5334 .expect("signatures field present")
5335 .as_array()
5336 .expect("signatures serialized as an array");
5337 assert_eq!(sigs.len(), tx.signatures.len());
5338 assert_eq!(sigs[0].get("scheme").unwrap(), "0x1");
5339 assert!(sigs[0].get("signer").is_some());
5340 assert!(sigs[0].get("signature").is_some());
5341 assert!(sigs[0].get("msg").is_some());
5342 }
5343
5344 fn make_frame_tx_with_gas_limits(limits: Vec<u64>) -> FrameTransaction {
5345 let frames = limits
5346 .into_iter()
5347 .map(|gl| Frame {
5348 mode: FrameMode::Sender as u8,
5349 flags: 0x00,
5350 target: Some(Address::from_low_u64_be(0x1234)),
5351 gas_limit: gl,
5352 value: U256::zero(),
5353 data: Bytes::new(),
5354 })
5355 .collect();
5356 FrameTransaction {
5357 chain_id: 1,
5358 nonce: 0,
5359 sender: Address::from_low_u64_be(0xABCD),
5360 frames,
5361 signatures: vec![],
5362 max_priority_fee_per_gas: 1_000_000_000,
5363 max_fee_per_gas: 30_000_000_000,
5364 max_fee_per_blob_gas: U256::zero(),
5365 blob_versioned_hashes: vec![],
5366 inner_hash: OnceCell::new(),
5367 cached_canonical: OnceCell::new(),
5368 }
5369 }
5370
5371 #[test]
5372 fn per_frame_gas_limit_above_i64_max_is_rejected() {
5373 let tx = make_frame_tx_with_gas_limits(vec![(i64::MAX as u64) + 1]);
5374 let err = tx.validate_static_constraints().unwrap_err();
5375 assert!(err.contains("exceeds 2**63-1"), "unexpected error: {err}");
5376 }
5377
5378 #[test]
5379 fn cumulative_frame_gas_limit_above_i64_max_is_rejected() {
5380 let half = (i64::MAX as u64) / 2 + 1;
5381 let tx = make_frame_tx_with_gas_limits(vec![half, half]);
5382 let err = tx.validate_static_constraints().unwrap_err();
5383 assert!(err.contains("cumulative"), "unexpected error: {err}");
5384 }
5385
5386 #[test]
5387 fn cumulative_frame_gas_limit_equal_to_i64_max_is_accepted() {
5388 let a = (i64::MAX as u64) / 2;
5389 let b = i64::MAX as u64 - a;
5390 let tx = make_frame_tx_with_gas_limits(vec![a, b]);
5391 tx.validate_static_constraints()
5392 .expect("exact i64::MAX total should be accepted");
5393 }
5394
5395 #[test]
5396 fn empty_frames_list_is_rejected_by_count_check_not_gas_check() {
5397 let tx = make_frame_tx_with_gas_limits(vec![]);
5400 let err = tx.validate_static_constraints().unwrap_err();
5401 assert!(err.contains("between 1 and"), "unexpected error: {err}");
5402 }
5403
5404 #[test]
5405 fn sig_hash_covers_frame_value() {
5406 let tx = make_test_frame_tx();
5409 let baseline = tx.compute_sig_hash();
5410
5411 let mut with_sender_value = tx.clone();
5412 with_sender_value.frames[1].value = U256::from(1u64);
5413 assert_ne!(
5414 baseline,
5415 with_sender_value.compute_sig_hash(),
5416 "sig_hash must change when a SENDER frame's value changes"
5417 );
5418
5419 let mut with_verify_value = tx.clone();
5420 with_verify_value.frames[0].value = U256::from(1u64);
5421 assert_ne!(
5422 baseline,
5423 with_verify_value.compute_sig_hash(),
5424 "sig_hash must change when a VERIFY frame's value changes"
5425 );
5426
5427 let mut with_verify_data = tx;
5429 with_verify_data.frames[0].data = Bytes::from_static(b"different_verify_data");
5430 assert_ne!(
5431 baseline,
5432 with_verify_data.compute_sig_hash(),
5433 "VERIFY.data is now covered by sig_hash (no longer elided)"
5434 );
5435 }
5436
5437 #[test]
5438 fn validate_static_constraints_rejects_nonzero_value_on_non_sender_frames() {
5439 let verify_tx = FrameTransaction {
5441 chain_id: 1,
5442 nonce: 0,
5443 sender: Address::from_low_u64_be(0xABCD),
5444 frames: vec![Frame {
5445 mode: FrameMode::Verify as u8,
5446 flags: 0x01,
5447 target: None,
5448 gas_limit: 50_000,
5449 value: U256::from(1u64),
5450 data: Bytes::new(),
5451 }],
5452 signatures: vec![],
5453 max_priority_fee_per_gas: 1_000_000_000,
5454 max_fee_per_gas: 30_000_000_000,
5455 max_fee_per_blob_gas: U256::zero(),
5456 blob_versioned_hashes: vec![],
5457 inner_hash: OnceCell::new(),
5458 cached_canonical: OnceCell::new(),
5459 };
5460 let err = verify_tx.validate_static_constraints().unwrap_err();
5461 assert!(
5462 err.contains("non-zero value only allowed in SENDER mode"),
5463 "unexpected error for VERIFY: {err}"
5464 );
5465
5466 let default_tx = FrameTransaction {
5468 frames: vec![Frame {
5469 mode: FrameMode::Default as u8,
5470 flags: 0x00,
5471 target: Some(Address::from_low_u64_be(0x1234)),
5472 gas_limit: 50_000,
5473 value: U256::from(1u64),
5474 data: Bytes::new(),
5475 }],
5476 ..verify_tx
5477 };
5478 let err = default_tx.validate_static_constraints().unwrap_err();
5479 assert!(
5480 err.contains("non-zero value only allowed in SENDER mode"),
5481 "unexpected error for DEFAULT: {err}"
5482 );
5483
5484 let sender_tx = FrameTransaction {
5486 frames: vec![Frame {
5487 mode: FrameMode::Sender as u8,
5488 flags: 0x00,
5489 target: Some(Address::from_low_u64_be(0x1234)),
5490 gas_limit: 50_000,
5491 value: U256::from(1u64),
5492 data: Bytes::new(),
5493 }],
5494 ..default_tx
5495 };
5496 sender_tx
5497 .validate_static_constraints()
5498 .expect("SENDER frames may carry non-zero value");
5499 }
5500
5501 fn chain_config_with_hegota(hegota_time: Option<u64>) -> crate::types::ChainConfig {
5504 crate::types::ChainConfig {
5505 hegota_time,
5506 ..Default::default()
5507 }
5508 }
5509
5510 #[test]
5511 fn test_frame_tx_pre_fork_chain_config_rejects() {
5512 let cfg = chain_config_with_hegota(None);
5513 assert!(!cfg.is_hegota_activated(0));
5514 assert!(!cfg.is_hegota_activated(u64::MAX));
5515 }
5516
5517 #[test]
5518 fn test_frame_tx_post_fork_admits() {
5519 let cfg = chain_config_with_hegota(Some(1000));
5520 assert!(cfg.is_hegota_activated(2000));
5521 }
5522
5523 #[test]
5524 fn test_frame_tx_fork_boundary_admits() {
5525 let activation_time = 1_700_000_000u64;
5526 let cfg = chain_config_with_hegota(Some(activation_time));
5527 assert!(cfg.is_hegota_activated(activation_time));
5528 assert!(!cfg.is_hegota_activated(activation_time - 1));
5529 }
5530
5531 #[test]
5532 fn test_frame_tx_devnet_fork_epoch_zero_admits() {
5533 let cfg = chain_config_with_hegota(Some(0));
5534 assert!(cfg.is_hegota_activated(0));
5535 assert!(cfg.is_hegota_activated(1));
5536 assert!(cfg.is_hegota_activated(u64::MAX));
5537 }
5538
5539 #[test]
5540 fn test_frame_tx_pre_fork_rlp_roundtrip_still_decodes() {
5541 let tx = make_test_frame_tx();
5544 let mut buf = Vec::new();
5545 tx.encode(&mut buf);
5546 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5547 assert!(rest.is_empty());
5548 assert_eq!(decoded.chain_id, tx.chain_id);
5549 assert_eq!(decoded.nonce, tx.nonce);
5550 assert_eq!(decoded.sender, tx.sender);
5551 }
5552
5553 fn expiry_frame(deadline: u64) -> Frame {
5556 Frame {
5557 mode: FrameMode::Verify as u8,
5558 flags: 0x00,
5559 target: Some(frame_tx_expiry_verifier()),
5560 gas_limit: 30_000,
5561 value: U256::zero(),
5562 data: Bytes::copy_from_slice(&deadline.to_be_bytes()),
5563 }
5564 }
5565
5566 #[test]
5567 fn expiry_verifier_frame_passes_static_validation() {
5568 let mut tx = make_test_frame_tx();
5569 tx.frames.insert(0, expiry_frame(1_700_000_000));
5570 assert!(tx.validate_static_constraints().is_ok());
5571 }
5572
5573 #[test]
5574 fn expiry_verifier_frame_rejects_bad_data_length() {
5575 let mut tx = make_test_frame_tx();
5576 let mut f = expiry_frame(0);
5577 f.data = Bytes::from_static(b"short");
5578 tx.frames.insert(0, f);
5579 let err = tx.validate_static_constraints().unwrap_err();
5580 assert!(err.contains("8 bytes"), "{err}");
5581 }
5582
5583 #[test]
5584 fn expiry_verifier_frame_rejects_nonzero_flags() {
5585 let mut tx = make_test_frame_tx();
5586 let mut f = expiry_frame(0);
5587 f.flags = 0x01;
5588 tx.frames.insert(0, f);
5589 let err = tx.validate_static_constraints().unwrap_err();
5590 assert!(err.contains("flags == 0"), "{err}");
5591 }
5592
5593 #[test]
5594 fn at_most_one_expiry_verifier_frame() {
5595 let mut tx = make_test_frame_tx();
5596 tx.frames.insert(0, expiry_frame(1));
5597 tx.frames.insert(0, expiry_frame(2));
5598 let err = tx.validate_static_constraints().unwrap_err();
5599 assert!(err.contains("more than one expiry"), "{err}");
5600 }
5601
5602 #[test]
5603 fn verify_frame_with_zero_scope_is_now_statically_valid() {
5604 let mut tx = make_test_frame_tx();
5609 tx.frames[0].flags = 0x00;
5610 assert!(tx.validate_static_constraints().is_ok());
5611 }
5612
5613 #[test]
5614 fn sig_hash_covers_expiry_deadline_and_all_verify_data() {
5615 let mut tx_a = make_test_frame_tx();
5617 tx_a.frames.insert(0, expiry_frame(100));
5618 let mut tx_b = make_test_frame_tx();
5619 tx_b.frames.insert(0, expiry_frame(200));
5620 assert_ne!(tx_a.compute_sig_hash(), tx_b.compute_sig_hash());
5622
5623 let tx_c = make_test_frame_tx();
5625 let mut tx_d = make_test_frame_tx();
5626 tx_d.frames[0].data = Bytes::from_static(b"different_verify_data");
5627 assert_ne!(tx_c.compute_sig_hash(), tx_d.compute_sig_hash());
5628 }
5629
5630 #[test]
5631 fn expiry_deadline_parses_or_none() {
5632 let mut tx = make_test_frame_tx();
5633 assert_eq!(tx.expiry_deadline(), None);
5634 tx.frames.insert(0, expiry_frame(1_700_000_123));
5635 assert_eq!(tx.expiry_deadline(), Some(1_700_000_123));
5636 }
5637
5638 #[test]
5639 fn signature_rlp_roundtrip() {
5640 let sig = FrameSignature {
5641 scheme: FRAME_SIG_SCHEME_P256,
5642 signer: Some(Address::from_low_u64_be(0x1234)),
5643 msg: Bytes::from(vec![7u8; 32]),
5644 signature: Bytes::from(vec![9u8; 128]),
5645 };
5646 let mut buf = Vec::new();
5647 sig.encode(&mut buf);
5648 let (decoded, rest) = FrameSignature::decode_unfinished(&buf).unwrap();
5649 assert!(rest.is_empty());
5650 assert_eq!(decoded, sig);
5651 }
5652
5653 #[test]
5654 fn frame_tx_with_signatures_rlp_roundtrip() {
5655 let tx = make_test_frame_tx();
5656 assert!(!tx.signatures.is_empty());
5657 let mut buf = Vec::new();
5658 tx.encode(&mut buf);
5659 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5660 assert!(rest.is_empty());
5661 assert_eq!(decoded.signatures, tx.signatures);
5662 assert_eq!(decoded.frames, tx.frames);
5663 assert_eq!(decoded.nonce, tx.nonce);
5664 }
5665
5666 #[test]
5667 fn p2p_frame_transaction_rlp_roundtrip() {
5668 let ft = make_test_frame_tx();
5671 assert!(!ft.signatures.is_empty());
5672 assert!(!ft.frames.is_empty());
5673 let original = P2PTransaction::FrameTransaction(ft);
5674
5675 let encoded = original.encode_to_vec();
5676 let (decoded, rest) = P2PTransaction::decode_unfinished(&encoded).unwrap();
5677 assert!(rest.is_empty());
5678 assert_eq!(decoded, original);
5679 assert_eq!(decoded.tx_type(), TxType::Frame);
5680
5681 let as_tx: Transaction = decoded.try_into().unwrap();
5684 assert!(matches!(as_tx, Transaction::FrameTransaction(_)));
5685 }
5686
5687 #[test]
5688 fn static_validation_rejects_unknown_scheme() {
5689 let mut tx = make_test_frame_tx();
5690 tx.signatures[0].scheme = 3;
5691 assert!(
5692 tx.validate_static_constraints()
5693 .unwrap_err()
5694 .contains("unsupported scheme"),
5695 );
5696 }
5697
5698 #[test]
5699 fn static_validation_accepts_arbitrary_with_empty_signer() {
5700 let mut tx = make_test_frame_tx();
5701 tx.signatures[0].scheme = FRAME_SIG_SCHEME_ARBITRARY;
5703 tx.signatures[0].signer = None;
5704 assert!(tx.validate_static_constraints().is_ok());
5705 assert_eq!(tx.signature_verification_cost(), 100);
5706 }
5707
5708 #[test]
5709 fn static_validation_rejects_arbitrary_with_signer() {
5710 let mut tx = make_test_frame_tx();
5711 tx.signatures[0].scheme = FRAME_SIG_SCHEME_ARBITRARY;
5712 tx.signatures[0].signer = Some(Address::from_low_u64_be(0xABCD));
5713 assert!(
5714 tx.validate_static_constraints()
5715 .unwrap_err()
5716 .contains("ARBITRARY signatures must not name a signer"),
5717 );
5718 }
5719
5720 #[test]
5721 fn static_validation_rejects_bad_msg_length() {
5722 let mut tx = make_test_frame_tx();
5723 tx.signatures[0].msg = Bytes::from(vec![1u8; 16]);
5724 assert!(
5725 tx.validate_static_constraints()
5726 .unwrap_err()
5727 .contains("32 bytes"),
5728 );
5729 }
5730
5731 #[test]
5732 fn static_validation_rejects_zero_explicit_msg() {
5733 let mut tx = make_test_frame_tx();
5734 tx.signatures[0].msg = Bytes::from(vec![0u8; 32]);
5735 assert!(
5736 tx.validate_static_constraints()
5737 .unwrap_err()
5738 .contains("zero digest"),
5739 );
5740 }
5741
5742 #[test]
5743 fn sig_hash_elides_empty_msg_signature_bytes() {
5744 let mut a = make_test_frame_tx();
5745 let mut b = make_test_frame_tx();
5746 assert!(a.signatures[0].msg.is_empty());
5748 a.signatures[0].signature = Bytes::from(vec![1u8; 65]);
5749 b.signatures[0].signature = Bytes::from(vec![2u8; 65]);
5750 assert_eq!(a.compute_sig_hash(), b.compute_sig_hash());
5751 }
5752
5753 #[test]
5754 fn sig_hash_covers_frame_data_now() {
5755 let mut a = make_test_frame_tx();
5756 let mut b = make_test_frame_tx();
5757 a.frames[0].data = Bytes::from_static(b"aaaa");
5759 b.frames[0].data = Bytes::from_static(b"bbbb");
5760 assert_ne!(a.compute_sig_hash(), b.compute_sig_hash());
5761 }
5762
5763 #[test]
5764 fn sig_hash_covers_explicit_msg_signature_bytes() {
5765 let mut a = make_test_frame_tx();
5766 let mut b = make_test_frame_tx();
5767 a.signatures[0].msg = Bytes::from(vec![9u8; 32]);
5768 b.signatures[0].msg = Bytes::from(vec![9u8; 32]);
5769 a.signatures[0].signature = Bytes::from(vec![1u8; 65]);
5770 b.signatures[0].signature = Bytes::from(vec![2u8; 65]);
5771 assert_ne!(a.compute_sig_hash(), b.compute_sig_hash());
5773 }
5774
5775 #[test]
5776 fn max_gas_includes_signature_costs() {
5777 let mut tx = make_test_frame_tx();
5778 let base = tx.max_gas();
5779 tx.signatures.push(FrameSignature {
5781 scheme: FRAME_SIG_SCHEME_P256,
5782 signer: Some(Address::from_low_u64_be(1)),
5783 msg: Bytes::new(),
5784 signature: Bytes::from(vec![0u8; 128]),
5785 });
5786 assert!(tx.max_gas() >= base + 6700);
5787 assert_eq!(tx.signature_verification_cost(), 2800 + 6700);
5788 }
5789
5790 #[test]
5791 fn golden_frame_tx_rlp_and_sig_hash() {
5792 let tx = FrameTransaction {
5797 chain_id: 1,
5798 nonce: 7,
5799 sender: Address::from_low_u64_be(0xABCD),
5800 frames: vec![
5801 Frame {
5802 mode: 1,
5803 flags: 3,
5804 target: None,
5805 gas_limit: 0x5208,
5806 value: U256::zero(),
5807 data: Bytes::from_static(&[0x11, 0x22]),
5808 },
5809 Frame {
5810 mode: 2,
5811 flags: 0,
5812 target: Some(Address::from_low_u64_be(0x1234)),
5813 gas_limit: 0x9c40,
5814 value: U256::zero(),
5815 data: Bytes::new(),
5816 },
5817 ],
5818 signatures: vec![FrameSignature {
5819 scheme: FRAME_SIG_SCHEME_SECP256K1,
5820 signer: Some(Address::from_low_u64_be(0xABCD)),
5821 msg: Bytes::new(),
5822 signature: Bytes::from(vec![0x01u8; 65]),
5823 }],
5824 max_priority_fee_per_gas: 0x3b9aca00,
5825 max_fee_per_gas: 0x6fc23ac00,
5826 max_fee_per_blob_gas: U256::zero(),
5827 blob_versioned_hashes: vec![],
5828 inner_hash: OnceCell::new(),
5829 cached_canonical: OnceCell::new(),
5830 };
5831
5832 let mut buf = Vec::new();
5833 tx.encode(&mut buf);
5834 let rlp_hex = hex::encode(&buf);
5835 assert_eq!(
5837 rlp_hex,
5838 "f8ab010794000000000000000000000000000000000000abcde8ca01038082520880821122dc0280940000000000000000000000000000000000001234829c408080f85cf85a0194000000000000000000000000000000000000abcd80b8410101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101843b9aca008506fc23ac0080c0"
5839 );
5840
5841 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5843 assert!(rest.is_empty());
5844 assert_eq!(decoded, tx);
5845
5846 let sig_hash = tx.compute_sig_hash();
5847 assert_eq!(
5849 format!("{:#x}", sig_hash),
5850 "0xe7dc3f33413fc69c09f9c690be154ded294954e497aeea6ce0010ba513f2f26d",
5851 );
5852
5853 let mut tx2 = tx.clone();
5855 tx2.signatures[0].signature = Bytes::from(vec![0x02u8; 65]);
5856 assert_eq!(
5857 tx.compute_sig_hash(),
5858 tx2.compute_sig_hash(),
5859 "sig_hash must be independent of empty-msg signature bytes",
5860 );
5861 }
5862
5863 #[test]
5864 fn test_cost_without_base_fee_eip4844_includes_blob_gas() {
5865 use crate::constants::GAS_PER_BLOB;
5869
5870 let max_fee_per_gas: u64 = 100;
5871 let gas: u64 = 21_000;
5872 let value = U256::from(7u64);
5873 let max_fee_per_blob_gas = U256::from(50u64);
5874 let blob_count: usize = 1;
5875
5876 let tx = Transaction::EIP4844Transaction(EIP4844Transaction {
5877 max_fee_per_gas,
5878 gas,
5879 value,
5880 max_fee_per_blob_gas,
5881 blob_versioned_hashes: vec![H256::zero(); blob_count],
5882 ..Default::default()
5883 });
5884
5885 let got = tx.cost_without_base_fee().expect("cost is computable");
5886
5887 let gas_cost = U256::from(max_fee_per_gas) * U256::from(gas);
5888 let blob_gas = U256::from(GAS_PER_BLOB) * U256::from(blob_count as u64);
5889 let blob_cost = blob_gas * max_fee_per_blob_gas;
5890 let expected = gas_cost + blob_cost + value;
5891
5892 assert_eq!(
5893 got, expected,
5894 "blob-gas term missing from cost_without_base_fee() for EIP-4844"
5895 );
5896 }
5897}