1use 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#[derive(Debug, Default, PartialEq, Eq, Deserialize, Serialize, Clone)]
40#[serde(rename_all = "camelCase")]
41pub struct TransactionRequest {
42 pub from: Option<H160>,
44 pub to: Option<H160>,
46 pub gas_price: Option<U256>,
48 pub max_fee_per_gas: Option<U256>,
50 pub max_priority_fee_per_gas: Option<U256>,
52 pub gas: Option<U256>,
54 pub value: Option<U256>,
56 #[serde(default, flatten)]
58 pub input: TransactionInput,
59 pub nonce: Option<U256>,
61 pub access_list: Option<AccessList>,
63 #[serde(rename = "type")]
64 pub transaction_type: Option<U64>,
65 pub chain_id: Option<U256>,
67 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}