Files
tycho-protocol-sdk/substreams/ethereum-curve/src/pools.rs
2024-04-11 19:52:32 -05:00

91 lines
3.6 KiB
Rust

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<String>,
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: &eth::v2::Block,
) -> Result<Vec<ProtocolComponent>> {
let pools: HashMap<String, PoolQueryParams> = params
.split(PARAMS_SEPERATOR)
.map(|param| {
// TODO UNSAFE
let pool: PoolQueryParams = serde_qs::from_str(&param)
.with_context(|| format!("Failed to parse pool query params: {0}", param))?;
Ok((pool.tx_hash.clone(), pool))
})
.collect::<Result<HashMap<_, _>>>()
.with_context(|| "Failed to parse all pool query params")?;
let mut components: Vec<ProtocolComponent> = 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::<Result<Vec<_>>>()
.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::<Vec<_>>(),
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)
}