referrerpolicy=no-referrer-when-downgrade

pallet_revive/evm/api/
transaction.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10//  http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18#![allow(missing_docs)]
19
20use super::{
21	Byte, Bytes, TYPE_EIP1559, TYPE_EIP2930, TYPE_EIP4844, TYPE_EIP7702, TYPE_LEGACY, TypeEip1559,
22	TypeEip2930, TypeEip4844, TypeEip7702, TypeLegacy,
23};
24use alloc::vec::Vec;
25use codec::{Decode, DecodeWithMemTracking, Encode};
26use derive_more::{From, TryInto};
27use ethereum_types::*;
28use pallet_revive_types::runtime_api::*;
29use scale_info::TypeInfo;
30
31/// Transaction object generic to all types
32#[derive(Debug, Default, Clone, Eq, PartialEq)]
33pub struct GenericTransaction {
34	/// accessList
35	/// EIP-2930 access list
36	pub access_list: Option<AccessList>,
37	/// authorizationList
38	/// List of account code authorizations (EIP-7702)
39	pub authorization_list: Vec<AuthorizationListEntry>,
40	/// blobVersionedHashes
41	/// List of versioned blob hashes associated with the transaction's EIP-4844 data blobs.
42	pub blob_versioned_hashes: Vec<H256>,
43	/// blobs
44	/// Raw blob data.
45	pub blobs: Vec<Bytes>,
46	/// chainId
47	/// Chain ID that this transaction is valid on.
48	pub chain_id: Option<U256>,
49	/// from address
50	pub from: Option<Address>,
51	/// gas limit
52	pub gas: Option<U256>,
53	/// gas price
54	/// The gas price willing to be paid by the sender in wei
55	pub gas_price: Option<U256>,
56	/// input data
57	pub input: InputOrData,
58	/// max fee per blob gas
59	/// The maximum total fee per gas the sender is willing to pay for blob gas in wei
60	pub max_fee_per_blob_gas: Option<U256>,
61	/// max fee per gas
62	/// The maximum total fee per gas the sender is willing to pay (includes the network / base fee
63	/// and miner / priority fee) in wei
64	pub max_fee_per_gas: Option<U256>,
65	/// max priority fee per gas
66	/// Maximum fee per gas the sender is willing to pay to miners in wei
67	pub max_priority_fee_per_gas: Option<U256>,
68	/// nonce
69	pub nonce: Option<U256>,
70	/// to address
71	pub to: Option<Address>,
72	/// type
73	pub r#type: Option<Byte>,
74	/// value
75	pub value: Option<U256>,
76}
77
78impl GenericTransaction {
79	/// Create a new [`GenericTransaction`] from a signed transaction.
80	pub fn from_signed(tx: TransactionSigned, base_gas_price: U256, from: Option<H160>) -> Self {
81		Self::from_unsigned(tx.into(), base_gas_price, from)
82	}
83
84	/// Returns `true` when the transaction's payload fields look like those of a simple value
85	/// transfer: empty calldata, no access list, no EIP-7702 authorization list, no EIP-4844 blob
86	/// payload, and no blob gas fee. The destination address is validated separately by the caller.
87	pub fn has_simple_transfer_fields(&self) -> bool {
88		self.input.is_empty() &&
89			self.access_list.as_ref().is_none_or(|list| list.is_empty()) &&
90			self.authorization_list.is_empty() &&
91			self.blob_versioned_hashes.is_empty() &&
92			self.blobs.is_empty() &&
93			self.max_fee_per_blob_gas.is_none()
94	}
95
96	/// The gas price that is actually paid (including priority fee).
97	pub fn effective_gas_price(&self, base_gas_price: U256) -> Option<U256> {
98		let effective_gas_price = if let Some(prio_price) = self.max_priority_fee_per_gas {
99			let max_price = self.max_fee_per_gas?;
100			Some(max_price.min(base_gas_price.saturating_add(prio_price)))
101		} else {
102			self.gas_price
103		};
104
105		// we do not implement priority fee as it does not map to tip well
106		// hence the effective gas price cannot be higher than the base price
107		effective_gas_price.map(|e| e.min(base_gas_price))
108	}
109
110	/// Create a new [`GenericTransaction`] from a unsigned transaction.
111	pub fn from_unsigned(
112		tx: TransactionUnsigned,
113		base_gas_price: U256,
114		from: Option<H160>,
115	) -> Self {
116		use TransactionUnsigned::*;
117		let mut tx = match tx {
118			TransactionLegacyUnsigned(tx) => GenericTransaction {
119				from,
120				r#type: Some(tx.r#type.as_byte()),
121				chain_id: tx.chain_id,
122				input: tx.input.into(),
123				nonce: Some(tx.nonce),
124				value: Some(tx.value),
125				to: tx.to,
126				gas: Some(tx.gas),
127				gas_price: Some(tx.gas_price),
128				..Default::default()
129			},
130			Transaction4844Unsigned(tx) => GenericTransaction {
131				from,
132				r#type: Some(tx.r#type.as_byte()),
133				chain_id: Some(tx.chain_id),
134				input: tx.input.into(),
135				nonce: Some(tx.nonce),
136				value: Some(tx.value),
137				to: Some(tx.to),
138				gas: Some(tx.gas),
139				access_list: Some(tx.access_list),
140				blob_versioned_hashes: tx.blob_versioned_hashes,
141				max_fee_per_blob_gas: Some(tx.max_fee_per_blob_gas),
142				max_fee_per_gas: Some(tx.max_fee_per_gas),
143				max_priority_fee_per_gas: Some(tx.max_priority_fee_per_gas),
144				..Default::default()
145			},
146			Transaction1559Unsigned(tx) => GenericTransaction {
147				from,
148				r#type: Some(tx.r#type.as_byte()),
149				chain_id: Some(tx.chain_id),
150				input: tx.input.into(),
151				nonce: Some(tx.nonce),
152				value: Some(tx.value),
153				to: tx.to,
154				gas: Some(tx.gas),
155				access_list: Some(tx.access_list),
156				max_fee_per_gas: Some(tx.max_fee_per_gas),
157				max_priority_fee_per_gas: Some(tx.max_priority_fee_per_gas),
158				..Default::default()
159			},
160			Transaction2930Unsigned(tx) => GenericTransaction {
161				from,
162				r#type: Some(tx.r#type.as_byte()),
163				chain_id: Some(tx.chain_id),
164				input: tx.input.into(),
165				nonce: Some(tx.nonce),
166				value: Some(tx.value),
167				to: tx.to,
168				gas: Some(tx.gas),
169				gas_price: Some(tx.gas_price),
170				access_list: Some(tx.access_list),
171				..Default::default()
172			},
173			Transaction7702Unsigned(tx) => GenericTransaction {
174				from,
175				r#type: Some(tx.r#type.as_byte()),
176				chain_id: Some(tx.chain_id),
177				input: tx.input.into(),
178				nonce: Some(tx.nonce),
179				value: Some(tx.value),
180				to: Some(tx.to),
181				gas: Some(tx.gas),
182				access_list: Some(tx.access_list),
183				authorization_list: tx.authorization_list,
184				max_fee_per_gas: Some(tx.max_fee_per_gas),
185				max_priority_fee_per_gas: Some(tx.max_priority_fee_per_gas),
186				..Default::default()
187			},
188		};
189		tx.gas_price = tx.effective_gas_price(base_gas_price);
190		tx
191	}
192
193	/// Convert to a [`TransactionUnsigned`].
194	pub fn try_into_unsigned(self) -> Result<TransactionUnsigned, ()> {
195		match self.r#type.unwrap_or_default().0 {
196			TYPE_LEGACY => Ok(TransactionLegacyUnsigned {
197				r#type: TypeLegacy {},
198				chain_id: self.chain_id,
199				input: self.input.to_bytes(),
200				nonce: self.nonce.unwrap_or_default(),
201				value: self.value.unwrap_or_default(),
202				to: self.to,
203				gas: self.gas.unwrap_or_default(),
204				gas_price: self.gas_price.unwrap_or_default(),
205			}
206			.into()),
207			TYPE_EIP1559 => Ok(Transaction1559Unsigned {
208				r#type: TypeEip1559 {},
209				chain_id: self.chain_id.unwrap_or_default(),
210				input: self.input.to_bytes(),
211				nonce: self.nonce.unwrap_or_default(),
212				value: self.value.unwrap_or_default(),
213				to: self.to,
214				gas: self.gas.unwrap_or_default(),
215				gas_price: self.max_fee_per_gas.unwrap_or_default(),
216				access_list: self.access_list.unwrap_or_default(),
217				max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(),
218				max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(),
219			}
220			.into()),
221			TYPE_EIP2930 => Ok(Transaction2930Unsigned {
222				r#type: TypeEip2930 {},
223				chain_id: self.chain_id.unwrap_or_default(),
224				input: self.input.to_bytes(),
225				nonce: self.nonce.unwrap_or_default(),
226				value: self.value.unwrap_or_default(),
227				to: self.to,
228				gas: self.gas.unwrap_or_default(),
229				gas_price: self.gas_price.unwrap_or_default(),
230				access_list: self.access_list.unwrap_or_default(),
231			}
232			.into()),
233			TYPE_EIP4844 => Ok(Transaction4844Unsigned {
234				r#type: TypeEip4844 {},
235				chain_id: self.chain_id.unwrap_or_default(),
236				input: self.input.to_bytes(),
237				nonce: self.nonce.unwrap_or_default(),
238				value: self.value.unwrap_or_default(),
239				to: self.to.unwrap_or_default(),
240				gas: self.gas.unwrap_or_default(),
241				max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(),
242				max_fee_per_blob_gas: self.max_fee_per_blob_gas.unwrap_or_default(),
243				max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(),
244				access_list: self.access_list.unwrap_or_default(),
245				blob_versioned_hashes: self.blob_versioned_hashes,
246			}
247			.into()),
248			TYPE_EIP7702 => Ok(Transaction7702Unsigned {
249				r#type: TypeEip7702 {},
250				chain_id: self.chain_id.unwrap_or_default(),
251				input: self.input.to_bytes(),
252				nonce: self.nonce.unwrap_or_default(),
253				value: self.value.unwrap_or_default(),
254				to: self.to.unwrap_or_default(),
255				gas: self.gas.unwrap_or_default(),
256				max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(),
257				max_priority_fee_per_gas: self.max_priority_fee_per_gas.unwrap_or_default(),
258				access_list: self.access_list.unwrap_or_default(),
259				authorization_list: self.authorization_list,
260			}
261			.into()),
262			_ => Err(()),
263		}
264	}
265}
266
267impl From<GenericTransactionV1> for GenericTransaction {
268	fn from(value: GenericTransactionV1) -> Self {
269		Self {
270			access_list: value.access_list.map(|list| list.into_iter().map(Into::into).collect()),
271			authorization_list: value.authorization_list.into_iter().map(Into::into).collect(),
272			blob_versioned_hashes: value.blob_versioned_hashes,
273			blobs: value.blobs,
274			chain_id: value.chain_id,
275			from: value.from,
276			gas: value.gas,
277			gas_price: value.gas_price,
278			input: value.input.into(),
279			max_fee_per_blob_gas: value.max_fee_per_blob_gas,
280			max_fee_per_gas: value.max_fee_per_gas,
281			max_priority_fee_per_gas: value.max_priority_fee_per_gas,
282			nonce: value.nonce,
283			to: value.to,
284			r#type: value.r#type,
285			value: value.value,
286		}
287	}
288}
289
290impl From<GenericTransaction> for GenericTransactionV1 {
291	fn from(value: GenericTransaction) -> Self {
292		Self {
293			access_list: value.access_list.map(|list| list.into_iter().map(Into::into).collect()),
294			authorization_list: value.authorization_list.into_iter().map(Into::into).collect(),
295			blob_versioned_hashes: value.blob_versioned_hashes,
296			blobs: value.blobs,
297			chain_id: value.chain_id,
298			from: value.from,
299			gas: value.gas,
300			gas_price: value.gas_price,
301			input: value.input.into(),
302			max_fee_per_blob_gas: value.max_fee_per_blob_gas,
303			max_fee_per_gas: value.max_fee_per_gas,
304			max_priority_fee_per_gas: value.max_priority_fee_per_gas,
305			nonce: value.nonce,
306			to: value.to,
307			r#type: value.r#type,
308			value: value.value,
309		}
310	}
311}
312
313/// Transaction information
314#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode)]
315pub struct TransactionInfo {
316	/// block hash
317	pub block_hash: H256,
318	/// block number
319	pub block_number: U256,
320	/// from address
321	pub from: Address,
322	/// transaction hash
323	pub hash: H256,
324	/// transaction index
325	pub transaction_index: U256,
326	pub transaction_signed: TransactionSigned,
327}
328
329impl From<TransactionInfo> for TransactionInfoV1 {
330	fn from(value: TransactionInfo) -> Self {
331		Self {
332			block_hash: value.block_hash,
333			block_number: value.block_number,
334			from: value.from,
335			hash: value.hash,
336			transaction_index: value.transaction_index,
337			transaction_signed: value.transaction_signed.into(),
338		}
339	}
340}
341
342#[derive(
343	Debug, Clone, From, TryInto, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking,
344)]
345pub enum TransactionSigned {
346	Transaction7702Signed(Transaction7702Signed),
347	Transaction4844Signed(Transaction4844Signed),
348	Transaction1559Signed(Transaction1559Signed),
349	Transaction2930Signed(Transaction2930Signed),
350	TransactionLegacySigned(TransactionLegacySigned),
351}
352
353impl Default for TransactionSigned {
354	fn default() -> Self {
355		TransactionSigned::TransactionLegacySigned(Default::default())
356	}
357}
358
359impl From<TransactionSigned> for TransactionSignedV1 {
360	fn from(value: TransactionSigned) -> Self {
361		match value {
362			TransactionSigned::Transaction7702Signed(tx) => Self::Transaction7702Signed(tx.into()),
363			TransactionSigned::Transaction4844Signed(tx) => Self::Transaction4844Signed(tx.into()),
364			TransactionSigned::Transaction1559Signed(tx) => Self::Transaction1559Signed(tx.into()),
365			TransactionSigned::Transaction2930Signed(tx) => Self::Transaction2930Signed(tx.into()),
366			TransactionSigned::TransactionLegacySigned(tx) => {
367				Self::TransactionLegacySigned(tx.into())
368			},
369		}
370	}
371}
372
373#[derive(Debug, Clone, From, TryInto, Eq, PartialEq)]
374pub enum TransactionUnsigned {
375	Transaction7702Unsigned(Transaction7702Unsigned),
376	Transaction4844Unsigned(Transaction4844Unsigned),
377	Transaction1559Unsigned(Transaction1559Unsigned),
378	Transaction2930Unsigned(Transaction2930Unsigned),
379	TransactionLegacyUnsigned(TransactionLegacyUnsigned),
380}
381
382impl Default for TransactionUnsigned {
383	fn default() -> Self {
384		TransactionUnsigned::TransactionLegacyUnsigned(Default::default())
385	}
386}
387
388/// Build the deterministic payload of the synthetic transaction that carries a block's
389/// "outside-of-frame" logs: logs emitted with no enclosing ethereum transaction (e.g. pallet-assets
390/// balance-change mirrors on non-`eth_transact` paths).
391///
392/// Shared by the runtime — which emits this transaction into the block so its logs enter the
393/// `logs_bloom`, `receipts_root` and transaction trie — and by the eth-rpc layer, which rebuilds
394/// the identical bytes to recover its hash. It is an unsigned legacy transaction distinguished only
395/// by `nonce = block_number` (so the hash is unique per block) and is not an executable
396/// transaction.
397pub fn synthetic_log_transaction(block_number: U256, chain_id: U256) -> Vec<u8> {
398	TransactionUnsigned::from(TransactionLegacyUnsigned {
399		nonce: block_number,
400		chain_id: Some(chain_id),
401		..Default::default()
402	})
403	.dummy_signed_payload()
404}
405
406impl From<TransactionSigned> for TransactionUnsigned {
407	fn from(tx: TransactionSigned) -> Self {
408		use TransactionSigned::*;
409		match tx {
410			Transaction7702Signed(tx) => tx.transaction_7702_unsigned.into(),
411			Transaction4844Signed(tx) => tx.transaction_4844_unsigned.into(),
412			Transaction1559Signed(tx) => tx.transaction_1559_unsigned.into(),
413			Transaction2930Signed(tx) => tx.transaction_2930_unsigned.into(),
414			TransactionLegacySigned(tx) => tx.transaction_legacy_unsigned.into(),
415		}
416	}
417}
418
419/// EIP-1559 transaction.
420#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
421pub struct Transaction1559Unsigned {
422	/// accessList
423	/// EIP-2930 access list
424	pub access_list: AccessList,
425	/// chainId
426	/// Chain ID that this transaction is valid on.
427	pub chain_id: U256,
428	/// gas limit
429	pub gas: U256,
430	/// gas price
431	/// The effective gas price paid by the sender in wei. For transactions not yet included in a
432	/// block, this value should be set equal to the max fee per gas. This field is DEPRECATED,
433	/// please transition to using effectiveGasPrice in the receipt object going forward.
434	pub gas_price: U256,
435	/// input data
436	pub input: Bytes,
437	/// max fee per gas
438	/// The maximum total fee per gas the sender is willing to pay (includes the network / base fee
439	/// and miner / priority fee) in wei
440	pub max_fee_per_gas: U256,
441	/// max priority fee per gas
442	/// Maximum fee per gas the sender is willing to pay to miners in wei
443	pub max_priority_fee_per_gas: U256,
444	/// nonce
445	pub nonce: U256,
446	/// to address
447	pub to: Option<Address>,
448	/// type
449	pub r#type: TypeEip1559,
450	/// value
451	pub value: U256,
452}
453
454impl From<Transaction1559Unsigned> for Transaction1559UnsignedV1 {
455	fn from(value: Transaction1559Unsigned) -> Self {
456		Self {
457			access_list: value.access_list.into_iter().map(Into::into).collect(),
458			chain_id: value.chain_id,
459			gas: value.gas,
460			gas_price: value.gas_price,
461			input: value.input,
462			max_fee_per_gas: value.max_fee_per_gas,
463			max_priority_fee_per_gas: value.max_priority_fee_per_gas,
464			nonce: value.nonce,
465			to: value.to,
466			r#type: value.r#type,
467			value: value.value,
468		}
469	}
470}
471
472/// EIP-2930 transaction.
473#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
474pub struct Transaction2930Unsigned {
475	/// accessList
476	/// EIP-2930 access list
477	pub access_list: AccessList,
478	/// chainId
479	/// Chain ID that this transaction is valid on.
480	pub chain_id: U256,
481	/// gas limit
482	pub gas: U256,
483	/// gas price
484	/// The gas price willing to be paid by the sender in wei
485	pub gas_price: U256,
486	/// input data
487	pub input: Bytes,
488	/// nonce
489	pub nonce: U256,
490	/// to address
491	pub to: Option<Address>,
492	/// type
493	pub r#type: TypeEip2930,
494	/// value
495	pub value: U256,
496}
497
498impl From<Transaction2930Unsigned> for Transaction2930UnsignedV1 {
499	fn from(value: Transaction2930Unsigned) -> Self {
500		Self {
501			access_list: value.access_list.into_iter().map(Into::into).collect(),
502			chain_id: value.chain_id,
503			gas: value.gas,
504			gas_price: value.gas_price,
505			input: value.input,
506			nonce: value.nonce,
507			to: value.to,
508			r#type: value.r#type,
509			value: value.value,
510		}
511	}
512}
513
514/// EIP-4844 transaction.
515#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
516pub struct Transaction4844Unsigned {
517	/// accessList
518	/// EIP-2930 access list
519	pub access_list: AccessList,
520	/// blobVersionedHashes
521	/// List of versioned blob hashes associated with the transaction's EIP-4844 data blobs.
522	pub blob_versioned_hashes: Vec<H256>,
523	/// chainId
524	/// Chain ID that this transaction is valid on.
525	pub chain_id: U256,
526	/// gas limit
527	pub gas: U256,
528	/// input data
529	pub input: Bytes,
530	/// max fee per blob gas
531	/// The maximum total fee per gas the sender is willing to pay for blob gas in wei
532	pub max_fee_per_blob_gas: U256,
533	/// max fee per gas
534	/// The maximum total fee per gas the sender is willing to pay (includes the network / base fee
535	/// and miner / priority fee) in wei
536	pub max_fee_per_gas: U256,
537	/// max priority fee per gas
538	/// Maximum fee per gas the sender is willing to pay to miners in wei
539	pub max_priority_fee_per_gas: U256,
540	/// nonce
541	pub nonce: U256,
542	/// to address
543	pub to: Address,
544	/// type
545	pub r#type: TypeEip4844,
546	/// value
547	pub value: U256,
548}
549
550impl From<Transaction4844Unsigned> for Transaction4844UnsignedV1 {
551	fn from(value: Transaction4844Unsigned) -> Self {
552		Self {
553			access_list: value.access_list.into_iter().map(Into::into).collect(),
554			blob_versioned_hashes: value.blob_versioned_hashes,
555			chain_id: value.chain_id,
556			gas: value.gas,
557			input: value.input,
558			max_fee_per_blob_gas: value.max_fee_per_blob_gas,
559			max_fee_per_gas: value.max_fee_per_gas,
560			max_priority_fee_per_gas: value.max_priority_fee_per_gas,
561			nonce: value.nonce,
562			to: value.to,
563			r#type: value.r#type,
564			value: value.value,
565		}
566	}
567}
568
569/// Legacy transaction.
570#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
571pub struct TransactionLegacyUnsigned {
572	/// chainId
573	/// Chain ID that this transaction is valid on.
574	pub chain_id: Option<U256>,
575	/// gas limit
576	pub gas: U256,
577	/// gas price
578	/// The gas price willing to be paid by the sender in wei
579	pub gas_price: U256,
580	/// input data
581	pub input: Bytes,
582	/// nonce
583	pub nonce: U256,
584	/// to address
585	pub to: Option<Address>,
586	/// type
587	pub r#type: TypeLegacy,
588	/// value
589	pub value: U256,
590}
591
592impl From<TransactionLegacyUnsigned> for TransactionLegacyUnsignedV1 {
593	fn from(value: TransactionLegacyUnsigned) -> Self {
594		Self {
595			chain_id: value.chain_id,
596			gas: value.gas,
597			gas_price: value.gas_price,
598			input: value.input,
599			nonce: value.nonce,
600			to: value.to,
601			r#type: value.r#type,
602			value: value.value,
603		}
604	}
605}
606
607/// EIP-7702 transaction.
608#[derive(
609	Debug, Clone, Default, From, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking,
610)]
611pub struct Transaction7702Unsigned {
612	/// accessList
613	/// EIP-2930 access list
614	pub access_list: AccessList,
615	/// authorizationList
616	/// List of account code authorizations
617	pub authorization_list: Vec<AuthorizationListEntry>,
618	/// chainId
619	/// Chain ID that this transaction is valid on.
620	pub chain_id: U256,
621	/// gas limit
622	pub gas: U256,
623	/// input data
624	pub input: Bytes,
625	/// max fee per gas
626	/// The maximum total fee per gas the sender is willing to pay (includes the network / base fee
627	/// and miner / priority fee) in wei
628	pub max_fee_per_gas: U256,
629	/// max priority fee per gas
630	/// Maximum fee per gas the sender is willing to pay to miners in wei
631	pub max_priority_fee_per_gas: U256,
632	/// nonce
633	pub nonce: U256,
634	/// to address
635	///
636	/// # Note
637	///
638	/// Extracted from eip-7702: `Note, this implies a null destination is not valid.`
639	pub to: Address,
640	/// type
641	pub r#type: TypeEip7702,
642	/// value
643	pub value: U256,
644}
645
646impl From<Transaction7702Unsigned> for Transaction7702UnsignedV1 {
647	fn from(value: Transaction7702Unsigned) -> Self {
648		Self {
649			access_list: value.access_list.into_iter().map(Into::into).collect(),
650			authorization_list: value.authorization_list.into_iter().map(Into::into).collect(),
651			chain_id: value.chain_id,
652			gas: value.gas,
653			input: value.input,
654			max_fee_per_gas: value.max_fee_per_gas,
655			max_priority_fee_per_gas: value.max_priority_fee_per_gas,
656			nonce: value.nonce,
657			to: value.to,
658			r#type: value.r#type,
659			value: value.value,
660		}
661	}
662}
663
664/// Signed 7702 Transaction
665#[derive(Debug, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
666pub struct Transaction7702Signed {
667	pub transaction_7702_unsigned: Transaction7702Unsigned,
668	/// r
669	pub r: U256,
670	/// s
671	pub s: U256,
672	/// v
673	/// For backwards compatibility, `v` is optionally provided as an alternative to `yParity`.
674	/// This field is DEPRECATED and all use of it should migrate to `yParity`.
675	pub v: Option<U256>,
676	/// yParity
677	/// The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature.
678	pub y_parity: U256,
679}
680
681impl From<Transaction7702Signed> for Transaction7702SignedV1 {
682	fn from(value: Transaction7702Signed) -> Self {
683		Self {
684			transaction_7702_unsigned: value.transaction_7702_unsigned.into(),
685			r: value.r,
686			s: value.s,
687			v: value.v,
688			y_parity: value.y_parity,
689		}
690	}
691}
692
693/// Signed 1559 Transaction
694#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
695pub struct Transaction1559Signed {
696	pub transaction_1559_unsigned: Transaction1559Unsigned,
697	/// r
698	pub r: U256,
699	/// s
700	pub s: U256,
701	/// v
702	/// For backwards compatibility, `v` is optionally provided as an alternative to `yParity`.
703	/// This field is DEPRECATED and all use of it should migrate to `yParity`.
704	pub v: Option<U256>,
705	/// yParity
706	/// The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature.
707	pub y_parity: U256,
708}
709
710impl From<Transaction1559Signed> for Transaction1559SignedV1 {
711	fn from(value: Transaction1559Signed) -> Self {
712		Self {
713			transaction_1559_unsigned: value.transaction_1559_unsigned.into(),
714			r: value.r,
715			s: value.s,
716			v: value.v,
717			y_parity: value.y_parity,
718		}
719	}
720}
721
722/// Signed 2930 Transaction
723#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
724pub struct Transaction2930Signed {
725	pub transaction_2930_unsigned: Transaction2930Unsigned,
726	/// r
727	pub r: U256,
728	/// s
729	pub s: U256,
730	/// v
731	/// For backwards compatibility, `v` is optionally provided as an alternative to `yParity`.
732	/// This field is DEPRECATED and all use of it should migrate to `yParity`.
733	pub v: Option<U256>,
734	/// yParity
735	/// The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature.
736	pub y_parity: U256,
737}
738
739impl From<Transaction2930Signed> for Transaction2930SignedV1 {
740	fn from(value: Transaction2930Signed) -> Self {
741		Self {
742			transaction_2930_unsigned: value.transaction_2930_unsigned.into(),
743			r: value.r,
744			s: value.s,
745			v: value.v,
746			y_parity: value.y_parity,
747		}
748	}
749}
750
751/// Signed 4844 Transaction
752#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
753pub struct Transaction4844Signed {
754	pub transaction_4844_unsigned: Transaction4844Unsigned,
755	/// r
756	pub r: U256,
757	/// s
758	pub s: U256,
759	/// yParity
760	/// The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature.
761	pub y_parity: U256,
762}
763
764impl From<Transaction4844Signed> for Transaction4844SignedV1 {
765	fn from(value: Transaction4844Signed) -> Self {
766		Self {
767			transaction_4844_unsigned: value.transaction_4844_unsigned.into(),
768			r: value.r,
769			s: value.s,
770			y_parity: value.y_parity,
771		}
772	}
773}
774
775/// Signed Legacy Transaction
776#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
777pub struct TransactionLegacySigned {
778	pub transaction_legacy_unsigned: TransactionLegacyUnsigned,
779	/// r
780	pub r: U256,
781	/// s
782	pub s: U256,
783	/// v
784	pub v: U256,
785}
786
787impl From<TransactionLegacySigned> for TransactionLegacySignedV1 {
788	fn from(value: TransactionLegacySigned) -> Self {
789		Self {
790			transaction_legacy_unsigned: value.transaction_legacy_unsigned.into(),
791			r: value.r,
792			s: value.s,
793			v: value.v,
794		}
795	}
796}
797
798/// Access list
799pub type AccessList = Vec<AccessListEntry>;
800
801/// Access list entry
802#[derive(Debug, Default, Clone, Encode, Decode, TypeInfo, Eq, PartialEq, DecodeWithMemTracking)]
803pub struct AccessListEntry {
804	pub address: Address,
805	pub storage_keys: Vec<H256>,
806}
807
808impl From<AccessListEntryV1> for AccessListEntry {
809	fn from(value: AccessListEntryV1) -> Self {
810		Self { address: value.address, storage_keys: value.storage_keys }
811	}
812}
813
814impl From<AccessListEntry> for AccessListEntryV1 {
815	fn from(value: AccessListEntry) -> Self {
816		Self { address: value.address, storage_keys: value.storage_keys }
817	}
818}
819
820/// Authorization list entry for EIP-7702
821#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
822pub struct AuthorizationListEntry {
823	/// Chain ID that this authorization is valid on
824	pub chain_id: U256,
825	/// Address to authorize
826	pub address: Address,
827	/// Nonce of the authorization
828	pub nonce: U256,
829	/// y-parity of the signature
830	pub y_parity: U256,
831	/// r component of signature
832	pub r: U256,
833	/// s component of signature
834	pub s: U256,
835}
836
837impl AuthorizationListEntry {
838	/// Pack (r, s, y_parity) into a 65-byte ECDSA signature for `secp256k1_ecdsa_recover`.
839	pub fn signature(&self) -> [u8; 65] {
840		let mut signature = [0u8; 65];
841		signature[..32].copy_from_slice(&self.r.to_big_endian());
842		signature[32..64].copy_from_slice(&self.s.to_big_endian());
843		signature[64] = self.y_parity.low_u32() as u8;
844		signature
845	}
846}
847
848impl From<AuthorizationListEntryV1> for AuthorizationListEntry {
849	fn from(value: AuthorizationListEntryV1) -> Self {
850		Self {
851			chain_id: value.chain_id,
852			address: value.address,
853			nonce: value.nonce,
854			y_parity: value.y_parity,
855			r: value.r,
856			s: value.s,
857		}
858	}
859}
860
861impl From<AuthorizationListEntry> for AuthorizationListEntryV1 {
862	fn from(value: AuthorizationListEntry) -> Self {
863		Self {
864			chain_id: value.chain_id,
865			address: value.address,
866			nonce: value.nonce,
867			y_parity: value.y_parity,
868			r: value.r,
869			s: value.s,
870		}
871	}
872}
873
874#[derive(Debug, Clone, From, TryInto, Eq, PartialEq, TypeInfo, Encode, Decode)]
875pub enum HashesOrTransactionInfos {
876	/// Transaction hashes
877	Hashes(Vec<H256>),
878	/// Full transactions
879	TransactionInfos(Vec<TransactionInfo>),
880}
881
882impl Default for HashesOrTransactionInfos {
883	fn default() -> Self {
884		HashesOrTransactionInfos::Hashes(Default::default())
885	}
886}
887
888impl From<HashesOrTransactionInfos> for HashesOrTransactionInfosV1 {
889	fn from(value: HashesOrTransactionInfos) -> Self {
890		match value {
891			HashesOrTransactionInfos::Hashes(hashes) => Self::Hashes(hashes),
892			HashesOrTransactionInfos::TransactionInfos(infos) => {
893				Self::TransactionInfos(infos.into_iter().map(Into::into).collect())
894			},
895		}
896	}
897}
898
899impl HashesOrTransactionInfos {
900	pub fn push_hash(&mut self, hash: H256) {
901		match self {
902			HashesOrTransactionInfos::Hashes(hashes) => hashes.push(hash),
903			_ => {},
904		}
905	}
906
907	pub fn len(&self) -> usize {
908		match self {
909			HashesOrTransactionInfos::Hashes(v) => v.len(),
910			HashesOrTransactionInfos::TransactionInfos(v) => v.len(),
911		}
912	}
913
914	pub fn is_empty(&self) -> bool {
915		self.len() == 0
916	}
917
918	pub fn contains_tx(&self, hash: H256) -> bool {
919		match self {
920			HashesOrTransactionInfos::Hashes(hashes) => hashes.iter().any(|h256| *h256 == hash),
921			HashesOrTransactionInfos::TransactionInfos(transaction_infos) => {
922				transaction_infos.iter().any(|ti| ti.hash == hash)
923			},
924		}
925	}
926}
927
928/// Input of a `GenericTransaction`
929#[derive(Debug, Default, Clone, Eq, PartialEq)]
930pub struct InputOrData {
931	input: Option<Bytes>,
932	data: Option<Bytes>,
933}
934
935impl From<Bytes> for InputOrData {
936	fn from(value: Bytes) -> Self {
937		InputOrData { input: Some(value), data: None }
938	}
939}
940
941impl From<Vec<u8>> for InputOrData {
942	fn from(value: Vec<u8>) -> Self {
943		InputOrData { input: Some(Bytes(value)), data: None }
944	}
945}
946
947impl InputOrData {
948	/// Get the input as `Bytes`.
949	pub fn to_bytes(self) -> Bytes {
950		match self {
951			InputOrData { input: Some(input), data: _ } => input,
952			InputOrData { input: None, data: Some(data) } => data,
953			_ => Default::default(),
954		}
955	}
956
957	/// Get the input as `Vec<u8>`.
958	pub fn to_vec(self) -> Vec<u8> {
959		self.to_bytes().0
960	}
961
962	/// Returns the input as a byte slice, preferring `input` over `data`.
963	pub fn as_slice(&self) -> &[u8] {
964		self.input
965			.as_ref()
966			.or(self.data.as_ref())
967			.map(|bytes| bytes.0.as_slice())
968			.unwrap_or_default()
969	}
970
971	/// Returns true if the input carries no bytes.
972	pub fn is_empty(&self) -> bool {
973		self.as_slice().is_empty()
974	}
975}
976
977impl From<InputOrDataV1> for InputOrData {
978	fn from(value: InputOrDataV1) -> Self {
979		Self { input: value.input, data: value.data }
980	}
981}
982
983impl From<InputOrData> for InputOrDataV1 {
984	fn from(value: InputOrData) -> Self {
985		Self { input: value.input, data: value.data }
986	}
987}
988
989#[cfg(test)]
990mod tests {
991	use crate::evm::*;
992
993	#[test]
994	fn from_unsigned_works_for_legacy() {
995		let base_gas_price = U256::from(10);
996		let tx = TransactionUnsigned::from(TransactionLegacyUnsigned {
997			chain_id: Some(U256::from(1)),
998			input: Bytes::from(vec![1u8]),
999			nonce: U256::from(1),
1000			value: U256::from(1),
1001			to: Some(H160::zero()),
1002			gas: U256::from(1),
1003			gas_price: U256::from(10),
1004			..Default::default()
1005		});
1006
1007		let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
1008		assert_eq!(generic.gas_price, Some(U256::from(10)));
1009
1010		let tx2 = generic.try_into_unsigned().unwrap();
1011		assert_eq!(tx, tx2);
1012	}
1013
1014	#[test]
1015	fn from_unsigned_works_for_1559() {
1016		let base_gas_price = U256::from(10);
1017		let tx = TransactionUnsigned::from(Transaction1559Unsigned {
1018			chain_id: U256::from(1),
1019			input: Bytes::from(vec![1u8]),
1020			nonce: U256::from(1),
1021			value: U256::from(1),
1022			to: Some(H160::zero()),
1023			gas: U256::from(1),
1024			gas_price: U256::from(20),
1025			max_fee_per_gas: U256::from(20),
1026			max_priority_fee_per_gas: U256::from(1),
1027			..Default::default()
1028		});
1029
1030		let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
1031		assert_eq!(generic.gas_price, Some(U256::from(10)));
1032
1033		let tx2 = generic.try_into_unsigned().unwrap();
1034		assert_eq!(tx, tx2);
1035	}
1036
1037	#[test]
1038	fn from_unsigned_works_for_7702() {
1039		let base_gas_price = U256::from(10);
1040		let tx = TransactionUnsigned::from(Transaction7702Unsigned {
1041			chain_id: U256::from(1),
1042			input: Bytes::from(vec![1u8]),
1043			nonce: U256::from(1),
1044			value: U256::from(1),
1045			to: H160::zero(),
1046			gas: U256::from(1),
1047			max_fee_per_gas: U256::from(20),
1048			max_priority_fee_per_gas: U256::from(1),
1049			authorization_list: vec![AuthorizationListEntry {
1050				chain_id: U256::from(1),
1051				address: H160::from_low_u64_be(42),
1052				nonce: U256::from(0),
1053				y_parity: U256::from(1),
1054				r: U256::from(1),
1055				s: U256::from(2),
1056			}],
1057			..Default::default()
1058		});
1059
1060		let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
1061		assert_eq!(generic.gas_price, Some(U256::from(10)));
1062
1063		let tx2 = generic.try_into_unsigned().unwrap();
1064		assert_eq!(tx, tx2);
1065	}
1066}