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 }
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 }
112 }
113}
114
115#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
117#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
118pub enum DeriveTxTypeError {
119 MissingTargetForEip4844,
121 MissingTargetForEip7702,
123 MissingTargetForEip7873,
125}
126
127impl TxEnv {
128 pub fn derive_tx_type(&mut self) -> Result<(), DeriveTxTypeError> {
131 if !self.access_list.0.is_empty() {
132 self.tx_type = TransactionType::Eip2930 as u8;
133 }
134
135 if self.gas_priority_fee.is_some() {
136 self.tx_type = TransactionType::Eip1559 as u8;
137 }
138
139 if !self.blob_hashes.is_empty() || self.max_fee_per_blob_gas > 0 {
140 if let TxKind::Call(_) = self.kind {
141 self.tx_type = TransactionType::Eip4844 as u8;
142 return Ok(());
143 } else {
144 return Err(DeriveTxTypeError::MissingTargetForEip4844);
145 }
146 }
147
148 if !self.authorization_list.is_empty() {
149 if let TxKind::Call(_) = self.kind {
150 self.tx_type = TransactionType::Eip7702 as u8;
151 return Ok(());
152 } else {
153 return Err(DeriveTxTypeError::MissingTargetForEip7702);
154 }
155 }
156
157 Ok(())
168 }
169
170 pub fn set_signed_authorization(&mut self, auth: Vec<SignedAuthorization>) {
172 self.authorization_list = auth.into_iter().map(Either::Left).collect();
173 }
174
175 pub fn set_recovered_authorization(&mut self, auth: Vec<RecoveredAuthorization>) {
177 self.authorization_list = auth.into_iter().map(Either::Right).collect();
178 }
179}
180
181impl Transaction for TxEnv {
182 type AccessListItem<'a> = &'a AccessListItem;
183 type Authorization<'a> = &'a Either<SignedAuthorization, RecoveredAuthorization>;
184
185 fn tx_type(&self) -> u8 {
186 self.tx_type
187 }
188
189 fn kind(&self) -> TxKind {
190 self.kind
191 }
192
193 fn caller(&self) -> Address {
194 self.caller
195 }
196
197 fn gas_limit(&self) -> u64 {
198 self.gas_limit
199 }
200
201 fn gas_price(&self) -> u128 {
202 self.gas_price
203 }
204
205 fn value(&self) -> U256 {
206 self.value
207 }
208
209 fn nonce(&self) -> u64 {
210 self.nonce
211 }
212
213 fn chain_id(&self) -> Option<u64> {
214 self.chain_id
215 }
216
217 fn access_list(&self) -> Option<impl Iterator<Item = Self::AccessListItem<'_>>> {
218 Some(self.access_list.0.iter())
219 }
220
221 fn max_fee_per_gas(&self) -> u128 {
222 self.gas_price
223 }
224
225 fn max_fee_per_blob_gas(&self) -> u128 {
226 self.max_fee_per_blob_gas
227 }
228
229 fn authorization_list_len(&self) -> usize {
230 self.authorization_list.len()
231 }
232
233 fn authorization_list(&self) -> impl Iterator<Item = Self::Authorization<'_>> {
234 self.authorization_list.iter()
235 }
236
237 fn input(&self) -> &Bytes {
238 &self.data
239 }
240
241 fn blob_versioned_hashes(&self) -> &[B256] {
242 &self.blob_hashes
243 }
244
245 fn max_priority_fee_per_gas(&self) -> Option<u128> {
246 self.gas_priority_fee
247 }
248
249 }
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 fn effective_gas_setup(
260 tx_type: TransactionType,
261 gas_price: u128,
262 gas_priority_fee: Option<u128>,
263 ) -> u128 {
264 let tx = TxEnv {
265 tx_type: tx_type as u8,
266 gas_price,
267 gas_priority_fee,
268 ..Default::default()
269 };
270 let base_fee = 100;
271 tx.effective_gas_price(base_fee)
272 }
273
274 #[test]
275 fn test_effective_gas_price() {
276 assert_eq!(90, effective_gas_setup(TransactionType::Legacy, 90, None));
277 assert_eq!(
278 90,
279 effective_gas_setup(TransactionType::Legacy, 90, Some(0))
280 );
281 assert_eq!(
282 90,
283 effective_gas_setup(TransactionType::Legacy, 90, Some(10))
284 );
285 assert_eq!(
286 120,
287 effective_gas_setup(TransactionType::Legacy, 120, Some(10))
288 );
289 assert_eq!(90, effective_gas_setup(TransactionType::Eip2930, 90, None));
290 assert_eq!(
291 90,
292 effective_gas_setup(TransactionType::Eip2930, 90, Some(0))
293 );
294 assert_eq!(
295 90,
296 effective_gas_setup(TransactionType::Eip2930, 90, Some(10))
297 );
298 assert_eq!(
299 120,
300 effective_gas_setup(TransactionType::Eip2930, 120, Some(10))
301 );
302 assert_eq!(90, effective_gas_setup(TransactionType::Eip1559, 90, None));
303 assert_eq!(
304 90,
305 effective_gas_setup(TransactionType::Eip1559, 90, Some(0))
306 );
307 assert_eq!(
308 90,
309 effective_gas_setup(TransactionType::Eip1559, 90, Some(10))
310 );
311 assert_eq!(
312 110,
313 effective_gas_setup(TransactionType::Eip1559, 120, Some(10))
314 );
315 assert_eq!(90, effective_gas_setup(TransactionType::Eip4844, 90, None));
316 assert_eq!(
317 90,
318 effective_gas_setup(TransactionType::Eip4844, 90, Some(0))
319 );
320 assert_eq!(
321 90,
322 effective_gas_setup(TransactionType::Eip4844, 90, Some(10))
323 );
324 assert_eq!(
325 110,
326 effective_gas_setup(TransactionType::Eip4844, 120, Some(10))
327 );
328 assert_eq!(90, effective_gas_setup(TransactionType::Eip7702, 90, None));
329 assert_eq!(
330 90,
331 effective_gas_setup(TransactionType::Eip7702, 90, Some(0))
332 );
333 assert_eq!(
334 90,
335 effective_gas_setup(TransactionType::Eip7702, 90, Some(10))
336 );
337 assert_eq!(
338 110,
339 effective_gas_setup(TransactionType::Eip7702, 120, Some(10))
340 );
341 }
342}