tycho_simulation/evm/protocol/vm/
erc20_token.rs1use std::{collections::HashMap, str::FromStr};
2
3use alloy::primitives::{Address, U256};
4use lazy_static::lazy_static;
5
6use super::utils::get_storage_slot_index_at_key;
7use crate::evm::{ContractCompiler, SlotId};
8
9pub(crate) type Overwrites = HashMap<SlotId, U256>;
10
11lazy_static! {
13 pub static ref IMPLEMENTATION_SLOT: SlotId =
14 U256::from_str("0x6677C72CDEB41ACAF2B17EC8A6E275C4205F27DBFE4DE34EBAF2E928A7E610DB")
15 .unwrap();
16 static ref BALANCES_MAPPING_POSITION: SlotId =
17 U256::from_str("0x474F5FD57EE674F7B6851BC6F07E751B49076DFB356356985B9DAF10E9ABC941")
18 .unwrap();
19 static ref HAS_CUSTOM_BALANCE_POSITION: SlotId =
20 U256::from_str("0x7EAD8EDE9DBB385B0664952C7462C9938A5821E6F78E859DA2E683216E99411B")
21 .unwrap();
22 static ref CUSTOM_APPROVAL_MAPPING_POSITION: SlotId =
23 U256::from_str("0x71A54E125991077003BEF7E7CA57369C919DAC6D2458895F1EAB4D03960F4AEB")
24 .unwrap();
25 static ref HAS_CUSTOM_APPROVAL_MAPPING_POSITION: SlotId =
26 U256::from_str("0x9F0C1BC0E9C3078F9AD5FC59C8606416B3FABCBD4C8353FED22937C66C866CE3")
27 .unwrap();
28 static ref CUSTOM_NAME_POSITION: SlotId =
29 U256::from_str("0xCC1E513FB5BDA80DC466AD9D44DF38805A8DEE4C82B3C6DF3D9B25D3D5355D1C")
30 .unwrap();
31 static ref CUSTOM_SYMBOL_POSITION: SlotId =
32 U256::from_str("0xDC17DD3380A9A034A702A2B2B1C6C25D39EBF0E89796E0D15E1E04D23E3BB221")
33 .unwrap();
34 static ref CUSTOM_DECIMALS_POSITION: SlotId =
35 U256::from_str("0xADD486B234562DE9AC745F036F538CDA2547EF6DBB4DA3FA1C017625F888A8E8")
36 .unwrap();
37 static ref CUSTOM_TOTAL_SUPPLY_POSITION: SlotId =
38 U256::from_str("0x6014AF1E8E9BB2844581B2FA9E5E3620181C3192EEFD3258319AEC23538DA9F5")
39 .unwrap();
40 static ref HAS_CUSTOM_METADATA_POSITION: SlotId =
41 U256::from_str("0x9F37243DE61714BE9CC00628D4B9BF9897AE670218AF52ADE6D192B4339D7616")
42 .unwrap();
43}
44
45pub(crate) struct TokenProxyOverwriteFactory {
46 token_address: Address,
47 overwrites: Overwrites,
48 compiler: ContractCompiler,
49}
50
51impl TokenProxyOverwriteFactory {
52 pub(crate) fn new(token_address: Address, proxy_address: Option<Address>) -> Self {
53 let mut instance = Self {
54 token_address,
55 overwrites: HashMap::new(),
56 compiler: ContractCompiler::Solidity,
57 };
58
59 if let Some(proxy_addr) = proxy_address {
60 instance.set_original_address(proxy_addr);
61 }
62
63 instance
64 }
65
66 pub(crate) fn set_original_address(&mut self, implementation: Address) {
68 self.overwrites
69 .insert(*IMPLEMENTATION_SLOT, U256::from_be_slice(implementation.as_slice()));
70 }
71
72 pub(crate) fn set_balance(&mut self, balance: U256, owner: Address) {
73 let storage_index =
75 get_storage_slot_index_at_key(owner, *BALANCES_MAPPING_POSITION, self.compiler);
76 self.overwrites
77 .insert(storage_index, balance);
78
79 let has_balance_index =
81 get_storage_slot_index_at_key(owner, *HAS_CUSTOM_BALANCE_POSITION, self.compiler);
82 self.overwrites
83 .insert(has_balance_index, U256::from(1)); }
85
86 pub(crate) fn set_allowance(&mut self, allowance: U256, spender: Address, owner: Address) {
87 let owner_slot =
89 get_storage_slot_index_at_key(owner, *CUSTOM_APPROVAL_MAPPING_POSITION, self.compiler);
90 let storage_index = get_storage_slot_index_at_key(spender, owner_slot, self.compiler);
91 self.overwrites
92 .insert(storage_index, allowance);
93
94 let has_approval_index = get_storage_slot_index_at_key(
96 owner,
97 *HAS_CUSTOM_APPROVAL_MAPPING_POSITION,
98 self.compiler,
99 );
100 self.overwrites
101 .insert(has_approval_index, U256::from(1)); }
103
104 #[allow(dead_code)]
105 pub(crate) fn set_total_supply(&mut self, supply: U256) {
106 self.overwrites
107 .insert(*CUSTOM_TOTAL_SUPPLY_POSITION, supply);
108 }
109
110 #[allow(dead_code)]
112 fn set_metadata_flag(&mut self, key: &str) {
113 let key_bytes = string_to_storage_bytes(key);
114 let mapping_slot_bytes: [u8; 32] = HAS_CUSTOM_METADATA_POSITION.to_be_bytes();
115 let has_metadata_index = self
116 .compiler
117 .compute_map_slot(&key_bytes, &mapping_slot_bytes);
118 self.overwrites
119 .insert(has_metadata_index, U256::from(1)); }
121
122 #[allow(dead_code)]
123 pub(crate) fn set_name(&mut self, name: &str) {
124 let name_value = U256::from_be_bytes(string_to_storage_bytes(name));
126 self.overwrites
127 .insert(*CUSTOM_NAME_POSITION, name_value);
128
129 self.set_metadata_flag("name");
131 }
132
133 #[allow(dead_code)]
134 pub(crate) fn set_symbol(&mut self, symbol: &str) {
135 let symbol_value = U256::from_be_bytes(string_to_storage_bytes(symbol));
137 self.overwrites
138 .insert(*CUSTOM_SYMBOL_POSITION, symbol_value);
139
140 self.set_metadata_flag("symbol");
142 }
143
144 #[allow(dead_code)]
145 pub(crate) fn set_decimals(&mut self, decimals: u8) {
146 self.overwrites
147 .insert(*CUSTOM_DECIMALS_POSITION, U256::from(decimals));
148
149 self.set_metadata_flag("decimals");
151 }
152
153 pub(crate) fn get_overwrites(&self) -> HashMap<Address, Overwrites> {
154 let mut result = HashMap::new();
155 result.insert(self.token_address, self.overwrites.clone());
156 result
157 }
158}
159
160pub fn string_to_storage_bytes(s: &str) -> [u8; 32] {
162 let mut padded = [0u8; 32];
163 let len = s.len().min(31);
164 padded[..len].copy_from_slice(&s.as_bytes()[..len]);
165 padded[31] = (len * 2) as u8; padded
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 fn get_metadata_slot(key: &str) -> SlotId {
174 let key_bytes = string_to_storage_bytes(key);
175 let mapping_slot_bytes: [u8; 32] = HAS_CUSTOM_METADATA_POSITION.to_be_bytes();
176 ContractCompiler::Solidity.compute_map_slot(&key_bytes, &mapping_slot_bytes)
177 }
178
179 #[test]
180 fn test_token_proxy_factory_new() {
181 let token_address = Address::random();
182 let factory = TokenProxyOverwriteFactory::new(token_address, None);
183 assert_eq!(factory.token_address, token_address);
184 assert!(factory.overwrites.is_empty());
185 }
186
187 #[test]
188 fn test_token_proxy_factory_with_implementation() {
189 let token_address = Address::random();
190 let implementation = Address::random();
191 let factory = TokenProxyOverwriteFactory::new(token_address, Some(implementation));
192
193 let mut expected_bytes = [0u8; 32];
195 expected_bytes[12..].copy_from_slice(implementation.as_slice());
196 let expected_value = U256::from_be_bytes(expected_bytes);
197
198 assert_eq!(factory.overwrites[&*IMPLEMENTATION_SLOT], expected_value);
199 }
200
201 #[test]
202 fn test_token_proxy_set_balance() {
203 let mut factory = TokenProxyOverwriteFactory::new(Address::random(), None);
204 let owner = Address::random();
205 let balance = U256::from(1000);
206
207 factory.set_balance(balance, owner);
208
209 let storage_index =
211 get_storage_slot_index_at_key(owner, *BALANCES_MAPPING_POSITION, factory.compiler);
212 assert_eq!(factory.overwrites[&storage_index], balance);
213
214 let has_balance_index =
216 get_storage_slot_index_at_key(owner, *HAS_CUSTOM_BALANCE_POSITION, factory.compiler);
217 assert_eq!(factory.overwrites[&has_balance_index], U256::from(1));
218 }
219
220 #[test]
221 fn test_token_proxy_set_allowance() {
222 let mut factory = TokenProxyOverwriteFactory::new(Address::random(), None);
223 let owner = Address::random();
224 let spender = Address::random();
225 let allowance = U256::from(500);
226
227 factory.set_allowance(allowance, spender, owner);
228
229 let owner_slot = get_storage_slot_index_at_key(
231 owner,
232 *CUSTOM_APPROVAL_MAPPING_POSITION,
233 factory.compiler,
234 );
235 let storage_index = get_storage_slot_index_at_key(spender, owner_slot, factory.compiler);
236 assert_eq!(factory.overwrites[&storage_index], allowance);
237
238 let has_approval_index = get_storage_slot_index_at_key(
240 owner,
241 *HAS_CUSTOM_APPROVAL_MAPPING_POSITION,
242 factory.compiler,
243 );
244 assert_eq!(factory.overwrites[&has_approval_index], U256::from(1));
245 }
246
247 #[test]
248 fn test_token_proxy_set_total_supply() {
249 let mut factory = TokenProxyOverwriteFactory::new(Address::random(), None);
250 let supply = U256::from(1_000_000);
251
252 factory.set_total_supply(supply);
253
254 assert_eq!(factory.overwrites[&*CUSTOM_TOTAL_SUPPLY_POSITION], supply);
255 }
256
257 #[test]
258 fn test_token_proxy_set_name() {
259 let mut factory = TokenProxyOverwriteFactory::new(Address::random(), None);
260 let name = "Test Token";
261
262 factory.set_name(name);
263
264 let mut expected_bytes = [0u8; 32];
266 let name_bytes = name.as_bytes();
267 expected_bytes[..name_bytes.len()].copy_from_slice(name_bytes);
268 expected_bytes[31] = (name_bytes.len() * 2) as u8; let expected_value = U256::from_be_bytes(expected_bytes);
270 assert_eq!(factory.overwrites[&*CUSTOM_NAME_POSITION], expected_value);
271
272 let has_metadata_index = get_metadata_slot("name");
274 assert_eq!(factory.overwrites[&has_metadata_index], U256::from(1));
275 }
276
277 #[test]
278 fn test_token_proxy_set_symbol() {
279 let mut factory = TokenProxyOverwriteFactory::new(Address::random(), None);
280 let symbol = "TEST";
281
282 factory.set_symbol(symbol);
283
284 let mut expected_bytes = [0u8; 32];
286 let symbol_bytes = symbol.as_bytes();
287 expected_bytes[..symbol_bytes.len()].copy_from_slice(symbol_bytes);
288 expected_bytes[31] = (symbol_bytes.len() * 2) as u8; let expected_value = U256::from_be_bytes(expected_bytes);
290 assert_eq!(factory.overwrites[&*CUSTOM_SYMBOL_POSITION], expected_value);
291
292 let has_metadata_index = get_metadata_slot("symbol");
294 assert_eq!(factory.overwrites[&has_metadata_index], U256::from(1));
295 }
296
297 #[test]
298 fn test_token_proxy_set_decimals() {
299 let mut factory = TokenProxyOverwriteFactory::new(Address::random(), None);
300 let decimals = 18u8;
301
302 factory.set_decimals(decimals);
303
304 assert_eq!(factory.overwrites[&*CUSTOM_DECIMALS_POSITION], U256::from(decimals));
305
306 let has_metadata_index = get_metadata_slot("decimals");
308 assert_eq!(factory.overwrites[&has_metadata_index], U256::from(1));
309 }
310
311 #[test]
312 fn test_token_proxy_get_overwrites() {
313 let mut factory = TokenProxyOverwriteFactory::new(Address::random(), None);
314 let supply = U256::from(1_000_000);
315 factory.set_total_supply(supply);
316
317 let overwrites = factory.get_overwrites();
318
319 assert_eq!(overwrites.len(), 1);
320 assert!(overwrites.contains_key(&factory.token_address));
321 assert_eq!(overwrites[&factory.token_address][&*CUSTOM_TOTAL_SUPPLY_POSITION], supply);
322 }
323
324 #[test]
325 fn test_token_proxy_set_long_name_truncated() {
326 let mut factory = TokenProxyOverwriteFactory::new(Address::random(), None);
327 let name = "This is a very long token name that exceeds 31 bytes";
328
329 factory.set_name(name);
330
331 let mut expected_bytes = [0u8; 32];
333 expected_bytes[..31].copy_from_slice(&name.as_bytes()[..31]);
334 expected_bytes[31] = 62; let expected_value = U256::from_be_bytes(expected_bytes);
336 assert_eq!(factory.overwrites[&*CUSTOM_NAME_POSITION], expected_value);
337
338 let has_metadata_index = get_metadata_slot("name");
340 assert_eq!(factory.overwrites[&has_metadata_index], U256::from(1));
341 }
342
343 #[test]
344 fn test_token_proxy_set_long_symbol_truncated() {
345 let mut factory = TokenProxyOverwriteFactory::new(Address::random(), None);
346 let symbol = "This is a very long token symbol that exceeds 31 bytes";
347
348 factory.set_symbol(symbol);
349
350 let mut expected_bytes = [0u8; 32];
352 expected_bytes[..31].copy_from_slice(&symbol.as_bytes()[..31]);
353 expected_bytes[31] = 62; let expected_value = U256::from_be_bytes(expected_bytes);
355 assert_eq!(factory.overwrites[&*CUSTOM_SYMBOL_POSITION], expected_value);
356
357 let has_metadata_index = get_metadata_slot("symbol");
359 assert_eq!(factory.overwrites[&has_metadata_index], U256::from(1));
360 }
361
362 #[test]
363 fn test_string_to_storage_bytes() {
364 let short = "Test";
366 let bytes = string_to_storage_bytes(short);
367 assert_eq!(bytes[..4], short.as_bytes()[..4]);
368 assert_eq!(bytes[31], 8); let long = "This is a very long string that exceeds 31 bytes";
372 let bytes = string_to_storage_bytes(long);
373 assert_eq!(bytes[..31], long.as_bytes()[..31]);
374 assert_eq!(bytes[31], 62); }
376
377 #[test]
378 fn test_set_metadata_flag() {
379 let mut factory = TokenProxyOverwriteFactory::new(Address::random(), None);
380
381 factory.set_metadata_flag("test_key");
383
384 let key_bytes = string_to_storage_bytes("test_key");
386 let mapping_slot_bytes: [u8; 32] = HAS_CUSTOM_METADATA_POSITION.to_be_bytes();
387 let has_metadata_index =
388 ContractCompiler::Solidity.compute_map_slot(&key_bytes, &mapping_slot_bytes);
389 assert_eq!(factory.overwrites[&has_metadata_index], U256::from(1));
390 }
391}