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;
8use revm::precompile::{PrecompileId, PrecompileOutput, PrecompileResult};
9
10use crate::{ArbPrecompileError, interfaces::IArbNativeTokenManager};
11
12pub const ARBNATIVETOKENMANAGER_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, 0x73,
16]);
17
18const SLOAD_GAS: u64 = 800;
19
20const MINT_BURN_GAS: u64 = 100 + 9000;
22
23const EVENT_GAS: u64 = 375 + 2 * 375 + 8 * 32;
25
26pub fn create_arbnativetokenmanager_precompile(ctx: Arc<ArbPrecompileCtx>) -> DynPrecompile {
27 DynPrecompile::new_stateful(
28 PrecompileId::custom("arbnativetokenmanager"),
29 move |input| handler(input, &ctx),
30 )
31}
32
33fn handler(mut input: PrecompileInput<'_>, ctx: &ArbPrecompileCtx) -> PrecompileResult {
34 if let Some(result) =
35 crate::check_precompile_version(ctx, arb_chainspec::arbos_version::ARBOS_VERSION_41)
36 {
37 return result;
38 }
39
40 let mut gas_used = 0u64;
41 let gas_limit = input.gas;
42 crate::init_precompile_gas(&mut gas_used, ctx, input.data.len());
43
44 let call = match IArbNativeTokenManager::ArbNativeTokenManagerCalls::abi_decode(input.data) {
45 Ok(c) => c,
46 Err(_) => return crate::burn_all_revert(gas_limit),
47 };
48 if let Some(r) = crate::reject_nonpayable_value(input.value, input.data, gas_limit, &[]) {
49 return r;
50 }
51 if let Some(r) = crate::reject_static_write(
52 input.is_static,
53 input.data,
54 gas_limit,
55 &[[0xa6, 0xf0, 0xf7, 0xc7], [0x1c, 0x67, 0x9a, 0x3c]],
56 ) {
57 return r;
58 }
59 if let Some(r) = crate::reject_delegate_nonpure(
60 input.target_address != input.bytecode_address,
61 input.data,
62 gas_limit,
63 &[],
64 ) {
65 return r;
66 }
67
68 use IArbNativeTokenManager::ArbNativeTokenManagerCalls;
69 let result = match call {
70 ArbNativeTokenManagerCalls::mintNativeToken(c) => {
71 handle_mint(&mut input, &mut gas_used, c.amount, ctx)
72 }
73 ArbNativeTokenManagerCalls::burnNativeToken(c) => {
74 handle_burn(&mut input, &mut gas_used, c.amount, ctx)
75 }
76 };
77 crate::gas_check(ctx, gas_limit, gas_used, result)
78}
79
80fn load_arbos(input: &mut PrecompileInput<'_>) -> Result<(), ArbPrecompileError> {
83 input
84 .internals_mut()
85 .load_account(ARBOS_STATE_ADDRESS)
86 .map_err(ArbPrecompileError::fatal)?;
87 Ok(())
88}
89
90fn is_native_token_owner(
92 input: &mut PrecompileInput<'_>,
93 addr: Address,
94 ctx: &ArbPrecompileCtx,
95) -> Result<bool, ArbPrecompileError> {
96 let internals = input.internals_mut();
97 let arb_state = ctx
98 .block
99 .arbos_state(internals)
100 .map_err(ArbPrecompileError::fatal)?;
101 arb_state
102 .native_token_owners
103 .is_member(internals, addr)
104 .map_err(ArbPrecompileError::fatal)
105}
106
107fn handle_mint(
108 input: &mut PrecompileInput<'_>,
109 gas_used: &mut u64,
110 amount: U256,
111 ctx: &ArbPrecompileCtx,
112) -> PrecompileResult {
113 let gas_limit = input.gas;
114 let caller = input.caller;
115 load_arbos(input)?;
116
117 if !is_native_token_owner(input, caller, ctx)? {
118 return crate::burn_all_revert(gas_limit);
119 }
120
121 input
122 .internals_mut()
123 .balance_incr(caller, amount)
124 .map_err(ArbPrecompileError::fatal)?;
125
126 let topic1 = B256::left_padding_from(caller.as_slice());
127 let event_data = amount.to_be_bytes::<32>().to_vec();
128 input.internals_mut().log(Log::new_unchecked(
129 ARBNATIVETOKENMANAGER_ADDRESS,
130 vec![
131 IArbNativeTokenManager::NativeTokenMinted::SIGNATURE_HASH,
132 topic1,
133 ],
134 event_data.into(),
135 ));
136
137 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
138 crate::charge_computation(gas_used, ctx, MINT_BURN_GAS);
139 crate::charge_history_growth(gas_used, ctx, EVENT_GAS);
140 Ok(PrecompileOutput::new(
141 (*gas_used).min(gas_limit),
142 vec![].into(),
143 ))
144}
145
146fn handle_burn(
147 input: &mut PrecompileInput<'_>,
148 gas_used: &mut u64,
149 amount: U256,
150 ctx: &ArbPrecompileCtx,
151) -> PrecompileResult {
152 let gas_limit = input.gas;
153 let caller = input.caller;
154 load_arbos(input)?;
155
156 if !is_native_token_owner(input, caller, ctx)? {
157 return crate::burn_all_revert(gas_limit);
158 }
159
160 let acct = input
161 .internals_mut()
162 .load_account(caller)
163 .map_err(ArbPrecompileError::fatal)?;
164 let current_balance = acct.data.info.balance;
165
166 if current_balance < amount {
167 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
168 crate::charge_computation(gas_used, ctx, MINT_BURN_GAS);
169 return Ok(PrecompileOutput::new_reverted(
170 (*gas_used).min(gas_limit),
171 Default::default(),
172 ));
173 }
174
175 let new_balance = current_balance - amount;
176 input
177 .internals_mut()
178 .set_balance(caller, new_balance)
179 .map_err(ArbPrecompileError::fatal)?;
180
181 let topic1 = B256::left_padding_from(caller.as_slice());
182 let event_data = amount.to_be_bytes::<32>().to_vec();
183 input.internals_mut().log(Log::new_unchecked(
184 ARBNATIVETOKENMANAGER_ADDRESS,
185 vec![
186 IArbNativeTokenManager::NativeTokenBurned::SIGNATURE_HASH,
187 topic1,
188 ],
189 event_data.into(),
190 ));
191
192 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
193 crate::charge_computation(gas_used, ctx, MINT_BURN_GAS);
194 crate::charge_history_growth(gas_used, ctx, EVENT_GAS);
195 Ok(PrecompileOutput::new(
196 (*gas_used).min(gas_limit),
197 vec![].into(),
198 ))
199}