Files
tycho-protocol-sdk/substreams/ethereum-ekubo/src/modules/4_map_balance_changes.rs
die-herdplatte e4609bed0b Ekubo Integration (#172)
* fix: Implement ethereum-ekubo

* fix: Remove unnecessary store

* fix: Correct balance accounting

* Adjust deltas by fee at PositionUpdated event

* Add partial Ekubo integration

* Generalize Hexable

* Native Ekubo integration

* cargo fmt & clippy

---------

Co-authored-by: kayibal <alan@datarevenue.com>
Co-authored-by: Zizou <111426680+zizou0x@users.noreply.github.com>
2025-03-27 12:29:39 +01:00

128 lines
4.5 KiB
Rust

use substreams::{
hex,
scalar::BigInt,
store::{StoreGet, StoreGetProto},
};
use substreams_helper::hex::Hexable;
use tycho_substreams::models::{BalanceDelta, BlockBalanceDeltas, Transaction};
use crate::pb::ekubo::{
block_transaction_events::transaction_events::pool_log::Event, BlockTransactionEvents,
PoolDetails,
};
#[substreams::handlers::map]
fn map_balance_changes(
block_tx_events: BlockTransactionEvents,
store: StoreGetProto<PoolDetails>,
) -> BlockBalanceDeltas {
BlockBalanceDeltas {
balance_deltas: block_tx_events
.block_transaction_events
.into_iter()
.flat_map(|tx_events| {
let tx: Transaction = tx_events.transaction.unwrap().into();
let store = &store;
tx_events
.pool_logs
.into_iter()
.flat_map(move |log| {
let component_id = log.pool_id.to_hex();
let pool_details = get_pool_details(store, &component_id);
let component_id_bytes = component_id.into_bytes();
let tx = tx.clone();
balance_deltas(log.event.unwrap(), pool_details)
.into_iter()
.map(move |reduced| BalanceDelta {
ord: log.ordinal,
tx: Some(tx.clone()),
token: reduced.token,
delta: reduced.delta,
component_id: component_id_bytes.clone(),
})
})
})
.collect(),
}
}
struct ReducedBalanceDelta {
token: Vec<u8>,
delta: Vec<u8>,
}
fn balance_deltas(ev: Event, pool_details: PoolDetails) -> Vec<ReducedBalanceDelta> {
match ev {
Event::Swapped(swapped) => {
vec![
ReducedBalanceDelta { token: pool_details.token0, delta: swapped.delta0 },
ReducedBalanceDelta { token: pool_details.token1, delta: swapped.delta1 },
]
}
Event::PositionUpdated(position_updated) => {
vec![
ReducedBalanceDelta {
token: pool_details.token0,
delta: adjust_delta_by_fee(
BigInt::from_signed_bytes_be(&position_updated.delta0),
pool_details.fee,
)
.to_signed_bytes_be(),
},
ReducedBalanceDelta {
token: pool_details.token1,
delta: adjust_delta_by_fee(
BigInt::from_signed_bytes_be(&position_updated.delta1),
pool_details.fee,
)
.to_signed_bytes_be(),
},
]
}
Event::PositionFeesCollected(position_fees_collected) => {
vec![
ReducedBalanceDelta {
token: pool_details.token0,
delta: BigInt::from_unsigned_bytes_be(&position_fees_collected.amount0)
.neg()
.to_signed_bytes_be(),
},
ReducedBalanceDelta {
token: pool_details.token1,
delta: BigInt::from_unsigned_bytes_be(&position_fees_collected.amount1)
.neg()
.to_signed_bytes_be(),
},
]
}
Event::FeesAccumulated(fees_accumulated) => {
vec![
ReducedBalanceDelta { token: pool_details.token0, delta: fees_accumulated.amount0 },
ReducedBalanceDelta { token: pool_details.token1, delta: fees_accumulated.amount1 },
]
}
_ => vec![],
}
}
// Negative deltas don't include the fees paid by the position owner, thus we need to add it back
// here (i.e. subtract from the component's balance)
fn adjust_delta_by_fee(delta: BigInt, fee: u64) -> BigInt {
if delta < BigInt::zero() {
let denom = BigInt::from_signed_bytes_be(&hex!("0100000000000000000000000000000000"));
(delta * denom.clone()) / (denom - fee)
} else {
delta
}
}
fn get_pool_details(store: &StoreGetProto<PoolDetails>, component_id: &str) -> PoolDetails {
store
.get_at(0, component_id)
.expect("pool id should exist in store")
}