Add tycho pb type constructors.

This allows us to remove a lot of unnecessary boilerplate code.
This commit is contained in:
kayibal
2024-03-13 22:10:54 +00:00
parent 18e9dfcec4
commit 312d322585
4 changed files with 228 additions and 266 deletions

View File

@@ -7,6 +7,7 @@
use std::collections::HashMap;
use substreams_ethereum::pb::eth;
use substreams_ethereum::pb::eth::v2::StorageChange;
use crate::pb::tycho::evm::v1::{self as tycho};
@@ -15,6 +16,12 @@ struct SlotValue {
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
@@ -30,6 +37,22 @@ struct InterimContractChange {
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 {
@@ -90,28 +113,20 @@ pub fn extract_contract_changes<F: Fn(&[u8]) -> bool>(
storage_changes
.iter()
.filter(|changes| inclusion_predicate(&changes.address))
.for_each(|storage_change| {
.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
},
.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(|| SlotValue {
new_value: storage_change.new_value.clone(),
start_value: storage_change.old_value.clone(),
});
.or_insert_with(|| storage_change.into());
slot_value
.new_value
@@ -124,16 +139,11 @@ pub fn extract_contract_changes<F: Fn(&[u8]) -> bool>(
.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
},
.or_insert_with(|| {
InterimContractChange::new(
&balance_change.address,
created_accounts.contains_key(&balance_change.address),
)
});
if let Some(new_balance) = &balance_change.new_value {
@@ -150,16 +160,11 @@ pub fn extract_contract_changes<F: Fn(&[u8]) -> bool>(
.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
},
.or_insert_with(|| {
InterimContractChange::new(
&code_change.address,
created_accounts.contains_key(&code_change.address),
)
});
contract_change.code.clear();
@@ -174,17 +179,7 @@ pub fn extract_contract_changes<F: Fn(&[u8]) -> bool>(
{
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![],
})
.or_insert_with(|| tycho::TransactionContractChanges::new(&(block_tx.into())))
.contract_changes
.extend(
changed_contracts

View File

@@ -3,6 +3,7 @@ pub mod tycho {
pub mod evm {
// @@protoc_insertion_point(attribute:tycho.evm.v1)
pub mod v1 {
use substreams_ethereum::pb::eth::v2::{self as sf};
include!("tycho.evm.v1.rs");
// @@protoc_insertion_point(tycho.evm.v1)
@@ -16,6 +17,104 @@ pub mod tycho {
}
}
}
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 {
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()),
}
}
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()),
}
}
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
}
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
}
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
}
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
}
}
}
}
}

View File

@@ -34,24 +34,14 @@ pub fn map_pools_created(block: eth::v2::Block) -> Result<BlockTransactionProtoc
call.call.address.as_slice(),
log,
call.call,
&tycho::Transaction {
hash: tx.hash.clone(),
from: tx.from.clone(),
to: tx.to.clone(),
index: tx.index.into(),
},
&(tx.into()),
)
})
.collect::<Vec<_>>();
if !components.is_empty() {
Some(TransactionProtocolComponents {
tx: Some(tycho::Transaction {
hash: tx.hash.clone(),
from: tx.from.clone(),
to: tx.to.clone(),
index: Into::<u64>::into(tx.index),
}),
tx: Some(tx.into()),
components,
})
} else {
@@ -106,12 +96,7 @@ pub fn map_balance_deltas(
for (token, delta) in ev.tokens.iter().zip(ev.deltas.iter()) {
deltas.push(BalanceDelta {
ord: vault_log.ordinal(),
tx: Some(tycho::Transaction {
hash: vault_log.receipt.transaction.hash.clone(),
from: vault_log.receipt.transaction.from.clone(),
to: vault_log.receipt.transaction.to.clone(),
index: vault_log.receipt.transaction.index.into(),
}),
tx: Some(vault_log.receipt.transaction.into()),
token: token.to_vec(),
delta: delta.to_signed_bytes_be(),
component_id: component_id.clone(),
@@ -133,24 +118,14 @@ pub fn map_balance_deltas(
deltas.extend_from_slice(&[
BalanceDelta {
ord: vault_log.ordinal(),
tx: Some(tycho::Transaction {
hash: vault_log.receipt.transaction.hash.clone(),
from: vault_log.receipt.transaction.from.clone(),
to: vault_log.receipt.transaction.to.clone(),
index: vault_log.receipt.transaction.index.into(),
}),
tx: Some(vault_log.receipt.transaction.into()),
token: ev.token_in.to_vec(),
delta: ev.amount_in.to_signed_bytes_be(),
component_id: component_id.clone(),
},
BalanceDelta {
ord: vault_log.ordinal(),
tx: Some(tycho::Transaction {
hash: vault_log.receipt.transaction.hash.clone(),
from: vault_log.receipt.transaction.from.clone(),
to: vault_log.receipt.transaction.to.clone(),
index: vault_log.receipt.transaction.index.into(),
}),
tx: Some(vault_log.receipt.transaction.into()),
token: ev.token_out.to_vec(),
delta: ev.amount_out.neg().to_signed_bytes_be(),
component_id,
@@ -234,17 +209,7 @@ pub fn map_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 {
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(),
}),
block: Some((&block).into()),
changes: transaction_contract_changes
.drain()
.sorted_unstable_by_key(|(index, _)| *index)

View File

@@ -3,9 +3,7 @@ use substreams_ethereum::{Event, Function};
use crate::abi;
use substreams::hex;
use tycho_substreams::pb::tycho::evm::v1::{
self as tycho, FinancialType, ImplementationType, ProtocolType, Transaction,
};
use tycho_substreams::pb::tycho::evm::v1::{ImplementationType, ProtocolComponent, Transaction};
use substreams::scalar::BigInt;
@@ -39,16 +37,16 @@ impl SerializableVecBigInt for Vec<BigInt> {
/// - Stable Pool Factories
/// (Balancer does have a bit more (esp. in the deprecated section) that could be implemented as
/// desired.)
/// We use the specific ABIs to decode both the log event and cooresponding call to gather
/// We use the specific ABIs to decode both the log event and corresponding call to gather
/// `PoolCreated` event information alongside the `Create` call data that provide us details to
/// fufill both the required details + any extra `Attributes`
/// fulfill both the required details + any extra `Attributes`
/// Ref: https://docs.balancer.fi/reference/contracts/deployment-addresses/mainnet.html
pub fn address_map(
pool_factory_address: &[u8],
log: &Log,
call: &Call,
tx: &Transaction,
) -> Option<tycho::ProtocolComponent> {
) -> Option<ProtocolComponent> {
match *pool_factory_address {
hex!("897888115Ada5773E02aA29F775430BFB5F34c51") => {
let create_call =
@@ -56,31 +54,18 @@ pub fn address_map(
let pool_created =
abi::weighted_pool_factory::events::PoolCreated::match_and_decode(log)?;
Some(tycho::ProtocolComponent {
id: hex::encode(&pool_created.pool),
tokens: create_call.tokens,
contracts: vec![pool_created.pool],
static_att: vec![
tycho::Attribute {
name: "pool_type".into(),
value: "WeightedPoolFactory".into(),
change: tycho::ChangeType::Creation.into(),
},
tycho::Attribute {
name: "normalized_weights".into(),
value: create_call.normalized_weights.serialize_bytes(),
change: tycho::ChangeType::Creation.into(),
},
],
change: tycho::ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
name: "balancer_pool".to_string(),
financial_type: FinancialType::Swap.into(),
attribute_schema: vec![],
implementation_type: ImplementationType::Vm.into(),
}),
tx: Some(tx.clone()),
})
Some(
ProtocolComponent::at_contract(&pool_created.pool, &tx)
.with_tokens(&create_call.tokens)
.with_attributes(&[
("pool_type", "WeightedPoolFactory".as_bytes()),
(
"normalized_weights",
&create_call.normalized_weights.serialize_bytes(),
),
])
.as_swap_type("balancer_pool", ImplementationType::Vm),
)
}
hex!("DB8d758BCb971e482B2C45f7F8a7740283A1bd3A") => {
let create_call =
@@ -88,24 +73,12 @@ pub fn address_map(
let pool_created =
abi::composable_stable_pool_factory::events::PoolCreated::match_and_decode(log)?;
Some(tycho::ProtocolComponent {
id: hex::encode(&pool_created.pool),
tokens: create_call.tokens,
contracts: vec![pool_created.pool],
static_att: vec![tycho::Attribute {
name: "pool_type".into(),
value: "ComposableStablePoolFactory".into(),
change: tycho::ChangeType::Creation.into(),
}],
change: tycho::ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
name: "balancer_pool".to_string(),
financial_type: FinancialType::Swap.into(),
attribute_schema: vec![],
implementation_type: ImplementationType::Vm.into(),
}),
tx: Some(tx.clone()),
})
Some(
ProtocolComponent::at_contract(&pool_created.pool, &tx)
.with_tokens(&create_call.tokens)
.with_attributes(&[("pool_type", "ComposableStablePoolFactory".as_bytes())])
.as_swap_type("balancer_pool", ImplementationType::Vm),
)
}
hex!("813EE7a840CE909E7Fea2117A44a90b8063bd4fd") => {
let create_call =
@@ -113,33 +86,18 @@ pub fn address_map(
let pool_created =
abi::erc_linear_pool_factory::events::PoolCreated::match_and_decode(log)?;
Some(tycho::ProtocolComponent {
id: hex::encode(&pool_created.pool),
tokens: vec![create_call.main_token, create_call.wrapped_token],
contracts: vec![pool_created.pool],
static_att: vec![
tycho::Attribute {
name: "pool_type".into(),
value: "ERC4626LinearPoolFactory".into(),
change: tycho::ChangeType::Creation.into(),
},
tycho::Attribute {
name: "upper_target".into(),
value: create_call.upper_target.to_signed_bytes_be(),
change: tycho::ChangeType::Creation.into(),
},
// Note, `lower_target` is generally hardcoded for all pools, not located in call data
// Note, rate provider might be provided as `create.protocol_id`, but as a BigInt. needs investigation
],
change: tycho::ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
name: "balancer_pool".to_string(),
financial_type: FinancialType::Swap.into(),
attribute_schema: vec![],
implementation_type: ImplementationType::Vm.into(),
}),
tx: Some(tx.clone()),
})
Some(
ProtocolComponent::at_contract(&pool_created.pool, &tx)
.with_tokens(&[create_call.main_token, create_call.wrapped_token])
.with_attributes(&[
("pool_type", "ERC4626LinearPoolFactory".as_bytes()),
(
"upper_target",
&create_call.upper_target.to_signed_bytes_be(),
),
])
.as_swap_type("balancer_pool", ImplementationType::Vm),
)
}
hex!("5F43FBa61f63Fa6bFF101a0A0458cEA917f6B347") => {
let create_call =
@@ -147,31 +105,18 @@ pub fn address_map(
let pool_created =
abi::euler_linear_pool_factory::events::PoolCreated::match_and_decode(log)?;
Some(tycho::ProtocolComponent {
id: hex::encode(&pool_created.pool),
tokens: vec![create_call.main_token, create_call.wrapped_token],
contracts: vec![pool_created.pool],
static_att: vec![
tycho::Attribute {
name: "pool_type".into(),
value: "EulerLinearPoolFactory".into(),
change: tycho::ChangeType::Creation.into(),
},
tycho::Attribute {
name: "upper_target".into(),
value: create_call.upper_target.to_signed_bytes_be(),
change: tycho::ChangeType::Creation.into(),
},
],
change: tycho::ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
name: "balancer_pool".to_string(),
financial_type: FinancialType::Swap.into(),
attribute_schema: vec![],
implementation_type: ImplementationType::Vm.into(),
}),
tx: Some(tx.clone()),
})
Some(
ProtocolComponent::at_contract(&pool_created.pool, &tx)
.with_tokens(&[create_call.main_token, create_call.wrapped_token])
.with_attributes(&[
("pool_type", "EulerLinearPoolFactory".as_bytes()),
(
"upper_target",
&create_call.upper_target.to_signed_bytes_be(),
),
])
.as_swap_type("balancer_pool", ImplementationType::Vm),
)
}
// ❌ Reading the deployed factory for Gearbox showcases that it's currently disabled
// hex!("39A79EB449Fc05C92c39aA6f0e9BfaC03BE8dE5B") => {
@@ -226,31 +171,18 @@ pub fn address_map(
let pool_created =
abi::silo_linear_pool_factory::events::PoolCreated::match_and_decode(log)?;
Some(tycho::ProtocolComponent {
id: hex::encode(&pool_created.pool),
tokens: vec![create_call.main_token, create_call.wrapped_token],
contracts: vec![pool_created.pool],
static_att: vec![
tycho::Attribute {
name: "pool_type".into(),
value: "SiloLinearPoolFactory".into(),
change: tycho::ChangeType::Creation.into(),
},
tycho::Attribute {
name: "upper_target".into(),
value: create_call.upper_target.to_signed_bytes_be(),
change: tycho::ChangeType::Creation.into(),
},
],
change: tycho::ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
name: "balancer_pool".to_string(),
financial_type: FinancialType::Swap.into(),
attribute_schema: vec![],
implementation_type: ImplementationType::Vm.into(),
}),
tx: Some(tx.clone()),
})
Some(
ProtocolComponent::at_contract(&pool_created.pool, &tx)
.with_tokens(&[create_call.main_token, create_call.wrapped_token])
.with_attributes(&[
("pool_type", "SiloLinearPoolFactory".as_bytes()),
(
"upper_target",
&create_call.upper_target.to_signed_bytes_be(),
),
])
.as_swap_type("balancer_pool", ImplementationType::Vm),
)
}
hex!("5F5222Ffa40F2AEd6380D022184D6ea67C776eE0") => {
let create_call =
@@ -258,65 +190,36 @@ pub fn address_map(
let pool_created =
abi::yearn_linear_pool_factory::events::PoolCreated::match_and_decode(log)?;
Some(tycho::ProtocolComponent {
id: hex::encode(&pool_created.pool),
tokens: vec![create_call.main_token, create_call.wrapped_token],
contracts: vec![pool_created.pool],
static_att: vec![
tycho::Attribute {
name: "pool_type".into(),
value: "YearnLinearPoolFactory".into(),
change: tycho::ChangeType::Creation.into(),
},
tycho::Attribute {
name: "upper_target".into(),
value: create_call.upper_target.to_signed_bytes_be(),
change: tycho::ChangeType::Creation.into(),
},
],
change: tycho::ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
name: "balancer_pool".to_string(),
financial_type: FinancialType::Swap.into(),
attribute_schema: vec![],
implementation_type: ImplementationType::Vm.into(),
}),
tx: Some(tx.clone()),
})
Some(
ProtocolComponent::at_contract(&pool_created.pool, &tx)
.with_tokens(&[create_call.main_token, create_call.wrapped_token])
.with_attributes(&[
("pool_type", "YearnLinearPoolFactory".as_bytes()),
(
"upper_target",
&create_call.upper_target.to_signed_bytes_be(),
),
])
.as_swap_type("balancer_pool", ImplementationType::Vm),
)
}
// The `WeightedPool2TokenFactory` is a deprecated contract but we've included it since one
// of the highest TVL pools, 80BAL-20WETH, is able to be tracked.
// The `WeightedPool2TokenFactory` is a deprecated contract, but we've included
// it to be able to track one of the highest TVL pools: 80BAL-20WETH.
hex!("A5bf2ddF098bb0Ef6d120C98217dD6B141c74EE0") => {
let create_call =
abi::weighted_pool_tokens_factory::functions::Create::match_and_decode(call)?;
let pool_created =
abi::weighted_pool_tokens_factory::events::PoolCreated::match_and_decode(log)?;
Some(tycho::ProtocolComponent {
id: hex::encode(&pool_created.pool),
tokens: create_call.tokens,
contracts: vec![pool_created.pool],
static_att: vec![
tycho::Attribute {
name: "pool_type".into(),
value: "WeightedPool2TokensFactory".into(),
change: tycho::ChangeType::Creation.into(),
},
tycho::Attribute {
name: "weights".into(),
value: create_call.weights.serialize_bytes(),
change: tycho::ChangeType::Creation.into(),
},
],
change: tycho::ChangeType::Creation.into(),
protocol_type: Some(ProtocolType {
name: "balancer_pool".to_string(),
financial_type: FinancialType::Swap.into(),
attribute_schema: vec![],
implementation_type: ImplementationType::Vm.into(),
}),
tx: Some(tx.clone()),
})
Some(
ProtocolComponent::at_contract(&pool_created.pool, &tx)
.with_tokens(&create_call.tokens)
.with_attributes(&[
("pool_type", "WeightedPool2TokensFactory".as_bytes()),
("weights", &create_call.weights.serialize_bytes()),
])
.as_swap_type("balancer_pool", ImplementationType::Vm),
)
}
_ => None,
}