use substreams::{ hex, scalar::BigInt, store::{StoreGet, StoreGetInt64, StoreGetString}, }; use substreams_ethereum::{block_view::LogView, Event}; use tycho_substreams::prelude::*; use crate::abi; 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(), index: Into::::into(log.receipt.transaction.index), } } /// This function emits balance deltas for mints, burns, and exchanges in Curve pools. Since some /// pools contain differing ABIs, we load in several examples of abis in order to best match the /// topic ID to the correct event. The repetition in this function is dervived from the fact that /// most of these events have similar structures, but the specific topic id differs. pub fn emit_deltas( log: LogView, pools_store: &StoreGetInt64, tokens_store: &StoreGetString, ) -> Option> { let pool_key = format!("pool:{}", hex::encode(&log.address())); if pools_store.get_last(pool_key).is_none() { return None; } let tokens = tokens_store .get_last(format!("pool:{}", hex::encode(log.address())))? .split(":") .map(|token| token.to_owned()) .collect::>(); if let Some(event) = abi::pool::events::TokenExchange::match_and_decode(log) { token_change_deltas(tokens, event, log) } else if let Some(event) = abi::pool_3pool::events::TokenExchange::match_and_decode(log) { token_change_deltas( tokens, abi::pool::events::TokenExchange { sold_id: event.sold_id, bought_id: event.bought_id, tokens_sold: event.tokens_sold, tokens_bought: event.tokens_bought, buyer: event.buyer, }, log, ) } else if let Some(event) = abi::pool_steth::events::TokenExchange::match_and_decode(log) { token_change_deltas( tokens, abi::pool::events::TokenExchange { sold_id: event.sold_id, bought_id: event.bought_id, tokens_sold: event.tokens_sold, tokens_bought: event.tokens_bought, buyer: event.buyer, }, log, ) } else if let Some(event) = abi::pool_tricrypto::events::TokenExchange::match_and_decode(log) { token_change_deltas( tokens, abi::pool::events::TokenExchange { sold_id: event.sold_id, bought_id: event.bought_id, tokens_sold: event.tokens_sold, tokens_bought: event.tokens_bought, buyer: event.buyer, }, log, ) } else if let Some(event) = abi::pool::events::AddLiquidity::match_and_decode(log) { add_liquidity_deltas(event.token_amounts.into(), &tokens, log) } else if let Some(event) = abi::pool_3pool::events::AddLiquidity::match_and_decode(log) { add_liquidity_deltas(event.token_amounts.into(), &tokens, log) } else if let Some(event) = abi::pool_steth::events::AddLiquidity::match_and_decode(log) { add_liquidity_deltas(event.token_amounts.into(), &tokens, log) } else if let Some(event) = abi::pool_tricrypto::events::AddLiquidity::match_and_decode(log) { add_liquidity_deltas(event.token_amounts.into(), &tokens, log) } else if let Some(event) = abi::pool::events::RemoveLiquidity::match_and_decode(log) { remove_liquidity_deltas(event.token_amounts.into(), &tokens, log) } else if let Some(event) = abi::pool_3pool::events::RemoveLiquidity::match_and_decode(log) { remove_liquidity_deltas(event.token_amounts.into(), &tokens, log) } else if let Some(event) = abi::pool_steth::events::RemoveLiquidity::match_and_decode(log) { remove_liquidity_deltas(event.token_amounts.into(), &tokens, log) } else if let Some(event) = abi::pool_tricrypto::events::RemoveLiquidity::match_and_decode(log) { remove_liquidity_deltas(event.token_amounts.into(), &tokens, log) } else { None } } fn token_change_deltas( tokens: Vec, event: abi::pool::events::TokenExchange, log: LogView<'_>, ) -> Option> { let tokens_bought_delta: BigInt = event.tokens_bought * -1; let sold_token_id = &tokens[event.sold_id.to_u64() as usize]; let bought_token_id = &tokens[event.bought_id.to_u64() as usize]; Some(vec![ BalanceDelta { ord: log.log.ordinal, tx: Some(tx_from_log(&log)), token: hex::decode(sold_token_id.clone()).unwrap(), delta: event.tokens_sold.to_signed_bytes_be(), component_id: hex::encode(log.address()).into(), }, BalanceDelta { ord: log.log.ordinal, tx: Some(tx_from_log(&log)), token: hex::decode(bought_token_id.clone()).unwrap(), delta: tokens_bought_delta.to_signed_bytes_be(), component_id: hex::encode(log.address()).into(), }, ]) } fn add_liquidity_deltas( amounts: Vec, tokens: &Vec, log: LogView<'_>, ) -> Option> { Some( amounts .iter() .zip(tokens) .map(move |(token_amount, token_id)| BalanceDelta { ord: log.log.ordinal, tx: Some(tx_from_log(&log)), token: hex::decode(token_id).unwrap(), delta: token_amount.to_signed_bytes_be(), component_id: hex::encode(log.address()).into(), }) .collect::>(), ) } fn remove_liquidity_deltas( amounts: Vec, tokens: &Vec, log: LogView<'_>, ) -> Option> { Some( amounts .iter() .zip(tokens) .map(move |(token_amount, token_id)| BalanceDelta { ord: log.log.ordinal, tx: Some(tx_from_log(&log)), token: hex::decode(token_id).unwrap(), delta: token_amount.to_signed_bytes_be(), component_id: hex::encode(log.address()).into(), }) .collect::>(), ) }