1use std::sync::Arc;
2
3use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
4use alloy_primitives::{Address, B256, Log, U256};
5use alloy_sol_types::{SolEvent, SolInterface};
6use arb_context::ArbPrecompileCtx;
7use arb_storage::{ARBOS_STATE_ADDRESS, FILTERED_TX_STATE_ADDRESS};
8use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult};
9
10use crate::{ArbPrecompileError, interfaces::IArbFilteredTxManager};
11
12pub const ARBFILTEREDTXMANAGER_ADDRESS: Address = Address::new([
14 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15 0x00, 0x00, 0x00, 0x74,
16]);
17
18const SLOAD_GAS: u64 = 800;
19const SSTORE_GAS: u64 = 20_000;
20const SSTORE_CLEAR_GAS: u64 = 5_000;
21const COPY_GAS: u64 = 3;
22const LOG_GAS: u64 = 375 + 2 * 375;
23
24pub fn create_arbfilteredtxmanager_precompile(ctx: Arc<ArbPrecompileCtx>) -> DynPrecompile {
25 DynPrecompile::new_stateful(PrecompileId::custom("arbfilteredtxmanager"), move |input| {
26 handler(input, &ctx)
27 })
28}
29
30fn handler(mut input: PrecompileInput<'_>, ctx: &ArbPrecompileCtx) -> PrecompileResult {
31 if let Some(result) = crate::check_precompile_version(
32 ctx,
33 arb_chainspec::arbos_version::ARBOS_VERSION_TRANSACTION_FILTERING,
34 ) {
35 return result;
36 }
37
38 let gas_limit = input.gas;
39
40 let mg_snapshot = ctx.snapshot_precompile_multi_gas();
46 let mut wrapper_gas_used = 0u64;
47 crate::charge_storage_read(&mut wrapper_gas_used, ctx, SLOAD_GAS);
48 let caller = input.caller;
49 load_accounts(&mut input)?;
50 let is_filterer = {
51 let internals = input.internals_mut();
52 let arb_state = ctx
53 .block
54 .arbos_state(internals)
55 .map_err(ArbPrecompileError::fatal)?;
56 let res = arb_state
57 .transaction_filterers
58 .is_member(internals, caller)
59 .map_err(ArbPrecompileError::fatal)?;
60 crate::charge_storage_read(&mut wrapper_gas_used, ctx, SLOAD_GAS);
61 res
62 };
63 let wrapper_gas = wrapper_gas_used;
64
65 let call =
66 match IArbFilteredTxManager::ArbFilteredTransactionsManagerCalls::abi_decode(input.data) {
67 Ok(c) => c,
68 Err(_) => {
69 let final_gas = if is_filterer {
72 0
73 } else {
74 wrapper_gas.min(gas_limit)
75 };
76 if is_filterer {
79 ctx.restore_precompile_multi_gas(mg_snapshot);
80 }
81 return Ok(PrecompileOutput::new_reverted(
82 final_gas,
83 Default::default(),
84 ));
85 }
86 };
87
88 let mut gas_used = 0u64;
89 crate::init_precompile_gas(&mut gas_used, ctx, input.data.len());
90 use IArbFilteredTxManager::ArbFilteredTransactionsManagerCalls as Calls;
91 let inner_result = match call {
92 Calls::addFilteredTransaction(c) => {
93 handle_add_filtered_tx(&mut input, &mut gas_used, c.txHash, ctx)
94 }
95 Calls::deleteFilteredTransaction(c) => {
96 handle_delete_filtered_tx(&mut input, &mut gas_used, c.txHash, ctx)
97 }
98 Calls::isTransactionFiltered(c) => {
99 handle_is_tx_filtered(&mut input, &mut gas_used, c.txHash, ctx)
100 }
101 };
102
103 let final_gas = if is_filterer {
105 0
106 } else {
107 wrapper_gas.min(gas_limit)
108 };
109 ctx.restore_precompile_multi_gas(mg_snapshot);
110 if !is_filterer {
111 ctx.add_precompile_multi_gas(
113 arb_primitives::multigas::ResourceKind::StorageAccessRead,
114 2 * SLOAD_GAS,
115 );
116 }
117 match inner_result {
118 Ok(_) if gas_used > gas_limit => Ok(PrecompileOutput::new_reverted(
119 final_gas,
120 Default::default(),
121 )),
122 Ok(mut output) => {
123 output.gas_used = final_gas;
124 Ok(output)
125 }
126 Err(PrecompileError::Other(_)) => Ok(PrecompileOutput::new_reverted(
127 final_gas,
128 Default::default(),
129 )),
130 Err(e) => Err(e),
131 }
132}
133
134fn load_accounts(input: &mut PrecompileInput<'_>) -> Result<(), ArbPrecompileError> {
137 input
138 .internals_mut()
139 .load_account(ARBOS_STATE_ADDRESS)
140 .map_err(ArbPrecompileError::fatal)?;
141 input
142 .internals_mut()
143 .load_account(FILTERED_TX_STATE_ADDRESS)
144 .map_err(ArbPrecompileError::fatal)?;
145 Ok(())
146}
147
148fn is_transaction_filterer(
150 input: &mut PrecompileInput<'_>,
151 gas_used: &mut u64,
152 addr: Address,
153 ctx: &ArbPrecompileCtx,
154) -> Result<bool, ArbPrecompileError> {
155 let internals = input.internals_mut();
156 let arb_state = ctx
157 .block
158 .arbos_state(internals)
159 .map_err(ArbPrecompileError::fatal)?;
160 let is_member = arb_state
161 .transaction_filterers
162 .is_member(internals, addr)
163 .map_err(ArbPrecompileError::fatal)?;
164 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
165 Ok(is_member)
166}
167
168fn handle_is_tx_filtered(
169 input: &mut PrecompileInput<'_>,
170 gas_used: &mut u64,
171 tx_hash: B256,
172 ctx: &ArbPrecompileCtx,
173) -> PrecompileResult {
174 let gas_limit = input.gas;
175 if !input.value.is_zero() || input.target_address != input.bytecode_address {
177 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
178 }
179 load_accounts(input)?;
180
181 let internals = input.internals_mut();
182 let arb_state = ctx
183 .block
184 .arbos_state(internals)
185 .map_err(ArbPrecompileError::fatal)?;
186 let is_filtered_bool = arb_state
187 .filtered_transactions
188 .is_filtered(internals, tx_hash)
189 .map_err(ArbPrecompileError::fatal)?;
190 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
191
192 let is_filtered = if is_filtered_bool {
193 U256::from(1u64)
194 } else {
195 U256::ZERO
196 };
197
198 crate::charge_computation(gas_used, ctx, COPY_GAS);
199 Ok(PrecompileOutput::new(
200 (*gas_used).min(gas_limit),
201 is_filtered.to_be_bytes::<32>().to_vec().into(),
202 ))
203}
204
205fn handle_add_filtered_tx(
206 input: &mut PrecompileInput<'_>,
207 gas_used: &mut u64,
208 tx_hash: B256,
209 ctx: &ArbPrecompileCtx,
210) -> PrecompileResult {
211 let gas_limit = input.gas;
212 let caller = input.caller;
213 if !input.value.is_zero() || input.is_static || input.target_address != input.bytecode_address {
215 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
216 }
217 load_accounts(input)?;
218
219 if !is_transaction_filterer(input, gas_used, caller, ctx)? {
220 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
221 }
222
223 {
224 let internals = input.internals_mut();
225 let arb_state = ctx
226 .block
227 .arbos_state(internals)
228 .map_err(ArbPrecompileError::fatal)?;
229 arb_state
230 .filtered_transactions
231 .set(internals, tx_hash, true)
232 .map_err(ArbPrecompileError::fatal)?;
233 crate::charge_storage_write(gas_used, ctx, SSTORE_GAS);
234 }
235
236 input.internals_mut().log(Log::new_unchecked(
237 ARBFILTEREDTXMANAGER_ADDRESS,
238 vec![
239 IArbFilteredTxManager::FilteredTransactionAdded::SIGNATURE_HASH,
240 tx_hash,
241 ],
242 Default::default(),
243 ));
244 crate::charge_history_growth(gas_used, ctx, LOG_GAS);
245
246 Ok(PrecompileOutput::new(
247 (*gas_used).min(gas_limit),
248 vec![].into(),
249 ))
250}
251
252fn handle_delete_filtered_tx(
253 input: &mut PrecompileInput<'_>,
254 gas_used: &mut u64,
255 tx_hash: B256,
256 ctx: &ArbPrecompileCtx,
257) -> PrecompileResult {
258 let gas_limit = input.gas;
259 let caller = input.caller;
260 if !input.value.is_zero() || input.is_static || input.target_address != input.bytecode_address {
262 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
263 }
264 load_accounts(input)?;
265
266 if !is_transaction_filterer(input, gas_used, caller, ctx)? {
267 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
268 }
269
270 {
271 let internals = input.internals_mut();
272 let arb_state = ctx
273 .block
274 .arbos_state(internals)
275 .map_err(ArbPrecompileError::fatal)?;
276 arb_state
277 .filtered_transactions
278 .set(internals, tx_hash, false)
279 .map_err(ArbPrecompileError::fatal)?;
280 crate::charge_storage_write(gas_used, ctx, SSTORE_CLEAR_GAS);
281 }
282
283 input.internals_mut().log(Log::new_unchecked(
284 ARBFILTEREDTXMANAGER_ADDRESS,
285 vec![
286 IArbFilteredTxManager::FilteredTransactionDeleted::SIGNATURE_HASH,
287 tx_hash,
288 ],
289 Default::default(),
290 ));
291 crate::charge_history_growth(gas_used, ctx, LOG_GAS);
292
293 Ok(PrecompileOutput::new(
294 (*gas_used).min(gas_limit),
295 vec![].into(),
296 ))
297}