tycho_simulation/evm/protocol/etherfi/
decoder.rs1use 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_u16_attribute, decode_u64_attribute, BucketAttributes, BucketLimit,
9 EtherfiState, PoolState, RedemptionInfo, Venue, WrapperState, BURN_BUCKET, EXIT_FEE_BPS_ATTR,
10 EXIT_FEE_SPLIT_TO_TREASURY_BPS_ATTR, LOW_WATERMARK_BPS_ATTR, MINT_BUCKET, POOL_COMPONENT_ID,
11 REDEMPTION_BUCKET, TOTAL_SHARES_ATTR, TOTAL_VALUE_IN_LP_ATTR, TOTAL_VALUE_OUT_OF_LP_ATTR,
12 WEETH_SHARES_ATTR, WRAPPER_COMPONENT_ID,
13};
14use crate::protocol::{
15 errors::InvalidSnapshotError,
16 models::{DecoderContext, TryFromWithBlock},
17};
18
19impl TryFromWithBlock<ComponentWithState, BlockHeader> for EtherfiState {
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 let raw = |name: &str| -> Result<&Bytes, InvalidSnapshotError> {
30 snapshot
31 .state
32 .attributes
33 .get(name)
34 .ok_or_else(|| InvalidSnapshotError::MissingAttribute(name.to_string()))
35 };
36 let value = |name: &str| -> Result<U256, InvalidSnapshotError> {
37 decode_attribute(name, raw(name)?).map_err(InvalidSnapshotError::ValueError)
38 };
39 let value_u64 = |name: &str| -> Result<u64, InvalidSnapshotError> {
40 decode_u64_attribute(name, raw(name)?).map_err(InvalidSnapshotError::ValueError)
41 };
42 let value_u16 = |name: &str| -> Result<u16, InvalidSnapshotError> {
43 decode_u16_attribute(name, raw(name)?).map_err(InvalidSnapshotError::ValueError)
44 };
45 let bucket = |names: &BucketAttributes| -> Result<BucketLimit, InvalidSnapshotError> {
46 Ok(BucketLimit {
47 capacity: value_u64(names.capacity)?,
48 remaining: value_u64(names.remaining)?,
49 last_refill: value_u64(names.last_refill)?,
50 refill_rate: value_u64(names.refill_rate)?,
51 })
52 };
53
54 let id = &snapshot.component.id;
55 let venue = if id.eq_ignore_ascii_case(POOL_COMPONENT_ID) {
56 Venue::Pool(PoolState {
57 redemption: RedemptionInfo {
58 limit: bucket(&REDEMPTION_BUCKET)?,
59 exit_fee_split_to_treasury_bps: value_u16(EXIT_FEE_SPLIT_TO_TREASURY_BPS_ATTR)?,
60 exit_fee_bps: value_u16(EXIT_FEE_BPS_ATTR)?,
61 low_watermark_bps: value_u16(LOW_WATERMARK_BPS_ATTR)?,
62 },
63 mint_limit: bucket(&MINT_BUCKET)?,
64 burn_limit: bucket(&BURN_BUCKET)?,
65 })
66 } else if id.eq_ignore_ascii_case(WRAPPER_COMPONENT_ID) {
67 Venue::Wrapper(WrapperState { weeth_shares: value(WEETH_SHARES_ATTR)? })
68 } else {
69 return Err(InvalidSnapshotError::ValueError(format!(
70 "unknown EtherFi component id {id}"
71 )));
72 };
73
74 Ok(EtherfiState::new(
77 block.timestamp,
78 value(TOTAL_VALUE_OUT_OF_LP_ATTR)?,
79 value(TOTAL_VALUE_IN_LP_ATTR)?,
80 value(TOTAL_SHARES_ATTR)?,
81 venue,
82 ))
83 }
84}