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 AuthorizationListEntry {
820	/// Pack (r, s, y_parity) into a 65-byte ECDSA signature for `secp256k1_ecdsa_recover`.
821	pub fn signature(&self) -> [u8; 65] {
822		let mut signature = [0u8; 65];
823		signature[..32].copy_from_slice(&self.r.to_big_endian());
824		signature[32..64].copy_from_slice(&self.s.to_big_endian());
825		signature[64] = self.y_parity.low_u32() as u8;
826		signature
827	}
828}
829
830impl From<AuthorizationListEntryV1> for AuthorizationListEntry {
831	fn from(value: AuthorizationListEntryV1) -> Self {
832		Self {
833			chain_id: value.chain_id,
834			address: value.address,
835			nonce: value.nonce,
836			y_parity: value.y_parity,
837			r: value.r,
838			s: value.s,
839		}
840	}
841}
842
843impl From<AuthorizationListEntry> for AuthorizationListEntryV1 {
844	fn from(value: AuthorizationListEntry) -> Self {
845		Self {
846			chain_id: value.chain_id,
847			address: value.address,
848			nonce: value.nonce,
849			y_parity: value.y_parity,
850			r: value.r,
851			s: value.s,
852		}
853	}
854}
855
856#[derive(Debug, Clone, From, TryInto, Eq, PartialEq, TypeInfo, Encode, Decode)]
857pub enum HashesOrTransactionInfos {
858	/// Transaction hashes
859	Hashes(Vec<H256>),
860	/// Full transactions
861	TransactionInfos(Vec<TransactionInfo>),
862}
863
864impl Default for HashesOrTransactionInfos {
865	fn default() -> Self {
866		HashesOrTransactionInfos::Hashes(Default::default())
867	}
868}
869
870impl From<HashesOrTransactionInfos> for HashesOrTransactionInfosV1 {
871	fn from(value: HashesOrTransactionInfos) -> Self {
872		match value {
873			HashesOrTransactionInfos::Hashes(hashes) => Self::Hashes(hashes),
874			HashesOrTransactionInfos::TransactionInfos(infos) => {
875				Self::TransactionInfos(infos.into_iter().map(Into::into).collect())
876			},
877		}
878	}
879}
880
881impl HashesOrTransactionInfos {
882	pub fn push_hash(&mut self, hash: H256) {
883		match self {
884			HashesOrTransactionInfos::Hashes(hashes) => hashes.push(hash),
885			_ => {},
886		}
887	}
888
889	pub fn len(&self) -> usize {
890		match self {
891			HashesOrTransactionInfos::Hashes(v) => v.len(),
892			HashesOrTransactionInfos::TransactionInfos(v) => v.len(),
893		}
894	}
895
896	pub fn is_empty(&self) -> bool {
897		self.len() == 0
898	}
899
900	pub fn contains_tx(&self, hash: H256) -> bool {
901		match self {
902			HashesOrTransactionInfos::Hashes(hashes) => hashes.iter().any(|h256| *h256 == hash),
903			HashesOrTransactionInfos::TransactionInfos(transaction_infos) => {
904				transaction_infos.iter().any(|ti| ti.hash == hash)
905			},
906		}
907	}
908}
909
910/// Input of a `GenericTransaction`
911#[derive(Debug, Default, Clone, Eq, PartialEq)]
912pub struct InputOrData {
913	input: Option<Bytes>,
914	data: Option<Bytes>,
915}
916
917impl From<Bytes> for InputOrData {
918	fn from(value: Bytes) -> Self {
919		InputOrData { input: Some(value), data: None }
920	}
921}
922
923impl From<Vec<u8>> for InputOrData {
924	fn from(value: Vec<u8>) -> Self {
925		InputOrData { input: Some(Bytes(value)), data: None }
926	}
927}
928
929impl InputOrData {
930	/// Get the input as `Bytes`.
931	pub fn to_bytes(self) -> Bytes {
932		match self {
933			InputOrData { input: Some(input), data: _ } => input,
934			InputOrData { input: None, data: Some(data) } => data,
935			_ => Default::default(),
936		}
937	}
938
939	/// Get the input as `Vec<u8>`.
940	pub fn to_vec(self) -> Vec<u8> {
941		self.to_bytes().0
942	}
943
944	/// Returns the input as a byte slice, preferring `input` over `data`.
945	pub fn as_slice(&self) -> &[u8] {
946		self.input
947			.as_ref()
948			.or(self.data.as_ref())
949			.map(|bytes| bytes.0.as_slice())
950			.unwrap_or_default()
951	}
952
953	/// Returns true if the input carries no bytes.
954	pub fn is_empty(&self) -> bool {
955		self.as_slice().is_empty()
956	}
957}
958
959impl From<InputOrDataV1> for InputOrData {
960	fn from(value: InputOrDataV1) -> Self {
961		Self { input: value.input, data: value.data }
962	}
963}
964
965impl From<InputOrData> for InputOrDataV1 {
966	fn from(value: InputOrData) -> Self {
967		Self { input: value.input, data: value.data }
968	}
969}
970
971#[cfg(test)]
972mod tests {
973	use crate::evm::*;
974
975	#[test]
976	fn from_unsigned_works_for_legacy() {
977		let base_gas_price = U256::from(10);
978		let tx = TransactionUnsigned::from(TransactionLegacyUnsigned {
979			chain_id: Some(U256::from(1)),
980			input: Bytes::from(vec![1u8]),
981			nonce: U256::from(1),
982			value: U256::from(1),
983			to: Some(H160::zero()),
984			gas: U256::from(1),
985			gas_price: U256::from(10),
986			..Default::default()
987		});
988
989		let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
990		assert_eq!(generic.gas_price, Some(U256::from(10)));
991
992		let tx2 = generic.try_into_unsigned().unwrap();
993		assert_eq!(tx, tx2);
994	}
995
996	#[test]
997	fn from_unsigned_works_for_1559() {
998		let base_gas_price = U256::from(10);
999		let tx = TransactionUnsigned::from(Transaction1559Unsigned {
1000			chain_id: U256::from(1),
1001			input: Bytes::from(vec![1u8]),
1002			nonce: U256::from(1),
1003			value: U256::from(1),
1004			to: Some(H160::zero()),
1005			gas: U256::from(1),
1006			gas_price: U256::from(20),
1007			max_fee_per_gas: U256::from(20),
1008			max_priority_fee_per_gas: U256::from(1),
1009			..Default::default()
1010		});
1011
1012		let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
1013		assert_eq!(generic.gas_price, Some(U256::from(10)));
1014
1015		let tx2 = generic.try_into_unsigned().unwrap();
1016		assert_eq!(tx, tx2);
1017	}
1018
1019	#[test]
1020	fn from_unsigned_works_for_7702() {
1021		let base_gas_price = U256::from(10);
1022		let tx = TransactionUnsigned::from(Transaction7702Unsigned {
1023			chain_id: U256::from(1),
1024			input: Bytes::from(vec![1u8]),
1025			nonce: U256::from(1),
1026			value: U256::from(1),
1027			to: H160::zero(),
1028			gas: U256::from(1),
1029			max_fee_per_gas: U256::from(20),
1030			max_priority_fee_per_gas: U256::from(1),
1031			authorization_list: vec![AuthorizationListEntry {
1032				chain_id: U256::from(1),
1033				address: H160::from_low_u64_be(42),
1034				nonce: U256::from(0),
1035				y_parity: U256::from(1),
1036				r: U256::from(1),
1037				s: U256::from(2),
1038			}],
1039			..Default::default()
1040		});
1041
1042		let generic = GenericTransaction::from_unsigned(tx.clone(), base_gas_price, None);
1043		assert_eq!(generic.gas_price, Some(U256::from(10)));
1044
1045		let tx2 = generic.try_into_unsigned().unwrap();
1046		assert_eq!(tx, tx2);
1047	}
1048}