1#![allow(deprecated)]
2use std::{
3 any::Any,
4 collections::{HashMap, HashSet},
5 fmt::{self, Debug},
6 str::FromStr,
7 time::{SystemTime, UNIX_EPOCH},
8};
9
10use alloy::primitives::{Address, U256};
11use itertools::Itertools;
12use num_bigint::BigUint;
13use revm::DatabaseRef;
14use serde::{Deserialize, Serialize};
15use tokio::sync::watch;
16use tycho_common::{
17 dto::ProtocolStateDelta,
18 models::token::Token,
19 simulation::{
20 errors::{SimulationError, TransitionError},
21 protocol_sim::{Balances, GetAmountOutResult, ProtocolSim},
22 },
23 Bytes,
24};
25
26use super::{
27 constants::{EXTERNAL_ACCOUNT, MAX_BALANCE},
28 erc20_token::{Overwrites, TokenProxyOverwriteFactory},
29 models::Capability,
30 tycho_simulation_contract::TychoSimulationContract,
31};
32use crate::evm::{
33 engine_db::{engine_db_interface::EngineDatabaseInterface, tycho_db::PreCachedDB},
34 override_stream::OverrideSnapshot,
35 protocol::{
36 u256_num::{u256_to_biguint, u256_to_f64},
37 utils::bytes_to_address,
38 },
39 simulation::BlockEnvOverrides,
40};
41
42#[derive(Clone)]
43pub struct EVMPoolState<D: EngineDatabaseInterface + Clone + Debug>
44where
45 <D as DatabaseRef>::Error: Debug,
46 <D as EngineDatabaseInterface>::Error: Debug,
47{
48 id: String,
50 pub tokens: Vec<Bytes>,
52 balances: HashMap<Address, U256>,
54 #[deprecated(note = "Use contract_balances instead")]
58 balance_owner: Option<Address>,
59 spot_prices: HashMap<(Address, Address), f64>,
61 capabilities: HashSet<Capability>,
63 block_lasting_overwrites: HashMap<Address, Overwrites>,
66 involved_contracts: HashSet<Address>,
68 contract_balances: HashMap<Address, HashMap<Address, U256>>,
70 manual_updates: bool,
74 adapter_contract: TychoSimulationContract<D>,
76 disable_overwrite_tokens: HashSet<Address>,
78 self_contained_tokens: HashSet<Address>,
86 block_overrides: Option<BlockEnvOverrides>,
88 live_overrides: Option<watch::Receiver<OverrideSnapshot>>,
95}
96
97impl<D> Debug for EVMPoolState<D>
98where
99 D: EngineDatabaseInterface + Clone + Debug,
100 <D as DatabaseRef>::Error: Debug,
101 <D as EngineDatabaseInterface>::Error: Debug,
102{
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 f.debug_struct("EVMPoolState")
105 .field("id", &self.id)
106 .field("tokens", &self.tokens)
107 .field("balances", &self.balances)
108 .field("involved_contracts", &self.involved_contracts)
109 .field("contract_balances", &self.contract_balances)
110 .finish_non_exhaustive()
111 }
112}
113
114impl<D> EVMPoolState<D>
115where
116 D: EngineDatabaseInterface + Clone + Debug + 'static,
117 <D as DatabaseRef>::Error: Debug,
118 <D as EngineDatabaseInterface>::Error: Debug,
119{
120 #[allow(clippy::too_many_arguments)]
125 pub fn new(
126 id: String,
127 tokens: Vec<Bytes>,
128 component_balances: HashMap<Address, U256>,
129 balance_owner: Option<Address>,
130 contract_balances: HashMap<Address, HashMap<Address, U256>>,
131 spot_prices: HashMap<(Address, Address), f64>,
132 capabilities: HashSet<Capability>,
133 block_lasting_overwrites: HashMap<Address, Overwrites>,
134 involved_contracts: HashSet<Address>,
135 manual_updates: bool,
136 adapter_contract: TychoSimulationContract<D>,
137 disable_overwrite_tokens: HashSet<Address>,
138 self_contained_tokens: HashSet<Address>,
139 block_overrides: Option<BlockEnvOverrides>,
140 ) -> Self {
141 Self {
142 id,
143 tokens,
144 balances: component_balances,
145 balance_owner,
146 spot_prices,
147 capabilities,
148 block_lasting_overwrites,
149 involved_contracts,
150 contract_balances,
151 manual_updates,
152 adapter_contract,
153 disable_overwrite_tokens,
154 self_contained_tokens,
155 block_overrides,
156 live_overrides: None,
157 }
158 }
159
160 pub fn set_live_overrides(&mut self, receiver: watch::Receiver<OverrideSnapshot>) {
164 self.live_overrides = Some(receiver);
165 }
166
167 fn get_live_snapshot(&self) -> Option<OverrideSnapshot> {
177 let snapshot = self
178 .live_overrides
179 .as_ref()
180 .map(|receiver| receiver.borrow().clone())?;
181 let now = SystemTime::now()
182 .duration_since(UNIX_EPOCH)
183 .map(|elapsed| elapsed.as_secs())
184 .unwrap_or(0);
185 if snapshot.is_expired(now) {
186 return None;
187 }
188 Some(snapshot)
189 }
190
191 fn block_env(&self, live: Option<&OverrideSnapshot>) -> Option<BlockEnvOverrides> {
195 let base = self.block_overrides.to_owned();
196 let Some(snapshot) = live else {
197 return base;
198 };
199 if snapshot.block_number.is_none() && snapshot.block_timestamp.is_none() {
200 return base;
201 }
202 let mut overrides = base.unwrap_or_default();
203 if snapshot.block_number.is_some() {
204 overrides.number = snapshot.block_number;
205 }
206 if snapshot.block_timestamp.is_some() {
207 overrides.timestamp = snapshot.block_timestamp;
208 }
209 Some(overrides)
210 }
211
212 fn ensure_capability(&self, capability: Capability) -> Result<(), SimulationError> {
223 if !self.capabilities.contains(&capability) {
224 return Err(SimulationError::FatalError(format!(
225 "capability {:?} not supported",
226 capability.to_string()
227 )));
228 }
229 Ok(())
230 }
231 pub fn set_spot_prices(
267 &mut self,
268 tokens: &HashMap<Bytes, Token>,
269 ) -> Result<(), SimulationError> {
270 let live_snapshot = self.get_live_snapshot();
274 let block_overrides = self.block_env(live_snapshot.as_ref());
275 match self.ensure_capability(Capability::PriceFunction) {
276 Ok(_) => {
277 for [sell_token_address, buy_token_address] in self
278 .tokens
279 .iter()
280 .permutations(2)
281 .map(|p| [p[0], p[1]])
282 {
283 let sell_token_address = bytes_to_address(sell_token_address)?;
284 let buy_token_address = bytes_to_address(buy_token_address)?;
285
286 let overwrites = Some(self.get_overwrites(
287 vec![sell_token_address, buy_token_address],
288 *MAX_BALANCE / U256::from(100),
289 live_snapshot.as_ref(),
290 )?);
291
292 let (sell_amount_limit, _) = self.get_amount_limits(
293 vec![sell_token_address, buy_token_address],
294 overwrites.clone(),
295 block_overrides.clone(),
296 )?;
297 let price_result = self.adapter_contract.price(
298 &self.id,
299 sell_token_address,
300 buy_token_address,
301 vec![sell_amount_limit / U256::from(100)],
302 overwrites,
303 block_overrides.clone(),
304 )?;
305
306 let price = if self
307 .capabilities
308 .contains(&Capability::ScaledPrice)
309 {
310 *price_result.first().ok_or_else(|| {
311 SimulationError::FatalError(
312 "Calculated price array is empty".to_string(),
313 )
314 })?
315 } else {
316 let unscaled_price = price_result.first().ok_or_else(|| {
317 SimulationError::FatalError(
318 "Calculated price array is empty".to_string(),
319 )
320 })?;
321 let sell_token_decimals = self.get_decimals(tokens, &sell_token_address)?;
322 let buy_token_decimals = self.get_decimals(tokens, &buy_token_address)?;
323 *unscaled_price * 10f64.powi(sell_token_decimals as i32) /
324 10f64.powi(buy_token_decimals as i32)
325 };
326
327 self.spot_prices
328 .insert((sell_token_address, buy_token_address), price);
329 }
330 }
331 Err(SimulationError::FatalError(_)) => {
332 for iter_tokens in self.tokens.iter().permutations(2) {
336 let t0 = bytes_to_address(iter_tokens[0])?;
337 let t1 = bytes_to_address(iter_tokens[1])?;
338
339 let overwrites = Some(self.get_overwrites(
340 vec![t0, t1],
341 *MAX_BALANCE / U256::from(100),
342 live_snapshot.as_ref(),
343 )?);
344
345 let x1 =
347 self.get_amount_limits(
348 vec![t0, t1],
349 overwrites.clone(),
350 block_overrides.clone(),
351 )?
352 .0 / U256::from(100);
353
354 let x2 = x1 + (x1 / U256::from(100));
357
358 let y1 = self
361 .adapter_contract
362 .swap(
363 &self.id,
364 t0,
365 t1,
366 false,
367 x1,
368 overwrites.clone(),
369 block_overrides.clone(),
370 )?
371 .0
372 .received_amount;
373
374 let y2 = self
377 .adapter_contract
378 .swap(&self.id, t0, t1, false, x2, overwrites, block_overrides.clone())?
379 .0
380 .received_amount;
381
382 let sell_token_decimals = self.get_decimals(tokens, &t0)?;
383 let buy_token_decimals = self.get_decimals(tokens, &t1)?;
384
385 let num = y2 - y1;
386 let den = x2 - x1;
387
388 let token_correction =
390 10f64.powi(sell_token_decimals as i32 - buy_token_decimals as i32);
391 let num_f64 = u256_to_f64(num)?;
392 let den_f64 = u256_to_f64(den)?;
393 if den_f64 == 0.0 {
394 return Err(SimulationError::FatalError(
395 "Failed to compute marginal price: denominator converted to 0".into(),
396 ));
397 }
398 let marginal_price = num_f64 / den_f64 * token_correction;
399
400 self.spot_prices
401 .insert((t0, t1), marginal_price);
402 }
403 }
404 Err(e) => return Err(e),
405 }
406
407 Ok(())
408 }
409
410 fn get_decimals(
411 &self,
412 tokens: &HashMap<Bytes, Token>,
413 sell_token_address: &Address,
414 ) -> Result<usize, SimulationError> {
415 tokens
416 .get(&Bytes::from(sell_token_address.as_slice()))
417 .map(|t| t.decimals as usize)
418 .ok_or_else(|| {
419 SimulationError::FatalError(format!(
420 "Failed to scale spot prices! Pool: {} Token 0x{:x} is not available!",
421 self.id, sell_token_address
422 ))
423 })
424 }
425
426 fn get_amount_limits(
443 &self,
444 tokens: Vec<Address>,
445 overwrites: Option<HashMap<Address, HashMap<U256, U256>>>,
446 block_overrides: Option<BlockEnvOverrides>,
447 ) -> Result<(U256, U256), SimulationError> {
448 let limits = self.adapter_contract.get_limits(
449 &self.id,
450 tokens[0],
451 tokens[1],
452 overwrites,
453 block_overrides,
454 )?;
455
456 Ok(limits)
457 }
458
459 fn update_pool_state(
470 &mut self,
471 tokens: &HashMap<Bytes, Token>,
472 balances: &Balances,
473 ) -> Result<(), SimulationError> {
474 self.adapter_contract
476 .engine
477 .clear_temp_storage()
478 .map_err(|err| {
479 SimulationError::FatalError(format!("Failed to clear temporary storage: {err:?}",))
480 })?;
481 self.block_lasting_overwrites.clear();
482
483 if !self.balances.is_empty() {
485 if let Some(bals) = balances
487 .component_balances
488 .get(&self.id)
489 {
490 for (token, bal) in bals {
493 let addr = bytes_to_address(token).map_err(|_| {
494 SimulationError::FatalError(format!(
495 "Invalid token address in balance update: {token:?}"
496 ))
497 })?;
498 self.balances
499 .insert(addr, U256::from_be_slice(bal));
500 }
501 }
502 } else {
503 for contract in &self.involved_contracts {
505 if let Some(bals) = balances
506 .account_balances
507 .get(&Bytes::from(contract.as_slice()))
508 {
509 let contract_entry = self
510 .contract_balances
511 .entry(*contract)
512 .or_default();
513 for (token, bal) in bals {
514 let addr = bytes_to_address(token).map_err(|_| {
515 SimulationError::FatalError(format!(
516 "Invalid token address in balance update: {token:?}"
517 ))
518 })?;
519 contract_entry.insert(addr, U256::from_be_slice(bal));
520 }
521 }
522 }
523 }
524
525 self.set_spot_prices(tokens)?;
527 Ok(())
528 }
529
530 fn get_overwrites(
531 &self,
532 tokens: Vec<Address>,
533 max_amount: U256,
534 live: Option<&OverrideSnapshot>,
535 ) -> Result<HashMap<Address, Overwrites>, SimulationError> {
536 let token_overwrites = self.get_token_overwrites(tokens, max_amount)?;
537
538 let mut merged_overwrites =
540 self.merge(self.block_lasting_overwrites.clone(), token_overwrites);
541
542 if let Some(live) = live {
544 if !live.storage.is_empty() {
545 merged_overwrites = self.merge(merged_overwrites, live.storage.as_ref().clone());
546 }
547 }
548
549 Ok(merged_overwrites)
550 }
551
552 fn get_token_overwrites(
553 &self,
554 tokens: Vec<Address>,
555 max_amount: U256,
556 ) -> Result<HashMap<Address, Overwrites>, SimulationError> {
557 let sell_token = &tokens[0].clone(); let mut res: Vec<HashMap<Address, Overwrites>> = Vec::new();
559 if !self
560 .capabilities
561 .contains(&Capability::TokenBalanceIndependent)
562 {
563 res.push(self.get_balance_overwrites()?);
564 }
565
566 let mut overwrites = TokenProxyOverwriteFactory::new(*sell_token, None);
567
568 overwrites.set_balance(max_amount, Address::from_slice(&*EXTERNAL_ACCOUNT.0));
569
570 overwrites.set_allowance(max_amount, self.adapter_contract.address, *EXTERNAL_ACCOUNT);
572
573 res.push(overwrites.get_overwrites());
574
575 for token in tokens.iter().skip(1) {
579 if self
580 .self_contained_tokens
581 .contains(token) &&
582 !self
583 .disable_overwrite_tokens
584 .contains(token)
585 {
586 let mut recipient = TokenProxyOverwriteFactory::new(*token, None);
587 recipient.set_balance(U256::ZERO, *EXTERNAL_ACCOUNT);
588 res.push(recipient.get_overwrites());
589 }
590 }
591
592 Ok(res
594 .into_iter()
595 .fold(HashMap::new(), |acc, overwrite| self.merge(acc, overwrite)))
596 }
597
598 fn get_balance_overwrites(&self) -> Result<HashMap<Address, Overwrites>, SimulationError> {
609 let mut balance_overwrites: HashMap<Address, Overwrites> = HashMap::new();
610
611 let address = match self.balance_owner {
613 Some(owner) => Some(owner),
614 None if !self.contract_balances.is_empty() => None,
615 None => Some(self.id.parse().map_err(|_| {
616 SimulationError::FatalError(
617 "Failed to get balance overwrites: Pool ID is not an address".into(),
618 )
619 })?),
620 };
621
622 if let Some(address) = address {
623 for (token, bal) in &self.balances {
626 let mut overwrites = TokenProxyOverwriteFactory::new(*token, None);
627 overwrites.set_balance(*bal, address);
628 if self
631 .self_contained_tokens
632 .contains(token)
633 {
634 overwrites.set_has_custom_approval(address);
635 }
636 balance_overwrites.extend(overwrites.get_overwrites());
637 }
638 }
639
640 for (contract, balances) in &self.contract_balances {
643 for (token, balance) in balances {
644 let mut overwrites = TokenProxyOverwriteFactory::new(*token, None);
645 overwrites.set_balance(*balance, *contract);
646 if self
649 .self_contained_tokens
650 .contains(token)
651 {
652 overwrites.set_has_custom_approval(*contract);
653 }
654 balance_overwrites.extend(overwrites.get_overwrites());
655 }
656 }
657
658 for token in &self.disable_overwrite_tokens {
660 balance_overwrites.remove(token);
661 }
662
663 Ok(balance_overwrites)
664 }
665
666 fn merge(
668 &self,
669 mut target: HashMap<Address, Overwrites>,
670 source: HashMap<Address, Overwrites>,
671 ) -> HashMap<Address, Overwrites> {
672 for (key, source_inner) in source {
673 target
674 .entry(key)
675 .or_default()
676 .extend(source_inner);
677 }
678
679 target
680 }
681
682 #[cfg(test)]
683 pub fn get_involved_contracts(&self) -> HashSet<Address> {
684 self.involved_contracts.clone()
685 }
686
687 #[cfg(test)]
688 pub fn get_manual_updates(&self) -> bool {
689 self.manual_updates
690 }
691
692 #[cfg(test)]
693 pub fn get_balance_owner(&self) -> Option<Address> {
694 self.balance_owner
695 }
696
697 pub fn get_balances(&self) -> &HashMap<Address, U256> {
699 &self.balances
700 }
701
702 #[cfg(test)]
703 pub fn get_block_overrides(&self) -> Option<BlockEnvOverrides> {
704 let live_snapshot = self.get_live_snapshot();
705 self.block_env(live_snapshot.as_ref())
706 }
707}
708
709impl<D> Serialize for EVMPoolState<D>
710where
711 D: EngineDatabaseInterface + Clone + Debug,
712 <D as DatabaseRef>::Error: Debug,
713 <D as EngineDatabaseInterface>::Error: Debug,
714{
715 fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
716 where
717 S: serde::Serializer,
718 {
719 Err(serde::ser::Error::custom("not supported due vm state deps"))
720 }
721}
722
723impl<'de, D> Deserialize<'de> for EVMPoolState<D>
724where
725 D: EngineDatabaseInterface + Clone + Debug,
726 <D as DatabaseRef>::Error: Debug,
727 <D as EngineDatabaseInterface>::Error: Debug,
728{
729 fn deserialize<De>(_deserializer: De) -> Result<Self, De::Error>
730 where
731 De: serde::Deserializer<'de>,
732 {
733 Err(serde::de::Error::custom("not supported due vm state deps"))
734 }
735}
736
737#[typetag::serialize]
738impl<D> ProtocolSim for EVMPoolState<D>
739where
740 D: EngineDatabaseInterface + Clone + Debug + 'static,
741 <D as DatabaseRef>::Error: Debug,
742 <D as EngineDatabaseInterface>::Error: Debug,
743{
744 fn fee(&self) -> f64 {
745 todo!()
746 }
747
748 fn spot_price(&self, base: &Token, quote: &Token) -> Result<f64, SimulationError> {
749 let base_address = bytes_to_address(&base.address)?;
750 let quote_address = bytes_to_address("e.address)?;
751 self.spot_prices
752 .get(&(base_address, quote_address))
753 .cloned()
754 .ok_or(SimulationError::FatalError(format!(
755 "Spot price not found for base token {base_address} and quote token {quote_address}"
756 )))
757 }
758
759 fn get_amount_out(
760 &self,
761 amount_in: BigUint,
762 token_in: &Token,
763 token_out: &Token,
764 ) -> Result<GetAmountOutResult, SimulationError> {
765 let sell_token_address = bytes_to_address(&token_in.address)?;
766 let buy_token_address = bytes_to_address(&token_out.address)?;
767 let sell_amount = U256::from_be_slice(&amount_in.to_bytes_be());
768 let live_snapshot = self.get_live_snapshot();
770 let block_overrides = self.block_env(live_snapshot.as_ref());
771 let overwrites = self.get_overwrites(
772 vec![sell_token_address, buy_token_address],
773 *MAX_BALANCE / U256::from(100),
774 live_snapshot.as_ref(),
775 )?;
776 let (sell_amount_limit, _) = self.get_amount_limits(
777 vec![sell_token_address, buy_token_address],
778 Some(overwrites.clone()),
779 block_overrides.clone(),
780 )?;
781 let (sell_amount_respecting_limit, sell_amount_exceeds_limit) = if self
782 .capabilities
783 .contains(&Capability::HardLimits) &&
784 sell_amount_limit < sell_amount
785 {
786 (sell_amount_limit, true)
787 } else {
788 (sell_amount, false)
789 };
790
791 let overwrites_with_sell_limit = self.get_overwrites(
792 vec![sell_token_address, buy_token_address],
793 sell_amount_limit,
794 live_snapshot.as_ref(),
795 )?;
796 let complete_overwrites = self.merge(overwrites, overwrites_with_sell_limit);
797
798 let (trade, state_changes) = self.adapter_contract.swap(
799 &self.id,
800 sell_token_address,
801 buy_token_address,
802 false,
803 sell_amount_respecting_limit,
804 Some(complete_overwrites),
805 block_overrides,
806 )?;
807
808 let mut new_state = self.clone();
809
810 for (address, state_update) in state_changes {
812 if let Some(storage) = state_update.storage {
813 let block_overwrites = new_state
814 .block_lasting_overwrites
815 .entry(address)
816 .or_default();
817 for (slot, value) in storage {
818 let slot = U256::from_str(&slot.to_string()).map_err(|_| {
819 SimulationError::FatalError("Failed to decode slot index".to_string())
820 })?;
821 let value = U256::from_str(&value.to_string()).map_err(|_| {
822 SimulationError::FatalError("Failed to decode slot overwrite".to_string())
823 })?;
824 block_overwrites.insert(slot, value);
825 }
826 }
827 }
828
829 let tokens = HashMap::from([
831 (token_in.address.clone(), token_in.clone()),
832 (token_out.address.clone(), token_out.clone()),
833 ]);
834 let _ = new_state.set_spot_prices(&tokens);
835
836 let buy_amount = trade.received_amount;
837
838 if sell_amount_exceeds_limit {
839 return Err(SimulationError::InvalidInput(
840 format!("Sell amount exceeds limit {sell_amount_limit}"),
841 Some(GetAmountOutResult::new(
842 u256_to_biguint(buy_amount),
843 u256_to_biguint(trade.gas_used),
844 Box::new(new_state.clone()),
845 )),
846 ));
847 }
848 Ok(GetAmountOutResult::new(
849 u256_to_biguint(buy_amount),
850 u256_to_biguint(trade.gas_used),
851 Box::new(new_state.clone()),
852 ))
853 }
854
855 fn get_limits(
856 &self,
857 sell_token: Bytes,
858 buy_token: Bytes,
859 ) -> Result<(BigUint, BigUint), SimulationError> {
860 let sell_token = bytes_to_address(&sell_token)?;
861 let buy_token = bytes_to_address(&buy_token)?;
862 let live_snapshot = self.get_live_snapshot();
863 let overwrites = self.get_overwrites(
864 vec![sell_token, buy_token],
865 *MAX_BALANCE / U256::from(100),
866 live_snapshot.as_ref(),
867 )?;
868 let limits = self.get_amount_limits(
869 vec![sell_token, buy_token],
870 Some(overwrites),
871 self.block_env(live_snapshot.as_ref()),
872 )?;
873 Ok((u256_to_biguint(limits.0), u256_to_biguint(limits.1)))
874 }
875
876 fn delta_transition(
877 &mut self,
878 delta: ProtocolStateDelta,
879 tokens: &HashMap<Bytes, Token>,
880 balances: &Balances,
881 ) -> Result<(), TransitionError> {
882 if let Some(block_number) = delta
883 .updated_attributes
884 .get("override_block_number")
885 {
886 let number = <[u8; 8]>::try_from(block_number.as_ref())
887 .map(u64::from_be_bytes)
888 .map_err(|_| {
889 TransitionError::DecodeError(
890 "override_block_number attribute must be an 8-byte big-endian u64"
891 .to_string(),
892 )
893 })?;
894 self.block_overrides
895 .get_or_insert_with(BlockEnvOverrides::default)
896 .number = Some(number);
897 }
898
899 if let Some(block_timestamp) = delta
900 .updated_attributes
901 .get("override_block_timestamp")
902 {
903 let timestamp = <[u8; 8]>::try_from(block_timestamp.as_ref())
904 .map(u64::from_be_bytes)
905 .map_err(|_| {
906 TransitionError::DecodeError(
907 "override_block_timestamp attribute must be an 8-byte big-endian u64"
908 .to_string(),
909 )
910 })?;
911 self.block_overrides
912 .get_or_insert_with(BlockEnvOverrides::default)
913 .timestamp = Some(timestamp);
914 }
915
916 if self.manual_updates {
917 if let Some(marker) = delta
919 .updated_attributes
920 .get("update_marker")
921 {
922 if !marker.is_empty() && marker[0] != 0 {
924 self.update_pool_state(tokens, balances)?;
925 }
926 }
927 } else {
928 self.update_pool_state(tokens, balances)?;
929 }
930
931 Ok(())
932 }
933
934 fn query_pool_swap(
935 &self,
936 params: &tycho_common::simulation::protocol_sim::QueryPoolSwapParams,
937 ) -> Result<tycho_common::simulation::protocol_sim::PoolSwap, SimulationError> {
938 crate::evm::query_pool_swap::query_pool_swap(self, params)
939 }
940
941 fn clone_box(&self) -> Box<dyn ProtocolSim> {
942 Box::new(self.clone())
943 }
944
945 fn as_any(&self) -> &dyn Any {
946 self
947 }
948
949 fn as_any_mut(&mut self) -> &mut dyn Any {
950 self
951 }
952
953 fn eq(&self, other: &dyn ProtocolSim) -> bool {
954 if let Some(other_state) = other
955 .as_any()
956 .downcast_ref::<EVMPoolState<PreCachedDB>>()
957 {
958 self.id == other_state.id
959 } else {
960 false
961 }
962 }
963
964 fn typetag_deserialize(&self) {
966 unreachable!("Only to catch missing typetag attribute on impl blocks. Not called.")
968 }
969}
970
971#[cfg(test)]
972mod tests {
973 use std::default::Default;
974
975 use num_traits::One;
976 use revm::{
977 primitives::KECCAK_EMPTY,
978 state::{AccountInfo, Bytecode},
979 };
980 use serde_json::Value;
981 use tycho_client::feed::BlockHeader;
982 use tycho_common::models::Chain;
983
984 use super::*;
985 use crate::evm::{
986 engine_db::{create_engine, SHARED_TYCHO_DB},
987 protocol::vm::{
988 constants::{BALANCER_V2, ERC20_PROXY_BYTECODE},
989 state_builder::EVMPoolStateBuilder,
990 },
991 simulation::SimulationEngine,
992 tycho_models::AccountUpdate,
993 };
994
995 fn dai() -> Token {
996 Token::new(
997 &Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(),
998 "DAI",
999 18,
1000 0,
1001 &[Some(10_000)],
1002 Chain::Ethereum,
1003 100,
1004 )
1005 }
1006
1007 fn bal() -> Token {
1008 Token::new(
1009 &Bytes::from_str("0xba100000625a3754423978a60c9317c58a424e3d").unwrap(),
1010 "BAL",
1011 18,
1012 0,
1013 &[Some(10_000)],
1014 Chain::Ethereum,
1015 100,
1016 )
1017 }
1018
1019 fn dai_addr() -> Address {
1020 bytes_to_address(&dai().address).unwrap()
1021 }
1022
1023 fn bal_addr() -> Address {
1024 bytes_to_address(&bal().address).unwrap()
1025 }
1026
1027 async fn setup_pool_state() -> EVMPoolState<PreCachedDB> {
1028 let data_str = include_str!("assets/balancer_contract_storage_block_20463609.json");
1029 let data: Value = serde_json::from_str(data_str).expect("Failed to parse JSON");
1030
1031 let accounts: Vec<AccountUpdate> = serde_json::from_value(data["accounts"].clone())
1032 .expect("Expected accounts to match AccountUpdate structure");
1033
1034 let db = SHARED_TYCHO_DB.clone();
1035 let engine: SimulationEngine<_> = create_engine(db.clone(), false).unwrap();
1036
1037 let block = BlockHeader {
1038 number: 20463609,
1039 hash: Bytes::from_str(
1040 "0x4315fd1afc25cc2ebc72029c543293f9fd833eeb305e2e30159459c827733b1b",
1041 )
1042 .unwrap(),
1043 timestamp: 1722875891,
1044 ..Default::default()
1045 };
1046
1047 for account in accounts.clone() {
1048 engine
1049 .state
1050 .init_account(
1051 account.address,
1052 AccountInfo {
1053 balance: account.balance.unwrap_or_default(),
1054 nonce: 0u64,
1055 code_hash: KECCAK_EMPTY,
1056 code: account
1057 .code
1058 .clone()
1059 .map(|arg0: Vec<u8>| Bytecode::new_raw(arg0.into())),
1060 },
1061 None,
1062 false,
1063 )
1064 .expect("Failed to initialize account");
1065 }
1066 db.update(accounts, Some(block))
1067 .unwrap();
1068
1069 let tokens = vec![dai().address, bal().address];
1070 for token in &tokens {
1071 engine
1072 .state
1073 .init_account(
1074 bytes_to_address(token).unwrap(),
1075 AccountInfo {
1076 balance: U256::from(0),
1077 nonce: 0,
1078 code_hash: KECCAK_EMPTY,
1079 code: Some(Bytecode::new_raw(ERC20_PROXY_BYTECODE.into())),
1080 },
1081 None,
1082 true,
1083 )
1084 .expect("Failed to initialize account");
1085 }
1086
1087 let block = BlockHeader {
1088 number: 18485417,
1089 hash: Bytes::from_str(
1090 "0x28d41d40f2ac275a4f5f621a636b9016b527d11d37d610a45ac3a821346ebf8c",
1091 )
1092 .expect("Invalid block hash"),
1093 timestamp: 0,
1094 ..Default::default()
1095 };
1096 db.update(vec![], Some(block.clone()))
1097 .unwrap();
1098
1099 let pool_id: String =
1100 "0x4626d81b3a1711beb79f4cecff2413886d461677000200000000000000000011".into();
1101
1102 let stateless_contracts = HashMap::from([(
1103 String::from("0x3de27efa2f1aa663ae5d458857e731c129069f29"),
1104 Some(Vec::new()),
1105 )]);
1106
1107 let balances = HashMap::from([
1108 (dai_addr(), U256::from_str("178754012737301807104").unwrap()),
1109 (bal_addr(), U256::from_str("91082987763369885696").unwrap()),
1110 ]);
1111 let adapter_address =
1112 Address::from_str("0xA2C5C98A892fD6656a7F39A2f63228C0Bc846270").unwrap();
1113
1114 EVMPoolStateBuilder::new(pool_id, tokens, adapter_address)
1115 .balances(balances)
1116 .balance_owner(Address::from_str("0xBA12222222228d8Ba445958a75a0704d566BF2C8").unwrap())
1117 .adapter_contract_bytecode(Bytecode::new_raw(BALANCER_V2.into()))
1118 .stateless_contracts(stateless_contracts)
1119 .build(SHARED_TYCHO_DB.clone())
1120 .await
1121 .expect("Failed to build pool state")
1122 }
1123
1124 #[tokio::test]
1125 async fn test_init() {
1126 SHARED_TYCHO_DB
1128 .clear()
1129 .expect("Failed to cleared SHARED TX");
1130 let pool_state = setup_pool_state().await;
1131
1132 let expected_capabilities = vec![
1133 Capability::SellSide,
1134 Capability::BuySide,
1135 Capability::PriceFunction,
1136 Capability::HardLimits,
1137 ]
1138 .into_iter()
1139 .collect::<HashSet<_>>();
1140
1141 let capabilities_adapter_contract = pool_state
1142 .adapter_contract
1143 .get_capabilities(
1144 &pool_state.id,
1145 bytes_to_address(&pool_state.tokens[0]).unwrap(),
1146 bytes_to_address(&pool_state.tokens[1]).unwrap(),
1147 )
1148 .unwrap();
1149
1150 assert_eq!(capabilities_adapter_contract, expected_capabilities.clone());
1151
1152 let capabilities_state = pool_state.clone().capabilities;
1153
1154 assert_eq!(capabilities_state, expected_capabilities.clone());
1155
1156 for capability in expected_capabilities.clone() {
1157 assert!(pool_state
1158 .clone()
1159 .ensure_capability(capability)
1160 .is_ok());
1161 }
1162
1163 assert!(pool_state
1164 .clone()
1165 .ensure_capability(Capability::MarginalPrice)
1166 .is_err());
1167
1168 let engine_accounts = pool_state
1170 .adapter_contract
1171 .engine
1172 .state
1173 .clone()
1174 .get_account_storage()
1175 .expect("Failed to get account storage");
1176 for token in pool_state.tokens.clone() {
1177 let account = engine_accounts
1178 .get_account_info(&bytes_to_address(&token).unwrap())
1179 .unwrap();
1180 assert_eq!(account.balance, U256::from(0));
1181 assert_eq!(account.nonce, 0u64);
1182 assert_eq!(account.code_hash, KECCAK_EMPTY);
1183 assert!(account.code.is_some());
1184 }
1185
1186 let external_account = engine_accounts
1188 .get_account_info(&EXTERNAL_ACCOUNT)
1189 .unwrap();
1190 assert_eq!(external_account.balance, U256::from(*MAX_BALANCE));
1191 assert_eq!(external_account.nonce, 0u64);
1192 assert_eq!(external_account.code_hash, KECCAK_EMPTY);
1193 assert!(external_account.code.is_none());
1194 }
1195
1196 #[tokio::test]
1197 async fn test_get_amount_out() -> Result<(), Box<dyn std::error::Error>> {
1198 let pool_state = setup_pool_state().await;
1199
1200 let result = pool_state
1201 .get_amount_out(BigUint::from_str("1000000000000000000").unwrap(), &dai(), &bal())
1202 .unwrap();
1203 let new_state = result
1204 .new_state
1205 .as_any()
1206 .downcast_ref::<EVMPoolState<PreCachedDB>>()
1207 .unwrap();
1208 assert_eq!(result.amount, BigUint::from_str("137780051463393923").unwrap());
1209 assert_ne!(new_state.spot_prices, pool_state.spot_prices);
1210 assert!(pool_state
1211 .block_lasting_overwrites
1212 .is_empty());
1213 Ok(())
1214 }
1215
1216 #[tokio::test]
1217 async fn test_sequential_get_amount_outs() {
1218 let pool_state = setup_pool_state().await;
1219
1220 let result = pool_state
1221 .get_amount_out(BigUint::from_str("1000000000000000000").unwrap(), &dai(), &bal())
1222 .unwrap();
1223 let new_state = result
1224 .new_state
1225 .as_any()
1226 .downcast_ref::<EVMPoolState<PreCachedDB>>()
1227 .unwrap();
1228 assert_eq!(result.amount, BigUint::from_str("137780051463393923").unwrap());
1229 assert_ne!(new_state.spot_prices, pool_state.spot_prices);
1230
1231 let new_result = new_state
1232 .get_amount_out(BigUint::from_str("1000000000000000000").unwrap(), &dai(), &bal())
1233 .unwrap();
1234 let new_state_second_swap = new_result
1235 .new_state
1236 .as_any()
1237 .downcast_ref::<EVMPoolState<PreCachedDB>>()
1238 .unwrap();
1239
1240 assert_eq!(new_result.amount, BigUint::from_str("136964651490065626").unwrap());
1241 assert_ne!(new_state_second_swap.spot_prices, new_state.spot_prices);
1242 }
1243
1244 #[tokio::test]
1245 async fn test_get_amount_out_dust() {
1246 let pool_state = setup_pool_state().await;
1247
1248 let result = pool_state
1249 .get_amount_out(BigUint::one(), &dai(), &bal())
1250 .unwrap();
1251
1252 let _ = result
1253 .new_state
1254 .as_any()
1255 .downcast_ref::<EVMPoolState<PreCachedDB>>()
1256 .unwrap();
1257 assert_eq!(result.amount, BigUint::ZERO);
1258 }
1259
1260 #[tokio::test]
1261 async fn test_get_amount_out_sell_limit() {
1262 let pool_state = setup_pool_state().await;
1263
1264 let result = pool_state.get_amount_out(
1265 BigUint::from_str("100379494253364362835").unwrap(),
1267 &dai(),
1268 &bal(),
1269 );
1270
1271 assert!(result.is_err());
1272
1273 match result {
1274 Err(SimulationError::InvalidInput(msg1, amount_out_result)) => {
1275 assert_eq!(msg1, "Sell amount exceeds limit 100279494253364362835");
1276 assert!(amount_out_result.is_some());
1277 }
1278 _ => panic!("Test failed: was expecting an Err(SimulationError::RetryDifferentInput(_, _)) value"),
1279 }
1280 }
1281
1282 #[tokio::test]
1283 async fn test_get_amount_limits() {
1284 let pool_state = setup_pool_state().await;
1285
1286 let overwrites = pool_state
1287 .get_overwrites(
1288 vec![
1289 bytes_to_address(&pool_state.tokens[0]).unwrap(),
1290 bytes_to_address(&pool_state.tokens[1]).unwrap(),
1291 ],
1292 *MAX_BALANCE / U256::from(100),
1293 None,
1294 )
1295 .unwrap();
1296 let (dai_limit, _) = pool_state
1297 .get_amount_limits(
1298 vec![dai_addr(), bal_addr()],
1299 Some(overwrites.clone()),
1300 pool_state.block_env(None),
1301 )
1302 .unwrap();
1303 assert_eq!(dai_limit, U256::from_str("100279494253364362835").unwrap());
1304
1305 let (bal_limit, _) = pool_state
1306 .get_amount_limits(
1307 vec![
1308 bytes_to_address(&pool_state.tokens[1]).unwrap(),
1309 bytes_to_address(&pool_state.tokens[0]).unwrap(),
1310 ],
1311 Some(overwrites),
1312 pool_state.block_env(None),
1313 )
1314 .unwrap();
1315 assert_eq!(bal_limit, U256::from_str("13997408640689987484").unwrap());
1316 }
1317
1318 #[tokio::test]
1319 async fn test_set_spot_prices() {
1320 let mut pool_state = setup_pool_state().await;
1321
1322 pool_state
1323 .set_spot_prices(
1324 &vec![bal(), dai()]
1325 .into_iter()
1326 .map(|t| (t.address.clone(), t))
1327 .collect(),
1328 )
1329 .unwrap();
1330
1331 let dai_bal_spot_price = pool_state
1332 .spot_prices
1333 .get(&(
1334 bytes_to_address(&pool_state.tokens[0]).unwrap(),
1335 bytes_to_address(&pool_state.tokens[1]).unwrap(),
1336 ))
1337 .unwrap();
1338 let bal_dai_spot_price = pool_state
1339 .spot_prices
1340 .get(&(
1341 bytes_to_address(&pool_state.tokens[1]).unwrap(),
1342 bytes_to_address(&pool_state.tokens[0]).unwrap(),
1343 ))
1344 .unwrap();
1345 assert_eq!(dai_bal_spot_price, &0.137_778_914_319_047_9);
1346 assert_eq!(bal_dai_spot_price, &7.071_503_245_428_246);
1347 }
1348
1349 #[tokio::test]
1350 async fn test_set_spot_prices_without_capability() {
1351 let mut pool_state = setup_pool_state().await;
1353
1354 pool_state
1355 .capabilities
1356 .remove(&Capability::PriceFunction);
1357
1358 pool_state
1359 .set_spot_prices(
1360 &vec![bal(), dai()]
1361 .into_iter()
1362 .map(|t| (t.address.clone(), t))
1363 .collect(),
1364 )
1365 .unwrap();
1366
1367 let dai_bal_spot_price = pool_state
1368 .spot_prices
1369 .get(&(
1370 bytes_to_address(&pool_state.tokens[0]).unwrap(),
1371 bytes_to_address(&pool_state.tokens[1]).unwrap(),
1372 ))
1373 .unwrap();
1374 let bal_dai_spot_price = pool_state
1375 .spot_prices
1376 .get(&(
1377 bytes_to_address(&pool_state.tokens[1]).unwrap(),
1378 bytes_to_address(&pool_state.tokens[0]).unwrap(),
1379 ))
1380 .unwrap();
1381 assert_eq!(dai_bal_spot_price, &0.13736685496467538);
1382 assert_eq!(bal_dai_spot_price, &7.050354297665408);
1383 }
1384
1385 #[tokio::test]
1386 async fn test_get_balance_overwrites_with_component_balances() {
1387 let pool_state: EVMPoolState<PreCachedDB> = setup_pool_state().await;
1388
1389 let overwrites = pool_state
1390 .get_balance_overwrites()
1391 .unwrap();
1392
1393 let dai_address = dai_addr();
1394 let bal_address = bal_addr();
1395 assert!(overwrites.contains_key(&dai_address));
1396 assert!(overwrites.contains_key(&bal_address));
1397 }
1398
1399 #[tokio::test]
1400 async fn test_get_balance_overwrites_with_contract_balances() {
1401 let mut pool_state: EVMPoolState<PreCachedDB> = setup_pool_state().await;
1402
1403 let contract_address =
1404 Address::from_str("0xBA12222222228d8Ba445958a75a0704d566BF2C8").unwrap();
1405
1406 pool_state.balances.clear();
1408 pool_state.balance_owner = None;
1409
1410 let dai_address = dai_addr();
1412 let bal_address = bal_addr();
1413 pool_state.contract_balances = HashMap::from([(
1414 contract_address,
1415 HashMap::from([
1416 (dai_address, U256::from_str("7500000000000000000000").unwrap()), (bal_address, U256::from_str("1500000000000000000000").unwrap()), ]),
1419 )]);
1420
1421 let overwrites = pool_state
1422 .get_balance_overwrites()
1423 .unwrap();
1424
1425 assert!(overwrites.contains_key(&dai_address));
1426 assert!(overwrites.contains_key(&bal_address));
1427 }
1428
1429 #[tokio::test]
1430 async fn test_balance_merging_during_delta_transition() {
1431 use std::str::FromStr;
1432
1433 let mut pool_state = setup_pool_state().await;
1434 let pool_id = pool_state.id.clone();
1435
1436 let dai_addr = dai_addr();
1439 let bal_addr = bal_addr();
1440 let new_token = Address::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); pool_state.balances.clear();
1444 pool_state
1445 .balances
1446 .insert(dai_addr, U256::from(1000000000u64));
1447 pool_state
1448 .balances
1449 .insert(bal_addr, U256::from(2000000000u64));
1450 pool_state
1451 .balances
1452 .insert(new_token, U256::from(3000000000u64));
1453
1454 let mut tokens = HashMap::new();
1456 tokens.insert(dai().address.clone(), dai());
1457 tokens.insert(bal().address.clone(), bal());
1458
1459 let mut component_balances = HashMap::new();
1461 let mut delta_balances = HashMap::new();
1462 delta_balances.insert(dai().address.clone(), Bytes::from(vec![0x77, 0x35, 0x94, 0x00])); component_balances.insert(pool_id.clone(), delta_balances);
1465
1466 let balances = Balances { component_balances, account_balances: HashMap::new() };
1467
1468 let initial_balance_count = pool_state.balances.len();
1470 assert_eq!(initial_balance_count, 3);
1471
1472 pool_state
1474 .update_pool_state(&tokens, &balances)
1475 .unwrap();
1476
1477 assert_eq!(
1479 pool_state.balances.len(),
1480 3,
1481 "All balances should be preserved after delta transition"
1482 );
1483 assert!(
1484 pool_state
1485 .balances
1486 .contains_key(&dai_addr),
1487 "DAI balance should be present"
1488 );
1489 assert!(
1490 pool_state
1491 .balances
1492 .contains_key(&bal_addr),
1493 "BAL balance should be present"
1494 );
1495 assert!(
1496 pool_state
1497 .balances
1498 .contains_key(&new_token),
1499 "New token balance should be preserved from before delta"
1500 );
1501
1502 assert_eq!(
1504 pool_state.balances[&dai_addr],
1505 U256::from(2000000000u64),
1506 "DAI balance should be updated"
1507 );
1508
1509 assert_eq!(
1511 pool_state.balances[&bal_addr],
1512 U256::from(2000000000u64),
1513 "BAL balance should be unchanged"
1514 );
1515 assert_eq!(
1516 pool_state.balances[&new_token],
1517 U256::from(3000000000u64),
1518 "New token balance should be unchanged"
1519 );
1520 }
1521
1522 #[tokio::test]
1523 async fn test_delta_transition_updates_block_overrides() {
1524 let mut pool_state = setup_pool_state().await;
1525 pool_state.manual_updates = true;
1526 pool_state.block_overrides = None;
1527
1528 let delta = ProtocolStateDelta {
1529 component_id: pool_state.id.clone(),
1530 updated_attributes: HashMap::from([
1531 ("override_block_number".to_string(), Bytes::from(123_u64.to_be_bytes().to_vec())),
1532 (
1533 "override_block_timestamp".to_string(),
1534 Bytes::from(456_u64.to_be_bytes().to_vec()),
1535 ),
1536 ]),
1537 deleted_attributes: HashSet::new(),
1538 };
1539
1540 pool_state
1541 .delta_transition(delta, &HashMap::new(), &Balances::default())
1542 .unwrap();
1543
1544 assert_eq!(
1545 pool_state.block_overrides,
1546 Some(BlockEnvOverrides { number: Some(123), timestamp: Some(456) })
1547 );
1548 }
1549
1550 #[tokio::test]
1551 async fn test_delta_transition_updates_partial_block_overrides() {
1552 let mut pool_state = setup_pool_state().await;
1553 pool_state.manual_updates = true;
1554 pool_state.block_overrides =
1555 Some(BlockEnvOverrides { number: Some(123), timestamp: Some(456) });
1556
1557 let delta = ProtocolStateDelta {
1558 component_id: pool_state.id.clone(),
1559 updated_attributes: HashMap::from([(
1560 "override_block_number".to_string(),
1561 Bytes::from(789_u64.to_be_bytes().to_vec()),
1562 )]),
1563 deleted_attributes: HashSet::new(),
1564 };
1565
1566 pool_state
1567 .delta_transition(delta, &HashMap::new(), &Balances::default())
1568 .unwrap();
1569
1570 assert_eq!(
1571 pool_state.block_overrides,
1572 Some(BlockEnvOverrides { number: Some(789), timestamp: Some(456) })
1573 );
1574 }
1575
1576 #[test]
1577 fn should_not_panic_at_typetag_deserialize() {
1578 let deserialized: Result<Box<dyn ProtocolSim>, _> = serde_json::from_str(
1579 r#"{"protocol":"EVMPoolState","state":{"reserve_0":1,"reserve_1":2}}"#,
1580 );
1581
1582 assert!(deserialized.is_err());
1583 }
1584
1585 #[tokio::test]
1588 async fn test_block_env_prefers_live_overrides() {
1589 let mut pool_state = setup_pool_state().await;
1590 pool_state.block_overrides =
1591 Some(BlockEnvOverrides { number: Some(100), timestamp: Some(1_000) });
1592
1593 assert_eq!(
1595 pool_state.block_env(None),
1596 Some(BlockEnvOverrides { number: Some(100), timestamp: Some(1_000) })
1597 );
1598
1599 let empty = OverrideSnapshot::default();
1601 assert_eq!(
1602 pool_state.block_env(Some(&empty)),
1603 Some(BlockEnvOverrides { number: Some(100), timestamp: Some(1_000) })
1604 );
1605
1606 let live = OverrideSnapshot {
1608 block_number: Some(200),
1609 block_timestamp: None,
1610 ..Default::default()
1611 };
1612 assert_eq!(
1613 pool_state.block_env(Some(&live)),
1614 Some(BlockEnvOverrides { number: Some(200), timestamp: Some(1_000) })
1615 );
1616
1617 pool_state.block_overrides = None;
1619 let live = OverrideSnapshot {
1620 block_number: Some(300),
1621 block_timestamp: Some(3_000),
1622 ..Default::default()
1623 };
1624 assert_eq!(
1625 pool_state.block_env(Some(&live)),
1626 Some(BlockEnvOverrides { number: Some(300), timestamp: Some(3_000) })
1627 );
1628 }
1629
1630 #[tokio::test]
1634 async fn test_get_overwrites_applies_live_storage() {
1635 let pool_state = setup_pool_state().await;
1636 let tokens = vec![dai_addr(), bal_addr()];
1637 let max = *MAX_BALANCE / U256::from(100);
1638
1639 let baseline = pool_state
1640 .get_overwrites(tokens.clone(), max, None)
1641 .unwrap();
1642
1643 let empty = OverrideSnapshot::default();
1645 assert_eq!(
1646 pool_state
1647 .get_overwrites(tokens.clone(), max, Some(&empty))
1648 .unwrap(),
1649 baseline
1650 );
1651
1652 let (conflict_addr, conflict_slot, baseline_val) = {
1654 let (addr, slots) = baseline
1655 .iter()
1656 .next()
1657 .expect("baseline has overwrites");
1658 let (slot, val) = slots
1659 .iter()
1660 .next()
1661 .expect("address has slots");
1662 (*addr, *slot, *val)
1663 };
1664 let sentinel = baseline_val + U256::from(1);
1665 let fresh = Address::from([0xAB; 20]);
1666 assert!(
1667 !baseline.contains_key(&fresh),
1668 "fresh address must originate from the live snapshot"
1669 );
1670
1671 let live = OverrideSnapshot {
1672 storage: std::sync::Arc::new(HashMap::from([
1673 (fresh, HashMap::from([(U256::from(7), U256::from(123))])),
1674 (conflict_addr, HashMap::from([(conflict_slot, sentinel)])),
1675 ])),
1676 ..Default::default()
1677 };
1678 let with_live = pool_state
1679 .get_overwrites(tokens, max, Some(&live))
1680 .unwrap();
1681
1682 assert_eq!(
1683 with_live
1684 .get(&fresh)
1685 .and_then(|slots| slots.get(&U256::from(7))),
1686 Some(&U256::from(123)),
1687 "live storage for a fresh contract must be merged in"
1688 );
1689 assert_eq!(
1690 with_live
1691 .get(&conflict_addr)
1692 .and_then(|slots| slots.get(&conflict_slot)),
1693 Some(&sentinel),
1694 "live override must win on slot conflict"
1695 );
1696 }
1697
1698 #[tokio::test]
1703 async fn test_get_live_snapshot_drops_expired() {
1704 let mut pool_state = setup_pool_state().await;
1705
1706 assert!(pool_state.get_live_snapshot().is_none());
1708
1709 let never =
1711 OverrideSnapshot { block_number: Some(1), expires_at: None, ..Default::default() };
1712 let (_never_tx, never_rx) = watch::channel(never);
1713 pool_state.set_live_overrides(never_rx);
1714 assert_eq!(
1715 pool_state
1716 .get_live_snapshot()
1717 .and_then(|snapshot| snapshot.block_number),
1718 Some(1)
1719 );
1720
1721 let fresh = OverrideSnapshot {
1723 block_number: Some(42),
1724 expires_at: Some(u64::MAX),
1725 ..Default::default()
1726 };
1727 let (_fresh_tx, fresh_rx) = watch::channel(fresh);
1728 pool_state.set_live_overrides(fresh_rx);
1729 assert_eq!(
1730 pool_state
1731 .get_live_snapshot()
1732 .and_then(|snapshot| snapshot.block_number),
1733 Some(42)
1734 );
1735
1736 let expired =
1738 OverrideSnapshot { block_number: Some(42), expires_at: Some(1), ..Default::default() };
1739 let (_expired_tx, expired_rx) = watch::channel(expired);
1740 pool_state.set_live_overrides(expired_rx);
1741 assert!(pool_state.get_live_snapshot().is_none());
1742 }
1743}