use alloy::{json_abi::Event, primitives::Address, rpc::types::Filter};
use anyhow::Result;
use clap::{Parser, Subcommand};
use ethl::rpc::config::{ProviderOptions, ProviderSettings};
pub mod cat;
pub mod extract;
pub mod merge;
pub mod repair;
#[derive(Debug, Subcommand)]
pub enum Commands {
#[command(arg_required_else_help = true)]
Extract(extract::ExtractArgs),
#[command(arg_required_else_help = false)]
Cat(cat::CatArgs),
#[command(arg_required_else_help = true)]
Merge(merge::MergeArgs),
#[command(arg_required_else_help = true)]
Repair(repair::RepairArgs),
}
#[derive(Debug, Parser)]
pub struct ProviderArgs {
#[arg(long, env, global = true)]
chain_id: Option<u64>,
#[arg(long, env, short, global = true)]
ankr_api_key: Option<String>,
#[arg(long, env, global = true)]
infura_api_key: Option<String>,
#[arg(long, env, global = true)]
quicknode_api_key: Option<String>,
#[arg(long, env, global = true)]
alchemy_api_key: Option<String>,
#[arg(long, env, global = true)]
rpc_url: Option<Vec<String>>,
#[arg(long, env, global = true)]
ws_url: Option<Vec<String>>,
}
impl TryFrom<ProviderArgs> for ProviderSettings {
type Error = anyhow::Error;
fn try_from(args: ProviderArgs) -> Result<Self, Self::Error> {
ProviderSettings::build(
ProviderOptions {
ankr_api_key: args.ankr_api_key,
infura_api_key: args.infura_api_key,
quicknode_api_key: args.quicknode_api_key,
alchemy_api_key: args.alchemy_api_key,
rpc_urls: args.rpc_url,
ws_urls: args.ws_url,
},
args.chain_id.unwrap_or(1), )
}
}
#[derive(Debug, Parser)]
pub struct FilterArgs {
#[arg(long, required = false)]
events: Option<Vec<String>>,
#[arg(long, short, required = false, value_delimiter = ',')]
addresses: Option<Vec<Address>>,
#[arg(long)]
to_block: Option<u64>,
#[arg(long)]
from_block: Option<u64>,
}
impl FilterArgs {
pub fn parsed_events(&self) -> Option<Result<Vec<Event>>> {
self.events.as_ref().map(|e| full_signatures_to_events(e))
}
}
impl TryInto<Option<Filter>> for &FilterArgs {
type Error = anyhow::Error;
fn try_into(self) -> Result<Option<Filter>> {
if self.addresses.is_none() && self.events.is_none() {
return Ok(None);
}
let mut filter = Filter::new();
if let Some(addresses) = &self.addresses {
filter = filter.address(addresses.clone());
}
let events: Option<Vec<Event>> = self.try_into()?;
if let Some(events) = events {
filter = filter.events(
events
.iter()
.map(|e| e.signature())
.collect::<Vec<String>>(),
);
}
if let Some(from_block) = self.from_block {
filter = filter.from_block(from_block);
}
if let Some(to_block) = self.to_block {
filter = filter.to_block(to_block);
}
Ok(Some(filter))
}
}
impl TryInto<Option<Vec<Event>>> for &FilterArgs {
type Error = anyhow::Error;
fn try_into(self) -> Result<Option<Vec<Event>>> {
self.events
.as_ref()
.map(|e| full_signatures_to_events(e))
.transpose()
}
}
fn full_signatures_to_events(signatures: &[String]) -> Result<Vec<Event>> {
signatures
.iter()
.map(|s| Event::parse(s).map_err(anyhow::Error::from))
.collect::<Result<Vec<Event>>>()
}