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,
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::{SignatureType, authorizeKeyCall},
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
40const PATH_USD: Address = PATH_USD_ADDRESS;
41const ALPHA_USD: Address = ALPHA_USD_ADDRESS;
42const BETA_USD: Address = BETA_USD_ADDRESS;
43const THETA_USD: Address = THETA_USD_ADDRESS;
44
45pub struct AnvilStorageProvider<'a> {
47 db: &'a mut dyn Db,
48 chain_id: u64,
49 timestamp: U256,
50 block_number: u64,
51 gas_used: u64,
52 gas_refunded: i64,
53 transient: HashMap<(Address, U256), U256>,
54 hardfork: TempoHardfork,
55}
56
57impl<'a> AnvilStorageProvider<'a> {
58 pub fn new(
59 db: &'a mut dyn Db,
60 chain_id: u64,
61 timestamp: U256,
62 block_number: u64,
63 hardfork: TempoHardfork,
64 ) -> Self {
65 Self {
66 db,
67 chain_id,
68 timestamp,
69 block_number,
70 gas_used: 0,
71 gas_refunded: 0,
72 transient: HashMap::new(),
73 hardfork,
74 }
75 }
76}
77
78impl PrecompileStorageProvider for AnvilStorageProvider<'_> {
79 fn spec(&self) -> TempoHardfork {
80 self.hardfork
81 }
82
83 fn chain_id(&self) -> u64 {
84 self.chain_id
85 }
86
87 fn timestamp(&self) -> U256 {
88 self.timestamp
89 }
90
91 fn block_number(&self) -> u64 {
92 self.block_number
93 }
94
95 fn set_code(&mut self, address: Address, code: Bytecode) -> Result<(), TempoPrecompileError> {
96 self.db.insert_account(
97 address,
98 AccountInfo {
99 code_hash: code.hash_slow(),
100 code: Some(code),
101 nonce: 1,
102 ..Default::default()
103 },
104 );
105 Ok(())
106 }
107
108 fn with_account_info(
109 &mut self,
110 address: Address,
111 f: &mut dyn FnMut(&AccountInfo),
112 ) -> Result<(), TempoPrecompileError> {
113 use revm::DatabaseRef;
114 if let Some(info) =
115 self.db.basic_ref(address).map_err(|e| TempoPrecompileError::Fatal(e.to_string()))?
116 {
117 f(&info);
118 Ok(())
119 } else {
120 Err(TempoPrecompileError::Fatal(format!("account '{address}' not found")))
121 }
122 }
123
124 fn sstore(
125 &mut self,
126 address: Address,
127 key: U256,
128 value: U256,
129 ) -> Result<(), TempoPrecompileError> {
130 use alloy_primitives::B256;
131 self.db
132 .set_storage_at(address, B256::from(key), B256::from(value))
133 .map_err(|e| TempoPrecompileError::Fatal(e.to_string()))
134 }
135
136 fn sload(&mut self, address: Address, key: U256) -> Result<U256, TempoPrecompileError> {
137 revm::Database::storage(self.db, address, key)
138 .map_err(|e| TempoPrecompileError::Fatal(e.to_string()))
139 }
140
141 fn tstore(
142 &mut self,
143 address: Address,
144 key: U256,
145 value: U256,
146 ) -> Result<(), TempoPrecompileError> {
147 self.transient.insert((address, key), value);
148 Ok(())
149 }
150
151 fn tload(&mut self, address: Address, key: U256) -> Result<U256, TempoPrecompileError> {
152 Ok(self.transient.get(&(address, key)).copied().unwrap_or(U256::ZERO))
153 }
154
155 fn emit_event(
156 &mut self,
157 _address: Address,
158 _event: alloy_primitives::LogData,
159 ) -> Result<(), TempoPrecompileError> {
160 Ok(())
161 }
162
163 fn deduct_gas(&mut self, gas: u64) -> Result<(), TempoPrecompileError> {
164 self.gas_used = self.gas_used.saturating_add(gas);
165 Ok(())
166 }
167
168 fn gas_used(&self) -> u64 {
169 self.gas_used
170 }
171
172 fn gas_refunded(&self) -> i64 {
173 self.gas_refunded
174 }
175
176 fn refund_gas(&mut self, gas: i64) {
177 self.gas_refunded = self.gas_refunded.saturating_add(gas);
178 }
179
180 fn beneficiary(&self) -> Address {
181 Address::ZERO
182 }
183
184 fn is_static(&self) -> bool {
185 false
186 }
187
188 fn checkpoint(&mut self) -> JournalCheckpoint {
189 JournalCheckpoint { log_i: 0, journal_i: 0, selfdestructed_i: 0 }
190 }
191
192 fn checkpoint_commit(&mut self, _checkpoint: JournalCheckpoint) {}
193
194 fn checkpoint_revert(&mut self, _checkpoint: JournalCheckpoint) {}
195}
196
197pub fn initialize_tempo_precompiles(
205 db: &mut dyn Db,
206 chain_id: u64,
207 timestamp: u64,
208 test_accounts: &[Address],
209 hardfork: TempoHardfork,
210) -> Result<(), TempoPrecompileError> {
211 let timestamp = U256::from(timestamp);
212
213 let mut storage = AnvilStorageProvider::new(db, chain_id, timestamp, 0, hardfork);
214
215 initialize_tempo_genesis(&mut storage, ADMIN, SENDER)?;
217
218 let mint_amount = U256::from(u64::MAX);
221 let tokens = [PATH_USD, ALPHA_USD, BETA_USD, THETA_USD];
222
223 StorageCtx::enter(&mut storage, || -> Result<(), TempoPrecompileError> {
224 for &token_address in &tokens {
226 let mut token = TIP20Token::from_address(token_address)?;
227 for &account in test_accounts {
228 token.mint(ADMIN, ITIP20::mintCall { to: account, amount: mint_amount })?;
229 }
230 }
231
232 let mut keychain = AccountKeychain::new();
236 for &account in test_accounts {
237 keychain.set_tx_origin(account)?;
240 keychain.authorize_key(
241 account, authorizeKeyCall {
243 keyId: account, signatureType: SignatureType::Secp256k1,
245 expiry: u64::MAX, enforceLimits: false, limits: vec![],
248 },
249 )?;
250 }
251
252 let mut fee_manager = TipFeeManager::new();
255 fee_manager.initialize()?;
256
257 for (i, &account) in test_accounts.iter().enumerate() {
258 let fee_token = match i {
259 0 => ALPHA_USD, 1 => BETA_USD, 2 => THETA_USD, _ => PATH_USD, };
264 fee_manager
265 .set_user_token(account, IFeeManager::setUserTokenCall { token: fee_token })?;
266 }
267
268 for &token_address in &tokens {
270 let mut token = TIP20Token::from_address(token_address)?;
271 token.mint(
272 ADMIN,
273 ITIP20::mintCall { to: TIP_FEE_MANAGER_ADDRESS, amount: mint_amount },
274 )?;
275 }
276
277 let liquidity_amount = U256::from(10u64.pow(10));
281
282 for &user_token in &tokens {
285 for &validator_token in &tokens {
286 if user_token != validator_token {
287 fee_manager.mint(
288 ADMIN,
289 user_token,
290 validator_token,
291 liquidity_amount,
292 ADMIN,
293 )?;
294 }
295 }
296 }
297
298 Ok(())
299 })?;
300
301 Ok(())
302}