Move crates under substreams directory.
This way we can run GHA jobs depending on what files changed.
This commit is contained in:
1063
substreams/crates/tycho-substreams/Cargo.lock
generated
Normal file
1063
substreams/crates/tycho-substreams/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
11
substreams/crates/tycho-substreams/Cargo.toml
Normal file
11
substreams/crates/tycho-substreams/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "tycho-substreams"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
substreams-ethereum = "0.9.9"
|
||||
substreams = "0.5"
|
||||
prost = "0.11"
|
||||
hex = "0.4.3"
|
||||
itertools = "0.12.0"
|
||||
18
substreams/crates/tycho-substreams/Readme.md
Normal file
18
substreams/crates/tycho-substreams/Readme.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Tycho Substreams SDK
|
||||
|
||||
Some shared functionality that is used to create tycho substream packages.
|
||||
|
||||
## Protobuf Models
|
||||
|
||||
Protobuf models are manually synced from the `tycho-indexer` repository whenever they
|
||||
changed.
|
||||
|
||||
To generate the rust structs run the following command from within the `./proto`
|
||||
directory:
|
||||
|
||||
```bash
|
||||
buf generate \
|
||||
--path tycho \
|
||||
--template ../substreams/crates/tycho-substreams/buf.gen.yaml \
|
||||
--output ../substreams/crates/tycho-substreams/
|
||||
```
|
||||
12
substreams/crates/tycho-substreams/buf.gen.yaml
Normal file
12
substreams/crates/tycho-substreams/buf.gen.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
version: v1
|
||||
plugins:
|
||||
- plugin: buf.build/community/neoeinstein-prost:v0.2.2
|
||||
out: src/pb
|
||||
opt:
|
||||
- file_descriptor_set=false
|
||||
- type_attribute=.tycho.evm.v1.Transaction=#[derive(Eq\, Hash)]
|
||||
|
||||
- plugin: buf.build/community/neoeinstein-prost-crate:v0.3.1
|
||||
out: src/pb
|
||||
opt:
|
||||
- no_features
|
||||
13
substreams/crates/tycho-substreams/rustfmt.toml
Normal file
13
substreams/crates/tycho-substreams/rustfmt.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
reorder_imports = true
|
||||
imports_granularity = "Crate"
|
||||
use_small_heuristics = "Max"
|
||||
comment_width = 100
|
||||
wrap_comments = true
|
||||
binop_separator = "Back"
|
||||
trailing_comma = "Vertical"
|
||||
trailing_semicolon = false
|
||||
use_field_init_shorthand = true
|
||||
chain_width = 40
|
||||
ignore = [
|
||||
"src/pb",
|
||||
]
|
||||
343
substreams/crates/tycho-substreams/src/balances.rs
Normal file
343
substreams/crates/tycho-substreams/src/balances.rs
Normal file
@@ -0,0 +1,343 @@
|
||||
//! Utilities to handle relative balances.
|
||||
//!
|
||||
//!
|
||||
//! To aggregate relative balances changes to absolute balances the general approach is:
|
||||
//!
|
||||
//! 1. Use a map function that will extract a `BlockBalanceDeltas` message. BalanceDeltas
|
||||
//! within this message are required to have increasing ordinals so that
|
||||
//! the order of relative balance changes is unambiguous.
|
||||
//! 2. Store the balances changes with a store handler. You can use the
|
||||
//! `store_balance_changes` library method directly for this.
|
||||
//! 3. In the output module, use aggregate_balance_changes to receive an
|
||||
//! aggregated map of absolute balances.
|
||||
//!
|
||||
use crate::pb::tycho::evm::v1::{BalanceChange, BlockBalanceDeltas, Transaction};
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use substreams::key;
|
||||
use substreams::pb::substreams::StoreDeltas;
|
||||
use substreams::prelude::{BigInt, StoreAdd};
|
||||
|
||||
/// Store relative balances changes in a additive manner.
|
||||
///
|
||||
/// Effectively aggregates the relative balances changes into an absolute balances.
|
||||
///
|
||||
/// ## Arguments
|
||||
///
|
||||
/// * `deltas` - A `BlockBalanceDeltas` message containing the relative balances changes.
|
||||
/// Note: relative balance deltas must have strictly increasing ordinals per token
|
||||
/// address, will panic otherwise.
|
||||
/// * `store` - An AddStore that will add relative balance changes.
|
||||
///
|
||||
/// This method is meant to be used in combination with `aggregate_balances_changes`
|
||||
/// which consumes the store filled with this methods in
|
||||
/// [deltas mode](https://substreams.streamingfast.io/documentation/develop/manifest-modules/types#deltas-mode).
|
||||
pub fn store_balance_changes(deltas: BlockBalanceDeltas, store: impl StoreAdd<BigInt>) {
|
||||
let mut previous_ordinal = HashMap::<String, u64>::new();
|
||||
deltas
|
||||
.balance_deltas
|
||||
.iter()
|
||||
.for_each(|delta| {
|
||||
let balance_key = format!(
|
||||
"{0}:{1}",
|
||||
String::from_utf8(delta.component_id.clone())
|
||||
.expect("delta.component_id is not valid utf-8!"),
|
||||
hex::encode(&delta.token)
|
||||
);
|
||||
let current_ord = delta.ord;
|
||||
previous_ordinal
|
||||
.entry(balance_key.clone())
|
||||
.and_modify(|ord| {
|
||||
// ordinals must arrive in increasing order
|
||||
if *ord >= current_ord {
|
||||
panic!(
|
||||
"Invalid ordinal sequence for {}: {} >= {}",
|
||||
balance_key, *ord, current_ord
|
||||
);
|
||||
}
|
||||
*ord = current_ord;
|
||||
})
|
||||
.or_insert(delta.ord);
|
||||
store.add(delta.ord, balance_key, BigInt::from_signed_bytes_be(&delta.delta));
|
||||
});
|
||||
}
|
||||
|
||||
/// Aggregates absolute balances per transaction and token.
|
||||
///
|
||||
/// ## Arguments
|
||||
/// * `balance_store` - A `StoreDeltas` with all changes that occured in the source
|
||||
/// store module.
|
||||
/// * `deltas` - A `BlockBalanceDeltas` message containing the relative balances changes.
|
||||
///
|
||||
/// Reads absolute balance values from the additive store (see `store_balance_changes`
|
||||
/// on how to create such a store), proceeds to zip them with the relative balance
|
||||
/// deltas to associate balance values to token and component.
|
||||
///
|
||||
/// Will keep the last balance change per token per transaction if there are multiple
|
||||
/// changes.
|
||||
///
|
||||
/// Returns a map of transactions hashes to the full transaction and aggregated
|
||||
/// absolute balance changes.
|
||||
pub fn aggregate_balances_changes(
|
||||
balance_store: StoreDeltas,
|
||||
deltas: BlockBalanceDeltas,
|
||||
) -> HashMap<Vec<u8>, (Transaction, HashMap<Vec<u8>, BalanceChange>)> {
|
||||
balance_store
|
||||
.deltas
|
||||
.into_iter()
|
||||
.zip(deltas.balance_deltas)
|
||||
.map(|(store_delta, balance_delta)| {
|
||||
let component_id = key::segment_at(&store_delta.key, 0);
|
||||
let token_id = key::segment_at(&store_delta.key, 1);
|
||||
// store_delta.new_value is an ASCII string representing an integer
|
||||
let ascii_string =
|
||||
String::from_utf8(store_delta.new_value.clone()).expect("Invalid UTF-8 sequence");
|
||||
let balance = BigInt::from_str(&ascii_string).expect("Failed to parse integer");
|
||||
let big_endian_bytes_balance = balance.to_bytes_be().1;
|
||||
|
||||
(
|
||||
balance_delta
|
||||
.tx
|
||||
.expect("Missing transaction on delta"),
|
||||
BalanceChange {
|
||||
token: hex::decode(token_id).expect("Token ID not valid hex"),
|
||||
balance: big_endian_bytes_balance,
|
||||
component_id: component_id.as_bytes().to_vec(),
|
||||
},
|
||||
)
|
||||
})
|
||||
// We need to group the balance changes by tx hash for the `TransactionContractChanges` agg
|
||||
.group_by(|(tx, _)| tx.hash.clone())
|
||||
.into_iter()
|
||||
.map(|(txh, group)| {
|
||||
let (mut transactions, balance_changes): (Vec<_>, Vec<_>) = group.into_iter().unzip();
|
||||
|
||||
let balances = balance_changes
|
||||
.into_iter()
|
||||
.map(|balance_change| (balance_change.token.clone(), balance_change))
|
||||
.collect();
|
||||
(txh, (transactions.pop().unwrap(), balances))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mock_store::MockStore;
|
||||
use crate::pb::tycho::evm::v1::{BalanceDelta, Transaction};
|
||||
use substreams::pb::substreams::StoreDelta;
|
||||
use substreams::prelude::{StoreGet, StoreNew};
|
||||
|
||||
fn block_balance_deltas() -> BlockBalanceDeltas {
|
||||
let comp_id = "0x42c0ffee"
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
let token_0 = hex::decode("bad999").unwrap();
|
||||
let token_1 = hex::decode("babe00").unwrap();
|
||||
BlockBalanceDeltas {
|
||||
balance_deltas: vec![
|
||||
BalanceDelta {
|
||||
ord: 0,
|
||||
tx: Some(Transaction {
|
||||
hash: vec![0, 1],
|
||||
from: vec![9, 9],
|
||||
to: vec![8, 8],
|
||||
index: 0,
|
||||
}),
|
||||
token: token_0.clone(),
|
||||
delta: BigInt::from_str("+1000")
|
||||
.unwrap()
|
||||
.to_signed_bytes_be(),
|
||||
component_id: comp_id.clone(),
|
||||
},
|
||||
BalanceDelta {
|
||||
ord: 2,
|
||||
tx: Some(Transaction {
|
||||
hash: vec![0, 1],
|
||||
from: vec![9, 9],
|
||||
to: vec![8, 8],
|
||||
index: 0,
|
||||
}),
|
||||
token: token_1.clone(),
|
||||
delta: BigInt::from_str("+100")
|
||||
.unwrap()
|
||||
.to_signed_bytes_be(),
|
||||
component_id: comp_id.clone(),
|
||||
},
|
||||
BalanceDelta {
|
||||
ord: 3,
|
||||
tx: Some(Transaction {
|
||||
hash: vec![0, 1],
|
||||
from: vec![9, 9],
|
||||
to: vec![8, 8],
|
||||
index: 0,
|
||||
}),
|
||||
token: token_1.clone(),
|
||||
delta: BigInt::from_str("50")
|
||||
.unwrap()
|
||||
.to_signed_bytes_be(),
|
||||
component_id: comp_id.clone(),
|
||||
},
|
||||
BalanceDelta {
|
||||
ord: 10,
|
||||
tx: Some(Transaction {
|
||||
hash: vec![0, 1],
|
||||
from: vec![9, 9],
|
||||
to: vec![8, 8],
|
||||
index: 0,
|
||||
}),
|
||||
token: token_0.clone(),
|
||||
delta: BigInt::from_str("-1")
|
||||
.unwrap()
|
||||
.to_signed_bytes_be(),
|
||||
component_id: comp_id.clone(),
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
fn store_deltas() -> StoreDeltas {
|
||||
let comp_id = "0x42c0ffee"
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
let token_0 = hex::decode("bad999").unwrap();
|
||||
let token_1 = hex::decode("babe00").unwrap();
|
||||
|
||||
let t0_key =
|
||||
format!("{}:{}", String::from_utf8(comp_id.clone()).unwrap(), hex::encode(&token_0));
|
||||
let t1_key =
|
||||
format!("{}:{}", String::from_utf8(comp_id.clone()).unwrap(), hex::encode(&token_1));
|
||||
StoreDeltas {
|
||||
deltas: vec![
|
||||
StoreDelta {
|
||||
operation: 0,
|
||||
ordinal: 0,
|
||||
key: t0_key.clone(),
|
||||
old_value: BigInt::from(0)
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
new_value: BigInt::from(1000)
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
},
|
||||
StoreDelta {
|
||||
operation: 0,
|
||||
ordinal: 2,
|
||||
key: t1_key.clone(),
|
||||
old_value: BigInt::from(0)
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
new_value: BigInt::from(100)
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
},
|
||||
StoreDelta {
|
||||
operation: 0,
|
||||
ordinal: 3,
|
||||
key: t1_key.clone(),
|
||||
old_value: BigInt::from(100)
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
new_value: BigInt::from(150)
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
},
|
||||
StoreDelta {
|
||||
operation: 0,
|
||||
ordinal: 10,
|
||||
key: t0_key.clone(),
|
||||
old_value: BigInt::from(1000)
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
new_value: BigInt::from(999)
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_store_balances() {
|
||||
let comp_id = "0x42c0ffee"
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
let token_0 = hex::decode("bad999").unwrap();
|
||||
let token_1 = hex::decode("babe00").unwrap();
|
||||
let deltas = block_balance_deltas();
|
||||
let store = <MockStore as StoreNew>::new();
|
||||
|
||||
store_balance_changes(deltas, store.clone());
|
||||
let res_0 = store.get_last(format!(
|
||||
"{}:{}",
|
||||
String::from_utf8(comp_id.clone()).unwrap(),
|
||||
hex::encode(&token_0)
|
||||
));
|
||||
let res_1 = store.get_last(format!(
|
||||
"{}:{}",
|
||||
String::from_utf8(comp_id.clone()).unwrap(),
|
||||
hex::encode(&token_1)
|
||||
));
|
||||
|
||||
assert_eq!(res_0, Some(BigInt::from_str("+999").unwrap()));
|
||||
assert_eq!(res_1, Some(BigInt::from_str("+150").unwrap()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_aggregate_balances_changes() {
|
||||
let store_deltas = store_deltas();
|
||||
let balance_deltas = block_balance_deltas();
|
||||
let comp_id = "0x42c0ffee"
|
||||
.to_string()
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
let token_0 = hex::decode("bad999").unwrap();
|
||||
let token_1 = hex::decode("babe00").unwrap();
|
||||
|
||||
let exp = [(
|
||||
vec![0, 1],
|
||||
(
|
||||
Transaction { hash: vec![0, 1], from: vec![9, 9], to: vec![8, 8], index: 0 },
|
||||
[
|
||||
(
|
||||
token_0.clone(),
|
||||
BalanceChange {
|
||||
token: token_0,
|
||||
balance: BigInt::from(999)
|
||||
.to_signed_bytes_be()
|
||||
.to_vec(),
|
||||
component_id: comp_id.clone(),
|
||||
},
|
||||
),
|
||||
(
|
||||
token_1.clone(),
|
||||
BalanceChange {
|
||||
token: token_1,
|
||||
balance: vec![150],
|
||||
component_id: comp_id.clone(),
|
||||
},
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect::<HashMap<_, _>>(),
|
||||
),
|
||||
)]
|
||||
.into_iter()
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let res = aggregate_balances_changes(store_deltas, balance_deltas);
|
||||
assert_eq!(res, exp);
|
||||
}
|
||||
}
|
||||
211
substreams/crates/tycho-substreams/src/contract.rs
Normal file
211
substreams/crates/tycho-substreams/src/contract.rs
Normal file
@@ -0,0 +1,211 @@
|
||||
/// Helpers to extract relevant contract storage.
|
||||
///
|
||||
/// 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).
|
||||
///
|
||||
/// ## Warning
|
||||
/// ⚠️ These helpers *only* work if the **extended block model** is available,
|
||||
/// more [here](https://streamingfastio.medium.com/new-block-model-to-accelerate-chain-integration-9f65126e5425)
|
||||
use std::collections::HashMap;
|
||||
|
||||
use substreams_ethereum::pb::eth;
|
||||
use substreams_ethereum::pb::eth::v2::block::DetailLevel;
|
||||
use substreams_ethereum::pb::eth::v2::StorageChange;
|
||||
|
||||
use crate::pb::tycho::evm::v1::{self as tycho};
|
||||
|
||||
struct SlotValue {
|
||||
new_value: Vec<u8>,
|
||||
start_value: Vec<u8>,
|
||||
}
|
||||
|
||||
impl From<&StorageChange> for SlotValue {
|
||||
fn from(change: &StorageChange) -> Self {
|
||||
Self { new_value: change.new_value.clone(), start_value: change.old_value.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
struct InterimContractChange {
|
||||
address: Vec<u8>,
|
||||
balance: Vec<u8>,
|
||||
code: Vec<u8>,
|
||||
slots: HashMap<Vec<u8>, SlotValue>,
|
||||
change: tycho::ChangeType,
|
||||
}
|
||||
|
||||
impl InterimContractChange {
|
||||
fn new(address: &[u8], creation: bool) -> Self {
|
||||
Self {
|
||||
address: address.to_vec(),
|
||||
balance: vec![],
|
||||
code: vec![],
|
||||
slots: Default::default(),
|
||||
change: if creation {
|
||||
tycho::ChangeType::Creation.into()
|
||||
} else {
|
||||
tycho::ChangeType::Update.into()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts relevant contract changes from the block.
|
||||
///
|
||||
/// Contract changes include changes in storage, code and native balance.
|
||||
///
|
||||
/// ## Arguments
|
||||
///
|
||||
/// * `block` - The block to extract changes from. Must be the extended block model.
|
||||
/// * `inclusion_predicate` - A predicate function that determines if a contract address is relevant.
|
||||
/// * `transaction_contract_changes` - A mutable map to store the contract changes in.
|
||||
///
|
||||
/// ## Panics
|
||||
/// Will panic in case the detail level of the block is not extended.
|
||||
pub fn extract_contract_changes<F: Fn(&[u8]) -> bool>(
|
||||
block: ð::v2::Block,
|
||||
inclusion_predicate: F,
|
||||
transaction_contract_changes: &mut HashMap<u64, tycho::TransactionContractChanges>,
|
||||
) {
|
||||
if block.detail_level != Into::<i32>::into(DetailLevel::DetaillevelExtended) {
|
||||
panic!("Only extended blocks are supported");
|
||||
}
|
||||
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 && inclusion_predicate(&call.address))
|
||||
.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()
|
||||
.filter(|changes| inclusion_predicate(&changes.address))
|
||||
.for_each(|&storage_change| {
|
||||
let contract_change = changed_contracts
|
||||
.entry(storage_change.address.clone())
|
||||
.or_insert_with(|| {
|
||||
InterimContractChange::new(
|
||||
&storage_change.address,
|
||||
created_accounts.contains_key(&storage_change.address),
|
||||
)
|
||||
});
|
||||
|
||||
let slot_value = contract_change
|
||||
.slots
|
||||
.entry(storage_change.key.clone())
|
||||
.or_insert_with(|| storage_change.into());
|
||||
|
||||
slot_value
|
||||
.new_value
|
||||
.copy_from_slice(&storage_change.new_value);
|
||||
});
|
||||
|
||||
balance_changes
|
||||
.iter()
|
||||
.filter(|changes| inclusion_predicate(&changes.address))
|
||||
.for_each(|balance_change| {
|
||||
let contract_change = changed_contracts
|
||||
.entry(balance_change.address.clone())
|
||||
.or_insert_with(|| {
|
||||
InterimContractChange::new(
|
||||
&balance_change.address,
|
||||
created_accounts.contains_key(&balance_change.address),
|
||||
)
|
||||
});
|
||||
|
||||
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()
|
||||
.filter(|changes| inclusion_predicate(&changes.address))
|
||||
.for_each(|code_change| {
|
||||
let contract_change = changed_contracts
|
||||
.entry(code_change.address.clone())
|
||||
.or_insert_with(|| {
|
||||
InterimContractChange::new(
|
||||
&code_change.address,
|
||||
created_accounts.contains_key(&code_change.address),
|
||||
)
|
||||
});
|
||||
|
||||
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::new(&(block_tx.into())))
|
||||
.contract_changes
|
||||
.extend(
|
||||
changed_contracts
|
||||
.drain()
|
||||
.map(|(_, change)| change.into()),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
9
substreams/crates/tycho-substreams/src/lib.rs
Normal file
9
substreams/crates/tycho-substreams/src/lib.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
pub mod balances;
|
||||
pub mod contract;
|
||||
mod mock_store;
|
||||
pub mod models;
|
||||
mod pb;
|
||||
|
||||
pub mod prelude {
|
||||
pub use super::models::*;
|
||||
}
|
||||
93
substreams/crates/tycho-substreams/src/mock_store.rs
Normal file
93
substreams/crates/tycho-substreams/src/mock_store.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
//! Contains a mock store for internal testing.
|
||||
//!
|
||||
//! Might make this public alter to users can test their store handlers.
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use substreams::prelude::{BigInt, StoreDelete, StoreGet, StoreNew};
|
||||
use substreams::store::StoreAdd;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MockStore {
|
||||
data: Rc<RefCell<HashMap<String, Vec<(u64, BigInt)>>>>,
|
||||
}
|
||||
|
||||
impl StoreDelete for MockStore {
|
||||
fn delete_prefix(&self, _ord: i64, prefix: &String) {
|
||||
self.data
|
||||
.borrow_mut()
|
||||
.retain(|k, _| !k.starts_with(prefix));
|
||||
}
|
||||
}
|
||||
|
||||
impl StoreNew for MockStore {
|
||||
fn new() -> Self {
|
||||
Self { data: Rc::new(RefCell::new(HashMap::new())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl StoreAdd<BigInt> for MockStore {
|
||||
fn add<K: AsRef<str>>(&self, ord: u64, key: K, value: BigInt) {
|
||||
let mut guard = self.data.borrow_mut();
|
||||
guard
|
||||
.entry(key.as_ref().to_string())
|
||||
.and_modify(|v| {
|
||||
let prev_value = v.last().unwrap().1.clone();
|
||||
v.push((ord, prev_value + value.clone()));
|
||||
})
|
||||
.or_insert(vec![(ord, value)]);
|
||||
}
|
||||
|
||||
fn add_many<K: AsRef<str>>(&self, _ord: u64, _keys: &Vec<K>, _value: BigInt) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl StoreGet<BigInt> for MockStore {
|
||||
fn new(_idx: u32) -> Self {
|
||||
Self { data: Rc::new(RefCell::new(HashMap::new())) }
|
||||
}
|
||||
|
||||
fn get_at<K: AsRef<str>>(&self, ord: u64, key: K) -> Option<BigInt> {
|
||||
self.data
|
||||
.borrow()
|
||||
.get(&key.as_ref().to_string())
|
||||
.map(|v| {
|
||||
v.iter()
|
||||
.find(|(current_ord, _)| *current_ord == ord)
|
||||
.unwrap()
|
||||
.1
|
||||
.clone()
|
||||
})
|
||||
}
|
||||
|
||||
fn get_last<K: AsRef<str>>(&self, key: K) -> Option<BigInt> {
|
||||
self.data
|
||||
.borrow()
|
||||
.get(&key.as_ref().to_string())
|
||||
.map(|v| v.last().unwrap().1.clone())
|
||||
}
|
||||
|
||||
fn get_first<K: AsRef<str>>(&self, key: K) -> Option<BigInt> {
|
||||
self.data
|
||||
.borrow()
|
||||
.get(&key.as_ref().to_string())
|
||||
.map(|v| v.first().unwrap().1.clone())
|
||||
}
|
||||
|
||||
fn has_at<K: AsRef<str>>(&self, ord: u64, key: K) -> bool {
|
||||
self.data
|
||||
.borrow()
|
||||
.get(&key.as_ref().to_string())
|
||||
.map(|v| v.iter().any(|(v, _)| *v == ord))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn has_last<K: AsRef<str>>(&self, _key: K) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn has_first<K: AsRef<str>>(&self, _key: K) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
123
substreams/crates/tycho-substreams/src/models.rs
Normal file
123
substreams/crates/tycho-substreams/src/models.rs
Normal file
@@ -0,0 +1,123 @@
|
||||
use substreams_ethereum::pb::eth::v2::{self as sf};
|
||||
|
||||
// re-export the protobuf types here.
|
||||
pub use crate::pb::tycho::evm::v1::*;
|
||||
|
||||
impl TransactionContractChanges {
|
||||
/// Creates a new empty `TransactionContractChanges` instance.
|
||||
pub fn new(tx: &Transaction) -> Self {
|
||||
Self {
|
||||
tx: Some(tx.clone()),
|
||||
contract_changes: vec![],
|
||||
component_changes: vec![],
|
||||
balance_changes: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&sf::TransactionTrace> for Transaction {
|
||||
fn from(tx: &sf::TransactionTrace) -> Self {
|
||||
Self {
|
||||
hash: tx.hash.clone(),
|
||||
from: tx.from.clone(),
|
||||
to: tx.to.clone(),
|
||||
index: tx.index.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&sf::Block> for Block {
|
||||
fn from(block: &sf::Block) -> Self {
|
||||
Self {
|
||||
number: block.number,
|
||||
hash: block.hash.clone(),
|
||||
parent_hash: block
|
||||
.header
|
||||
.as_ref()
|
||||
.expect("Block header not present")
|
||||
.parent_hash
|
||||
.clone(),
|
||||
ts: block.timestamp_seconds(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProtocolComponent {
|
||||
/// Creates a new empty `ProtocolComponent` instance.
|
||||
///
|
||||
/// You can use the `with_*` methods to set the fields in a convience way.
|
||||
pub fn new(id: &str, tx: &Transaction) -> Self {
|
||||
Self {
|
||||
id: id.to_string(),
|
||||
tokens: vec![],
|
||||
contracts: vec![],
|
||||
static_att: vec![],
|
||||
change: ChangeType::Creation.into(),
|
||||
protocol_type: None,
|
||||
tx: Some(tx.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand to create a component with a 1-1 relationship to a contract.
|
||||
///
|
||||
/// Will set the component id to a hex encoded address with a 0x prefix
|
||||
/// and add the contract to contracts attributes.
|
||||
pub fn at_contract(id: &[u8], tx: &Transaction) -> Self {
|
||||
Self {
|
||||
id: format!("0x{}", hex::encode(id)),
|
||||
tokens: vec![],
|
||||
contracts: vec![id.to_vec()],
|
||||
static_att: vec![],
|
||||
change: ChangeType::Creation.into(),
|
||||
protocol_type: None,
|
||||
tx: Some(tx.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces the tokens on this component.
|
||||
pub fn with_tokens<B: AsRef<[u8]>>(mut self, tokens: &[B]) -> Self {
|
||||
self.tokens = tokens
|
||||
.iter()
|
||||
.map(|e| e.as_ref().to_vec())
|
||||
.collect::<Vec<Vec<u8>>>();
|
||||
self
|
||||
}
|
||||
|
||||
/// Replaces the contracts associated with this component.
|
||||
pub fn with_contracts<B: AsRef<[u8]>>(mut self, contracts: &[B]) -> Self {
|
||||
self.contracts = contracts
|
||||
.iter()
|
||||
.map(|e| e.as_ref().to_vec())
|
||||
.collect::<Vec<Vec<u8>>>();
|
||||
self
|
||||
}
|
||||
|
||||
/// Replaces the static attributes on this component.
|
||||
///
|
||||
/// The change type will be set to Creation.
|
||||
pub fn with_attributes<K: AsRef<str>, V: AsRef<[u8]>>(mut self, attributes: &[(K, V)]) -> Self {
|
||||
self.static_att = attributes
|
||||
.iter()
|
||||
.map(|(k, v)| Attribute {
|
||||
name: k.as_ref().to_string(),
|
||||
value: v.as_ref().to_vec(),
|
||||
change: ChangeType::Creation.into(),
|
||||
})
|
||||
.collect::<Vec<Attribute>>();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the protocol_type on this component.
|
||||
///
|
||||
/// Will set the `financial_type` to FinancialType::Swap and the
|
||||
/// `attribute_schema` to an empty list.
|
||||
pub fn as_swap_type(mut self, name: &str, implementation_type: ImplementationType) -> Self {
|
||||
self.protocol_type = Some(ProtocolType {
|
||||
name: name.to_string(),
|
||||
financial_type: FinancialType::Swap.into(),
|
||||
attribute_schema: vec![],
|
||||
implementation_type: implementation_type.into(),
|
||||
});
|
||||
self
|
||||
}
|
||||
}
|
||||
10
substreams/crates/tycho-substreams/src/pb/mod.rs
Normal file
10
substreams/crates/tycho-substreams/src/pb/mod.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
// @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)
|
||||
}
|
||||
}
|
||||
}
|
||||
358
substreams/crates/tycho-substreams/src/pb/tycho.evm.v1.rs
Normal file
358
substreams/crates/tycho-substreams/src/pb/tycho.evm.v1.rs
Normal file
@@ -0,0 +1,358 @@
|
||||
// @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<u8>,
|
||||
/// The parent blocks hash.
|
||||
#[prost(bytes="vec", tag="2")]
|
||||
pub parent_hash: ::prost::alloc::vec::Vec<u8>,
|
||||
/// 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.
|
||||
#[derive(Eq, Hash)]
|
||||
#[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<u8>,
|
||||
/// The sender of the transaction.
|
||||
#[prost(bytes="vec", tag="2")]
|
||||
pub from: ::prost::alloc::vec::Vec<u8>,
|
||||
/// The receiver of the transaction.
|
||||
#[prost(bytes="vec", tag="3")]
|
||||
pub to: ::prost::alloc::vec::Vec<u8>,
|
||||
/// 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<u8>,
|
||||
/// The type of change the attribute underwent.
|
||||
#[prost(enumeration="ChangeType", tag="3")]
|
||||
pub change: i32,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtocolType {
|
||||
#[prost(string, tag="1")]
|
||||
pub name: ::prost::alloc::string::String,
|
||||
#[prost(enumeration="FinancialType", tag="2")]
|
||||
pub financial_type: i32,
|
||||
#[prost(message, repeated, tag="3")]
|
||||
pub attribute_schema: ::prost::alloc::vec::Vec<Attribute>,
|
||||
#[prost(enumeration="ImplementationType", tag="4")]
|
||||
pub implementation_type: 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<u8>>,
|
||||
/// 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<u8>>,
|
||||
/// 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<Attribute>,
|
||||
/// Type of change the component underwent.
|
||||
#[prost(enumeration="ChangeType", tag="5")]
|
||||
pub change: i32,
|
||||
/// / Represents the functionality of the component.
|
||||
#[prost(message, optional, tag="6")]
|
||||
pub protocol_type: ::core::option::Option<ProtocolType>,
|
||||
/// Transaction where this component was created
|
||||
#[prost(message, optional, tag="7")]
|
||||
pub tx: ::core::option::Option<Transaction>,
|
||||
}
|
||||
/// 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<u8>,
|
||||
/// The new balance of the token. Note: it must be a big endian encoded int.
|
||||
#[prost(bytes="vec", tag="2")]
|
||||
pub balance: ::prost::alloc::vec::Vec<u8>,
|
||||
/// The id of the component whose TVL is tracked. Note: This MUST be utf8 encoded.
|
||||
/// 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<u8>,
|
||||
}
|
||||
/// 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<Self> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||
#[repr(i32)]
|
||||
pub enum FinancialType {
|
||||
Swap = 0,
|
||||
Lend = 1,
|
||||
Leverage = 2,
|
||||
Psm = 3,
|
||||
}
|
||||
impl FinancialType {
|
||||
/// 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 {
|
||||
FinancialType::Swap => "SWAP",
|
||||
FinancialType::Lend => "LEND",
|
||||
FinancialType::Leverage => "LEVERAGE",
|
||||
FinancialType::Psm => "PSM",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"SWAP" => Some(Self::Swap),
|
||||
"LEND" => Some(Self::Lend),
|
||||
"LEVERAGE" => Some(Self::Leverage),
|
||||
"PSM" => Some(Self::Psm),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||
#[repr(i32)]
|
||||
pub enum ImplementationType {
|
||||
Vm = 0,
|
||||
Custom = 1,
|
||||
}
|
||||
impl ImplementationType {
|
||||
/// 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 {
|
||||
ImplementationType::Vm => "VM",
|
||||
ImplementationType::Custom => "CUSTOM",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"VM" => Some(Self::Vm),
|
||||
"CUSTOM" => Some(Self::Custom),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct EntityChanges {
|
||||
/// A unique identifier of the entity within the protocol.
|
||||
#[prost(string, tag="1")]
|
||||
pub component_id: ::prost::alloc::string::String,
|
||||
/// The set of attributes that are associated with the entity.
|
||||
#[prost(message, repeated, tag="2")]
|
||||
pub attributes: ::prost::alloc::vec::Vec<Attribute>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct TransactionEntityChanges {
|
||||
#[prost(message, optional, tag="1")]
|
||||
pub tx: ::core::option::Option<Transaction>,
|
||||
#[prost(message, repeated, tag="2")]
|
||||
pub entity_changes: ::prost::alloc::vec::Vec<EntityChanges>,
|
||||
/// An array of newly added components.
|
||||
#[prost(message, repeated, tag="3")]
|
||||
pub component_changes: ::prost::alloc::vec::Vec<ProtocolComponent>,
|
||||
/// An array of balance changes to components.
|
||||
#[prost(message, repeated, tag="4")]
|
||||
pub balance_changes: ::prost::alloc::vec::Vec<BalanceChange>,
|
||||
}
|
||||
/// A set of transaction changes within a single block.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct BlockEntityChanges {
|
||||
/// The block for which these changes are collectively computed.
|
||||
#[prost(message, optional, tag="1")]
|
||||
pub block: ::core::option::Option<Block>,
|
||||
/// The set of transaction changes observed in the specified block.
|
||||
#[prost(message, repeated, tag="2")]
|
||||
pub changes: ::prost::alloc::vec::Vec<TransactionEntityChanges>,
|
||||
}
|
||||
/// A message containing relative balance changes.
|
||||
///
|
||||
/// Used to track token balances of protocol components in case they are only
|
||||
/// available as relative values within a block.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct BalanceDelta {
|
||||
/// The ordinal of the balance change. Must be unique & deterministic over all balances
|
||||
/// changes within a block.
|
||||
#[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<Transaction>,
|
||||
/// The address of the ERC20 token whose balance changed.
|
||||
#[prost(bytes="vec", tag="3")]
|
||||
pub token: ::prost::alloc::vec::Vec<u8>,
|
||||
/// The delta balance of the token.
|
||||
#[prost(bytes="vec", tag="4")]
|
||||
pub delta: ::prost::alloc::vec::Vec<u8>,
|
||||
/// 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<u8>,
|
||||
}
|
||||
/// A set of balances deltas, usually a group of changes within a single block.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct BlockBalanceDeltas {
|
||||
#[prost(message, repeated, tag="1")]
|
||||
pub balance_deltas: ::prost::alloc::vec::Vec<BalanceDelta>,
|
||||
}
|
||||
/// A message containing protocol components that were created by a single tx.
|
||||
#[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<Transaction>,
|
||||
#[prost(message, repeated, tag="2")]
|
||||
pub components: ::prost::alloc::vec::Vec<ProtocolComponent>,
|
||||
}
|
||||
/// All protocol components that were created within a block with their corresponding tx.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct BlockTransactionProtocolComponents {
|
||||
#[prost(message, repeated, tag="1")]
|
||||
pub tx_components: ::prost::alloc::vec::Vec<TransactionProtocolComponents>,
|
||||
}
|
||||
// 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<u8>,
|
||||
/// The new value for this storage slot.
|
||||
#[prost(bytes="vec", tag="3")]
|
||||
pub value: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
/// 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<u8>,
|
||||
/// The new native balance of the contract, empty bytes indicates no change.
|
||||
#[prost(bytes="vec", tag="2")]
|
||||
pub balance: ::prost::alloc::vec::Vec<u8>,
|
||||
/// The new code of the contract, empty bytes indicates no change.
|
||||
#[prost(bytes="vec", tag="3")]
|
||||
pub code: ::prost::alloc::vec::Vec<u8>,
|
||||
/// The changes to this contract's slots, empty sequence indicates no change.
|
||||
#[prost(message, repeated, tag="4")]
|
||||
pub slots: ::prost::alloc::vec::Vec<ContractSlot>,
|
||||
/// 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<Transaction>,
|
||||
/// 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<ContractChange>,
|
||||
/// An array of any component changes.
|
||||
#[prost(message, repeated, tag="3")]
|
||||
pub component_changes: ::prost::alloc::vec::Vec<ProtocolComponent>,
|
||||
/// An array of balance changes to components.
|
||||
#[prost(message, repeated, tag="4")]
|
||||
pub balance_changes: ::prost::alloc::vec::Vec<BalanceChange>,
|
||||
}
|
||||
/// 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<Block>,
|
||||
/// The set of transaction changes observed in the specified block.
|
||||
#[prost(message, repeated, tag="2")]
|
||||
pub changes: ::prost::alloc::vec::Vec<TransactionContractChanges>,
|
||||
}
|
||||
// @@protoc_insertion_point(module)
|
||||
@@ -19,7 +19,7 @@ anyhow = "1.0.75"
|
||||
prost-types = "0.12.3"
|
||||
num-bigint = "0.4.4"
|
||||
itertools = "0.12.0"
|
||||
tycho-substreams = {path ="../../crates/tycho-substreams"}
|
||||
tycho-substreams = {path ="../crates/tycho-substreams"}
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1"
|
||||
|
||||
Reference in New Issue
Block a user