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(address, AccountInfo::from_bytecode(code));
99 Ok(())
100 }
101
102 fn with_account_info(
103 &mut self,
104 address: Address,
105 f: &mut dyn FnMut(&AccountInfo),
106 ) -> Result<(), TempoPrecompileError> {
107 if let Some(info) =
108 self.db.basic_ref(address).map_err(|e| TempoPrecompileError::Fatal(e.to_string()))?
109 {
110 f(&info);
111 Ok(())
112 } else {
113 Err(TempoPrecompileError::Fatal(format!("account '{address}' not found")))
114 }
115 }
116
117 fn account_code(&mut self, address: Address) -> Result<(B256, Bytecode), TempoPrecompileError> {
118 let Some(info) =
119 self.db.basic_ref(address).map_err(|e| TempoPrecompileError::Fatal(e.to_string()))?
120 else {
121 return Ok((B256::ZERO, Bytecode::default()));
122 };
123 let code_hash = info.code_hash();
124 let code = if let Some(code) = info.code {
125 code
126 } else {
127 self.db
128 .code_by_hash_ref(code_hash)
129 .map_err(|e| TempoPrecompileError::Fatal(e.to_string()))?
130 };
131 Ok((code_hash, code))
132 }
133
134 fn sstore(
135 &mut self,
136 address: Address,
137 key: U256,
138 value: U256,
139 ) -> Result<(), TempoPrecompileError> {
140 self.db
141 .set_storage_at(address, B256::from(key), B256::from(value))
142 .map_err(|e| TempoPrecompileError::Fatal(e.to_string()))
143 }
144
145 fn sload(&mut self, address: Address, key: U256) -> Result<U256, TempoPrecompileError> {
146 revm::Database::storage(self.db, address, key)
147 .map_err(|e| TempoPrecompileError::Fatal(e.to_string()))
148 }
149
150 fn tstore(
151 &mut self,
152 address: Address,
153 key: U256,
154 value: U256,
155 ) -> Result<(), TempoPrecompileError> {
156 self.transient.insert((address, key), value);
157 Ok(())
158 }
159
160 fn tload(&mut self, address: Address, key: U256) -> Result<U256, TempoPrecompileError> {
161 Ok(self.transient.get(&(address, key)).copied().unwrap_or(U256::ZERO))
162 }
163
164 fn emit_event(
165 &mut self,
166 _address: Address,
167 _event: alloy_primitives::LogData,
168 ) -> Result<(), TempoPrecompileError> {
169 Ok(())
170 }
171
172 fn deduct_gas(&mut self, gas: u64) -> Result<(), TempoPrecompileError> {
173 self.gas_used = self.gas_used.saturating_add(gas);
174 Ok(())
175 }
176
177 fn gas_used(&self) -> u64 {
178 self.gas_used
179 }
180
181 fn state_gas_used(&self) -> u64 {
182 0
183 }
184
185 fn state_gas_spilled(&self) -> u64 {
186 0
187 }
188
189 fn gas_limit(&self) -> u64 {
190 u64::MAX
191 }
192
193 fn gas_refunded(&self) -> i64 {
194 self.gas_refunded
195 }
196
197 fn reservoir(&self) -> u64 {
198 self.reservoir
199 }
200
201 fn refund_gas(&mut self, gas: i64) {
202 self.gas_refunded = self.gas_refunded.saturating_add(gas);
203 }
204
205 fn is_static(&self) -> bool {
206 false
207 }
208
209 fn checkpoint(&mut self) -> JournalCheckpoint {
210 JournalCheckpoint { log_i: 0, journal_i: 0, selfdestructed_i: 0 }
211 }
212
213 fn checkpoint_commit(&mut self, _checkpoint: JournalCheckpoint) {}
214
215 fn checkpoint_revert(&mut self, _checkpoint: JournalCheckpoint) {}
216
217 fn amsterdam_eip8037_enabled(&self) -> bool {
218 false
219 }
220
221 fn set_tip1060_storage_credits(&mut self, enabled: bool) {
222 self.tip1060_storage_credits_enabled = enabled && self.hardfork.is_t7();
223 }
224}
225
226pub fn initialize_tempo_precompiles(
234 db: &mut dyn Db,
235 chain_id: u64,
236 timestamp: u64,
237 test_accounts: &[Address],
238 hardfork: TempoHardfork,
239) -> Result<(), TempoPrecompileError> {
240 let timestamp = U256::from(timestamp);
241
242 let mut storage = AnvilStorageProvider::new(db, chain_id, timestamp, 0, hardfork);
243
244 initialize_tempo_genesis_at_hardfork(&mut storage, ADMIN, SENDER, hardfork)?;
246
247 let mint_amount = U256::from(u64::MAX);
250 let tokens = [PATH_USD_ADDRESS, ALPHA_USD_ADDRESS, BETA_USD_ADDRESS, THETA_USD_ADDRESS];
251
252 StorageCtx::enter(&mut storage, || -> Result<(), TempoPrecompileError> {
253 for &token_address in &tokens {
255 let mut token = TIP20Token::from_address(token_address)?;
256 for &account in test_accounts {
257 token.mint(ADMIN, ITIP20::mintCall { to: account, amount: mint_amount })?;
258 }
259 }
260
261 let mut keychain = AccountKeychain::new();
265 for &account in test_accounts {
266 keychain.set_tx_origin(account)?;
269 keychain.authorize_key(
270 account, account, SignatureType::Secp256k1,
273 KeyRestrictions {
274 expiry: u64::MAX, enforceLimits: false, limits: vec![],
277 allowAnyCalls: true,
278 allowedCalls: vec![],
279 },
280 None,
281 )?;
282 }
283
284 let mut fee_manager = TipFeeManager::new();
287 fee_manager.initialize()?;
288
289 for (i, &account) in test_accounts.iter().enumerate() {
290 let fee_token = match i {
291 0 => ALPHA_USD_ADDRESS, 1 => BETA_USD_ADDRESS, 2 => THETA_USD_ADDRESS, _ => PATH_USD_ADDRESS, };
296 fee_manager
297 .set_user_token(account, IFeeManager::setUserTokenCall { token: fee_token })?;
298 }
299
300 for &token_address in &tokens {
302 let mut token = TIP20Token::from_address(token_address)?;
303 token.mint(
304 ADMIN,
305 ITIP20::mintCall { to: TIP_FEE_MANAGER_ADDRESS, amount: mint_amount },
306 )?;
307 }
308
309 let liquidity_amount = U256::from(10u64.pow(10));
313
314 for &user_token in &tokens {
317 for &validator_token in &tokens {
318 if user_token != validator_token {
319 fee_manager.mint(
320 ADMIN,
321 user_token,
322 validator_token,
323 liquidity_amount,
324 ADMIN,
325 )?;
326 }
327 }
328 }
329
330 Ok(())
331 })?;
332
333 Ok(())
334}