Skip to content

Commit a99792a

Browse files
committed
Use manual updates on singleton template.
1 parent 71db637 commit a99792a

File tree

4 files changed

+67
-51
lines changed

4 files changed

+67
-51
lines changed

substreams/ethereum-template-factory/src/modules.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
//! If your protocol supports native ETH, you may need to adjust the balance tracking
88
//! logic in `map_relative_component_balance` to account for native token handling.
99
//!
10+
//! ## Assumptions
11+
//! - Assumes each pool has a single newly deployed contract linked to it
12+
//! - Assumes pool identifier equals the deployed contract address
13+
//! - Assumes any price or liquidity updated correlates with a pools contract storage update.
14+
//!
1015
//! ## Alternative Module
1116
//! If your protocol uses a vault-like contract to manage balances, or if pools are
1217
//! registered within a singleton contract, refer to the `ethereum-template-singleton`

substreams/ethereum-template-singleton/src/abi/erc20.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
const INTERNAL_ERR: &'static str = "`ethabi_derive` internal error";
32
/// Contract's functions.
43
#[allow(dead_code, unused_imports, unused_variables)]

substreams/ethereum-template-singleton/src/modules.rs

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
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
@@ -17,10 +21,9 @@
1721
use crate::{pool_factories, pool_factories::DeploymentConfig};
1822
use anyhow::Result;
1923
use itertools::Itertools;
20-
use prost::Message;
2124
use std::collections::HashMap;
2225
use substreams::{pb::substreams::StoreDeltas, prelude::*};
23-
use substreams_ethereum::{block_view::CallView, pb::eth, Event};
26+
use substreams_ethereum::pb::eth;
2427
use 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]
9595
fn 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())

substreams/ethereum-template-singleton/src/pool_factories.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use serde::Deserialize;
22
use substreams_ethereum::pb::eth::v2::{Call, Log, TransactionTrace};
33
use tycho_substreams::models::{
4-
ChangeType, FinancialType, ImplementationType, ProtocolComponent, ProtocolType,
4+
Attribute, ChangeType, FinancialType, ImplementationType, ProtocolComponent, ProtocolType,
55
};
66

77
#[derive(Deserialize)]
@@ -31,10 +31,18 @@ pub fn maybe_create_component(
3131
// TODO: add the components tokens
3232
],
3333
contracts: vec![
34-
// TODO: any contracts required during swapping
34+
config.vault_address.clone(),
35+
// TODO: any additional contracts required during swapping
3536
],
3637
static_att: vec![
37-
// TODO: any additional metadata required, e.g. for swap encoding
38+
// Singleton components are marked as manual updates since we can't
39+
// infer component updates from vault storage updates. The template
40+
// later sets update markers on components that had a balance change.
41+
Attribute {
42+
name: "manual_updates".to_string(),
43+
value: vec![1u8],
44+
change: ChangeType::Creation.into(),
45+
}, // TODO: any additional metadata required, e.g. for swap encoding
3846
],
3947
change: ChangeType::Creation.into(),
4048
protocol_type: Some(ProtocolType {

0 commit comments

Comments
 (0)