tycho_simulation/evm/protocol/uniswap_v2/
decoder.rs1use std::collections::HashMap;
2
3use tycho_client::feed::{synchronizer::ComponentWithState, BlockHeader};
4use tycho_common::{models::token::Token, Bytes};
5
6use crate::{
7 evm::protocol::{cpmm::protocol::cpmm_try_from_with_header, uniswap_v2::state::UniswapV2State},
8 protocol::{
9 errors::InvalidSnapshotError,
10 models::{DecoderContext, TryFromWithBlock},
11 },
12};
13
14impl TryFromWithBlock<ComponentWithState, BlockHeader> for UniswapV2State {
15 type Error = InvalidSnapshotError;
16
17 async fn try_from_with_header(
20 snapshot: ComponentWithState,
21 _block: BlockHeader,
22 _account_balances: &HashMap<Bytes, HashMap<Bytes, Bytes>>,
23 _all_tokens: &HashMap<Bytes, Token>,
24 _decoder_context: &DecoderContext,
25 ) -> Result<Self, Self::Error> {
26 let (reserve0, reserve1) = cpmm_try_from_with_header(snapshot)?;
27 Ok(Self::new(reserve0, reserve1))
28 }
29}
30
31#[cfg(test)]
32mod tests {
33 use std::collections::HashMap;
34
35 use alloy::primitives::U256;
36 use rstest::rstest;
37 use tycho_client::feed::{synchronizer::ComponentWithState, BlockHeader};
38 use tycho_common::{dto::ResponseProtocolState, Bytes};
39
40 use super::super::state::UniswapV2State;
41 use crate::protocol::{
42 errors::InvalidSnapshotError,
43 models::{DecoderContext, TryFromWithBlock},
44 };
45
46 fn header() -> BlockHeader {
47 BlockHeader {
48 number: 1,
49 hash: Bytes::from(vec![0; 32]),
50 parent_hash: Bytes::from(vec![0; 32]),
51 revert: false,
52 timestamp: 1,
53 }
54 }
55
56 #[tokio::test]
57 async fn test_usv2_try_from() {
58 let snapshot = ComponentWithState {
59 state: ResponseProtocolState {
60 component_id: "State1".to_owned(),
61 attributes: HashMap::from([
62 ("reserve0".to_string(), Bytes::from(vec![0; 32])),
63 ("reserve1".to_string(), Bytes::from(vec![0; 32])),
64 ]),
65 balances: HashMap::new(),
66 },
67 component: Default::default(),
68 component_tvl: None,
69 entrypoints: Vec::new(),
70 };
71
72 let decoder_context = DecoderContext::new();
73 let result = UniswapV2State::try_from_with_header(
74 snapshot,
75 header(),
76 &HashMap::new(),
77 &HashMap::new(),
78 &decoder_context,
79 )
80 .await;
81
82 assert!(result.is_ok());
83 assert_eq!(result.unwrap(), UniswapV2State::new(U256::from(0u64), U256::from(0u64)));
84 }
85
86 #[tokio::test]
87 #[rstest]
88 #[case::missing_reserve0("reserve0")]
89 #[case::missing_reserve1("reserve1")]
90 async fn test_usv2_try_from_missing_attribute(#[case] missing_attribute: &str) {
91 let mut attributes = HashMap::from([
92 ("reserve0".to_string(), Bytes::from(vec![0; 32])),
93 ("reserve1".to_string(), Bytes::from(vec![0; 32])),
94 ]);
95 attributes.remove(missing_attribute);
96
97 let snapshot = ComponentWithState {
98 state: ResponseProtocolState {
99 component_id: "State1".to_owned(),
100 attributes,
101 balances: HashMap::new(),
102 },
103 component: Default::default(),
104 component_tvl: None,
105 entrypoints: Vec::new(),
106 };
107
108 let decoder_context = DecoderContext::new();
109 let result = UniswapV2State::try_from_with_header(
110 snapshot,
111 header(),
112 &HashMap::new(),
113 &HashMap::new(),
114 &decoder_context,
115 )
116 .await;
117
118 assert!(result.is_err());
119 assert!(matches!(
120 result.unwrap_err(),
121 InvalidSnapshotError::MissingAttribute(ref x) if x == missing_attribute
122 ));
123 }
124}