1use crate::TransactionType;
3use context_interface::{
4 either::Either,
5 transaction::{
6 AccessList, AccessListItem, RecoveredAuthorization, SignedAuthorization, Transaction,
7 },
8};
9use core::fmt::Debug;
10use primitives::{Address, Bytes, TxKind, B256, U256};
11use std::vec::Vec;
12
13#[derive(Clone, Debug, PartialEq, Eq)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct TxEnv {
17 pub tx_type: u8,
19 pub caller: Address,
21 pub gas_limit: u64,
23 pub gas_price: u128,
27 pub kind: TxKind,
29 pub value: U256,
31 pub data: Bytes,
33
34 pub nonce: u64,
36
37 pub chain_id: Option<u64>,
45
46 pub access_list: AccessList,
52
53 pub gas_priority_fee: Option<u128>,
59
60 pub blob_hashes: Vec<B256>,
68
69 pub max_fee_per_blob_gas: u128,
75
76 pub authorization_list: Vec<Either<SignedAuthorization, RecoveredAuthorization>>,
85
86 pub initcodes: Vec<Bytes>,
90}
91
92impl Default for TxEnv {
93 fn default() -> Self {
94 Self {
95 tx_type: 0,
96 caller: Address::default(),
97 gas_limit: 30_000_000,
98 gas_price: 0,
99 kind: TxKind::Call(Address::default()),
100 value: U256::ZERO,
101 data: Bytes::default(),
102 nonce: 0,
103 chain_id: Some(1), access_list: Default::default(),
105 gas_priority_fee: None,
106 blob_hashes: Vec::new(),
107 max_fee_per_blob_gas: 0,
108 authorization_list: Vec::new(),
109 initcodes: Vec::new(),
110 }
111 }
112}
113
114#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
117pub enum DeriveTxTypeError {
118 MissingTargetForEip4844,
120 MissingTargetForEip7702,
122 MissingTargetForEip7873,
124}
125
126impl TxEnv {
127 pub fn derive_tx_type(&mut self) -> Result<(), DeriveTxTypeError> {
130 if !self.access_list.0.is_empty() {
131 self.tx_type = TransactionType::Eip2930 as u8;
132 }
133
134 if self.gas_priority_fee.is_some() {
135 self.tx_type = TransactionType::Eip1559 as u8;
136 }
137
138 if !self.blob_hashes.is_empty() || self.max_fee_per_blob_gas > 0 {
139 if let TxKind::Call(_) = self.kind {
140 self.tx_type = TransactionType::Eip4844 as u8;
141 return Ok(());
142 } else {
143 return Err(DeriveTxTypeError::MissingTargetForEip4844);
144 }
145 }
146
147 if !self.authorization_list.is_empty() {
148 if let TxKind::Call(_) = self.kind {
149 self.tx_type = TransactionType::Eip7702 as u8;
150 return Ok(());
151 } else {
152 return Err(DeriveTxTypeError::MissingTargetForEip7702);
153 }
154 }
155
156 if !self.initcodes.is_empty() {
157 if let TxKind::Call(_) = self.kind {
158 self.tx_type = TransactionType::Eip7873 as u8;
159 return Ok(());
160 } else {
161 return Err(DeriveTxTypeError::MissingTargetForEip7873);
162 }
163 }
164 Ok(())
165 }
166
167 pub fn set_signed_authorization(&mut self, auth: Vec<SignedAuthorization>) {
169 self.authorization_list = auth.into_iter().map(Either::Left).collect();
170 }
171
172 pub fn set_recovered_authorization(&mut self, auth: Vec<RecoveredAuthorization>) {
174 self.authorization_list = auth.into_iter().map(Either::Right).collect();
175 }
176}
177
178impl Transaction for TxEnv {
179 type AccessListItem<'a> = &'a AccessListItem;
180 type Authorization<'a> = &'a Either<SignedAuthorization, RecoveredAuthorization>;
181
182 fn tx_type(&self) -> u8 {
183 self.tx_type
184 }
185
186 fn kind(&self) -> TxKind {
187 self.kind
188 }
189
190 fn caller(&self) -> Address {
191 self.caller
192 }
193
194 fn gas_limit(&self) -> u64 {
195 self.gas_limit
196 }
197
198 fn gas_price(&self) -> u128 {
199 self.gas_price
200 }
201
202 fn value(&self) -> U256 {
203 self.value
204 }
205
206 fn nonce(&self) -> u64 {
207 self.nonce
208 }
209
210 fn chain_id(&self) -> Option<u64> {
211 self.chain_id
212 }
213
214 fn access_list(&self) -> Option<impl Iterator<Item = Self::AccessListItem<'_>>> {
215 Some(self.access_list.0.iter())
216 }
217
218 fn max_fee_per_gas(&self) -> u128 {
219 self.gas_price
220 }
221
222 fn max_fee_per_blob_gas(&self) -> u128 {
223 self.max_fee_per_blob_gas
224 }
225
226 fn authorization_list_len(&self) -> usize {
227 self.authorization_list.len()
228 }
229
230 fn authorization_list(&self) -> impl Iterator<Item = Self::Authorization<'_>> {
231 self.authorization_list.iter()
232 }
233
234 fn input(&self) -> &Bytes {
235 &self.data
236 }
237
238 fn blob_versioned_hashes(&self) -> &[B256] {
239 &self.blob_hashes
240 }
241
242 fn max_priority_fee_per_gas(&self) -> Option<u128> {
243 self.gas_priority_fee
244 }
245
246 fn initcodes(&self) -> &[Bytes] {
247 &self.initcodes
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 fn effective_gas_setup(
256 tx_type: TransactionType,
257 gas_price: u128,
258 gas_priority_fee: Option<u128>,
259 ) -> u128 {
260 let tx = TxEnv {
261 tx_type: tx_type as u8,
262 gas_price,
263 gas_priority_fee,
264 ..Default::default()
265 };
266 let base_fee = 100;
267 tx.effective_gas_price(base_fee)
268 }
269
270 #[test]
271 fn test_effective_gas_price() {
272 assert_eq!(90, effective_gas_setup(TransactionType::Legacy, 90, None));
273 assert_eq!(
274 90,
275 effective_gas_setup(TransactionType::Legacy, 90, Some(0))
276 );
277 assert_eq!(
278 90,
279 effective_gas_setup(TransactionType::Legacy, 90, Some(10))
280 );
281 assert_eq!(
282 120,
283 effective_gas_setup(TransactionType::Legacy, 120, Some(10))
284 );
285 assert_eq!(90, effective_gas_setup(TransactionType::Eip2930, 90, None));
286 assert_eq!(
287 90,
288 effective_gas_setup(TransactionType::Eip2930, 90, Some(0))
289 );
290 assert_eq!(
291 90,
292 effective_gas_setup(TransactionType::Eip2930, 90, Some(10))
293 );
294 assert_eq!(
295 120,
296 effective_gas_setup(TransactionType::Eip2930, 120, Some(10))
297 );
298 assert_eq!(90, effective_gas_setup(TransactionType::Eip1559, 90, None));
299 assert_eq!(
300 90,
301 effective_gas_setup(TransactionType::Eip1559, 90, Some(0))
302 );
303 assert_eq!(
304 90,
305 effective_gas_setup(TransactionType::Eip1559, 90, Some(10))
306 );
307 assert_eq!(
308 110,
309 effective_gas_setup(TransactionType::Eip1559, 120, Some(10))
310 );
311 assert_eq!(90, effective_gas_setup(TransactionType::Eip4844, 90, None));
312 assert_eq!(
313 90,
314 effective_gas_setup(TransactionType::Eip4844, 90, Some(0))
315 );
316 assert_eq!(
317 90,
318 effective_gas_setup(TransactionType::Eip4844, 90, Some(10))
319 );
320 assert_eq!(
321 110,
322 effective_gas_setup(TransactionType::Eip4844, 120, Some(10))
323 );
324 assert_eq!(90, effective_gas_setup(TransactionType::Eip7702, 90, None));
325 assert_eq!(
326 90,
327 effective_gas_setup(TransactionType::Eip7702, 90, Some(0))
328 );
329 assert_eq!(
330 90,
331 effective_gas_setup(TransactionType::Eip7702, 90, Some(10))
332 );
333 assert_eq!(
334 110,
335 effective_gas_setup(TransactionType::Eip7702, 120, Some(10))
336 );
337 }
338}