1use alloy_primitives::{Address, U256};
2use arb_primitives::multigas::MultiGas;
3use arbos::{
4 tx_processor::{
5 EndTxFeeDistribution, EndTxNormalParams, GasChargingError, GasChargingParams, TxProcessor,
6 },
7 util::tx_type_has_poster_costs,
8};
9
10use crate::hooks::{
11 ArbOsHooks, EndTxContext, GasChargingContext, GasChargingResult, StartTxContext,
12};
13
14#[derive(Debug)]
19pub struct DefaultArbOsHooks {
20 pub tx_proc: TxProcessor,
22 pub arbos_version: u64,
24 pub network_fee_account: Address,
26 pub infra_fee_account: Address,
28 pub min_base_fee: U256,
30 pub per_block_gas_limit: u64,
32 pub per_tx_gas_limit: u64,
34 pub coinbase: Address,
36 pub is_eth_call: bool,
38 pub l1_base_fee: U256,
40 pub calldata_pricing_increase_enabled: bool,
42 pub collect_tips_enabled: bool,
45}
46
47impl DefaultArbOsHooks {
48 pub fn new(
49 coinbase: Address,
50 arbos_version: u64,
51 network_fee_account: Address,
52 infra_fee_account: Address,
53 min_base_fee: U256,
54 per_block_gas_limit: u64,
55 per_tx_gas_limit: u64,
56 is_eth_call: bool,
57 l1_base_fee: U256,
58 calldata_pricing_increase_enabled: bool,
59 collect_tips_enabled: bool,
60 ) -> Self {
61 Self {
62 tx_proc: TxProcessor::new(coinbase),
63 arbos_version,
64 network_fee_account,
65 infra_fee_account,
66 min_base_fee,
67 per_block_gas_limit,
68 per_tx_gas_limit,
69 coinbase,
70 is_eth_call,
71 l1_base_fee,
72 calldata_pricing_increase_enabled,
73 collect_tips_enabled,
74 }
75 }
76
77 pub fn compute_end_tx_fees(&self, ctx: &EndTxContext) -> EndTxFeeDistribution {
79 self.tx_proc
80 .compute_end_tx_fee_distribution(&EndTxNormalParams {
81 gas_used: ctx.gas_used,
82 gas_price: ctx.gas_price,
83 base_fee: ctx.base_fee,
84 coinbase: self.coinbase,
85 network_fee_account: self.network_fee_account,
86 infra_fee_account: self.infra_fee_account,
87 min_base_fee: self.min_base_fee,
88 arbos_version: self.arbos_version,
89 })
90 }
91}
92
93#[derive(Debug, thiserror::Error)]
95pub enum ArbHookError {
96 #[error("gas charging: {0}")]
97 GasCharging(#[from] GasChargingError),
98}
99
100impl ArbOsHooks for DefaultArbOsHooks {
101 type Error = ArbHookError;
102
103 fn start_tx(&mut self, ctx: &StartTxContext) -> Result<(), Self::Error> {
104 self.tx_proc.set_tx_type(ctx.tx_type as u8);
105 Ok(())
106 }
107
108 fn gas_charging(&mut self, ctx: &GasChargingContext) -> Result<GasChargingResult, Self::Error> {
109 let mut gas_remaining = ctx.gas_limit.saturating_sub(ctx.intrinsic_gas);
110
111 let skip_l1_charging = !tx_type_has_poster_costs(ctx.tx_type.as_u8());
112
113 let poster_cost = if skip_l1_charging {
115 U256::ZERO
116 } else {
117 ctx.poster_cost
118 };
119
120 let params = GasChargingParams {
121 base_fee: ctx.base_fee,
122 poster_cost,
123 is_gas_estimation: self.is_eth_call,
124 is_eth_call: self.is_eth_call,
125 skip_l1_charging,
126 min_base_fee: self.min_base_fee,
127 per_block_gas_limit: self.per_block_gas_limit,
128 per_tx_gas_limit: self.per_tx_gas_limit,
129 arbos_version: self.arbos_version,
130 };
131
132 self.tx_proc
133 .gas_charging_hook(&mut gas_remaining, ctx.intrinsic_gas, ¶ms)?;
134
135 let multi_gas = MultiGas::single_dim_gas(self.tx_proc.poster_gas);
137
138 Ok(GasChargingResult {
139 poster_cost: self.tx_proc.poster_fee,
140 poster_gas: self.tx_proc.poster_gas,
141 compute_hold_gas: self.tx_proc.compute_hold_gas,
142 calldata_units: ctx.calldata_units,
143 multi_gas,
144 })
145 }
146
147 fn end_tx(&mut self, _ctx: &EndTxContext) -> Result<(), Self::Error> {
148 Ok(())
151 }
152
153 fn nonrefundable_gas(&self) -> u64 {
154 self.tx_proc.nonrefundable_gas()
155 }
156
157 fn held_gas(&self) -> u64 {
158 self.tx_proc.held_gas()
159 }
160
161 fn scheduled_txs(&mut self) -> Vec<Vec<u8>> {
162 core::mem::take(&mut self.tx_proc.scheduled_txs)
163 }
164
165 fn drop_tip(&self) -> bool {
166 self.tx_proc
167 .drop_tip_with_collect(self.arbos_version, self.collect_tips_enabled)
168 }
169
170 fn gas_price_op(&self, gas_price: U256, base_fee: U256) -> U256 {
171 self.tx_proc.gas_price_op_with_collect(
172 self.arbos_version,
173 base_fee,
174 gas_price,
175 self.collect_tips_enabled,
176 )
177 }
178
179 fn msg_is_non_mutating(&self) -> bool {
180 self.is_eth_call
181 }
182
183 fn is_calldata_pricing_increase_enabled(&self) -> bool {
184 self.calldata_pricing_increase_enabled
185 }
186}