tycho_simulation/evm/protocol/pancakeswap_v2/
decoder.rs

1use 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    /// Decodes a `ComponentWithState` into a `PancakeswapV2State`. Errors with a
20    /// `InvalidSnapshotError` if either reserve0 or reserve1 attributes are missing.
21    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}