1use alloy_primitives::{Address, B256, U256, address};
11use foundry_evm::core::tempo::{
12 ALPHA_USD_ADDRESS, BETA_USD_ADDRESS, PATH_USD_ADDRESS, THETA_USD_ADDRESS,
13 initialize_tempo_genesis_at_hardfork,
14};
15use revm::{
16 DatabaseRef,
17 context::{BlockEnv, journaled_state::JournalCheckpoint},
18 state::{AccountInfo, Bytecode},
19};
20use std::collections::HashMap;
21use tempo_hardfork::TempoHardfork;
22use tempo_precompiles::{
23 TIP_FEE_MANAGER_ADDRESS,
24 account_keychain::{
25 AccountKeychain,
26 IAccountKeychain::{KeyRestrictions, SignatureType},
27 },
28 error::TempoPrecompileError,
29 storage::{PrecompileStorageProvider, StorageCtx},
30 tip_fee_manager::{IFeeManager, TipFeeManager},
31 tip20::{ITIP20, TIP20Token},
32};
33use tempo_primitives::TempoBlockEnv;
34
35use super::db::Db;
36
37const SENDER: Address = address!("0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38");
39const ADMIN: Address = address!("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f");
41
42pub struct AnvilStorageProvider<'a> {
44 db: &'a mut dyn Db,
45 chain_id: u64,
46 block_env: TempoBlockEnv,
47 gas_used: u64,
48 gas_refunded: i64,
49 reservoir: u64,
50 tip1060_storage_credits_enabled: bool,
51 transient: HashMap<(Address, U256), U256>,
52 hardfork: TempoHardfork,
53}
54
55impl<'a> AnvilStorageProvider<'a> {
56 pub fn new(
57 db: &'a mut dyn Db,
58 chain_id: u64,
59 timestamp: U256,
60 block_number: u64,
61 hardfork: TempoHardfork,
62 ) -> Self {
63 Self {
64 db,
65 chain_id,
66 block_env: TempoBlockEnv {
67 inner: BlockEnv {
68 timestamp,
69 number: U256::from(block_number),
70 ..Default::default()
71 },
72 ..Default::default()
73 },
74 gas_used: 0,
75 gas_refunded: 0,
76 reservoir: 0,
77 tip1060_storage_credits_enabled: hardfork.is_t7(),
78 transient: HashMap::new(),
79 hardfork,
80 }
81 }
82}
83
84impl PrecompileStorageProvider for AnvilStorageProvider<'_> {
85 fn spec(&self) -> TempoHardfork {
86 self.hardfork
87 }
88
89 fn chain_id(&self) -> u64 {
90 self.chain_id
91 }
92
93 fn block_env(&self) -> &TempoBlockEnv {
94 &self.block_env
95 }
96
97 fn set_code(&mut self, address: Address, code: Bytecode) -> Result<(), TempoPrecompileError> {
98 self.db.insert_account(
99 address,
100 AccountInfo {
101 code_hash: code.hash_slow(),
102 code: Some(code),
103 nonce: 1,
104 ..Default::default()
105 },
106 );
107 Ok(())
108 }
109
110 fn with_account_info(
111 &mut self,
112 address: Address,
113 f: &mut dyn FnMut(&AccountInfo),
114 ) -> Result<(), TempoPrecompileError> {
115 if let Some(info) =
116 self.db.basic_ref(address).map_err(|e| TempoPrecompileError::Fatal(e.to_string()))?
117 {
118 f(&info);
119 Ok(())
120 } else {
121 Err(TempoPrecompileError::Fatal(format!("account '{address}' not found")))
122 }
123 }
124
125 fn account_code(&mut self, address: Address) -> Result<(B256, Bytecode), TempoPrecompileError> {
126 let Some(info) =
127 self.db.basic_ref(address).map_err(|e| TempoPrecompileError::Fatal(e.to_string()))?
128 else {
129 return Ok((B256::ZERO, Bytecode::default()));
130 };
131 let code_hash = info.code_hash;
132 let code = if let Some(code) = info.code {
133 code
134 } else {
135 self.db
136 .code_by_hash_ref(code_hash)
137 .map_err(|e| TempoPrecompileError::Fatal(e.to_string()))?
138 };
139 Ok((code_hash, code))
140 }
141
142 fn sstore(
143 &mut self,
144 address: Address,
145 key: U256,
146 value: U256,
147 ) -> Result<(), TempoPrecompileError> {
148 self.db
149 .set_storage_at(address, B256::from(key), B256::from(value))
150 .map_err(|e| TempoPrecompileError::Fatal(e.to_string()))
151 }
152
153 fn sload(&mut self, address: Address, key: U256) -> Result<U256, TempoPrecompileError> {
154 revm::Database::storage(self.db, address, key)
155 .map_err(|e| TempoPrecompileError::Fatal(e.to_string()))
156 }
157
158 fn tstore(
159 &mut self,
160 address: Address,
161 key: U256,
162 value: U256,
163 ) -> Result<(), TempoPrecompileError> {
164 self.transient.insert((address, key), value);
165 Ok(())
166 }
167
168 fn tload(&mut self, address: Address, key: U256) -> Result<U256, TempoPrecompileError> {
169 Ok(self.transient.get(&(address, key)).copied().unwrap_or(U256::ZERO))
170 }
171
172 fn emit_event(
173 &mut self,
174 _address: Address,
175 _event: alloy_primitives::LogData,
176 ) -> Result<(), TempoPrecompileError> {
177 Ok(())
178 }
179
180 fn deduct_gas(&mut self, gas: u64) -> Result<(), TempoPrecompileError> {
181 self.gas_used = self.gas_used.saturating_add(gas);
182 Ok(())
183 }
184
185 fn gas_used(&self) -> u64 {
186 self.gas_used
187 }
188
189 fn state_gas_used(&self) -> u64 {
190 0
191 }
192
193 fn state_gas_spilled(&self) -> u64 {
194 0
195 }
196
197 fn gas_limit(&self) -> u64 {
198 u64::MAX
199 }
200
201 fn gas_refunded(&self) -> i64 {
202 self.gas_refunded
203 }
204
205 fn reservoir(&self) -> u64 {
206 self.reservoir
207 }
208
209 fn refund_gas(&mut self, gas: i64) {
210 self.gas_refunded = self.gas_refunded.saturating_add(gas);
211 }
212
213 fn is_static(&self) -> bool {
214 false
215 }
216
217 fn checkpoint(&mut self) -> JournalCheckpoint {
218 JournalCheckpoint { log_i: 0, journal_i: 0, selfdestructed_i: 0 }
219 }
220
221 fn checkpoint_commit(&mut self, _checkpoint: JournalCheckpoint) {}
222
223 fn checkpoint_revert(&mut self, _checkpoint: JournalCheckpoint) {}
224
225 fn amsterdam_eip8037_enabled(&self) -> bool {
226 false
227 }
228
229 fn set_tip1060_storage_credits(&mut self, enabled: bool) {
230 self.tip1060_storage_credits_enabled = enabled && self.hardfork.is_t7();
231 }
232}
233
234pub fn initialize_tempo_precompiles(
242 db: &mut dyn Db,
243 chain_id: u64,
244 timestamp: u64,
245 test_accounts: &[Address],
246 hardfork: TempoHardfork,
247) -> Result<(), TempoPrecompileError> {
248 let timestamp = U256::from(timestamp);
249
250 let mut storage = AnvilStorageProvider::new(db, chain_id, timestamp, 0, hardfork);
251
252 initialize_tempo_genesis_at_hardfork(&mut storage, ADMIN, SENDER, hardfork)?;
254
255 let mint_amount = U256::from(u64::MAX);
258 let tokens = [PATH_USD_ADDRESS, ALPHA_USD_ADDRESS, BETA_USD_ADDRESS, THETA_USD_ADDRESS];
259
260 StorageCtx::enter(&mut storage, || -> Result<(), TempoPrecompileError> {
261 for &token_address in &tokens {
263 let mut token = TIP20Token::from_address(token_address)?;
264 for &account in test_accounts {
265 token.mint(ADMIN, ITIP20::mintCall { to: account, amount: mint_amount })?;
266 }
267 }
268
269 let mut keychain = AccountKeychain::new();
273 for &account in test_accounts {
274 keychain.set_tx_origin(account)?;
277 keychain.authorize_key(
278 account, account, SignatureType::Secp256k1,
281 KeyRestrictions {
282 expiry: u64::MAX, enforceLimits: false, limits: vec![],
285 allowAnyCalls: true,
286 allowedCalls: vec![],
287 },
288 None,
289 )?;
290 }
291
292 let mut fee_manager = TipFeeManager::new();
295 fee_manager.initialize()?;
296
297 for (i, &account) in test_accounts.iter().enumerate() {
298 let fee_token = match i {
299 0 => ALPHA_USD_ADDRESS, 1 => BETA_USD_ADDRESS, 2 => THETA_USD_ADDRESS, _ => PATH_USD_ADDRESS, };
304 fee_manager
305 .set_user_token(account, IFeeManager::setUserTokenCall { token: fee_token })?;
306 }
307
308 for &token_address in &tokens {
310 let mut token = TIP20Token::from_address(token_address)?;
311 token.mint(
312 ADMIN,
313 ITIP20::mintCall { to: TIP_FEE_MANAGER_ADDRESS, amount: mint_amount },
314 )?;
315 }
316
317 let liquidity_amount = U256::from(10u64.pow(10));
321
322 for &user_token in &tokens {
325 for &validator_token in &tokens {
326 if user_token != validator_token {
327 fee_manager.mint(
328 ADMIN,
329 user_token,
330 validator_token,
331 liquidity_amount,
332 ADMIN,
333 )?;
334 }
335 }
336 }
337
338 Ok(())
339 })?;
340
341 Ok(())
342}