diff --git a/substreams/Cargo.lock b/substreams/Cargo.lock index bd53eeb..6819137 100644 --- a/substreams/Cargo.lock +++ b/substreams/Cargo.lock @@ -981,6 +981,25 @@ dependencies = [ "thiserror", ] +[[package]] +name = "substreams-maverick" +version = "0.1.0" +dependencies = [ + "anyhow", + "bytes", + "ethabi 18.0.0", + "getrandom", + "hex", + "hex-literal 0.4.1", + "itertools 0.12.1", + "num-bigint", + "prost 0.11.9", + "prost-types 0.12.3", + "substreams", + "substreams-ethereum", + "tycho-substreams", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/substreams/Cargo.toml b/substreams/Cargo.toml index 4a69dfc..9bf71d5 100644 --- a/substreams/Cargo.toml +++ b/substreams/Cargo.toml @@ -1,5 +1,10 @@ [workspace] -members = ["ethereum-balancer", "ethereum-curve", "crates/tycho-substreams"] +members = [ + "ethereum-balancer", + "ethereum-curve", + "ethereum-maverick", + "crates/tycho-substreams", +] resolver = "2" diff --git a/substreams/ethereum-curve/src/modules.rs b/substreams/ethereum-curve/src/modules.rs index d1289a0..db95c6b 100644 --- a/substreams/ethereum-curve/src/modules.rs +++ b/substreams/ethereum-curve/src/modules.rs @@ -235,7 +235,7 @@ pub fn map_relative_balances( /// It's significant to include both the `pool_id` and the `token_id` for each balance delta as the /// store key to ensure that there's a unique balance being tallied for each. #[substreams::handlers::store] -pub fn store_balance(deltas: BlockBalanceDeltas, store: StoreAddBigInt) { +pub fn store_balances(deltas: BlockBalanceDeltas, store: StoreAddBigInt) { store_balance_changes(deltas, store) } diff --git a/substreams/ethereum-curve/substreams.yaml b/substreams/ethereum-curve/substreams.yaml index 39c40a5..6432e0e 100644 --- a/substreams/ethereum-curve/substreams.yaml +++ b/substreams/ethereum-curve/substreams.yaml @@ -51,7 +51,7 @@ modules: output: type: proto:tycho.evm.v1.BalanceDeltas - - name: store_balance + - name: store_balances kind: store initialBlock: 19128828 updatePolicy: add @@ -67,7 +67,7 @@ modules: - map: map_components - map: map_relative_balances - store: store_components - - store: store_balance + - store: store_balances mode: deltas # This is the key property that simplifies `BalanceChange` handling output: type: proto:tycho.evm.v1.BlockContractChanges diff --git a/substreams/ethereum-maverick/Cargo.toml b/substreams/ethereum-maverick/Cargo.toml index 06d3af6..6b8973a 100644 --- a/substreams/ethereum-maverick/Cargo.toml +++ b/substreams/ethereum-maverick/Cargo.toml @@ -8,17 +8,18 @@ name = "substreams_maverick" crate-type = ["cdylib"] [dependencies] -substreams = "0.5" -substreams-ethereum = "0.9.9" -prost = "0.11" -hex-literal = "0.4.1" -ethabi = "18.0.0" -hex = "0.4.2" +substreams.workspace = true +substreams-ethereum.workspace = true +prost.workspace = true +prost-types.workspace = true +hex-literal.workspace = true +ethabi.workspace = true +hex.workspace = true bytes = "1.5.0" anyhow = "1.0.75" -prost-types = "0.12.3" num-bigint = "0.4.4" itertools = "0.12.0" +tycho-substreams.workspace = true [build-dependencies] anyhow = "1" diff --git a/substreams/ethereum-maverick/proto/tycho/evm/v1/common.proto b/substreams/ethereum-maverick/proto/tycho/evm/v1/common.proto deleted file mode 100644 index 1a5caf4..0000000 --- a/substreams/ethereum-maverick/proto/tycho/evm/v1/common.proto +++ /dev/null @@ -1,113 +0,0 @@ -syntax = "proto3"; - -package tycho.evm.v1; - -// This file contains the proto definitions for Substreams common to all integrations. - -// A struct describing a block. -message Block { - // The blocks hash. - bytes hash = 1; - // The parent blocks hash. - bytes parent_hash = 2; - // The block number. - uint64 number = 3; - // The block timestamp. - uint64 ts = 4; -} - -// A struct describing a transaction. -message Transaction { - // The transaction hash. - bytes hash = 1; - // The sender of the transaction. - bytes from = 2; - // The receiver of the transaction. - bytes to = 3; - // The transactions index within the block. - // TODO: should this be uint32? to match the type from the native substream type? - uint64 index = 4; -} - -// Enum to specify the type of a change. -enum ChangeType { - CHANGE_TYPE_UNSPECIFIED = 0; - CHANGE_TYPE_UPDATE = 1; - CHANGE_TYPE_CREATION = 2; - CHANGE_TYPE_DELETION = 3; -} - -// A custom struct representing an arbitrary attribute of a protocol component. -// This is mainly used by the native integration to track the necessary information about the protocol. -message Attribute { - // The name of the attribute. - string name = 1; - // The value of the attribute. - bytes value = 2; - // The type of change the attribute underwent. - ChangeType change = 3; -} - -// A struct describing a part of the protocol. -// Note: For example this can be a UniswapV2 pair, that tracks the two ERC20 tokens used by the pair, -// the component would represent a single contract. In case of VM integration, such component would -// not need any attributes, because all the relevant info would be tracked via storage slots and balance changes. -// It can also be a wrapping contract, like WETH, that has a constant price, but it allows swapping tokens. -// This is why the name ProtocolComponent is used instead of "Pool" or "Pair". -message ProtocolComponent { - // A unique identifier for the component within the protocol. - // Can be e.g. a stringified address or a string describing the trading pair. - string id = 1; - // Addresses of the ERC20 tokens used by the component. - repeated bytes tokens = 2; - // Addresses of the contracts used by the component. - // Usually it is a single contract, but some protocols use multiple contracts. - repeated bytes contracts = 3; - // Attributes of the component. Used mainly be the native integration. - // The inner ChangeType of the attribute has to match the ChangeType of the ProtocolComponent. - repeated Attribute static_att = 4; - // Type of change the component underwent. - ChangeType change = 5; -} - -message TransactionProtocolComponents { - Transaction tx = 1; - repeated ProtocolComponent components = 2; -} - -message GroupedTransactionProtocolComponents { - repeated TransactionProtocolComponents tx_components = 1; -} - -// A struct for following the changes of Total Value Locked (TVL) of a protocol component. -// Note that if a ProtocolComponent contains multiple contracts, the TVL is tracked for the component as a whole. -// E.g. for UniswapV2 pair WETH/USDC, this tracks the USDC and WETH balance of the pair contract. -message BalanceChange { - // The address of the ERC20 token whose balance changed. - bytes token = 1; - // The new balance of the token. - bytes balance = 2; - // The id of the component whose TVL is tracked. - // If the protocol component includes multiple contracts, the balance change must be aggregated to reflect how much tokens can be traded. - bytes component_id = 3; -} - -// A struct for following the changes of Total Value Locked (TVL) of a protocol component. -// Note that if a ProtocolComponent contains multiple contracts, the TVL is tracked for the component as a whole. -// E.g. for UniswapV2 pair WETH/USDC, this tracks the USDC and WETH balance of the pair contract. -message BalanceDelta { - uint64 ord = 1; - // The tx hash of the transaction that caused the balance change. - Transaction tx = 2; - // The address of the ERC20 token whose balance changed. - bytes token = 3; - // The delta balance of the token. - bytes delta = 4; - // The id of the component whose TVL is tracked. - // If the protocol component includes multiple contracts, the balance change must be aggregated to reflect how much tokens can be traded. - bytes component_id = 5; -} - -message BalanceDeltas { - repeated BalanceDelta balance_deltas = 1; -} diff --git a/substreams/ethereum-maverick/proto/tycho/evm/v1/entity.proto b/substreams/ethereum-maverick/proto/tycho/evm/v1/entity.proto deleted file mode 100644 index 14539e4..0000000 --- a/substreams/ethereum-maverick/proto/tycho/evm/v1/entity.proto +++ /dev/null @@ -1,32 +0,0 @@ -syntax = "proto3"; - -package tycho.evm.v1; - -import "tycho/evm/v1/common.proto"; - -// This file contains the definition for the native integration of Substreams. - -// A component is a set of attributes that are associated with a custom entity. -message EntityChanges { - // A unique identifier of the entity within the protocol. - string component_id = 1; - // The set of attributes that are associated with the entity. - repeated Attribute attributes = 2; -} - -message TransactionEntityChanges { - Transaction tx = 1; - repeated EntityChanges entity_changes = 2; - // An array of newly added components. - repeated ProtocolComponent component_changes = 3; - // An array of balance changes to components. - repeated BalanceChange balance_changes = 4; -} - -// A set of transaction changes within a single block. -message BlockEntityChanges { - // The block for which these changes are collectively computed. - Block block = 1; - // The set of transaction changes observed in the specified block. - repeated TransactionEntityChanges changes = 2; -} diff --git a/substreams/ethereum-maverick/proto/tycho/evm/v1/vm.proto b/substreams/ethereum-maverick/proto/tycho/evm/v1/vm.proto deleted file mode 100644 index a49dcf0..0000000 --- a/substreams/ethereum-maverick/proto/tycho/evm/v1/vm.proto +++ /dev/null @@ -1,50 +0,0 @@ -syntax = "proto3"; - -package tycho.evm.v1; - -import "tycho/evm/v1/common.proto"; - -// This file contains proto definitions specific to the VM integration. - -// A key value entry into contract storage. -message ContractSlot { - // A contract's storage slot. - bytes slot = 2; - // The new value for this storage slot. - bytes value = 3; -} - -// Changes made to a single contract's state. -message ContractChange { - // The contract's address - bytes address = 1; - // The new native balance of the contract, empty bytes indicates no change. - bytes balance = 2; - // The new code of the contract, empty bytes indicates no change. - bytes code = 3; - // The changes to this contract's slots, empty sequence indicates no change. - repeated ContractSlot slots = 4; - // Whether this is an update, a creation or a deletion. - ChangeType change = 5; -} - -// A set of changes aggregated by transaction. -message TransactionContractChanges { - // The transaction instance that results in the changes. - Transaction tx = 1; - // Contains the changes induced by the above transaction, aggregated on a per-contract basis. - // Must include changes to every contract that is tracked by all ProtocolComponents. - repeated ContractChange contract_changes = 2; - // An array of any component changes. - repeated ProtocolComponent component_changes = 3; - // An array of balance changes to components. - repeated BalanceChange balance_changes = 4; -} - -// A set of transaction changes within a single block. -message BlockContractChanges { - // The block for which these changes are collectively computed. - Block block = 1; - // The set of transaction changes observed in the specified block. - repeated TransactionContractChanges changes = 2; -} diff --git a/substreams/ethereum-maverick/src/contract_changes.rs b/substreams/ethereum-maverick/src/contract_changes.rs deleted file mode 100644 index 4ab49fe..0000000 --- a/substreams/ethereum-maverick/src/contract_changes.rs +++ /dev/null @@ -1,190 +0,0 @@ -/// This file contains helpers to capture contract changes from the expanded block model. These -/// leverage the `code_changes`, `balance_changes`, and `storage_changes` fields available on the -/// `Call` type provided by block model in a substream (i.e. `logs_and_calls`, etc). -/// -/// ⚠️ These helpers *only* work if the **expanded block model** is available, more info blow. -/// https://streamingfastio.medium.com/new-block-model-to-accelerate-chain-integration-9f65126e5425 -use std::collections::HashMap; - -use substreams_ethereum::pb::eth; - -use pb::tycho::evm::v1::{self as tycho}; - -use substreams::store::{StoreGet, StoreGetInt64}; - -use crate::pb; - -struct SlotValue { - new_value: Vec, - start_value: Vec, -} - -impl SlotValue { - fn has_changed(&self) -> bool { - self.start_value != self.new_value - } -} - -// Uses a map for slots, protobuf does not allow bytes in hashmap keys -pub struct InterimContractChange { - address: Vec, - balance: Vec, - code: Vec, - slots: HashMap, SlotValue>, - change: tycho::ChangeType, -} - -impl From for tycho::ContractChange { - fn from(value: InterimContractChange) -> Self { - tycho::ContractChange { - address: value.address, - balance: value.balance, - code: value.code, - slots: value - .slots - .into_iter() - .filter(|(_, value)| value.has_changed()) - .map(|(slot, value)| tycho::ContractSlot { - slot, - value: value.new_value, - }) - .collect(), - change: value.change.into(), - } - } -} - -pub fn extract_contract_changes( - block: ð::v2::Block, - contracts: StoreGetInt64, - transaction_contract_changes: &mut HashMap, -) { - let mut changed_contracts: HashMap, InterimContractChange> = HashMap::new(); - - // Collect all accounts created in this block - let created_accounts: HashMap<_, _> = block - .transactions() - .flat_map(|tx| { - tx.calls.iter().flat_map(|call| { - call.account_creations - .iter() - .map(|ac| (&ac.account, ac.ordinal)) - }) - }) - .collect(); - - block.transactions().for_each(|block_tx| { - let mut storage_changes = Vec::new(); - let mut balance_changes = Vec::new(); - let mut code_changes = Vec::new(); - - block_tx - .calls - .iter() - .filter(|call| { - !call.state_reverted - && contracts - .get_last(format!("pool:{0}", hex::encode(&call.address))) - .is_some() - }) - .for_each(|call| { - storage_changes.extend(call.storage_changes.iter()); - balance_changes.extend(call.balance_changes.iter()); - code_changes.extend(call.code_changes.iter()); - }); - - storage_changes.sort_unstable_by_key(|change| change.ordinal); - balance_changes.sort_unstable_by_key(|change| change.ordinal); - code_changes.sort_unstable_by_key(|change| change.ordinal); - - storage_changes.iter().for_each(|storage_change| { - let contract_change = changed_contracts - .entry(storage_change.address.clone()) - .or_insert_with(|| InterimContractChange { - address: storage_change.address.clone(), - balance: Vec::new(), - code: Vec::new(), - slots: HashMap::new(), - change: if created_accounts.contains_key(&storage_change.address) { - tycho::ChangeType::Creation - } else { - tycho::ChangeType::Update - }, - }); - - let slot_value = contract_change - .slots - .entry(storage_change.key.clone()) - .or_insert_with(|| SlotValue { - new_value: storage_change.new_value.clone(), - start_value: storage_change.old_value.clone(), - }); - - slot_value - .new_value - .copy_from_slice(&storage_change.new_value); - }); - - balance_changes.iter().for_each(|balance_change| { - let contract_change = changed_contracts - .entry(balance_change.address.clone()) - .or_insert_with(|| InterimContractChange { - address: balance_change.address.clone(), - balance: Vec::new(), - code: Vec::new(), - slots: HashMap::new(), - change: if created_accounts.contains_key(&balance_change.address) { - tycho::ChangeType::Creation - } else { - tycho::ChangeType::Update - }, - }); - - if let Some(new_balance) = &balance_change.new_value { - contract_change.balance.clear(); - contract_change - .balance - .extend_from_slice(&new_balance.bytes); - } - }); - - code_changes.iter().for_each(|code_change| { - let contract_change = changed_contracts - .entry(code_change.address.clone()) - .or_insert_with(|| InterimContractChange { - address: code_change.address.clone(), - balance: Vec::new(), - code: Vec::new(), - slots: HashMap::new(), - change: if created_accounts.contains_key(&code_change.address) { - tycho::ChangeType::Creation - } else { - tycho::ChangeType::Update - }, - }); - - contract_change.code.clear(); - contract_change - .code - .extend_from_slice(&code_change.new_code); - }); - - if !storage_changes.is_empty() || !balance_changes.is_empty() || !code_changes.is_empty() { - transaction_contract_changes - .entry(block_tx.index.into()) - .or_insert_with(|| tycho::TransactionContractChanges { - tx: Some(tycho::Transaction { - hash: block_tx.hash.clone(), - from: block_tx.from.clone(), - to: block_tx.to.clone(), - index: block_tx.index as u64, - }), - contract_changes: vec![], - component_changes: vec![], - balance_changes: vec![], - }) - .contract_changes - .extend(changed_contracts.drain().map(|(_, change)| change.into())); - } - }); -} diff --git a/substreams/ethereum-maverick/src/lib.rs b/substreams/ethereum-maverick/src/lib.rs index 5bf4b5d..7665a0a 100644 --- a/substreams/ethereum-maverick/src/lib.rs +++ b/substreams/ethereum-maverick/src/lib.rs @@ -1,4 +1,3 @@ mod abi; -mod contract_changes; mod modules; mod pb; diff --git a/substreams/ethereum-maverick/src/modules.rs b/substreams/ethereum-maverick/src/modules.rs index 25b6fd1..c5be905 100644 --- a/substreams/ethereum-maverick/src/modules.rs +++ b/substreams/ethereum-maverick/src/modules.rs @@ -1,26 +1,27 @@ use std::collections::HashMap; use anyhow::Result; -use substreams::pb::substreams::StoreDeltas; -use substreams::store::{ - StoreAdd, StoreAddBigInt, StoreAddInt64, StoreGet, StoreGetInt64, StoreGetString, StoreNew, - StoreSet, StoreSetString, +use substreams::{ + pb::substreams::StoreDeltas, + store::{ + StoreAdd, StoreAddBigInt, StoreAddInt64, StoreGet, StoreGetInt64, StoreGetString, StoreNew, + StoreSet, StoreSetString, + }, }; -use substreams::key; -use substreams::scalar::BigInt; -use substreams::{hex, log}; +use substreams::{hex, key, scalar::BigInt}; -use substreams_ethereum::block_view::LogView; -use substreams_ethereum::pb::eth; +use substreams_ethereum::{block_view::LogView, pb::eth}; use itertools::Itertools; -use pb::tycho::evm::v1::{self as tycho}; -use contract_changes::extract_contract_changes; use substreams_ethereum::Event; -use crate::{abi, contract_changes, pb}; +use crate::{abi, pb}; + +use tycho_substreams::{ + balances::store_balance_changes, contract::extract_contract_changes, prelude::*, +}; const FACTORY: [u8; 20] = hex!("Eb6625D65a0553c9dBc64449e56abFe519bd9c9B"); @@ -37,7 +38,7 @@ const FACTORY: [u8; 20] = hex!("Eb6625D65a0553c9dBc64449e56abFe519bd9c9B"); /// This struct purely exists to spoof the `PartialEq` trait for `Transaction` so we can use it in /// a later groupby operation. #[derive(Debug)] -struct TransactionWrapper(tycho::Transaction); +struct TransactionWrapper(Transaction); impl PartialEq for TransactionWrapper { fn eq(&self, other: &Self) -> bool { @@ -45,8 +46,8 @@ impl PartialEq for TransactionWrapper { } } -fn tx_from_log(log: &LogView) -> tycho::Transaction { - tycho::Transaction { +fn tx_from_log(log: &LogView) -> Transaction { + Transaction { hash: log.receipt.transaction.hash.clone(), from: log.receipt.transaction.from.clone(), to: log.receipt.transaction.to.clone(), @@ -55,49 +56,53 @@ fn tx_from_log(log: &LogView) -> tycho::Transaction { } #[substreams::handlers::map] -pub fn map_pools_created( - block: eth::v2::Block, -) -> Result { +pub fn map_components(block: eth::v2::Block) -> Result { // Gather contract changes by indexing `PoolCreated` events and analysing the `Create` call // We store these as a hashmap by tx hash since we need to agg by tx hash later - Ok(tycho::GroupedTransactionProtocolComponents { + Ok(BlockTransactionProtocolComponents { tx_components: block .transactions() .filter_map(|tx| { - let components: Vec = tx + let components: Vec = tx .logs_with_calls() .filter(|(_, call)| !call.call.state_reverted) .filter(|(log, _)| log.address == FACTORY) .filter_map(|(log, _)| { let pool_added = abi::factory::events::PoolCreated::match_and_decode(log)?; - log::info!("tacos"); - - Some(tycho::ProtocolComponent { + Some(ProtocolComponent { id: hex::encode(&pool_added.pool_address), + tx: Some(Transaction { + hash: tx.hash.clone(), + from: tx.from.clone(), + to: tx.to.clone(), + index: Into::::into(tx.index), + }), tokens: vec![pool_added.token_a, pool_added.token_b], contracts: vec![FACTORY.into(), pool_added.pool_address], static_att: vec![ - tycho::Attribute { + Attribute { name: "activeTick".into(), - value: pool_added.active_tick.to_signed_bytes_be(), - change: tycho::ChangeType::Creation.into(), + value: pool_added + .active_tick + .to_signed_bytes_be(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "lookback".into(), value: pool_added.lookback.to_signed_bytes_be(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, ], - - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), + ..Default::default() }) }) .collect::>(); if !components.is_empty() { - Some(tycho::TransactionProtocolComponents { - tx: Some(tycho::Transaction { + Some(TransactionProtocolComponents { + tx: Some(Transaction { hash: tx.hash.clone(), from: tx.from.clone(), to: tx.to.clone(), @@ -115,7 +120,7 @@ pub fn map_pools_created( /// Simply stores the `ProtocolComponent`s with the pool id as the key #[substreams::handlers::store] -pub fn store_pools_created(map: tycho::GroupedTransactionProtocolComponents, store: StoreAddInt64) { +pub fn store_components(map: BlockTransactionProtocolComponents, store: StoreAddInt64) { store.add_many( 0, &map.tx_components @@ -129,7 +134,7 @@ pub fn store_pools_created(map: tycho::GroupedTransactionProtocolComponents, sto /// Simply stores the `ProtocolComponent`s with the pool id as the key #[substreams::handlers::store] -pub fn store_pools_tokens(map: tycho::GroupedTransactionProtocolComponents, store: StoreSetString) { +pub fn store_component_tokens(map: BlockTransactionProtocolComponents, store: StoreSetString) { map.tx_components .iter() .flat_map(|tx_components| &tx_components.components) @@ -149,11 +154,11 @@ pub fn store_pools_tokens(map: tycho::GroupedTransactionProtocolComponents, stor /// Since the `Swap`, `AddLiquidity`, `RemoveLiuidity` events administer only deltas, we need to /// leverage a map and a store to be able to tally up final balances for tokens in a pool. #[substreams::handlers::map] -pub fn map_balance_deltas( +pub fn map_relative_balances( block: eth::v2::Block, pools_store: StoreGetInt64, tokens_store: StoreGetString, -) -> Result { +) -> Result { let deltas = block .logs() .filter(|log| { @@ -181,14 +186,14 @@ pub fn map_balance_deltas( ) }; vec![ - tycho::BalanceDelta { + BalanceDelta { ord: log.log.ordinal, tx: Some(tx_from_log(&log)), token: token_a, delta: event.amount_in.to_signed_bytes_be(), component_id: log.address().into(), }, - tycho::BalanceDelta { + BalanceDelta { ord: log.log.ordinal, tx: Some(tx_from_log(&log)), token: token_b, @@ -217,14 +222,14 @@ pub fn map_balance_deltas( .map(|token| token.to_owned()) // Clone the tokens .collect::>(); vec![ - tycho::BalanceDelta { + BalanceDelta { ord: log.log.ordinal, tx: Some(tx_from_log(&log)), token: hex::decode(tokens[0].clone()).unwrap(), delta: delta_a.to_signed_bytes_be(), component_id: log.address().into(), }, - tycho::BalanceDelta { + BalanceDelta { ord: log.log.ordinal, tx: Some(tx_from_log(&log)), token: hex::decode(tokens[1].clone()).unwrap(), @@ -258,14 +263,14 @@ pub fn map_balance_deltas( let neg_delta_a: BigInt = delta_a * -1; let neg_delta_b: BigInt = delta_b * -1; vec![ - tycho::BalanceDelta { + BalanceDelta { ord: log.log.ordinal, tx: Some(tx_from_log(&log)), token: hex::decode(tokens[0].clone()).unwrap(), delta: neg_delta_a.to_signed_bytes_be(), component_id: log.address().into(), }, - tycho::BalanceDelta { + BalanceDelta { ord: log.log.ordinal, tx: Some(tx_from_log(&log)), token: hex::decode(tokens[1].clone()).unwrap(), @@ -282,46 +287,33 @@ pub fn map_balance_deltas( }) .collect::>(); - Ok(tycho::BalanceDeltas { - balance_deltas: deltas, - }) + Ok(BlockBalanceDeltas { balance_deltas: deltas }) } /// It's significant to include both the `pool_id` and the `token_id` for each balance delta as the /// store key to ensure that there's a unique balance being tallied for each. #[substreams::handlers::store] -pub fn store_balance_changes(deltas: tycho::BalanceDeltas, store: StoreAddBigInt) { - deltas.balance_deltas.iter().for_each(|delta| { - store.add( - delta.ord, - format!( - "pool:{0}:token:{1}", - hex::encode(&delta.component_id), - hex::encode(&delta.token) - ), - BigInt::from_signed_bytes_be(&delta.delta), - ); - }); +pub fn store_balances(deltas: BlockBalanceDeltas, store: StoreAddBigInt) { + store_balance_changes(deltas, store) } /// This is the main map that handles most of the indexing of this substream. /// Every contract change is grouped by transaction index via the `transaction_contract_changes` /// map. Each block of code will extend the `TransactionContractChanges` struct with the /// cooresponding changes (balance, component, contract), inserting a new one if it doesn't exist. -/// At the very end, the map can easily be sorted by index to ensure the final `BlockContractChanges` -/// is ordered by transactions properly. +/// At the very end, the map can easily be sorted by index to ensure the final +/// `BlockContractChanges` is ordered by transactions properly. #[substreams::handlers::map] -pub fn map_changes( +pub fn map_protocol_changes( block: eth::v2::Block, - grouped_components: tycho::GroupedTransactionProtocolComponents, - deltas: tycho::BalanceDeltas, + grouped_components: BlockTransactionProtocolComponents, + deltas: BlockBalanceDeltas, components_store: StoreGetInt64, balance_store: StoreDeltas, // Note, this map module is using the `deltas` mode for the store. -) -> Result { +) -> Result { // We merge contract changes by transaction (identified by transaction index) making it easy to // sort them at the very end. - let mut transaction_contract_changes: HashMap<_, tycho::TransactionContractChanges> = - HashMap::new(); + let mut transaction_contract_changes: HashMap<_, TransactionContractChanges> = HashMap::new(); // `ProtocolComponents` are gathered from `map_pools_created` which just need a bit of work to // convert into `TransactionContractChanges` @@ -333,7 +325,7 @@ pub fn map_changes( transaction_contract_changes .entry(tx.index) - .or_insert_with(|| tycho::TransactionContractChanges { + .or_insert_with(|| TransactionContractChanges { tx: Some(tx.clone()), contract_changes: vec![], component_changes: vec![], @@ -356,7 +348,7 @@ pub fn map_changes( let token_id = key::segment_at(&store_delta.key, 3); ( balance_delta.tx.unwrap(), - tycho::BalanceChange { + BalanceChange { token: hex::decode(token_id).expect("Token ID not valid hex"), balance: store_delta.new_value, component_id: hex::decode(pool_id).expect("Token ID not valid hex"), @@ -371,7 +363,7 @@ pub fn map_changes( transaction_contract_changes .entry(tx.index) - .or_insert_with(|| tycho::TransactionContractChanges { + .or_insert_with(|| TransactionContractChanges { tx: Some(tx.clone()), contract_changes: vec![], component_changes: vec![], @@ -384,12 +376,20 @@ pub fn map_changes( // General helper for extracting contract changes. Uses block, our component store which holds // all of our tracked deployed pool addresses, and the map of tx contract changes which we // output into for final processing later. - extract_contract_changes(&block, components_store, &mut transaction_contract_changes); + extract_contract_changes( + &block, + |addr| { + components_store + .get_last(format!("pool:0x{0}", hex::encode(addr))) + .is_some() + }, + &mut transaction_contract_changes, + ); // Process all `transaction_contract_changes` for final output in the `BlockContractChanges`, // sorted by transaction index (the key). - Ok(tycho::BlockContractChanges { - block: Some(tycho::Block { + Ok(BlockContractChanges { + block: Some(Block { number: block.number, hash: block.hash.clone(), parent_hash: block @@ -404,9 +404,9 @@ pub fn map_changes( .drain() .sorted_unstable_by_key(|(index, _)| index.clone()) .filter_map(|(_, change)| { - if change.contract_changes.is_empty() - && change.component_changes.is_empty() - && change.balance_changes.is_empty() + if change.contract_changes.is_empty() && + change.component_changes.is_empty() && + change.balance_changes.is_empty() { None } else { @@ -418,11 +418,11 @@ pub fn map_changes( } // #[substreams::handlers::map] -// pub fn debug_block_events(block: eth::v2::Block) -> Result { +// pub fn debug_block_events(block: eth::v2::Block) -> Result { // log::info!("Block: {:?}", block); -// Ok(tycho::BlockContractChanges { -// block: Some(tycho::Block { +// Ok(BlockContractChanges { +// block: Some(Block { // number: block.number, // hash: block.hash.clone(), // parent_hash: block diff --git a/substreams/ethereum-maverick/substreams.yaml b/substreams/ethereum-maverick/substreams.yaml index 88e1217..e9ae952 100644 --- a/substreams/ethereum-maverick/substreams.yaml +++ b/substreams/ethereum-maverick/substreams.yaml @@ -8,7 +8,7 @@ protobuf: - tycho/evm/v1/vm.proto - tycho/evm/v1/common.proto importPaths: - - ../../proto/tycho/evm/v1/ + - ../../proto - ./proto binaries: @@ -31,7 +31,7 @@ modules: updatePolicy: add valueType: int64 inputs: - - map: map_pools_created + - map: map_pools_created - name: store_pools_tokens kind: store