tycho_simulation/evm/protocol/rocketpool/
decoder.rs1use std::collections::HashMap;
2
3use alloy::primitives::U256;
4use tycho_client::feed::{synchronizer::ComponentWithState, BlockHeader};
5use tycho_common::{models::token::Token, Bytes};
6use tycho_ethereum::BytesCodec;
7
8use super::state::RocketpoolState;
9use crate::protocol::{
10 errors::InvalidSnapshotError,
11 models::{DecoderContext, TryFromWithBlock},
12};
13
14impl TryFromWithBlock<ComponentWithState, BlockHeader> for RocketpoolState {
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 get_u256 = |name: &str| -> Result<U256, InvalidSnapshotError> {
27 snapshot
28 .state
29 .attributes
30 .get(name)
31 .map(U256::from_bytes)
32 .ok_or_else(|| InvalidSnapshotError::MissingAttribute(name.to_string()))
33 };
34
35 let get_bool = |name: &str| -> Result<bool, InvalidSnapshotError> {
36 snapshot
37 .state
38 .attributes
39 .get(name)
40 .map(|val| !U256::from_bytes(val).is_zero())
41 .ok_or_else(|| InvalidSnapshotError::MissingAttribute(name.to_string()))
42 };
43
44 Ok(RocketpoolState::new(
45 get_u256("reth_supply")?,
46 get_u256("total_eth")?,
47 get_u256("deposit_contract_balance")?,
48 get_u256("reth_contract_liquidity")?,
49 get_u256("deposit_fee")?,
50 get_bool("deposits_enabled")?,
51 get_u256("min_deposit_amount")?,
52 get_u256("max_deposit_pool_size")?,
53 get_bool("deposit_assigning_enabled")?,
54 get_u256("deposit_assign_maximum")?,
55 get_u256("deposit_assign_socialised_maximum")?,
56 get_u256("megapool_queue_requested_total")?,
57 get_u256("target_reth_collateral_rate")?,
58 ))
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use std::collections::HashMap;
65
66 use alloy::primitives::U256;
67 use rstest::rstest;
68 use tycho_client::feed::synchronizer::ComponentWithState;
69 use tycho_common::{dto::ResponseProtocolState, Bytes};
70
71 use super::super::state::RocketpoolState;
72 use crate::{
73 evm::protocol::test_utils::try_decode_snapshot_with_defaults,
74 protocol::errors::InvalidSnapshotError,
75 };
76
77 fn create_test_snapshot() -> ComponentWithState {
78 ComponentWithState {
79 state: ResponseProtocolState {
80 component_id: "Rocketpool".to_owned(),
81 attributes: HashMap::from([
82 (
83 "total_eth".to_string(),
84 Bytes::from(U256::from(100_000_000_000_000_000_000u128).to_be_bytes_vec()),
85 ),
86 (
87 "reth_supply".to_string(),
88 Bytes::from(U256::from(95_000_000_000_000_000_000u128).to_be_bytes_vec()),
89 ),
90 (
91 "deposit_contract_balance".to_string(),
92 Bytes::from(U256::from(50_000_000_000_000_000_000u128).to_be_bytes_vec()),
93 ), (
95 "reth_contract_liquidity".to_string(),
96 Bytes::from(U256::from(10_000_000_000_000_000_000u128).to_be_bytes_vec()),
97 ), ("deposits_enabled".to_string(), Bytes::from(vec![0x01])),
99 ("deposit_assigning_enabled".to_string(), Bytes::from(vec![0x01])),
100 (
101 "deposit_fee".to_string(),
102 Bytes::from(U256::from(5_000_000_000_000_000u128).to_be_bytes_vec()),
103 ), (
105 "min_deposit_amount".to_string(),
106 Bytes::from(U256::from(10_000_000_000_000_000u128).to_be_bytes_vec()),
107 ), (
109 "max_deposit_pool_size".to_string(),
110 Bytes::from(
111 U256::from(5_000_000_000_000_000_000_000u128).to_be_bytes_vec(),
112 ),
113 ), (
115 "deposit_assign_maximum".to_string(),
116 Bytes::from(U256::from(90u64).to_be_bytes_vec()),
117 ),
118 (
119 "deposit_assign_socialised_maximum".to_string(),
120 Bytes::from(U256::from(0u64).to_be_bytes_vec()),
121 ),
122 (
123 "megapool_queue_requested_total".to_string(),
124 Bytes::from(
125 U256::from(1_000_000_000_000_000_000_000_u128).to_be_bytes_vec(),
126 ),
127 ),
128 (
129 "target_reth_collateral_rate".to_string(),
130 Bytes::from(U256::from(10_000_000_000_000_000_u128).to_be_bytes_vec()),
131 ),
132 ]),
133 balances: HashMap::new(),
134 },
135 component: Default::default(),
136 component_tvl: None,
137 entrypoints: Vec::new(),
138 }
139 }
140
141 #[tokio::test]
142 async fn test_rocketpool_try_from() {
143 let snapshot = create_test_snapshot();
144 let result = try_decode_snapshot_with_defaults::<RocketpoolState>(snapshot).await;
145
146 assert!(result.is_ok());
147 let state = result.unwrap();
148 assert_eq!(state.total_eth, U256::from(100_000_000_000_000_000_000u128));
149 assert_eq!(state.reth_supply, U256::from(95_000_000_000_000_000_000u128));
150 assert_eq!(state.deposit_contract_balance, U256::from(50_000_000_000_000_000_000u128));
151 assert_eq!(state.reth_contract_liquidity, U256::from(10_000_000_000_000_000_000u128));
152 assert!(state.deposits_enabled);
153 assert!(state.deposit_assigning_enabled);
154 assert_eq!(state.deposit_assign_maximum, U256::from(90u64));
155 assert_eq!(
156 state.megapool_queue_requested_total,
157 U256::from(1_000_000_000_000_000_000_000u128)
158 );
159 }
160
161 #[tokio::test]
162 #[rstest]
163 #[case::missing_total_eth("total_eth")]
164 #[case::missing_reth_supply("reth_supply")]
165 #[case::missing_deposit_contract_balance("deposit_contract_balance")]
166 #[case::missing_reth_contract_liquidity("reth_contract_liquidity")]
167 #[case::missing_deposits_enabled("deposits_enabled")]
168 #[case::missing_deposit_assigning_enabled("deposit_assigning_enabled")]
169 #[case::missing_deposit_fee("deposit_fee")]
170 #[case::missing_min_deposit_amount("min_deposit_amount")]
171 #[case::missing_max_deposit_pool_size("max_deposit_pool_size")]
172 #[case::missing_deposit_assign_maximum("deposit_assign_maximum")]
173 #[case::missing_deposit_assign_socialised_maximum("deposit_assign_socialised_maximum")]
174 #[case::missing_megapool_queue_requested_total("megapool_queue_requested_total")]
175 #[case::missing_target_reth_collateral_rate("target_reth_collateral_rate")]
176 async fn test_rocketpool_try_from_missing_attribute(#[case] missing_attribute: &str) {
177 let mut snapshot = create_test_snapshot();
178 snapshot
179 .state
180 .attributes
181 .remove(missing_attribute);
182
183 let result = try_decode_snapshot_with_defaults::<RocketpoolState>(snapshot).await;
184
185 assert!(result.is_err());
186 assert!(matches!(
187 result.unwrap_err(),
188 InvalidSnapshotError::MissingAttribute(ref x) if x == missing_attribute
189 ));
190 }
191}