44//! pattern. Usually these protocols employ a fixed set of contracts instead of
55//! deploying new contracts per component.
66//!
7+ //! ## Assumptions
8+ //! - Assumes a single vault contract is enough to simulate all swaps
9+ //! - Assumes any price or liquidity change on a pool is linked to a tvl change
10+ //!
711//! ## Alternative Module
812//! If your protocol uses individual contracts deployed with a factory to manage
913//! components and balances, refer to the `ethereum-template-factory` substream for an
1721use crate :: { pool_factories, pool_factories:: DeploymentConfig } ;
1822use anyhow:: Result ;
1923use itertools:: Itertools ;
20- use prost:: Message ;
2124use std:: collections:: HashMap ;
2225use substreams:: { pb:: substreams:: StoreDeltas , prelude:: * } ;
23- use substreams_ethereum:: { block_view :: CallView , pb:: eth, Event } ;
26+ use substreams_ethereum:: pb:: eth;
2427use tycho_substreams:: {
2528 balances:: aggregate_balances_changes, contract:: extract_contract_changes_builder, prelude:: * ,
2629} ;
@@ -80,58 +83,60 @@ fn store_protocol_tokens(
8083
8184/// Extracts balance changes per component
8285///
83- /// This template function inspects ERC20 transfer events to/from the singleton contract
84- /// to extract balance changes. If a transfer to the component is detected, it's
85- /// balanced is increased and if a balance from the component is detected its balance
86- /// is decreased.
86+ /// This function parses protocol specific events that incur tvl changes into
87+ /// BalanceDelta structs.
8788///
8889/// ## Note:
89- /// - If your protocol emits events that let you calculate balance deltas more efficiently you may
90- /// want to use those instead of raw transfers.
91- /// - Changes are necessary if your protocol uses native ETH or your component burns or mints tokens
92- /// without emitting transfer events.
93- /// - You may want to ignore LP tokens if your protocol emits transfer events for these here.
90+ /// - You only need to account for balances that immediately available as liquidity, e.g. user
91+ /// deposits or accumulated swap fees should not be accounted for.
92+ /// - Take special care if your protocol uses native ETH or your component burns or mints tokens.
93+ /// - You may want to ignore LP tokens if the tvl is covered via regular erc20 tokens.
9494#[ substreams:: handlers:: map]
9595fn map_relative_component_balance (
9696 params : String ,
9797 block : eth:: v2:: Block ,
98- store : StoreGetInt64 ,
98+ _store : StoreGetInt64 ,
9999) -> Result < BlockBalanceDeltas > {
100- let config : DeploymentConfig = serde_qs:: from_str ( params. as_str ( ) ) ?;
100+ let _config : DeploymentConfig = serde_qs:: from_str ( params. as_str ( ) ) ?;
101101 let res = block
102102 . transactions ( )
103103 . flat_map ( |tx| {
104104 tx. logs_with_calls ( )
105- . filter_map ( |( log, call) | {
106- let token_addr_hex = hex:: encode ( & log. address ) ;
107- if !store. has_last ( & token_addr_hex) {
108- return None ;
109- }
105+ . map ( |( _log, _call) | -> Vec < BalanceDelta > {
106+ /*
107+ TODO: Parse events/calls to extract balance deltas
108+
109+ Please parse your protocols events here that incur tvl changes to
110+ components and emit a BalanceChange event for each affected
111+ component and token combination.
112+
113+ ## Example
110114
111- crate :: abi:: erc20:: events:: Transfer :: match_and_decode ( log) . map ( |transfer| {
112- let to_addr = transfer. to . as_slice ( ) ;
113- let from_addr = transfer. from . as_slice ( ) ;
114- if let Some ( component_id) = extract_component_id_from_call ( call) {
115- if to_addr == config. vault_address {
116- return Some ( BalanceDelta {
117- ord : log. ordinal ,
118- tx : Some ( tx. into ( ) ) ,
119- token : log. address . to_vec ( ) ,
120- delta : transfer. value . to_signed_bytes_be ( ) ,
121- component_id : component_id. encode_to_vec ( ) ,
122- } ) ;
123- } else if from_addr == config. vault_address {
124- return Some ( BalanceDelta {
125- ord : log. ordinal ,
126- tx : Some ( tx. into ( ) ) ,
127- token : log. address . to_vec ( ) ,
128- delta : ( transfer. value . neg ( ) ) . to_signed_bytes_be ( ) ,
129- component_id : component_id. encode_to_vec ( ) ,
130- } ) ;
115+ ```rust
116+ if let Some(ev) = core_events::Swapped::match_and_decode(log) {
117+ let pool_id = hash_pool_key(&ev.pool_key);
118+ vec![
119+ BalanceDelta {
120+ ord: log.ordinal,
121+ tx: Some(tx.into()),
122+ token: ev.pool_key.0.clone(),
123+ delta: ev.delta0.to_signed_bytes_be(),
124+ component_id: pool_id.clone().into(),
125+ },
126+ BalanceDelta {
127+ ord: log.ordinal,
128+ tx: Some(tx.into()),
129+ token: ev.pool_key.1.clone(),
130+ delta: &ev.delta1.to_signed_bytes_be(),
131+ component_id: pool_id.into(),
131132 }
132- }
133- None
134- } )
133+ ]
134+ } else {
135+ vec![]
136+ }
137+ ```
138+ */
139+ vec ! [ ]
135140 } )
136141 . flatten ( )
137142 } )
@@ -140,12 +145,6 @@ fn map_relative_component_balance(
140145 Ok ( BlockBalanceDeltas { balance_deltas : res } )
141146}
142147
143- // TODO: given a relevant balance changing call associate it with the respective
144- // component
145- fn extract_component_id_from_call ( _call : CallView ) -> Option < String > {
146- todo ! ( )
147- }
148-
149148/// Aggregates relative balances values into absolute values
150149///
151150/// Aggregate the relative balances in an additive store since tycho-indexer expects
@@ -221,6 +220,11 @@ fn map_protocol_changes(
221220 token_bc_map. values ( ) . for_each ( |bc| {
222221 // track component balance
223222 builder. add_balance_change ( bc) ;
223+ // Mark this component as updates since we are using manual update tracking
224+ // TODO: ensure this covers all cases a component should be marked as
225+ let component_id =
226+ String :: from_utf8 ( bc. component_id . clone ( ) ) . expect ( "bad component id" ) ;
227+ builder. mark_component_as_updated ( & component_id) ;
224228 // track vault contract balance
225229 contract_changes
226230 . upsert_token_balance ( bc. token . as_slice ( ) , bc. balance . as_slice ( ) )
0 commit comments