1use alloy_consensus::Transaction;
2use alloy_eips::eip2930::AccessList;
3use alloy_evm::tx::{FromRecoveredTx, FromTxWithEncoded, IntoTxEnv};
4use alloy_primitives::{Address, Bytes, U256};
5use arb_primitives::ArbTransactionSigned;
6use reth_ethereum_primitives::TransactionSigned;
7use revm::context::TxEnv;
8
9use arb_primitives::tx_types::ArbTxType;
10
11#[derive(Clone, Debug, Default, PartialEq, Eq)]
18pub struct ArbTransaction(pub TxEnv);
19
20impl ArbTransaction {
21 pub fn from_parts(
23 sender: Address,
24 tx_type: ArbTxType,
25 gas_limit: u64,
26 gas_price: u128,
27 value: U256,
28 to: revm::primitives::TxKind,
29 data: alloy_primitives::Bytes,
30 nonce: u64,
31 chain_id: Option<u64>,
32 ) -> Self {
33 let mut tx = TxEnv {
34 caller: sender,
35 gas_limit,
36 ..Default::default()
37 };
38
39 if matches!(
41 tx_type,
42 ArbTxType::ArbitrumInternalTx | ArbTxType::ArbitrumDepositTx
43 ) && gas_limit == 0
44 {
45 tx.gas_limit = 1_000_000;
46 }
47
48 tx.gas_priority_fee = Some(0);
49
50 match tx_type {
51 ArbTxType::ArbitrumDepositTx | ArbTxType::ArbitrumInternalTx => {
52 tx.value = U256::ZERO;
53 tx.gas_price = 0;
54 }
55 ArbTxType::ArbitrumSubmitRetryableTx => {
56 tx.value = U256::ZERO;
57 tx.gas_price = 0;
58 }
59 _ => {
60 tx.value = value;
61 tx.gas_price = gas_price;
62 }
63 }
64
65 tx.kind = to;
66 tx.data = data;
67 tx.nonce = nonce;
68 tx.chain_id = chain_id;
69
70 ArbTransaction(tx)
71 }
72
73 pub fn into_inner(self) -> TxEnv {
75 self.0
76 }
77}
78
79impl From<ArbTransaction> for TxEnv {
80 fn from(arb_tx: ArbTransaction) -> Self {
81 arb_tx.0
82 }
83}
84
85impl IntoTxEnv<ArbTransaction> for ArbTransaction {
86 fn into_tx_env(self) -> ArbTransaction {
87 self
88 }
89}
90
91impl revm::context_interface::Transaction for ArbTransaction {
92 type AccessListItem<'a>
93 = <TxEnv as revm::context_interface::Transaction>::AccessListItem<'a>
94 where
95 Self: 'a;
96 type Authorization<'a>
97 = <TxEnv as revm::context_interface::Transaction>::Authorization<'a>
98 where
99 Self: 'a;
100
101 fn tx_type(&self) -> u8 {
102 revm::context_interface::Transaction::tx_type(&self.0)
103 }
104 fn caller(&self) -> Address {
105 revm::context_interface::Transaction::caller(&self.0)
106 }
107 fn gas_limit(&self) -> u64 {
108 revm::context_interface::Transaction::gas_limit(&self.0)
109 }
110 fn value(&self) -> U256 {
111 revm::context_interface::Transaction::value(&self.0)
112 }
113 fn input(&self) -> &alloy_primitives::Bytes {
114 revm::context_interface::Transaction::input(&self.0)
115 }
116 fn nonce(&self) -> u64 {
117 revm::context_interface::Transaction::nonce(&self.0)
118 }
119 fn kind(&self) -> alloy_primitives::TxKind {
120 revm::context_interface::Transaction::kind(&self.0)
121 }
122 fn chain_id(&self) -> Option<u64> {
123 revm::context_interface::Transaction::chain_id(&self.0)
124 }
125 fn gas_price(&self) -> u128 {
126 revm::context_interface::Transaction::gas_price(&self.0)
127 }
128 fn access_list(&self) -> Option<impl Iterator<Item = Self::AccessListItem<'_>>> {
129 revm::context_interface::Transaction::access_list(&self.0)
130 }
131 fn blob_versioned_hashes(&self) -> &[alloy_primitives::B256] {
132 revm::context_interface::Transaction::blob_versioned_hashes(&self.0)
133 }
134 fn max_fee_per_blob_gas(&self) -> u128 {
135 revm::context_interface::Transaction::max_fee_per_blob_gas(&self.0)
136 }
137 fn authorization_list_len(&self) -> usize {
138 revm::context_interface::Transaction::authorization_list_len(&self.0)
139 }
140 fn authorization_list(&self) -> impl Iterator<Item = Self::Authorization<'_>> {
141 revm::context_interface::Transaction::authorization_list(&self.0)
142 }
143 fn max_priority_fee_per_gas(&self) -> Option<u128> {
144 revm::context_interface::Transaction::max_priority_fee_per_gas(&self.0)
145 }
146}
147
148impl reth_evm::TransactionEnv for ArbTransaction {
149 fn set_gas_limit(&mut self, gas_limit: u64) {
150 self.0.gas_limit = gas_limit;
151 }
152
153 fn nonce(&self) -> u64 {
154 self.0.nonce
155 }
156
157 fn set_nonce(&mut self, nonce: u64) {
158 self.0.nonce = nonce;
159 }
160
161 fn set_access_list(&mut self, access_list: AccessList) {
162 self.0.access_list = access_list;
163 }
164}
165
166impl crate::build::ArbTransactionEnv for ArbTransaction {
167 fn set_gas_price(&mut self, gas_price: u128) {
168 self.0.gas_price = gas_price;
169 }
170 fn set_gas_priority_fee(&mut self, fee: Option<u128>) {
171 self.0.gas_priority_fee = fee;
172 }
173}
174
175impl FromRecoveredTx<TransactionSigned> for ArbTransaction {
176 fn from_recovered_tx(tx: &TransactionSigned, sender: Address) -> Self {
177 ArbTransaction(TxEnv::from_recovered_tx(tx, sender))
178 }
179}
180
181impl FromTxWithEncoded<TransactionSigned> for ArbTransaction {
182 fn from_encoded_tx(tx: &TransactionSigned, sender: Address, encoded: Bytes) -> Self {
183 ArbTransaction(TxEnv::from_encoded_tx(tx, sender, encoded))
184 }
185}
186
187fn arb_tx_to_tx_env(tx: &ArbTransactionSigned, sender: Address) -> TxEnv {
189 use alloy_consensus::Typed2718;
190 let arb_type = ArbTxType::from_u8(Typed2718::ty(tx)).ok();
191 let is_system_tx = matches!(
192 arb_type,
193 Some(ArbTxType::ArbitrumInternalTx | ArbTxType::ArbitrumDepositTx)
194 );
195 let is_submit_retryable = arb_type == Some(ArbTxType::ArbitrumSubmitRetryableTx);
196
197 let mut env = TxEnv::default();
198 let raw_type = Typed2718::ty(tx);
203 env.tx_type = if raw_type < 0x64 { raw_type } else { 0 };
204 env.caller = sender;
205 env.gas_limit = tx.gas_limit();
206 env.nonce = tx.nonce();
207 env.chain_id = tx.chain_id();
208 env.kind = tx.to().map_or(
209 revm::primitives::TxKind::Create,
210 revm::primitives::TxKind::Call,
211 );
212 env.data = tx.input().clone();
213 env.gas_priority_fee = Some(0);
214
215 if is_system_tx {
216 env.gas_price = 0;
217 env.value = U256::ZERO;
218 if env.gas_limit == 0 {
219 env.gas_limit = 1_000_000;
220 }
221 } else if is_submit_retryable {
222 env.gas_price = 0;
223 env.value = U256::ZERO;
224 } else {
225 env.gas_price = tx.max_fee_per_gas();
226 env.value = tx.value();
227 }
228
229 if let Some(al) = tx.access_list() {
230 env.access_list = al.clone();
231 }
232
233 env
234}
235
236impl FromRecoveredTx<ArbTransactionSigned> for ArbTransaction {
237 fn from_recovered_tx(tx: &ArbTransactionSigned, sender: Address) -> Self {
238 ArbTransaction(arb_tx_to_tx_env(tx, sender))
239 }
240}
241
242impl FromTxWithEncoded<ArbTransactionSigned> for ArbTransaction {
243 fn from_encoded_tx(tx: &ArbTransactionSigned, sender: Address, _encoded: Bytes) -> Self {
244 ArbTransaction(arb_tx_to_tx_env(tx, sender))
245 }
246}