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