From 7302989617c3f4c116640ad80d5bd272c072eaf0 Mon Sep 17 00:00:00 2001 From: kayibal Date: Thu, 6 Feb 2025 10:11:06 -0600 Subject: [PATCH] Use manual updates on singleton template. --- .../ethereum-template-factory/src/modules.rs | 5 + .../src/abi/erc20.rs | 1 - .../src/modules.rs | 98 ++++++++++--------- .../src/pool_factories.rs | 14 ++- 4 files changed, 67 insertions(+), 51 deletions(-) diff --git a/substreams/ethereum-template-factory/src/modules.rs b/substreams/ethereum-template-factory/src/modules.rs index 5738625..ba3eb16 100644 --- a/substreams/ethereum-template-factory/src/modules.rs +++ b/substreams/ethereum-template-factory/src/modules.rs @@ -7,6 +7,11 @@ //! If your protocol supports native ETH, you may need to adjust the balance tracking //! logic in `map_relative_component_balance` to account for native token handling. //! +//! ## Assumptions +//! - Assumes each pool has a single newly deployed contract linked to it +//! - Assumes pool identifier equals the deployed contract address +//! - Assumes any price or liquidity updated correlates with a pools contract storage update. +//! //! ## Alternative Module //! If your protocol uses a vault-like contract to manage balances, or if pools are //! registered within a singleton contract, refer to the `ethereum-template-singleton` diff --git a/substreams/ethereum-template-singleton/src/abi/erc20.rs b/substreams/ethereum-template-singleton/src/abi/erc20.rs index a50ab7e..5dd70ca 100644 --- a/substreams/ethereum-template-singleton/src/abi/erc20.rs +++ b/substreams/ethereum-template-singleton/src/abi/erc20.rs @@ -1,4 +1,3 @@ - const INTERNAL_ERR: &'static str = "`ethabi_derive` internal error"; /// Contract's functions. #[allow(dead_code, unused_imports, unused_variables)] diff --git a/substreams/ethereum-template-singleton/src/modules.rs b/substreams/ethereum-template-singleton/src/modules.rs index 717eb6b..6d28fa7 100644 --- a/substreams/ethereum-template-singleton/src/modules.rs +++ b/substreams/ethereum-template-singleton/src/modules.rs @@ -4,6 +4,10 @@ //! pattern. Usually these protocols employ a fixed set of contracts instead of //! deploying new contracts per component. //! +//! ## Assumptions +//! - Assumes a single vault contract is enough to simulate all swaps +//! - Assumes any price or liquidity change on a pool is linked to a tvl change +//! //! ## Alternative Module //! If your protocol uses individual contracts deployed with a factory to manage //! components and balances, refer to the `ethereum-template-factory` substream for an @@ -17,10 +21,9 @@ use crate::{pool_factories, pool_factories::DeploymentConfig}; use anyhow::Result; use itertools::Itertools; -use prost::Message; use std::collections::HashMap; use substreams::{pb::substreams::StoreDeltas, prelude::*}; -use substreams_ethereum::{block_view::CallView, pb::eth, Event}; +use substreams_ethereum::pb::eth; use tycho_substreams::{ balances::aggregate_balances_changes, contract::extract_contract_changes_builder, prelude::*, }; @@ -80,58 +83,60 @@ fn store_protocol_tokens( /// Extracts balance changes per component /// -/// This template function inspects ERC20 transfer events to/from the singleton contract -/// to extract balance changes. If a transfer to the component is detected, it's -/// balanced is increased and if a balance from the component is detected its balance -/// is decreased. +/// This function parses protocol specific events that incur tvl changes into +/// BalanceDelta structs. /// /// ## Note: -/// - If your protocol emits events that let you calculate balance deltas more efficiently you may -/// want to use those instead of raw transfers. -/// - Changes are necessary if your protocol uses native ETH or your component burns or mints tokens -/// without emitting transfer events. -/// - You may want to ignore LP tokens if your protocol emits transfer events for these here. +/// - You only need to account for balances that immediately available as liquidity, e.g. user +/// deposits or accumulated swap fees should not be accounted for. +/// - Take special care if your protocol uses native ETH or your component burns or mints tokens. +/// - You may want to ignore LP tokens if the tvl is covered via regular erc20 tokens. #[substreams::handlers::map] fn map_relative_component_balance( params: String, block: eth::v2::Block, - store: StoreGetInt64, + _store: StoreGetInt64, ) -> Result { - let config: DeploymentConfig = serde_qs::from_str(params.as_str())?; + let _config: DeploymentConfig = serde_qs::from_str(params.as_str())?; let res = block .transactions() .flat_map(|tx| { tx.logs_with_calls() - .filter_map(|(log, call)| { - let token_addr_hex = hex::encode(&log.address); - if !store.has_last(&token_addr_hex) { - return None; - } + .map(|(_log, _call)| -> Vec { + /* + TODO: Parse events/calls to extract balance deltas - crate::abi::erc20::events::Transfer::match_and_decode(log).map(|transfer| { - let to_addr = transfer.to.as_slice(); - let from_addr = transfer.from.as_slice(); - if let Some(component_id) = extract_component_id_from_call(call) { - if to_addr == config.vault_address { - return Some(BalanceDelta { - ord: log.ordinal, - tx: Some(tx.into()), - token: log.address.to_vec(), - delta: transfer.value.to_signed_bytes_be(), - component_id: component_id.encode_to_vec(), - }); - } else if from_addr == config.vault_address { - return Some(BalanceDelta { - ord: log.ordinal, - tx: Some(tx.into()), - token: log.address.to_vec(), - delta: (transfer.value.neg()).to_signed_bytes_be(), - component_id: component_id.encode_to_vec(), - }); + Please parse your protocols events here that incur tvl changes to + components and emit a BalanceChange event for each affected + component and token combination. + + ## Example + + ```rust + if let Some(ev) = core_events::Swapped::match_and_decode(log) { + let pool_id = hash_pool_key(&ev.pool_key); + vec![ + BalanceDelta { + ord: log.ordinal, + tx: Some(tx.into()), + token: ev.pool_key.0.clone(), + delta: ev.delta0.to_signed_bytes_be(), + component_id: pool_id.clone().into(), + }, + BalanceDelta { + ord: log.ordinal, + tx: Some(tx.into()), + token: ev.pool_key.1.clone(), + delta: &ev.delta1.to_signed_bytes_be(), + component_id: pool_id.into(), } - } - None - }) + ] + } else { + vec![] + } + ``` + */ + vec![] }) .flatten() }) @@ -140,12 +145,6 @@ fn map_relative_component_balance( Ok(BlockBalanceDeltas { balance_deltas: res }) } -// TODO: given a relevant balance changing call associate it with the respective -// component -fn extract_component_id_from_call(_call: CallView) -> Option { - todo!() -} - /// Aggregates relative balances values into absolute values /// /// Aggregate the relative balances in an additive store since tycho-indexer expects @@ -221,6 +220,11 @@ fn map_protocol_changes( token_bc_map.values().for_each(|bc| { // track component balance builder.add_balance_change(bc); + // Mark this component as updates since we are using manual update tracking + // TODO: ensure this covers all cases a component should be marked as + let component_id = + String::from_utf8(bc.component_id.clone()).expect("bad component id"); + builder.mark_component_as_updated(&component_id); // track vault contract balance contract_changes .upsert_token_balance(bc.token.as_slice(), bc.balance.as_slice()) diff --git a/substreams/ethereum-template-singleton/src/pool_factories.rs b/substreams/ethereum-template-singleton/src/pool_factories.rs index 62afd35..a0976c1 100644 --- a/substreams/ethereum-template-singleton/src/pool_factories.rs +++ b/substreams/ethereum-template-singleton/src/pool_factories.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use substreams_ethereum::pb::eth::v2::{Call, Log, TransactionTrace}; use tycho_substreams::models::{ - ChangeType, FinancialType, ImplementationType, ProtocolComponent, ProtocolType, + Attribute, ChangeType, FinancialType, ImplementationType, ProtocolComponent, ProtocolType, }; #[derive(Deserialize)] @@ -31,10 +31,18 @@ pub fn maybe_create_component( // TODO: add the components tokens ], contracts: vec![ - // TODO: any contracts required during swapping + config.vault_address.clone(), + // TODO: any additional contracts required during swapping ], static_att: vec![ - // TODO: any additional metadata required, e.g. for swap encoding + // Singleton components are marked as manual updates since we can't + // infer component updates from vault storage updates. The template + // later sets update markers on components that had a balance change. + Attribute { + name: "manual_updates".to_string(), + value: vec![1u8], + change: ChangeType::Creation.into(), + }, // TODO: any additional metadata required, e.g. for swap encoding ], change: ChangeType::Creation.into(), protocol_type: Some(ProtocolType {