1use alloy::{json_abi::Event, primitives::Address, rpc::types::Filter};
2use anyhow::Result;
3use clap::{Parser, Subcommand, arg};
4use ethl::rpc::config::{ProviderOptions, ProviderSettings};
5
6pub mod cat;
7pub mod extract;
8pub mod merge;
9pub mod repair;
10
11#[derive(Debug, Subcommand)]
12pub enum Commands {
13 #[command(arg_required_else_help = true)]
15 Extract(extract::ExtractArgs),
16
17 #[command(arg_required_else_help = false)]
19 Cat(cat::CatArgs),
20
21 #[command(arg_required_else_help = true)]
23 Merge(merge::MergeArgs),
24
25 #[command(arg_required_else_help = true)]
27 Repair(repair::RepairArgs),
28}
29
30#[derive(Debug, Parser)]
32pub struct ProviderArgs {
33 #[arg(long, env, global = true)]
36 chain_id: Option<u64>,
37
38 #[arg(long, env, short, global = true)]
40 ankr_api_key: Option<String>,
41
42 #[arg(long, env, global = true)]
44 infura_api_key: Option<String>,
45
46 #[arg(long, env, global = true)]
48 quicknode_api_key: Option<String>,
49
50 #[arg(long, env, global = true)]
52 alchemy_api_key: Option<String>,
53
54 #[arg(long, env, global = true)]
56 rpc_url: Option<Vec<String>>,
57
58 #[arg(long, env, global = true)]
60 ws_url: Option<Vec<String>>,
61}
62
63impl TryFrom<ProviderArgs> for ProviderSettings {
64 type Error = anyhow::Error;
65 fn try_from(args: ProviderArgs) -> Result<Self, Self::Error> {
66 ProviderSettings::build(
67 ProviderOptions {
68 ankr_api_key: args.ankr_api_key,
69 infura_api_key: args.infura_api_key,
70 quicknode_api_key: args.quicknode_api_key,
71 alchemy_api_key: args.alchemy_api_key,
72 rpc_urls: args.rpc_url,
73 ws_urls: args.ws_url,
74 },
75 args.chain_id.unwrap_or(1), )
77 }
78}
79
80#[derive(Debug, Parser)]
81pub struct FilterArgs {
82 #[arg(long, required = false)]
84 events: Option<Vec<String>>,
85
86 #[arg(long, short, required = false, value_delimiter = ',')]
88 addresses: Option<Vec<Address>>,
89
90 #[arg(long)]
92 to_block: Option<u64>,
93
94 #[arg(long)]
96 from_block: Option<u64>,
97}
98
99impl FilterArgs {
100 pub fn parsed_events(&self) -> Option<Result<Vec<Event>>> {
101 self.events.as_ref().map(|e| full_signatures_to_events(e))
102 }
103}
104
105impl TryInto<Option<Filter>> for &FilterArgs {
106 type Error = anyhow::Error;
107
108 fn try_into(self) -> Result<Option<Filter>> {
109 if self.addresses.is_none() && self.events.is_none() {
110 return Ok(None);
111 }
112
113 let mut filter = Filter::new();
114 if let Some(addresses) = &self.addresses {
115 filter = filter.address(addresses.clone());
116 }
117
118 let events: Option<Vec<Event>> = self.try_into()?;
119
120 if let Some(events) = events {
121 filter = filter.events(
122 events
123 .iter()
124 .map(|e| e.signature())
125 .collect::<Vec<String>>(),
126 );
127 }
128
129 if let Some(from_block) = self.from_block {
130 filter = filter.from_block(from_block);
131 }
132
133 if let Some(to_block) = self.to_block {
134 filter = filter.to_block(to_block);
135 }
136
137 Ok(Some(filter))
138 }
139}
140
141impl TryInto<Option<Vec<Event>>> for &FilterArgs {
142 type Error = anyhow::Error;
143 fn try_into(self) -> Result<Option<Vec<Event>>> {
144 self.events
145 .as_ref()
146 .map(|e| full_signatures_to_events(e))
147 .transpose()
148 }
149}
150
151fn full_signatures_to_events(signatures: &[String]) -> Result<Vec<Event>> {
152 signatures
153 .iter()
154 .map(|s| Event::parse(s).map_err(anyhow::Error::from))
155 .collect::<Result<Vec<Event>>>()
156}