fix: maverick
This commit is contained in:
19
substreams/Cargo.lock
generated
19
substreams/Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<u8>,
|
||||
start_value: Vec<u8>,
|
||||
}
|
||||
|
||||
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<u8>,
|
||||
balance: Vec<u8>,
|
||||
code: Vec<u8>,
|
||||
slots: HashMap<Vec<u8>, SlotValue>,
|
||||
change: tycho::ChangeType,
|
||||
}
|
||||
|
||||
impl From<InterimContractChange> 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<u64, tycho::TransactionContractChanges>,
|
||||
) {
|
||||
let mut changed_contracts: HashMap<Vec<u8>, 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()));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
mod abi;
|
||||
mod contract_changes;
|
||||
mod modules;
|
||||
mod pb;
|
||||
|
||||
@@ -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<tycho::GroupedTransactionProtocolComponents> {
|
||||
pub fn map_components(block: eth::v2::Block) -> Result<BlockTransactionProtocolComponents> {
|
||||
// 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<tycho::ProtocolComponent> = tx
|
||||
let components: Vec<ProtocolComponent> = 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::<u64>::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::<Vec<_>>();
|
||||
|
||||
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<tycho::BalanceDeltas, anyhow::Error> {
|
||||
) -> Result<BlockBalanceDeltas, anyhow::Error> {
|
||||
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<_>>();
|
||||
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::<Vec<_>>();
|
||||
|
||||
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<tycho::BlockContractChanges> {
|
||||
) -> Result<BlockContractChanges> {
|
||||
// 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<tycho::BlockContractChanges> {
|
||||
// pub fn debug_block_events(block: eth::v2::Block) -> Result<BlockContractChanges> {
|
||||
// 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
|
||||
|
||||
@@ -8,7 +8,7 @@ protobuf:
|
||||
- tycho/evm/v1/vm.proto
|
||||
- tycho/evm/v1/common.proto
|
||||
importPaths:
|
||||
- ../../proto/tycho/evm/v1/
|
||||
- ../../proto
|
||||
- ./proto
|
||||
|
||||
binaries:
|
||||
|
||||
Reference in New Issue
Block a user