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