feat: Balancer V2 DCI integration (#219)

* feat: Add DCI Entrypoints to BalancerV2 components

* fix: miscellaneous fixes before Balancer V2 resync

This commit fixes the entrypoints created by Balancer v2 packages, removes some disabled factories and remove support for BPT tokens (this is still to be investigated but so far we won't be able to support them)

* refactor: fix CI and bump version

* chore: update comments

---------

Co-authored-by: Thales <thales@datarevenue.com>
Co-authored-by: zizou <111426680+flopell@users.noreply.github.com>
Co-authored-by: Louise Poole <louise@datarevenue.com>
This commit is contained in:
Zizou
2025-06-16 11:35:59 +02:00
committed by GitHub
parent de5c9503bc
commit 7da01c745b
7 changed files with 324 additions and 278 deletions

View File

@@ -9,7 +9,9 @@ use substreams::{
};
use substreams_ethereum::{pb::eth, Event};
use tycho_substreams::{
balances::aggregate_balances_changes, contract::extract_contract_changes_builder, prelude::*,
attributes::json_deserialize_address_list, balances::aggregate_balances_changes,
block_storage::get_block_storage_changes, contract::extract_contract_changes_builder,
entrypoint::create_entrypoint, models::entry_point_params::TraceData, prelude::*,
};
pub const VAULT_ADDRESS: &[u8] = &hex!("BA12222222228d8Ba445958a75a0704d566BF2C8");
@@ -197,6 +199,29 @@ pub fn map_protocol_changes(
.components
.iter()
.for_each(|component| {
let rate_providers = component
.static_att
.iter()
.find(|att| att.name == "rate_providers")
.map(|att| json_deserialize_address_list(&att.value));
if let Some(rate_providers) = rate_providers {
for rate_provider in rate_providers {
let trace_data = TraceData::Rpc(RpcTraceData {
caller: None,
calldata: hex::decode("679aefce").unwrap(), // getRate()
});
let (entrypoint, entrypoint_params) = create_entrypoint(
rate_provider,
"getRate()".to_string(),
component.id.clone(),
trace_data,
);
builder.add_entrypoint(&entrypoint);
builder.add_entrypoint_params(&entrypoint_params);
}
}
builder.add_protocol_component(component);
let entity_change = EntityChanges {
component_id: component.id.clone(),
@@ -261,6 +286,8 @@ pub fn map_protocol_changes(
// Process all `transaction_changes` for final output in the `BlockChanges`,
// sorted by transaction index (the key).
let block_storage_changes = get_block_storage_changes(&block);
Ok(BlockChanges {
block: Some((&block).into()),
changes: transaction_changes
@@ -268,5 +295,6 @@ pub fn map_protocol_changes(
.sorted_unstable_by_key(|(index, _)| *index)
.filter_map(|(_, builder)| builder.build())
.collect::<Vec<_>>(),
storage_changes: block_storage_changes,
})
}

View File

@@ -22,17 +22,18 @@ fn get_pool_registered(
.clone()
}
fn get_token_registered(
tx: &TransactionTrace,
pool_id: &[u8],
) -> abi::vault::events::TokensRegistered {
tx.logs_with_calls()
.filter(|(log, _)| log.address == VAULT_ADDRESS)
.filter_map(|(log, _)| abi::vault::events::TokensRegistered::match_and_decode(log))
.find(|ev| ev.pool_id == pool_id)
.unwrap()
.clone()
}
// TODO: add this back if we need to track BPT
// fn get_token_registered(
// tx: &TransactionTrace,
// pool_id: &[u8],
// ) -> abi::vault::events::TokensRegistered {
// tx.logs_with_calls()
// .filter(|(log, _)| log.address == VAULT_ADDRESS)
// .filter_map(|(log, _)| abi::vault::events::TokensRegistered::match_and_decode(log))
// .find(|ev| ev.pool_id == pool_id)
// .unwrap()
// .clone()
// }
// This is the main function that handles the creation of `ProtocolComponent`s with `Attribute`s
// based on the specific factory address. There's 3 factory groups that are represented here:
@@ -179,12 +180,14 @@ pub fn address_map(
let pool_created =
abi::composable_stable_pool_factory::events::PoolCreated::match_and_decode(log)?;
let pool_registered = get_pool_registered(tx, &pool_created.pool);
let tokens_registered = get_token_registered(tx, &pool_registered.pool_id);
// let tokens_registered = get_token_registered(tx, &pool_registered.pool_id);
Some(
ProtocolComponent::new(&format!("0x{}", hex::encode(pool_registered.pool_id)))
.with_contracts(&[pool_created.pool.clone(), VAULT_ADDRESS.to_vec()])
.with_tokens(&tokens_registered.tokens)
// .with_tokens(&tokens_registered.tokens) // TODO: add this back if we need to
// track BPT
.with_tokens(&create_call.tokens)
.with_attributes(&[
("pool_type", "ComposableStablePoolFactory".as_bytes()),
("bpt", &pool_created.pool),
@@ -209,12 +212,17 @@ pub fn address_map(
let pool_created =
abi::erc_linear_pool_factory::events::PoolCreated::match_and_decode(log)?;
let pool_registered = get_pool_registered(tx, &pool_created.pool);
let tokens_registered = get_token_registered(tx, &pool_registered.pool_id);
// let tokens_registered = get_token_registered(tx, &pool_registered.pool_id);
Some(
ProtocolComponent::new(&format!("0x{}", hex::encode(pool_registered.pool_id)))
.with_contracts(&[pool_created.pool.clone(), VAULT_ADDRESS.to_vec()])
.with_tokens(&tokens_registered.tokens)
// .with_tokens(&tokens_registered.tokens) //TODO: does it make sense to include
// BPT token here?
.with_tokens(&[
create_call.main_token.clone(),
create_call.wrapped_token.clone(),
])
.with_attributes(&[
("pool_type", "ERC4626LinearPoolFactory".as_bytes()),
(
@@ -237,156 +245,6 @@ pub fn address_map(
.as_swap_type("balancer_v2_pool", ImplementationType::Vm),
)
}
hex!("5F43FBa61f63Fa6bFF101a0A0458cEA917f6B347") => {
let create_call =
abi::euler_linear_pool_factory::functions::Create::match_and_decode(call)?;
let pool_created =
abi::euler_linear_pool_factory::events::PoolCreated::match_and_decode(log)?;
let pool_registered = get_pool_registered(tx, &pool_created.pool);
let tokens_registered = get_token_registered(tx, &pool_registered.pool_id);
Some(
ProtocolComponent::new(&format!("0x{}", hex::encode(pool_registered.pool_id)))
.with_contracts(&[pool_created.pool.clone(), VAULT_ADDRESS.to_vec()])
.with_tokens(&tokens_registered.tokens)
.with_attributes(&[
("pool_type", "EulerLinearPoolFactory".as_bytes()),
(
"upper_target",
&create_call
.upper_target
.to_signed_bytes_be(),
),
("manual_updates", &[1u8]),
("bpt", &pool_created.pool),
("main_token", &create_call.main_token),
("wrapped_token", &create_call.wrapped_token),
(
"fee",
&create_call
.swap_fee_percentage
.to_signed_bytes_be(),
),
])
.as_swap_type("balancer_v2_pool", ImplementationType::Vm),
)
}
// ❌ Reading the deployed factory for Gearbox showcases that it's currently disabled
// hex!("39A79EB449Fc05C92c39aA6f0e9BfaC03BE8dE5B") => {
// let create_call =
// abi::gearbox_linear_pool_factory::functions::Create::match_and_decode(call)?;
// let pool_created =
// abi::gearbox_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_addr.into(), pool_created.pool],
// static_att: vec![
// tycho::Attribute {
// name: "pool_type".into(),
// value: "GearboxLinearPoolFactory".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(),
// })
// }
// ❌ The `ManagedPoolFactory` is a bit ✨ unique ✨, so we'll leave it commented out for
// now Take a look at it's `Create` call to see how the params are structured.
// hex!("BF904F9F340745B4f0c4702c7B6Ab1e808eA6b93") => {
// let create_call =
// abi::managed_pool_factory::functions::Create::match_and_decode(call)?;
// let pool_created =
// abi::managed_pool_factory::events::PoolCreated::match_and_decode(log)?;
// Some(tycho::ProtocolComponent {
// id: hex::encode(&pool_created.pool),
// tokens: create_call.tokens,
// contracts: vec![pool_addr.into(), pool_created.pool],
// static_att: vec![
// tycho::Attribute {
// name: "pool_type".into(),
// value: "ManagedPoolFactory".into(),
// change: tycho::ChangeType::Creation.into(),
// },
// ],
// change: tycho::ChangeType::Creation.into(),
// })
// }
hex!("4E11AEec21baF1660b1a46472963cB3DA7811C89") => {
let create_call =
abi::silo_linear_pool_factory::functions::Create::match_and_decode(call)?;
let pool_created =
abi::silo_linear_pool_factory::events::PoolCreated::match_and_decode(log)?;
let pool_registered = get_pool_registered(tx, &pool_created.pool);
let tokens_registered = get_token_registered(tx, &pool_registered.pool_id);
Some(
ProtocolComponent::new(&format!("0x{}", hex::encode(pool_registered.pool_id)))
.with_contracts(&[pool_created.pool.clone(), VAULT_ADDRESS.to_vec()])
.with_tokens(&tokens_registered.tokens)
.with_attributes(&[
("pool_type", "SiloLinearPoolFactory".as_bytes()),
(
"upper_target",
&create_call
.upper_target
.to_signed_bytes_be(),
),
("manual_updates", &[1u8]),
("bpt", &pool_created.pool),
("main_token", &create_call.main_token),
("wrapped_token", &create_call.wrapped_token),
(
"fee",
&create_call
.swap_fee_percentage
.to_signed_bytes_be(),
),
])
.as_swap_type("balancer_v2_pool", ImplementationType::Vm),
)
}
hex!("5F5222Ffa40F2AEd6380D022184D6ea67C776eE0") => {
let create_call =
abi::yearn_linear_pool_factory::functions::Create::match_and_decode(call)?;
let pool_created =
abi::yearn_linear_pool_factory::events::PoolCreated::match_and_decode(log)?;
let pool_registered = get_pool_registered(tx, &pool_created.pool);
let tokens_registered = get_token_registered(tx, &pool_registered.pool_id);
Some(
ProtocolComponent::new(&format!("0x{}", hex::encode(pool_registered.pool_id)))
.with_contracts(&[pool_created.pool.clone(), VAULT_ADDRESS.to_vec()])
.with_tokens(&tokens_registered.tokens)
.with_attributes(&[
("pool_type", "YearnLinearPoolFactory".as_bytes()),
(
"upper_target",
&create_call
.upper_target
.to_signed_bytes_be(),
),
("manual_updates", &[1u8]),
("bpt", &pool_created.pool),
("main_token", &create_call.main_token),
("wrapped_token", &create_call.wrapped_token),
(
"fee",
&create_call
.swap_fee_percentage
.to_signed_bytes_be(),
),
])
.as_swap_type("balancer_v2_pool", ImplementationType::Vm),
)
}
// 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") => {
@@ -414,6 +272,168 @@ pub fn address_map(
.as_swap_type("balancer_v2_pool", ImplementationType::Vm),
)
}
// ❌ EulerLinearPoolFactory: factory is disabled and no existing pools have relevant
// liquidity hex!("5F43FBa61f63Fa6bFF101a0A0458cEA917f6B347") => {
// let create_call =
// abi::euler_linear_pool_factory::functions::Create::match_and_decode(call)?;
// let pool_created =
// abi::euler_linear_pool_factory::events::PoolCreated::match_and_decode(log)?;
// let pool_registered = get_pool_registered(tx, &pool_created.pool);
// let tokens_registered = get_token_registered(tx, &pool_registered.pool_id);
// Some(
// ProtocolComponent::new(&format!("0x{}", hex::encode(pool_registered.pool_id)))
// .with_contracts(&[pool_created.pool.clone(), VAULT_ADDRESS.to_vec()])
// // .with_tokens(&tokens_registered.tokens)
// .with_tokens(&[
// create_call.main_token.clone(),
// create_call.wrapped_token.clone(),
// ])
// .with_attributes(&[
// ("pool_type", "EulerLinearPoolFactory".as_bytes()),
// (
// "upper_target",
// &create_call
// .upper_target
// .to_signed_bytes_be(),
// ),
// ("manual_updates", &[1u8]),
// ("bpt", &pool_created.pool),
// ("main_token", &create_call.main_token),
// ("wrapped_token", &create_call.wrapped_token),
// (
// "fee",
// &create_call
// .swap_fee_percentage
// .to_signed_bytes_be(),
// ),
// ])
// .as_swap_type("balancer_v2_pool", ImplementationType::Vm),
// )
// }
// ❌ Reading the deployed factory for Gearbox showcases that it's currently disabled
// hex!("39A79EB449Fc05C92c39aA6f0e9BfaC03BE8dE5B") => {
// let create_call =
// abi::gearbox_linear_pool_factory::functions::Create::match_and_decode(call)?;
// let pool_created =
// abi::gearbox_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_addr.into(), pool_created.pool],
// static_att: vec![
// tycho::Attribute {
// name: "pool_type".into(),
// value: "GearboxLinearPoolFactory".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(),
// })
// }
// ❌ The `ManagedPoolFactory` is a bit ✨ unique ✨, so we'll leave it commented out for
// now. Take a look at it's `Create` call to see how the params are structured.
// hex!("BF904F9F340745B4f0c4702c7B6Ab1e808eA6b93") => {
// let create_call =
// abi::managed_pool_factory::functions::Create::match_and_decode(call)?;
// let pool_created =
// abi::managed_pool_factory::events::PoolCreated::match_and_decode(log)?;
// Some(tycho::ProtocolComponent {
// id: hex::encode(&pool_created.pool),
// tokens: create_call.tokens,
// contracts: vec![pool_addr.into(), pool_created.pool],
// static_att: vec![
// tycho::Attribute {
// name: "pool_type".into(),
// value: "ManagedPoolFactory".into(),
// change: tycho::ChangeType::Creation.into(),
// },
// ],
// change: tycho::ChangeType::Creation.into(),
// })
// }
// ❌ SiloLinearPoolFactory: factory is disabled and no existing pools have relevant
// liquidity hex!("4E11AEec21baF1660b1a46472963cB3DA7811C89") => {
// let create_call =
// abi::silo_linear_pool_factory::functions::Create::match_and_decode(call)?;
// let pool_created =
// abi::silo_linear_pool_factory::events::PoolCreated::match_and_decode(log)?;
// let pool_registered = get_pool_registered(tx, &pool_created.pool);
// let tokens_registered = get_token_registered(tx, &pool_registered.pool_id);
// Some(
// ProtocolComponent::new(&format!("0x{}", hex::encode(pool_registered.pool_id)))
// .with_contracts(&[pool_created.pool.clone(), VAULT_ADDRESS.to_vec()])
// .with_tokens(&tokens_registered.tokens)
// .with_attributes(&[
// ("pool_type", "SiloLinearPoolFactory".as_bytes()),
// (
// "upper_target",
// &create_call
// .upper_target
// .to_signed_bytes_be(),
// ),
// ("manual_updates", &[1u8]),
// ("bpt", &pool_created.pool),
// ("main_token", &create_call.main_token),
// ("wrapped_token", &create_call.wrapped_token),
// (
// "fee",
// &create_call
// .swap_fee_percentage
// .to_signed_bytes_be(),
// ),
// ])
// .as_swap_type("balancer_v2_pool", ImplementationType::Vm),
// )
// }
// ❌ YearnLinearPoolFactory: factory is disabled and no existing pools have relevant
// liquidity hex!("5F5222Ffa40F2AEd6380D022184D6ea67C776eE0") => {
// let create_call =
// abi::yearn_linear_pool_factory::functions::Create::match_and_decode(call)?;
// let pool_created =
// abi::yearn_linear_pool_factory::events::PoolCreated::match_and_decode(log)?;
// let pool_registered = get_pool_registered(tx, &pool_created.pool);
// let tokens_registered = get_token_registered(tx, &pool_registered.pool_id);
// Some(
// ProtocolComponent::new(&format!("0x{}", hex::encode(pool_registered.pool_id)))
// .with_contracts(&[pool_created.pool.clone(), VAULT_ADDRESS.to_vec()])
// .with_tokens(&tokens_registered.tokens)
// .with_attributes(&[
// ("pool_type", "YearnLinearPoolFactory".as_bytes()),
// (
// "upper_target",
// &create_call
// .upper_target
// .to_signed_bytes_be(),
// ),
// ("manual_updates", &[1u8]),
// ("bpt", &pool_created.pool),
// ("main_token", &create_call.main_token),
// ("wrapped_token", &create_call.wrapped_token),
// (
// "fee",
// &create_call
// .swap_fee_percentage
// .to_signed_bytes_be(),
// ),
// ])
// .as_swap_type("balancer_v2_pool", ImplementationType::Vm),
// )
// }
_ => None,
}
}