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