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