diff --git a/substreams/Cargo.lock b/substreams/Cargo.lock index f89bb92..bd53eeb 100644 --- a/substreams/Cargo.lock +++ b/substreams/Cargo.lock @@ -227,6 +227,25 @@ dependencies = [ "tycho-substreams", ] +[[package]] +name = "ethereum-curve" +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 = "ethereum-types" version = "0.13.1" diff --git a/substreams/Cargo.toml b/substreams/Cargo.toml index 8bcd85d..4a69dfc 100644 --- a/substreams/Cargo.toml +++ b/substreams/Cargo.toml @@ -1,8 +1,5 @@ [workspace] -members = [ - "ethereum-balancer", - "crates/tycho-substreams", -] +members = ["ethereum-balancer", "ethereum-curve", "crates/tycho-substreams"] resolver = "2" @@ -14,9 +11,9 @@ prost-types = "0.12.3" hex-literal = "0.4.1" hex = "0.4.3" ethabi = "18.0.0" -tycho-substreams = {path ="crates/tycho-substreams"} +tycho-substreams = { path = "crates/tycho-substreams" } [profile.release] lto = true opt-level = 's' -strip = "debuginfo" \ No newline at end of file +strip = "debuginfo" diff --git a/substreams/ethereum-curve/Cargo.toml b/substreams/ethereum-curve/Cargo.toml index c601e7b..5cd96fc 100644 --- a/substreams/ethereum-curve/Cargo.toml +++ b/substreams/ethereum-curve/Cargo.toml @@ -1,24 +1,25 @@ [package] -name = "substreams-curve" +name = "ethereum-curve" version = "0.1.0" edition = "2021" [lib] -name = "substreams_curve" +name = "ethereum_curve" 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-curve/proto/tycho/evm/v1/common.proto b/substreams/ethereum-curve/proto/tycho/evm/v1/common.proto deleted file mode 100644 index 1a5caf4..0000000 --- a/substreams/ethereum-curve/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-curve/proto/tycho/evm/v1/entity.proto b/substreams/ethereum-curve/proto/tycho/evm/v1/entity.proto deleted file mode 100644 index 14539e4..0000000 --- a/substreams/ethereum-curve/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-curve/proto/tycho/evm/v1/vm.proto b/substreams/ethereum-curve/proto/tycho/evm/v1/vm.proto deleted file mode 100644 index a49dcf0..0000000 --- a/substreams/ethereum-curve/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-curve/src/contract_changes.rs b/substreams/ethereum-curve/src/contract_changes.rs deleted file mode 100644 index 4ab49fe..0000000 --- a/substreams/ethereum-curve/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-curve/src/lib.rs b/substreams/ethereum-curve/src/lib.rs index 88ecc50..27bd15a 100644 --- a/substreams/ethereum-curve/src/lib.rs +++ b/substreams/ethereum-curve/src/lib.rs @@ -1,5 +1,3 @@ mod abi; -mod contract_changes; mod modules; -mod pb; mod pool_factories; diff --git a/substreams/ethereum-curve/src/modules.rs b/substreams/ethereum-curve/src/modules.rs index 6ceb2ee..bbecbee 100644 --- a/substreams/ethereum-curve/src/modules.rs +++ b/substreams/ethereum-curve/src/modules.rs @@ -1,31 +1,30 @@ 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 itertools::Itertools; +use substreams::{ + pb::substreams::StoreDeltas, + store::{ + StoreAdd, StoreAddBigInt, StoreAddInt64, StoreGet, StoreGetInt64, StoreGetString, StoreNew, + StoreSet, StoreSetString, + }, }; -use substreams::key; -use substreams::scalar::BigInt; +use substreams::{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, pool_factories}; -use std::convert::TryInto; +use crate::{abi, pool_factories}; +use tycho_substreams::{ + balances::store_balance_changes, contract::extract_contract_changes, prelude::*, +}; /// 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 { @@ -33,8 +32,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(), @@ -43,12 +42,10 @@ 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| { @@ -57,16 +54,21 @@ pub fn map_pools_created( .filter(|(_, call)| !call.call.state_reverted) .filter_map(|(log, call)| { Some(pool_factories::address_map( - call.call.address.as_slice().try_into().ok()?, // this shouldn't fail + call.call + .address + .as_slice() + .try_into() + .ok()?, // this shouldn't fail log, call.call, + tx, )?) }) .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(), @@ -84,7 +86,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 @@ -98,7 +100,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) @@ -118,11 +120,11 @@ pub fn store_pools_tokens(map: tycho::GroupedTransactionProtocolComponents, stor /// Since the `PoolBalanceChanged` 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 mut deltas = block .logs() .filter_map(|log| { @@ -137,14 +139,14 @@ pub fn map_balance_deltas( .flat_map(|(log, event)| { let tokens_bought_delta: BigInt = event.tokens_bought * -1; vec![ - tycho::BalanceDelta { + BalanceDelta { ord: log.log.ordinal, tx: Some(tx_from_log(&log)), token: event.sold_id.to_signed_bytes_be(), delta: event.tokens_sold.to_signed_bytes_be(), component_id: log.address().into(), }, - tycho::BalanceDelta { + BalanceDelta { ord: log.log.ordinal, tx: Some(tx_from_log(&log)), token: event.bought_id.to_signed_bytes_be(), @@ -178,7 +180,7 @@ pub fn map_balance_deltas( .token_amounts .iter() .zip(tokens) - .map(move |(token_amount, token_id)| tycho::BalanceDelta { + .map(move |(token_amount, token_id)| BalanceDelta { ord: log.log.ordinal, tx: Some(tx_from_log(&log)), token: token_id.into(), @@ -216,7 +218,7 @@ pub fn map_balance_deltas( .zip(tokens) .map(move |(token_amount, token_id)| { let negative_token_amount: BigInt = token_amount * BigInt::from(-1); - tycho::BalanceDelta { + BalanceDelta { ord: log.log.ordinal, tx: Some(tx_from_log(&log)), token: token_id.into(), @@ -229,46 +231,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_balance(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` @@ -280,7 +269,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![], @@ -303,7 +292,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"), @@ -318,7 +307,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![], @@ -331,12 +320,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 @@ -351,9 +348,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 { diff --git a/substreams/ethereum-curve/src/pb/mod.rs b/substreams/ethereum-curve/src/pb/mod.rs deleted file mode 100644 index 43d8838..0000000 --- a/substreams/ethereum-curve/src/pb/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -// @generated -pub mod tycho { - pub mod evm { - // @@protoc_insertion_point(attribute:tycho.evm.v1) - pub mod v1 { - include!("tycho.evm.v1.rs"); - // @@protoc_insertion_point(tycho.evm.v1) - } - } -} diff --git a/substreams/ethereum-curve/src/pb/tycho.evm.v1.rs b/substreams/ethereum-curve/src/pb/tycho.evm.v1.rs deleted file mode 100644 index affc411..0000000 --- a/substreams/ethereum-curve/src/pb/tycho.evm.v1.rs +++ /dev/null @@ -1,236 +0,0 @@ -// @generated -// This file contains the proto definitions for Substreams common to all integrations. - -/// A struct describing a block. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Block { - /// The blocks hash. - #[prost(bytes="vec", tag="1")] - pub hash: ::prost::alloc::vec::Vec, - /// The parent blocks hash. - #[prost(bytes="vec", tag="2")] - pub parent_hash: ::prost::alloc::vec::Vec, - /// The block number. - #[prost(uint64, tag="3")] - pub number: u64, - /// The block timestamp. - #[prost(uint64, tag="4")] - pub ts: u64, -} -/// A struct describing a transaction. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Transaction { - /// The transaction hash. - #[prost(bytes="vec", tag="1")] - pub hash: ::prost::alloc::vec::Vec, - /// The sender of the transaction. - #[prost(bytes="vec", tag="2")] - pub from: ::prost::alloc::vec::Vec, - /// The receiver of the transaction. - #[prost(bytes="vec", tag="3")] - pub to: ::prost::alloc::vec::Vec, - /// The transactions index within the block. - /// TODO: should this be uint32? to match the type from the native substream type? - #[prost(uint64, tag="4")] - pub index: u64, -} -/// 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. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Attribute { - /// The name of the attribute. - #[prost(string, tag="1")] - pub name: ::prost::alloc::string::String, - /// The value of the attribute. - #[prost(bytes="vec", tag="2")] - pub value: ::prost::alloc::vec::Vec, - /// The type of change the attribute underwent. - #[prost(enumeration="ChangeType", tag="3")] - pub change: i32, -} -/// 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". -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ProtocolComponent { - /// A unique identifier for the component within the protocol. - /// Can be e.g. a stringified address or a string describing the trading pair. - #[prost(string, tag="1")] - pub id: ::prost::alloc::string::String, - /// Addresses of the ERC20 tokens used by the component. - #[prost(bytes="vec", repeated, tag="2")] - pub tokens: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, - /// Addresses of the contracts used by the component. - /// Usually it is a single contract, but some protocols use multiple contracts. - #[prost(bytes="vec", repeated, tag="3")] - pub contracts: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, - /// Attributes of the component. Used mainly be the native integration. - /// The inner ChangeType of the attribute has to match the ChangeType of the ProtocolComponent. - #[prost(message, repeated, tag="4")] - pub static_att: ::prost::alloc::vec::Vec, - /// Type of change the component underwent. - #[prost(enumeration="ChangeType", tag="5")] - pub change: i32, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct TransactionProtocolComponents { - #[prost(message, optional, tag="1")] - pub tx: ::core::option::Option, - #[prost(message, repeated, tag="2")] - pub components: ::prost::alloc::vec::Vec, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GroupedTransactionProtocolComponents { - #[prost(message, repeated, tag="1")] - pub tx_components: ::prost::alloc::vec::Vec, -} -/// 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. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BalanceChange { - /// The address of the ERC20 token whose balance changed. - #[prost(bytes="vec", tag="1")] - pub token: ::prost::alloc::vec::Vec, - /// The new balance of the token. - #[prost(bytes="vec", tag="2")] - pub balance: ::prost::alloc::vec::Vec, - /// 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. - #[prost(bytes="vec", tag="3")] - pub component_id: ::prost::alloc::vec::Vec, -} -/// 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. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BalanceDelta { - #[prost(uint64, tag="1")] - pub ord: u64, - /// The tx hash of the transaction that caused the balance change. - #[prost(message, optional, tag="2")] - pub tx: ::core::option::Option, - /// The address of the ERC20 token whose balance changed. - #[prost(bytes="vec", tag="3")] - pub token: ::prost::alloc::vec::Vec, - /// The delta balance of the token. - #[prost(bytes="vec", tag="4")] - pub delta: ::prost::alloc::vec::Vec, - /// 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. - #[prost(bytes="vec", tag="5")] - pub component_id: ::prost::alloc::vec::Vec, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BalanceDeltas { - #[prost(message, repeated, tag="1")] - pub balance_deltas: ::prost::alloc::vec::Vec, -} -/// Enum to specify the type of a change. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum ChangeType { - Unspecified = 0, - Update = 1, - Creation = 2, - Deletion = 3, -} -impl ChangeType { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - ChangeType::Unspecified => "CHANGE_TYPE_UNSPECIFIED", - ChangeType::Update => "CHANGE_TYPE_UPDATE", - ChangeType::Creation => "CHANGE_TYPE_CREATION", - ChangeType::Deletion => "CHANGE_TYPE_DELETION", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "CHANGE_TYPE_UNSPECIFIED" => Some(Self::Unspecified), - "CHANGE_TYPE_UPDATE" => Some(Self::Update), - "CHANGE_TYPE_CREATION" => Some(Self::Creation), - "CHANGE_TYPE_DELETION" => Some(Self::Deletion), - _ => None, - } - } -} -// This file contains proto definitions specific to the VM integration. - -/// A key value entry into contract storage. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ContractSlot { - /// A contract's storage slot. - #[prost(bytes="vec", tag="2")] - pub slot: ::prost::alloc::vec::Vec, - /// The new value for this storage slot. - #[prost(bytes="vec", tag="3")] - pub value: ::prost::alloc::vec::Vec, -} -/// Changes made to a single contract's state. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ContractChange { - /// The contract's address - #[prost(bytes="vec", tag="1")] - pub address: ::prost::alloc::vec::Vec, - /// The new native balance of the contract, empty bytes indicates no change. - #[prost(bytes="vec", tag="2")] - pub balance: ::prost::alloc::vec::Vec, - /// The new code of the contract, empty bytes indicates no change. - #[prost(bytes="vec", tag="3")] - pub code: ::prost::alloc::vec::Vec, - /// The changes to this contract's slots, empty sequence indicates no change. - #[prost(message, repeated, tag="4")] - pub slots: ::prost::alloc::vec::Vec, - /// Whether this is an update, a creation or a deletion. - #[prost(enumeration="ChangeType", tag="5")] - pub change: i32, -} -/// A set of changes aggregated by transaction. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct TransactionContractChanges { - /// The transaction instance that results in the changes. - #[prost(message, optional, tag="1")] - pub tx: ::core::option::Option, - /// 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. - #[prost(message, repeated, tag="2")] - pub contract_changes: ::prost::alloc::vec::Vec, - /// An array of any component changes. - #[prost(message, repeated, tag="3")] - pub component_changes: ::prost::alloc::vec::Vec, - /// An array of balance changes to components. - #[prost(message, repeated, tag="4")] - pub balance_changes: ::prost::alloc::vec::Vec, -} -/// A set of transaction changes within a single block. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlockContractChanges { - /// The block for which these changes are collectively computed. - #[prost(message, optional, tag="1")] - pub block: ::core::option::Option, - /// The set of transaction changes observed in the specified block. - #[prost(message, repeated, tag="2")] - pub changes: ::prost::alloc::vec::Vec, -} -// @@protoc_insertion_point(module) diff --git a/substreams/ethereum-curve/src/pool_factories.rs b/substreams/ethereum-curve/src/pool_factories.rs index 45f1ea1..32088f1 100644 --- a/substreams/ethereum-curve/src/pool_factories.rs +++ b/substreams/ethereum-curve/src/pool_factories.rs @@ -1,10 +1,11 @@ -use substreams_ethereum::pb::eth::v2::{Call, Log}; -use substreams_ethereum::{Event, Function}; +use substreams_ethereum::{ + pb::eth::v2::{Call, Log, TransactionTrace}, + Event, Function, +}; use crate::abi; -use crate::pb; -use pb::tycho::evm::v1::{self as tycho}; use substreams::hex; +use tycho_substreams::prelude::*; use substreams::scalar::BigInt; @@ -42,7 +43,8 @@ pub fn address_map( call_address: &[u8; 20], log: &Log, call: &Call, -) -> Option { + tx: &TransactionTrace, +) -> Option { match *call_address { CRYPTO_SWAP_REGISTRY => { let pool_added = abi::crypto_swap_registry::events::PoolAdded::match_and_decode(log)?; @@ -77,9 +79,8 @@ pub fn address_map( })?; // We need to perform an eth_call in order to actually get the pool's tokens - let coins_function = abi::crypto_swap_registry::functions::GetCoins { - pool: add_pool.pool, - }; + let coins_function = + abi::crypto_swap_registry::functions::GetCoins { pool: add_pool.pool }; let coins = coins_function.call(CRYPTO_SWAP_REGISTRY.to_vec())?; let trimmed_coins: Vec<_> = coins @@ -87,29 +88,35 @@ pub fn address_map( .unwrap_or(&[]) .to_vec(); - Some(tycho::ProtocolComponent { + Some(ProtocolComponent { id: hex::encode(&pool_added.pool), + tx: Some(Transaction { + to: tx.to.clone(), + from: tx.from.clone(), + hash: tx.hash.clone(), + index: tx.index.into(), + }), tokens: trimmed_coins, contracts: vec![call_address.into(), pool_added.pool], static_att: vec![ - tycho::Attribute { + Attribute { name: "pool_type".into(), value: "CryptoSwap".into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "name".into(), value: add_pool.name.into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "lp_token".into(), value: add_pool.lp_token.into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, ], - - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), + ..Default::default() }) } MAIN_REGISTRY => { @@ -160,9 +167,8 @@ pub fn address_map( .or_else(|| abi::main_registry::functions::AddPool::match_and_decode(call))?; // We need to perform an eth_call in order to actually get the pool's tokens - let coins_function = abi::crypto_swap_registry::functions::GetCoins { - pool: add_pool.pool, - }; + let coins_function = + abi::crypto_swap_registry::functions::GetCoins { pool: add_pool.pool }; let coins = coins_function.call(CRYPTO_SWAP_REGISTRY.to_vec())?; let trimmed_coins: Vec<_> = coins @@ -170,28 +176,35 @@ pub fn address_map( .unwrap_or(&[]) .to_vec(); - Some(tycho::ProtocolComponent { + Some(ProtocolComponent { id: hex::encode(&pool_created.pool), + tx: Some(Transaction { + to: tx.to.clone(), + from: tx.from.clone(), + hash: tx.hash.clone(), + index: tx.index.into(), + }), tokens: trimmed_coins, contracts: vec![call_address.into(), pool_created.pool], static_att: vec![ - tycho::Attribute { + Attribute { name: "pool_type".into(), value: "MainRegistry".into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "name".into(), value: add_pool.name.into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "lp_token".into(), value: add_pool.lp_token.into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, ], - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), + ..Default::default() }) } CRYPTO_POOL_FACTORY => { @@ -200,68 +213,87 @@ pub fn address_map( let deploy_call = abi::crypto_pool_factory::functions::DeployPool::match_and_decode(call)?; - Some(tycho::ProtocolComponent { + Some(ProtocolComponent { id: hex::encode(&call.return_data), + tx: Some(Transaction { + to: tx.to.clone(), + from: tx.from.clone(), + hash: tx.hash.clone(), + index: tx.index.into(), + }), tokens: pool_added.coins.into(), contracts: vec![call_address.into(), call.return_data.clone()], static_att: vec![ - tycho::Attribute { + Attribute { name: "pool_type".into(), value: "CryptoPool".into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "name".into(), value: pool_added.a.to_string().into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "lp_token".into(), value: pool_added.gamma.to_string().into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "mid_fee".into(), value: deploy_call.mid_fee.to_string().into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "out_fee".into(), value: deploy_call.out_fee.to_string().into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "allowed_extra_profit".into(), - value: deploy_call.allowed_extra_profit.to_string().into(), - change: tycho::ChangeType::Creation.into(), + value: deploy_call + .allowed_extra_profit + .to_string() + .into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "fee_gamma".into(), value: deploy_call.fee_gamma.to_string().into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "adjustment_step".into(), - value: deploy_call.adjustment_step.to_string().into(), - change: tycho::ChangeType::Creation.into(), + value: deploy_call + .adjustment_step + .to_string() + .into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "admin_fee".into(), value: deploy_call.admin_fee.to_string().into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "ma_half_time".into(), - value: deploy_call.ma_half_time.to_string().into(), - change: tycho::ChangeType::Creation.into(), + value: deploy_call + .ma_half_time + .to_string() + .into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "initial_price".into(), - value: deploy_call.initial_price.to_string().into(), - change: tycho::ChangeType::Creation.into(), + value: deploy_call + .initial_price + .to_string() + .into(), + change: ChangeType::Creation.into(), }, ], - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), + ..Default::default() }) } META_POOL_FACTORY => { @@ -270,17 +302,15 @@ pub fn address_map( { let add_pool = abi::meta_pool_factory::functions::DeployPlainPool1::match_and_decode(call) - .map( - |add_pool| abi::meta_pool_factory::functions::DeployPlainPool3 { - name: add_pool.name, - symbol: add_pool.symbol, - coins: add_pool.coins, - a: add_pool.a, - fee: add_pool.fee, - asset_type: BigInt::from(0), - implementation_idx: BigInt::from(0), - }, - ) + .map(|add_pool| abi::meta_pool_factory::functions::DeployPlainPool3 { + name: add_pool.name, + symbol: add_pool.symbol, + coins: add_pool.coins, + a: add_pool.a, + fee: add_pool.fee, + asset_type: BigInt::from(0), + implementation_idx: BigInt::from(0), + }) .or_else(|| { abi::meta_pool_factory::functions::DeployPlainPool2::match_and_decode( call, @@ -302,69 +332,81 @@ pub fn address_map( call, ) })?; - Some(tycho::ProtocolComponent { + Some(ProtocolComponent { id: hex::encode(&call.return_data), + tx: Some(Transaction { + to: tx.to.clone(), + from: tx.from.clone(), + hash: tx.hash.clone(), + index: tx.index.into(), + }), tokens: pool_added.coins.into(), contracts: vec![call_address.into(), call.return_data.clone()], static_att: vec![ - tycho::Attribute { + Attribute { name: "pool_type".into(), value: "PlainPool".into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "name".into(), value: add_pool.name.into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "fee".into(), value: add_pool.fee.to_string().into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, - tycho::Attribute { + Attribute { name: "a".into(), value: add_pool.a.to_string().into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }, ], - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), + ..Default::default() }) } else if let Some(pool_added) = abi::meta_pool_factory::events::MetaPoolDeployed::match_and_decode(log) { let add_pool = abi::meta_pool_factory::functions::DeployMetapool1::match_and_decode(call) - .map( - |add_pool| abi::meta_pool_factory::functions::DeployMetapool2 { - base_pool: add_pool.base_pool, - name: add_pool.name, - symbol: add_pool.symbol, - coin: add_pool.coin, - a: add_pool.a, - fee: add_pool.fee, - implementation_idx: BigInt::from(0), - }, - ) + .map(|add_pool| abi::meta_pool_factory::functions::DeployMetapool2 { + base_pool: add_pool.base_pool, + name: add_pool.name, + symbol: add_pool.symbol, + coin: add_pool.coin, + a: add_pool.a, + fee: add_pool.fee, + implementation_idx: BigInt::from(0), + }) .or_else(|| { abi::meta_pool_factory::functions::DeployMetapool2::match_and_decode( call, ) })?; - Some(tycho::ProtocolComponent { + Some(ProtocolComponent { id: hex::encode(&call.return_data), + tx: Some(Transaction { + to: tx.to.clone(), + from: tx.from.clone(), + hash: tx.hash.clone(), + index: tx.index.into(), + }), tokens: vec![pool_added.coin, add_pool.base_pool.clone()], contracts: vec![ call_address.into(), call.return_data.clone(), add_pool.base_pool.clone(), ], - static_att: vec![tycho::Attribute { + static_att: vec![Attribute { name: "pool_type".into(), value: "MetaPool".into(), - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), }], - change: tycho::ChangeType::Creation.into(), + change: ChangeType::Creation.into(), + ..Default::default() }) } else { None diff --git a/substreams/ethereum-curve/substreams.yaml b/substreams/ethereum-curve/substreams.yaml index 9daab92..8c22124 100644 --- a/substreams/ethereum-curve/substreams.yaml +++ b/substreams/ethereum-curve/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: