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;
8
9#[derive(Debug, Subcommand)]
10pub enum Commands {
11 #[command(arg_required_else_help = true)]
13 Extract(extract::ExtractArgs),
14
15 #[command(arg_required_else_help = false)]
17 Cat(cat::CatArgs),
18}
19
20#[derive(Debug, Parser)]
22pub struct ProviderArgs {
23 #[arg(long, env, global = true)]
26 chain_id: Option<u64>,
27
28 #[arg(long, env, short, global = true)]
30 ankr_api_key: Option<String>,
31
32 #[arg(long, env, global = true)]
34 infura_api_key: Option<String>,
35
36 #[arg(long, env, global = true)]
38 quicknode_api_key: Option<String>,
39
40 #[arg(long, env, global = true)]
42 alchemy_api_key: Option<String>,
43
44 #[arg(long, env, global = true)]
46 rpc_url: Option<Vec<String>>,
47
48 #[arg(long, env, global = true)]
50 ws_url: Option<Vec<String>>,
51}
52
53impl TryFrom<ProviderArgs> for ProviderSettings {
54 type Error = anyhow::Error;
55 fn try_from(args: ProviderArgs) -> Result<Self, Self::Error> {
56 ProviderSettings::build(
57 ProviderOptions {
58 ankr_api_key: args.ankr_api_key,
59 infura_api_key: args.infura_api_key,
60 quicknode_api_key: args.quicknode_api_key,
61 alchemy_api_key: args.alchemy_api_key,
62 rpc_urls: args.rpc_url,
63 ws_urls: args.ws_url,
64 },
65 args.chain_id.unwrap_or(1), )
67 }
68}
69
70#[derive(Debug, Parser)]
71pub struct FilterArgs {
72 #[arg(long, required = false)]
74 events: Option<Vec<String>>,
75
76 #[arg(long, short, required = false, value_delimiter = ',')]
78 addresses: Option<Vec<Address>>,
79
80 #[arg(long)]
82 to_block: Option<u64>,
83
84 #[arg(long)]
86 from_block: Option<u64>,
87}
88
89impl TryInto<Option<Filter>> for &FilterArgs {
90 type Error = anyhow::Error;
91
92 fn try_into(self) -> Result<Option<Filter>> {
93 if self.addresses.is_none() && self.events.is_none() {
94 return Ok(None);
95 }
96
97 let mut filter = Filter::new();
98 if let Some(addresses) = &self.addresses {
99 filter = filter.address(addresses.clone());
100 }
101
102 let events: Option<Vec<Event>> = self.try_into()?;
103
104 if let Some(events) = events {
105 filter = filter.events(
106 events
107 .iter()
108 .map(|e| e.signature())
109 .collect::<Vec<String>>(),
110 );
111 }
112
113 if let Some(from_block) = self.from_block {
114 filter = filter.from_block(from_block);
115 }
116
117 if let Some(to_block) = self.to_block {
118 filter = filter.to_block(to_block);
119 }
120
121 Ok(Some(filter))
122 }
123}
124
125impl TryInto<Option<Vec<Event>>> for &FilterArgs {
126 type Error = anyhow::Error;
127 fn try_into(self) -> Result<Option<Vec<Event>>> {
128 if self.events.is_none() {
129 return Ok(None);
130 }
131
132 let events = self
133 .events
134 .as_ref()
135 .unwrap()
136 .iter()
137 .map(|signature| Event::parse(signature))
138 .collect::<Result<Vec<Event>, _>>()?;
139
140 Ok(Some(events))
141 }
142}