revm_statetest_types/
transaction.rs

1use crate::{deserializer::deserialize_maybe_empty, TestAuthorization};
2use revm::{
3    context::TransactionType,
4    context_interface::transaction::AccessList,
5    primitives::{Address, Bytes, B256, U256},
6};
7use serde::{Deserialize, Serialize};
8
9/// Transaction parts.
10#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct TransactionParts {
13    /// Transaction type (0=Legacy, 1=EIP-2930, 2=EIP-1559, 3=EIP-4844, 4=EIP-7702)
14    #[serde(rename = "type")]
15    pub tx_type: Option<u8>,
16    /// Transaction data/input (multiple variants for different test cases)
17    pub data: Vec<Bytes>,
18    /// Gas limit values (multiple variants for different test cases)
19    pub gas_limit: Vec<U256>,
20    /// Gas price (for legacy and EIP-2930 transactions)
21    pub gas_price: Option<U256>,
22    /// Transaction nonce
23    pub nonce: U256,
24    /// Private key for signing the transaction
25    pub secret_key: B256,
26    /// if sender is not present we need to derive it from secret key.
27    #[serde(default)]
28    pub sender: Option<Address>,
29    /// Recipient address (None for contract creation)
30    #[serde(default, deserialize_with = "deserialize_maybe_empty")]
31    pub to: Option<Address>,
32    /// Ether value to transfer (multiple variants for different test cases)
33    pub value: Vec<U256>,
34    /// Maximum fee per gas (EIP-1559 transactions)
35    pub max_fee_per_gas: Option<U256>,
36    /// Maximum priority fee per gas (EIP-1559 transactions)
37    pub max_priority_fee_per_gas: Option<U256>,
38    /// Initcodes for contract creation (EIP-7873)
39    pub initcodes: Option<Vec<Bytes>>,
40    /// Access lists for different test cases (EIP-2930)
41    #[serde(default)]
42    pub access_lists: Vec<Option<AccessList>>,
43    /// Authorization list (EIP-7702)
44    pub authorization_list: Option<Vec<TestAuthorization>>,
45    /// Blob versioned hashes (EIP-4844)
46    #[serde(default)]
47    pub blob_versioned_hashes: Vec<B256>,
48    /// Maximum fee per blob gas (EIP-4844)
49    pub max_fee_per_blob_gas: Option<U256>,
50}
51
52impl TransactionParts {
53    /// Returns the transaction type.   
54    ///
55    /// As this information is derived from the fields it is not stored in the struct.
56    ///
57    /// Returns `None` if the transaction is invalid:
58    ///   * It has both blob gas and no destination.
59    ///   * It has authorization list and no destination.
60    pub fn tx_type(&self, access_list_index: usize) -> Option<TransactionType> {
61        if let Some(tx_type) = self.tx_type {
62            return Some(TransactionType::from(tx_type));
63        }
64
65        let mut tx_type = TransactionType::Legacy;
66
67        // If it has access list it is EIP-2930 tx
68        if let Some(access_list) = self.access_lists.get(access_list_index) {
69            if access_list.is_some() {
70                tx_type = TransactionType::Eip2930;
71            }
72        }
73
74        // If there is max_fee it is EIP-1559 tx
75        if self.max_fee_per_gas.is_some() {
76            tx_type = TransactionType::Eip1559;
77        }
78
79        // If it has max_fee_per_blob_gas it is EIP-4844 tx
80        if self.max_fee_per_blob_gas.is_some() {
81            // target need to be present for EIP-4844 tx
82            self.to?;
83            return Some(TransactionType::Eip4844);
84        }
85
86        // And if it has authorization list it is EIP-7702 tx
87        if self.authorization_list.is_some() {
88            // Target need to be present for EIP-7702 tx
89            self.to?;
90            return Some(TransactionType::Eip7702);
91        }
92
93        // TODO(EOF)
94        // // And if it has initcodes it is EIP-7873 tx
95        // if self.initcodes.is_some() {
96        //     // Target need to be present for EIP-7873 tx
97        //     self.to?;
98        //     return Some(TransactionType::Eip7873);
99        // }
100
101        Some(tx_type)
102    }
103}
104
105/// Transaction part indices.
106#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
107#[serde(rename_all = "camelCase", deny_unknown_fields)]
108pub struct TxPartIndices {
109    /// Index into the data array
110    pub data: usize,
111    /// Index into the gas_limit array
112    pub gas: usize,
113    /// Index into the value array
114    pub value: usize,
115}
116
117#[cfg(test)]
118mod test {
119
120    use super::*;
121
122    #[test]
123    fn decode_tx_parts() {
124        let tx = r#"{
125            "nonce": "0x00",
126            "maxPriorityFeePerGas": "0x00",
127            "maxFeePerGas": "0x07",
128            "gasLimit": [
129                "0x0423ff"
130            ],
131            "to": "0x0000000000000000000000000000000000001000",
132            "value": [
133                "0x00"
134            ],
135            "data": [
136                "0x"
137            ],
138            "accessLists": [
139                [
140                    {
141                        "address": "0x6389e7f33ce3b1e94e4325ef02829cd12297ef71",
142                        "storageKeys": [
143                            "0x0000000000000000000000000000000000000000000000000000000000000000"
144                        ]
145                    }
146                ]
147            ],
148            "authorizationList": [
149                {
150                    "chainId": "0x00",
151                    "address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
152                    "nonce": "0x00",
153                    "v": "0x01",
154                    "r": "0x5a8cac98fd240d8ef83c22db4a061ffa0facb1801245283cc05fc809d8b92837",
155                    "s": "0x1c3162fe11d91bc24d4fa00fb19ca34531e0eacdf8142c804be44058d5b8244f",
156                    "signer": "0x6389e7f33ce3b1e94e4325ef02829cd12297ef71"
157                }
158            ],
159            "sender": "0x8a0a19589531694250d570040a0c4b74576919b8",
160            "secretKey": "0x9e7645d0cfd9c3a04eb7a9db59a4eb7d359f2e75c9164a9d6b9a7d54e1b6a36f"
161        }"#;
162
163        let _: TransactionParts = serde_json::from_str(tx).unwrap();
164    }
165}