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