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
388impl From<TransactionSigned> for TransactionUnsigned {
389	fn from(tx: TransactionSigned) -> Self {
390		use TransactionSigned::*;
391		match tx {
392			Transaction7702Signed(tx) => tx.transaction_7702_unsigned.into(),
393			Transaction4844Signed(tx) => tx.transaction_4844_unsigned.into(),
394			Transaction1559Signed(tx) => tx.transaction_1559_unsigned.into(),
395			Transaction2930Signed(tx) => tx.transaction_2930_unsigned.into(),
396			TransactionLegacySigned(tx) => tx.transaction_legacy_unsigned.into(),
397		}
398	}
399}
400
401/// EIP-1559 transaction.
402#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
403pub struct Transaction1559Unsigned {
404	/// accessList
405	/// EIP-2930 access list
406	pub access_list: AccessList,
407	/// chainId
408	/// Chain ID that this transaction is valid on.
409	pub chain_id: U256,
410	/// gas limit
411	pub gas: U256,
412	/// gas price
413	/// The effective gas price paid by the sender in wei. For transactions not yet included in a
414	/// block, this value should be set equal to the max fee per gas. This field is DEPRECATED,
415	/// please transition to using effectiveGasPrice in the receipt object going forward.
416	pub gas_price: U256,
417	/// input data
418	pub input: Bytes,
419	/// max fee per gas
420	/// The maximum total fee per gas the sender is willing to pay (includes the network / base fee
421	/// and miner / priority fee) in wei
422	pub max_fee_per_gas: U256,
423	/// max priority fee per gas
424	/// Maximum fee per gas the sender is willing to pay to miners in wei
425	pub max_priority_fee_per_gas: U256,
426	/// nonce
427	pub nonce: U256,
428	/// to address
429	pub to: Option<Address>,
430	/// type
431	pub r#type: TypeEip1559,
432	/// value
433	pub value: U256,
434}
435
436impl From<Transaction1559Unsigned> for Transaction1559UnsignedV1 {
437	fn from(value: Transaction1559Unsigned) -> Self {
438		Self {
439			access_list: value.access_list.into_iter().map(Into::into).collect(),
440			chain_id: value.chain_id,
441			gas: value.gas,
442			gas_price: value.gas_price,
443			input: value.input,
444			max_fee_per_gas: value.max_fee_per_gas,
445			max_priority_fee_per_gas: value.max_priority_fee_per_gas,
446			nonce: value.nonce,
447			to: value.to,
448			r#type: value.r#type,
449			value: value.value,
450		}
451	}
452}
453
454/// EIP-2930 transaction.
455#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
456pub struct Transaction2930Unsigned {
457	/// accessList
458	/// EIP-2930 access list
459	pub access_list: AccessList,
460	/// chainId
461	/// Chain ID that this transaction is valid on.
462	pub chain_id: U256,
463	/// gas limit
464	pub gas: U256,
465	/// gas price
466	/// The gas price willing to be paid by the sender in wei
467	pub gas_price: U256,
468	/// input data
469	pub input: Bytes,
470	/// nonce
471	pub nonce: U256,
472	/// to address
473	pub to: Option<Address>,
474	/// type
475	pub r#type: TypeEip2930,
476	/// value
477	pub value: U256,
478}
479
480impl From<Transaction2930Unsigned> for Transaction2930UnsignedV1 {
481	fn from(value: Transaction2930Unsigned) -> Self {
482		Self {
483			access_list: value.access_list.into_iter().map(Into::into).collect(),
484			chain_id: value.chain_id,
485			gas: value.gas,
486			gas_price: value.gas_price,
487			input: value.input,
488			nonce: value.nonce,
489			to: value.to,
490			r#type: value.r#type,
491			value: value.value,
492		}
493	}
494}
495
496/// EIP-4844 transaction.
497#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
498pub struct Transaction4844Unsigned {
499	/// accessList
500	/// EIP-2930 access list
501	pub access_list: AccessList,
502	/// blobVersionedHashes
503	/// List of versioned blob hashes associated with the transaction's EIP-4844 data blobs.
504	pub blob_versioned_hashes: Vec<H256>,
505	/// chainId
506	/// Chain ID that this transaction is valid on.
507	pub chain_id: U256,
508	/// gas limit
509	pub gas: U256,
510	/// input data
511	pub input: Bytes,
512	/// max fee per blob gas
513	/// The maximum total fee per gas the sender is willing to pay for blob gas in wei
514	pub max_fee_per_blob_gas: U256,
515	/// max fee per gas
516	/// The maximum total fee per gas the sender is willing to pay (includes the network / base fee
517	/// and miner / priority fee) in wei
518	pub max_fee_per_gas: U256,
519	/// max priority fee per gas
520	/// Maximum fee per gas the sender is willing to pay to miners in wei
521	pub max_priority_fee_per_gas: U256,
522	/// nonce
523	pub nonce: U256,
524	/// to address
525	pub to: Address,
526	/// type
527	pub r#type: TypeEip4844,
528	/// value
529	pub value: U256,
530}
531
532impl From<Transaction4844Unsigned> for Transaction4844UnsignedV1 {
533	fn from(value: Transaction4844Unsigned) -> Self {
534		Self {
535			access_list: value.access_list.into_iter().map(Into::into).collect(),
536			blob_versioned_hashes: value.blob_versioned_hashes,
537			chain_id: value.chain_id,
538			gas: value.gas,
539			input: value.input,
540			max_fee_per_blob_gas: value.max_fee_per_blob_gas,
541			max_fee_per_gas: value.max_fee_per_gas,
542			max_priority_fee_per_gas: value.max_priority_fee_per_gas,
543			nonce: value.nonce,
544			to: value.to,
545			r#type: value.r#type,
546			value: value.value,
547		}
548	}
549}
550
551/// Legacy transaction.
552#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
553pub struct TransactionLegacyUnsigned {
554	/// chainId
555	/// Chain ID that this transaction is valid on.
556	pub chain_id: Option<U256>,
557	/// gas limit
558	pub gas: U256,
559	/// gas price
560	/// The gas price willing to be paid by the sender in wei
561	pub gas_price: U256,
562	/// input data
563	pub input: Bytes,
564	/// nonce
565	pub nonce: U256,
566	/// to address
567	pub to: Option<Address>,
568	/// type
569	pub r#type: TypeLegacy,
570	/// value
571	pub value: U256,
572}
573
574impl From<TransactionLegacyUnsigned> for TransactionLegacyUnsignedV1 {
575	fn from(value: TransactionLegacyUnsigned) -> Self {
576		Self {
577			chain_id: value.chain_id,
578			gas: value.gas,
579			gas_price: value.gas_price,
580			input: value.input,
581			nonce: value.nonce,
582			to: value.to,
583			r#type: value.r#type,
584			value: value.value,
585		}
586	}
587}
588
589/// EIP-7702 transaction.
590#[derive(
591	Debug, Clone, Default, From, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking,
592)]
593pub struct Transaction7702Unsigned {
594	/// accessList
595	/// EIP-2930 access list
596	pub access_list: AccessList,
597	/// authorizationList
598	/// List of account code authorizations
599	pub authorization_list: Vec<AuthorizationListEntry>,
600	/// chainId
601	/// Chain ID that this transaction is valid on.
602	pub chain_id: U256,
603	/// gas limit
604	pub gas: U256,
605	/// input data
606	pub input: Bytes,
607	/// max fee per gas
608	/// The maximum total fee per gas the sender is willing to pay (includes the network / base fee
609	/// and miner / priority fee) in wei
610	pub max_fee_per_gas: U256,
611	/// max priority fee per gas
612	/// Maximum fee per gas the sender is willing to pay to miners in wei
613	pub max_priority_fee_per_gas: U256,
614	/// nonce
615	pub nonce: U256,
616	/// to address
617	///
618	/// # Note
619	///
620	/// Extracted from eip-7702: `Note, this implies a null destination is not valid.`
621	pub to: Address,
622	/// type
623	pub r#type: TypeEip7702,
624	/// value
625	pub value: U256,
626}
627
628impl From<Transaction7702Unsigned> for Transaction7702UnsignedV1 {
629	fn from(value: Transaction7702Unsigned) -> Self {
630		Self {
631			access_list: value.access_list.into_iter().map(Into::into).collect(),
632			authorization_list: value.authorization_list.into_iter().map(Into::into).collect(),
633			chain_id: value.chain_id,
634			gas: value.gas,
635			input: value.input,
636			max_fee_per_gas: value.max_fee_per_gas,
637			max_priority_fee_per_gas: value.max_priority_fee_per_gas,
638			nonce: value.nonce,
639			to: value.to,
640			r#type: value.r#type,
641			value: value.value,
642		}
643	}
644}
645
646/// Signed 7702 Transaction
647#[derive(Debug, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
648pub struct Transaction7702Signed {
649	pub transaction_7702_unsigned: Transaction7702Unsigned,
650	/// r
651	pub r: U256,
652	/// s
653	pub s: U256,
654	/// v
655	/// For backwards compatibility, `v` is optionally provided as an alternative to `yParity`.
656	/// This field is DEPRECATED and all use of it should migrate to `yParity`.
657	pub v: Option<U256>,
658	/// yParity
659	/// The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature.
660	pub y_parity: U256,
661}
662
663impl From<Transaction7702Signed> for Transaction7702SignedV1 {
664	fn from(value: Transaction7702Signed) -> Self {
665		Self {
666			transaction_7702_unsigned: value.transaction_7702_unsigned.into(),
667			r: value.r,
668			s: value.s,
669			v: value.v,
670			y_parity: value.y_parity,
671		}
672	}
673}
674
675/// Signed 1559 Transaction
676#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
677pub struct Transaction1559Signed {
678	pub transaction_1559_unsigned: Transaction1559Unsigned,
679	/// r
680	pub r: U256,
681	/// s
682	pub s: U256,
683	/// v
684	/// For backwards compatibility, `v` is optionally provided as an alternative to `yParity`.
685	/// This field is DEPRECATED and all use of it should migrate to `yParity`.
686	pub v: Option<U256>,
687	/// yParity
688	/// The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature.
689	pub y_parity: U256,
690}
691
692impl From<Transaction1559Signed> for Transaction1559SignedV1 {
693	fn from(value: Transaction1559Signed) -> Self {
694		Self {
695			transaction_1559_unsigned: value.transaction_1559_unsigned.into(),
696			r: value.r,
697			s: value.s,
698			v: value.v,
699			y_parity: value.y_parity,
700		}
701	}
702}
703
704/// Signed 2930 Transaction
705#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
706pub struct Transaction2930Signed {
707	pub transaction_2930_unsigned: Transaction2930Unsigned,
708	/// r
709	pub r: U256,
710	/// s
711	pub s: U256,
712	/// v
713	/// For backwards compatibility, `v` is optionally provided as an alternative to `yParity`.
714	/// This field is DEPRECATED and all use of it should migrate to `yParity`.
715	pub v: Option<U256>,
716	/// yParity
717	/// The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature.
718	pub y_parity: U256,
719}
720
721impl From<Transaction2930Signed> for Transaction2930SignedV1 {
722	fn from(value: Transaction2930Signed) -> Self {
723		Self {
724			transaction_2930_unsigned: value.transaction_2930_unsigned.into(),
725			r: value.r,
726			s: value.s,
727			v: value.v,
728			y_parity: value.y_parity,
729		}
730	}
731}
732
733/// Signed 4844 Transaction
734#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
735pub struct Transaction4844Signed {
736	pub transaction_4844_unsigned: Transaction4844Unsigned,
737	/// r
738	pub r: U256,
739	/// s
740	pub s: U256,
741	/// yParity
742	/// The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature.
743	pub y_parity: U256,
744}
745
746impl From<Transaction4844Signed> for Transaction4844SignedV1 {
747	fn from(value: Transaction4844Signed) -> Self {
748		Self {
749			transaction_4844_unsigned: value.transaction_4844_unsigned.into(),
750			r: value.r,
751			s: value.s,
752			y_parity: value.y_parity,
753		}
754	}
755}
756
757/// Signed Legacy Transaction
758#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
759pub struct TransactionLegacySigned {
760	pub transaction_legacy_unsigned: TransactionLegacyUnsigned,
761	/// r
762	pub r: U256,
763	/// s
764	pub s: U256,
765	/// v
766	pub v: U256,
767}
768
769impl From<TransactionLegacySigned> for TransactionLegacySignedV1 {
770	fn from(value: TransactionLegacySigned) -> Self {
771		Self {
772			transaction_legacy_unsigned: value.transaction_legacy_unsigned.into(),
773			r: value.r,
774			s: value.s,
775			v: value.v,
776		}
777	}
778}
779
780/// Access list
781pub type AccessList = Vec<AccessListEntry>;
782
783/// Access list entry
784#[derive(Debug, Default, Clone, Encode, Decode, TypeInfo, Eq, PartialEq, DecodeWithMemTracking)]
785pub struct AccessListEntry {
786	pub address: Address,
787	pub storage_keys: Vec<H256>,
788}
789
790impl From<AccessListEntryV1> for AccessListEntry {
791	fn from(value: AccessListEntryV1) -> Self {
792		Self { address: value.address, storage_keys: value.storage_keys }
793	}
794}
795
796impl From<AccessListEntry> for AccessListEntryV1 {
797	fn from(value: AccessListEntry) -> Self {
798		Self { address: value.address, storage_keys: value.storage_keys }
799	}
800}
801
802/// Authorization list entry for EIP-7702
803#[derive(Debug, Default, Clone, Eq, PartialEq, TypeInfo, Encode, Decode, DecodeWithMemTracking)]
804pub struct AuthorizationListEntry {
805	/// Chain ID that this authorization is valid on
806	pub chain_id: U256,
807	/// Address to authorize
808	pub address: Address,
809	/// Nonce of the authorization
810	pub nonce: U256,
811	/// y-parity of the signature
812	pub y_parity: U256,
813	/// r component of signature
814	pub r: U256,
815	/// s component of signature
816	pub s: U256,
817}
818
819impl From<AuthorizationListEntryV1> for AuthorizationListEntry {
820	fn from(value: AuthorizationListEntryV1) -> Self {
821		Self {
822			chain_id: value.chain_id,
823			address: value.address,
824			nonce: value.nonce,
825			y_parity: value.y_parity,
826			r: value.r,
827			s: value.s,
828		}
829	}
830}
831
832impl From<AuthorizationListEntry> for AuthorizationListEntryV1 {
833	fn from(value: AuthorizationListEntry) -> Self {
834		Self {
835			chain_id: value.chain_id,
836			address: value.address,
837			nonce: value.nonce,
838			y_parity: value.y_parity,
839			r: value.r,
840			s: value.s,
841		}
842	}
843}
844
845#[derive(Debug, Clone, From, TryInto, Eq, PartialEq, TypeInfo, Encode, Decode)]
846pub enum HashesOrTransactionInfos {
847	/// Transaction hashes
848	Hashes(Vec<H256>),
849	/// Full transactions
850	TransactionInfos(Vec<TransactionInfo>),
851}
852
853impl Default for HashesOrTransactionInfos {
854	fn default() -> Self {
855		HashesOrTransactionInfos::Hashes(Default::default())
856	}
857}
858
859impl From<HashesOrTransactionInfos> for HashesOrTransactionInfosV1 {
860	fn from(value: HashesOrTransactionInfos) -> Self {
861		match value {
862			HashesOrTransactionInfos::Hashes(hashes) => Self::Hashes(hashes),
863			HashesOrTransactionInfos::TransactionInfos(infos) => {
864				Self::TransactionInfos(infos.into_iter().map(Into::into).collect())
865			},
866		}
867	}
868}
869
870impl HashesOrTransactionInfos {
871	pub fn push_hash(&mut self, hash: H256) {
872		match self {
873			HashesOrTransactionInfos::Hashes(hashes) => hashes.push(hash),
874			_ => {},
875		}
876	}
877
878	pub fn len(&self) -> usize {
879		match self {
880			HashesOrTransactionInfos::Hashes(v) => v.len(),
881			HashesOrTransactionInfos::TransactionInfos(v) => v.len(),
882		}
883	}
884
885	pub fn is_empty(&self) -> bool {
886		self.len() == 0
887	}
888
889	pub fn contains_tx(&self, hash: H256) -> bool {
890		match self {
891			HashesOrTransactionInfos::Hashes(hashes) => hashes.iter().any(|h256| *h256 == hash),
892			HashesOrTransactionInfos::TransactionInfos(transaction_infos) => {
893				transaction_infos.iter().any(|ti| ti.hash == hash)
894			},
895		}
896	}
897}
898
899/// Input of a `GenericTransaction`
900#[derive(Debug, Default, Clone, Eq, PartialEq)]
901pub struct InputOrData {
902	input: Option<Bytes>,
903	data: Option<Bytes>,
904}
905
906impl From<Bytes> for InputOrData {
907	fn from(value: Bytes) -> Self {
908		InputOrData { input: Some(value), data: None }
909	}
910}
911
912impl From<Vec<u8>> for InputOrData {
913	fn from(value: Vec<u8>) -> Self {
914		InputOrData { input: Some(Bytes(value)), data: None }
915	}
916}
917
918impl InputOrData {
919	/// Get the input as `Bytes`.
920	pub fn to_bytes(self) -> Bytes {
921		match self {
922			InputOrData { input: Some(input), data: _ } => input,
923			InputOrData { input: None, data: Some(data) } => data,
924			_ => Default::default(),
925		}
926	}
927
928	/// Get the input as `Vec<u8>`.
929	pub fn to_vec(self) -> Vec<u8> {
930		self.to_bytes().0
931	}
932
933	/// Returns the input as a byte slice, preferring `input` over `data`.
934	pub fn as_slice(&self) -> &[u8] {
935		self.input
936			.as_ref()
937			.or(self.data.as_ref())
938			.map(|bytes| bytes.0.as_slice())
939			.unwrap_or_default()
940	}
941
942	/// Returns true if the input carries no bytes.
943	pub fn is_empty(&self) -> bool {
944		self.as_slice().is_empty()
945	}
946}
947
948impl From<InputOrDataV1> for InputOrData {
949	fn from(value: InputOrDataV1) -> Self {
950		Self { input: value.input, data: value.data }
951	}
952}
953
954impl From<InputOrData> for InputOrDataV1 {
955	fn from(value: InputOrData) -> Self {
956		Self { input: value.input, data: value.data }
957	}
958}
959
960#[cfg(test)]
961mod tests {
962	use crate::evm::*;
963
964	#[test]
965	fn from_unsigned_works_for_legacy() {
966		let base_gas_price = U256::from(10);
967		let tx = TransactionUnsigned::from(TransactionLegacyUnsigned {
968			chain_id: Some(U256::from(1)),
969			input: Bytes::from(vec![1u8]),
970			nonce: U256::from(1),
971			value: U256::from(1),
972			to: Some(H160::zero()),
973			gas: U256::from(1),
974			gas_price: U256::from(10),
975			..Default::default()
976		});
977
978		let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
979		assert_eq!(generic.gas_price, Some(U256::from(10)));
980
981		let tx2 = generic.try_into_unsigned().unwrap();
982		assert_eq!(tx, tx2);
983	}
984
985	#[test]
986	fn from_unsigned_works_for_1559() {
987		let base_gas_price = U256::from(10);
988		let tx = TransactionUnsigned::from(Transaction1559Unsigned {
989			chain_id: U256::from(1),
990			input: Bytes::from(vec![1u8]),
991			nonce: U256::from(1),
992			value: U256::from(1),
993			to: Some(H160::zero()),
994			gas: U256::from(1),
995			gas_price: U256::from(20),
996			max_fee_per_gas: U256::from(20),
997			max_priority_fee_per_gas: U256::from(1),
998			..Default::default()
999		});
1000
1001		let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
1002		assert_eq!(generic.gas_price, Some(U256::from(10)));
1003
1004		let tx2 = generic.try_into_unsigned().unwrap();
1005		assert_eq!(tx, tx2);
1006	}
1007
1008	#[test]
1009	fn from_unsigned_works_for_7702() {
1010		let base_gas_price = U256::from(10);
1011		let tx = TransactionUnsigned::from(Transaction7702Unsigned {
1012			chain_id: U256::from(1),
1013			input: Bytes::from(vec![1u8]),
1014			nonce: U256::from(1),
1015			value: U256::from(1),
1016			to: H160::zero(),
1017			gas: U256::from(1),
1018			max_fee_per_gas: U256::from(20),
1019			max_priority_fee_per_gas: U256::from(1),
1020			authorization_list: vec![AuthorizationListEntry {
1021				chain_id: U256::from(1),
1022				address: H160::from_low_u64_be(42),
1023				nonce: U256::from(0),
1024				y_parity: U256::from(1),
1025				r: U256::from(1),
1026				s: U256::from(2),
1027			}],
1028			..Default::default()
1029		});
1030
1031		let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
1032		assert_eq!(generic.gas_price, Some(U256::from(10)));
1033
1034		let tx2 = generic.try_into_unsigned().unwrap();
1035		assert_eq!(tx, tx2);
1036	}
1037}