cfx_rpc_eth_types/
transaction_request.rs

1// Copyright 2019-2021 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5// Copyright 2015-2020 Parity Technologies (UK) Ltd.
6// This file is part of OpenEthereum.
7
8// OpenEthereum is free software: you can redistribute it and/or modify
9// it under the terms of the GNU General Public License as published by
10// the Free Software Foundation, either version 3 of the License, or
11// (at your option) any later version.
12
13// OpenEthereum is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16// GNU General Public License for more details.
17
18// You should have received a copy of the GNU General Public License
19// along with OpenEthereum.  If not, see <http://www.gnu.org/licenses/>.
20
21use crate::{Error, SignedAuthorization};
22use alloy_rpc_types::TransactionInput;
23use cfx_parameters::block::DEFAULT_TARGET_BLOCK_GAS_LIMIT;
24use cfx_types::{Address, AddressSpaceUtil, H160, U256, U64};
25use primitives::{
26    transaction::{
27        Action, Eip1559Transaction, Eip155Transaction, Eip2930Transaction,
28        Eip7702Transaction, EthereumTransaction::*, SignedTransaction,
29        EIP1559_TYPE, EIP2930_TYPE, EIP7702_TYPE, LEGACY_TX_TYPE,
30    },
31    AccessList,
32};
33use serde::{Deserialize, Serialize};
34
35pub const DEFAULT_ETH_GAS_CALL_REQUEST: u64 =
36    DEFAULT_TARGET_BLOCK_GAS_LIMIT * 5 / 10;
37
38/// Call request
39#[derive(Debug, Default, PartialEq, Eq, Deserialize, Serialize, Clone)]
40#[serde(rename_all = "camelCase")]
41pub struct TransactionRequest {
42    /// From
43    pub from: Option<H160>,
44    /// To
45    pub to: Option<H160>,
46    /// Gas Price
47    pub gas_price: Option<U256>,
48    /// Max fee per gas
49    pub max_fee_per_gas: Option<U256>,
50    ///
51    pub max_priority_fee_per_gas: Option<U256>,
52    /// Gas
53    pub gas: Option<U256>,
54    /// Value
55    pub value: Option<U256>,
56    ///
57    #[serde(default, flatten)]
58    pub input: TransactionInput,
59    /// Nonce
60    pub nonce: Option<U256>,
61    /// Access list
62    pub access_list: Option<AccessList>,
63    #[serde(rename = "type")]
64    pub transaction_type: Option<U64>,
65    ///
66    pub chain_id: Option<U256>,
67    /// eip7702 authorization list
68    pub authorization_list: Option<Vec<SignedAuthorization>>,
69}
70
71impl TransactionRequest {
72    pub fn unset_zero_gas_and_price(&mut self) {
73        if self.gas_price == Some(U256::zero()) {
74            self.gas_price = None;
75        }
76
77        if self.gas == Some(U256::zero()) {
78            self.gas = None;
79        }
80    }
81
82    pub fn transaction_type(&self) -> u8 {
83        if let Some(tx_type) = self.transaction_type {
84            tx_type.as_usize() as u8
85        } else {
86            if self.authorization_list.is_some() {
87                EIP7702_TYPE
88            } else if self.max_fee_per_gas.is_some()
89                || self.max_priority_fee_per_gas.is_some()
90            {
91                EIP1559_TYPE
92            } else if self.access_list.is_some() {
93                EIP2930_TYPE
94            } else {
95                LEGACY_TX_TYPE
96            }
97        }
98    }
99
100    pub fn has_gas_price(&self) -> bool {
101        self.gas_price.is_some()
102            || self.max_fee_per_gas.is_some()
103            || self.max_priority_fee_per_gas.is_some()
104    }
105
106    pub fn sign_call(
107        self, chain_id: u32, max_gas: Option<U256>,
108    ) -> Result<SignedTransaction, Error> {
109        let request = self;
110        let max_gas = max_gas.unwrap_or(DEFAULT_ETH_GAS_CALL_REQUEST.into());
111        let gas = request.gas.unwrap_or(max_gas);
112        if gas > max_gas {
113            return Err(Error::InvalidParams(
114                "gas".into(),
115                "specified gas is larger than max gas".to_string(),
116            ));
117        }
118
119        let nonce = request.nonce.unwrap_or_default();
120        let action =
121            request.to.map_or(Action::Create, |addr| Action::Call(addr));
122        let value = request.value.unwrap_or_default();
123
124        let transaction_type = request.transaction_type();
125
126        let gas_price = request.gas_price.unwrap_or(1.into());
127        let max_fee_per_gas = request
128            .max_fee_per_gas
129            .or(request.max_priority_fee_per_gas)
130            .unwrap_or(gas_price);
131        let max_priority_fee_per_gas =
132            request.max_priority_fee_per_gas.unwrap_or(U256::zero());
133        let access_list = request.access_list.unwrap_or(vec![]);
134        let data = request
135            .input
136            .try_into_unique_input()
137            .map_err(|e| {
138                Error::InvalidParams("tx.input".to_string(), e.to_string())
139            })?
140            .unwrap_or_default()
141            .into();
142
143        let transaction = match transaction_type {
144            LEGACY_TX_TYPE => Eip155(Eip155Transaction {
145                nonce,
146                gas_price,
147                gas,
148                action,
149                value,
150                chain_id: Some(chain_id),
151                data,
152            }),
153            EIP2930_TYPE => Eip2930(Eip2930Transaction {
154                chain_id,
155                nonce,
156                gas_price,
157                gas,
158                action,
159                value,
160                data,
161                access_list,
162            }),
163            EIP1559_TYPE => Eip1559(Eip1559Transaction {
164                chain_id,
165                nonce,
166                max_priority_fee_per_gas,
167                max_fee_per_gas,
168                gas,
169                action,
170                value,
171                data,
172                access_list,
173            }),
174            EIP7702_TYPE => {
175                if request.to.is_none() {
176                    return Err(Error::InvalidParams(
177                        "to".to_string(),
178                        "to is required for EIP7702 transaction".to_string(),
179                    ));
180                }
181                Eip7702(Eip7702Transaction {
182                    chain_id,
183                    nonce,
184                    max_priority_fee_per_gas,
185                    max_fee_per_gas,
186                    gas,
187                    destination: request.to.unwrap(),
188                    value,
189                    data,
190                    access_list,
191                    authorization_list: request
192                        .authorization_list
193                        .unwrap_or_default()
194                        .into_iter()
195                        .map(|a| a.into())
196                        .collect(),
197                })
198            }
199            _ => {
200                return Err(Error::InvalidParams(
201                    "type".to_string(),
202                    "Unrecognized transaction type".to_string(),
203                ));
204            }
205        };
206
207        let from = request.from.unwrap_or(Address::zero());
208
209        Ok(transaction.fake_sign_rpc(from.with_evm_space()))
210    }
211}