Skip to main content

tycho_simulation/evm/protocol/lido_v4/
decoder.rs

1use std::collections::HashMap;
2
3use alloy::primitives::U256;
4use tycho_client::feed::{synchronizer::ComponentWithState, BlockHeader};
5use tycho_common::{models::token::Token, Bytes};
6
7use super::state::{
8    decode_attribute, decode_u32_attribute, LidoV4State, StakingState, BUFFERED_ETHER_ATTR,
9    CL_PENDING_BALANCE_ATTR, CL_VALIDATORS_BALANCE_ATTR, DEPOSITED_POST_REPORT_ATTR,
10    EXTERNAL_SHARES_ATTR, MAX_STAKE_LIMIT_ATTR, MAX_STAKE_LIMIT_GROWTH_BLOCKS_ATTR,
11    PREV_STAKE_BLOCK_NUMBER_ATTR, PREV_STAKE_LIMIT_ATTR, STETH_COMPONENT_ID, TOTAL_SHARES_ATTR,
12    WSTETH_SHARES_ATTR,
13};
14use crate::protocol::{
15    errors::InvalidSnapshotError,
16    models::{DecoderContext, TryFromWithBlock},
17};
18
19impl TryFromWithBlock<ComponentWithState, BlockHeader> for LidoV4State {
20    type Error = InvalidSnapshotError;
21
22    async fn try_from_with_header(
23        snapshot: ComponentWithState,
24        block: BlockHeader,
25        _account_balances: &HashMap<Bytes, HashMap<Bytes, Bytes>>,
26        _all_tokens: &HashMap<Bytes, Token>,
27        _decoder_context: &DecoderContext,
28    ) -> Result<Self, Self::Error> {
29        if !snapshot
30            .component
31            .id
32            .eq_ignore_ascii_case(STETH_COMPONENT_ID)
33        {
34            return Err(InvalidSnapshotError::ValueError(format!(
35                "unknown Lido V4 component id {}",
36                snapshot.component.id
37            )));
38        }
39
40        let raw = |name: &str| -> Result<&Bytes, InvalidSnapshotError> {
41            snapshot
42                .state
43                .attributes
44                .get(name)
45                .ok_or_else(|| InvalidSnapshotError::MissingAttribute(name.to_string()))
46        };
47        let value = |name: &str| -> Result<U256, InvalidSnapshotError> {
48            decode_attribute(name, raw(name)?).map_err(InvalidSnapshotError::ValueError)
49        };
50        let value_u32 = |name: &str| -> Result<u32, InvalidSnapshotError> {
51            decode_u32_attribute(name, raw(name)?).map_err(InvalidSnapshotError::ValueError)
52        };
53
54        let staking_state = StakingState::new(
55            value_u32(PREV_STAKE_BLOCK_NUMBER_ATTR)?,
56            value(PREV_STAKE_LIMIT_ATTR)?,
57            value_u32(MAX_STAKE_LIMIT_GROWTH_BLOCKS_ATTR)?,
58            value(MAX_STAKE_LIMIT_ATTR)?,
59        );
60
61        // Seeded from the observed header; `apply_block` moves it to the execution block before
62        // the state is quoted.
63        Ok(LidoV4State::new(
64            block.number,
65            value(TOTAL_SHARES_ATTR)?,
66            value(EXTERNAL_SHARES_ATTR)?,
67            value(BUFFERED_ETHER_ATTR)?,
68            value(DEPOSITED_POST_REPORT_ATTR)?,
69            value(CL_VALIDATORS_BALANCE_ATTR)?,
70            value(CL_PENDING_BALANCE_ATTR)?,
71            staking_state,
72            value(WSTETH_SHARES_ATTR)?,
73        ))
74    }
75}