use std::collections::HashMap; use anyhow::{Context, Result}; use serde::Deserialize; use substreams_ethereum::pb::eth; use tycho_substreams::prelude::*; const PARAMS_SEPERATOR: &str = ","; #[derive(Debug, Deserialize)] struct PoolQueryParams { address: String, tx_hash: String, tokens: Vec, attributes: Vec<(String, String)>, } /// This function parses the `params` string and extracts the pool query parameters. `params` are /// comma-separated, URL-encoded (defined by `serde-qs`) strings, with each component defining the /// pool query parameters defined in the struct above. We then iterate through the transactions in /// a block, and then if the transaction hash matches our parameter, we emit a `ProtocolComponent` /// defined by the metadata from above alongside some basic defaults that we know for Curve. /// /// Static attributes are defined as a vector of tuples with the name and value of the attribute. /// These contain things like the pool type, specific pool fees, etc. You can see /// `pool_factories.rs` for an example of the modern curve pool attributes and also the ones chosen /// for 3pool, etc. /// /// This function can error based on some basic parsing errors and deeper down hex decoding errors /// if various addresses are not formatted properly. pub fn emit_specific_pools( params: &String, block: ð::v2::Block, ) -> Result> { let pools: HashMap = params .split(PARAMS_SEPERATOR) .map(|param| { // TODO UNSAFE let pool: PoolQueryParams = serde_qs::from_str(¶m) .with_context(|| format!("Failed to parse pool query params: {0}", param))?; Ok((pool.tx_hash.clone(), pool)) }) .collect::>>() .with_context(|| "Failed to parse all pool query params")?; let mut components: Vec = vec![]; for tx in block.transactions() { let encoded_hash = hex::encode(tx.hash.clone()); if let Some(pool) = pools.get(&encoded_hash) { let component = ProtocolComponent { id: pool.address.clone(), tx: Some(Transaction { to: tx.to.clone(), from: tx.from.clone(), hash: tx.hash.clone(), index: tx.index.into(), }), tokens: pool .tokens .clone() .into_iter() .map(|token| Result::Ok(hex::decode(token)?)) .collect::>>() .with_context(|| "Token addresses were not formatted properly")?, static_att: pool .attributes .clone() .into_iter() .map(|attr| Attribute { name: attr.0, value: attr.1.into(), change: ChangeType::Creation.into(), }) .collect::>(), contracts: vec![hex::decode(pool.address.clone()) .with_context(|| "Pool address was not formatted properly")?], change: ChangeType::Creation.into(), protocol_type: Some(ProtocolType { name: "curve_pool".into(), financial_type: FinancialType::Swap.into(), attribute_schema: Vec::new(), implementation_type: ImplementationType::Vm.into(), }), }; components.push(component); } } Ok(components) }