91 lines
3.6 KiB
Rust
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: ð::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(¶m)
|
|
.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)
|
|
}
|