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