1#![allow(missing_docs)]
4
5use crate::{abi::abi_decode_calldata, provider::runtime_transport::RuntimeTransportBuilder};
6use alloy_json_abi::JsonAbi;
7use alloy_primitives::{map::HashMap, Selector, B256};
8use eyre::Context;
9use itertools::Itertools;
10use serde::{de::DeserializeOwned, Deserialize, Serialize};
11use std::{
12 fmt,
13 sync::{
14 atomic::{AtomicBool, AtomicUsize, Ordering},
15 Arc,
16 },
17 time::Duration,
18};
19
20const BASE_URL: &str = "https://api.openchain.xyz";
21const SELECTOR_LOOKUP_URL: &str = "https://api.openchain.xyz/signature-database/v1/lookup";
22const SELECTOR_IMPORT_URL: &str = "https://api.openchain.xyz/signature-database/v1/import";
23
24const REQ_TIMEOUT: Duration = Duration::from_secs(15);
26
27const MAX_TIMEDOUT_REQ: usize = 4usize;
29
30pub type OpenChainSignatures = Vec<String>;
32
33#[derive(Clone, Debug)]
35pub struct OpenChainClient {
36 inner: reqwest::Client,
37 spurious_connection: Arc<AtomicBool>,
39 timedout_requests: Arc<AtomicUsize>,
41 max_timedout_requests: usize,
43}
44
45impl OpenChainClient {
46 pub fn new() -> eyre::Result<Self> {
48 let inner = RuntimeTransportBuilder::new(BASE_URL.parse().unwrap())
49 .with_timeout(REQ_TIMEOUT)
50 .build()
51 .reqwest_client()
52 .wrap_err("failed to build OpenChain client")?;
53 Ok(Self {
54 inner,
55 spurious_connection: Default::default(),
56 timedout_requests: Default::default(),
57 max_timedout_requests: MAX_TIMEDOUT_REQ,
58 })
59 }
60
61 async fn get_text(&self, url: impl reqwest::IntoUrl + fmt::Display) -> reqwest::Result<String> {
62 trace!(%url, "GET");
63 self.inner
64 .get(url)
65 .send()
66 .await
67 .inspect_err(|err| self.on_reqwest_err(err))?
68 .text()
69 .await
70 .inspect_err(|err| self.on_reqwest_err(err))
71 }
72
73 async fn post_json<T: Serialize + std::fmt::Debug, R: DeserializeOwned>(
75 &self,
76 url: &str,
77 body: &T,
78 ) -> reqwest::Result<R> {
79 trace!(%url, body=?serde_json::to_string(body), "POST");
80 self.inner
81 .post(url)
82 .json(body)
83 .send()
84 .await
85 .inspect_err(|err| self.on_reqwest_err(err))?
86 .json()
87 .await
88 .inspect_err(|err| self.on_reqwest_err(err))
89 }
90
91 fn on_reqwest_err(&self, err: &reqwest::Error) {
92 fn is_connectivity_err(err: &reqwest::Error) -> bool {
93 if err.is_timeout() || err.is_connect() {
94 return true;
95 }
96 if let Some(status) = err.status() {
98 let code = status.as_u16();
99 if (500..600).contains(&code) {
100 return true;
101 }
102 }
103 false
104 }
105
106 if is_connectivity_err(err) {
107 warn!("spurious network detected for OpenChain");
108 let previous = self.timedout_requests.fetch_add(1, Ordering::SeqCst);
109 if previous >= self.max_timedout_requests {
110 self.set_spurious();
111 }
112 }
113 }
114
115 fn is_spurious(&self) -> bool {
117 self.spurious_connection.load(Ordering::Relaxed)
118 }
119
120 fn set_spurious(&self) {
122 self.spurious_connection.store(true, Ordering::Relaxed)
123 }
124
125 fn ensure_not_spurious(&self) -> eyre::Result<()> {
126 if self.is_spurious() {
127 eyre::bail!("Spurious connection detected")
128 }
129 Ok(())
130 }
131
132 pub async fn decode_selector(
134 &self,
135 selector: SelectorKind,
136 ) -> eyre::Result<OpenChainSignatures> {
137 Ok(self.decode_selectors(&[selector]).await?.pop().unwrap())
138 }
139
140 pub async fn decode_selectors(
142 &self,
143 selectors: &[SelectorKind],
144 ) -> eyre::Result<Vec<OpenChainSignatures>> {
145 if selectors.is_empty() {
146 return Ok(vec![]);
147 }
148
149 if enabled!(tracing::Level::TRACE) {
150 trace!(?selectors, "decoding selectors");
151 } else {
152 debug!(len = selectors.len(), "decoding selectors");
153 }
154
155 self.ensure_not_spurious()?;
157
158 let mut url: url::Url = SELECTOR_LOOKUP_URL.parse().unwrap();
160 {
161 let mut query = url.query_pairs_mut();
162 let functions = selectors.iter().filter_map(SelectorKind::as_function);
163 if functions.clone().next().is_some() {
164 query.append_pair("function", &functions.format(",").to_string());
165 }
166 let events = selectors.iter().filter_map(SelectorKind::as_event);
167 if events.clone().next().is_some() {
168 query.append_pair("event", &events.format(",").to_string());
169 }
170 let _ = query.finish();
171 }
172
173 let text = self.get_text(url).await?;
174 let SignatureResponse { ok, result } = match serde_json::from_str(&text) {
175 Ok(response) => response,
176 Err(err) => eyre::bail!("could not decode response: {err}: {text}"),
177 };
178 if !ok {
179 eyre::bail!("OpenChain returned an error: {text}");
180 }
181
182 Ok(selectors
183 .iter()
184 .map(|selector| {
185 let signatures = match selector {
186 SelectorKind::Function(selector) | SelectorKind::Error(selector) => {
187 result.function.get(selector)
188 }
189 SelectorKind::Event(hash) => result.event.get(hash),
190 };
191 signatures
192 .map(Option::as_deref)
193 .unwrap_or_default()
194 .unwrap_or_default()
195 .iter()
196 .map(|sig| sig.name.clone())
197 .collect()
198 })
199 .collect())
200 }
201
202 pub async fn decode_function_selector(
204 &self,
205 selector: Selector,
206 ) -> eyre::Result<OpenChainSignatures> {
207 self.decode_selector(SelectorKind::Function(selector)).await
208 }
209
210 pub async fn decode_calldata(&self, calldata: &str) -> eyre::Result<OpenChainSignatures> {
212 let calldata = calldata.strip_prefix("0x").unwrap_or(calldata);
213 if calldata.len() < 8 {
214 eyre::bail!(
215 "Calldata too short: expected at least 8 characters (excluding 0x prefix), got {}.",
216 calldata.len()
217 )
218 }
219
220 let mut sigs = self.decode_function_selector(calldata[..8].parse().unwrap()).await?;
221 sigs.retain(|sig| abi_decode_calldata(sig, calldata, true, true).is_ok());
223 Ok(sigs)
224 }
225
226 pub async fn decode_event_topic(&self, topic: B256) -> eyre::Result<OpenChainSignatures> {
228 self.decode_selector(SelectorKind::Event(topic)).await
229 }
230
231 pub async fn pretty_calldata(
249 &self,
250 calldata: impl AsRef<str>,
251 offline: bool,
252 ) -> eyre::Result<PossibleSigs> {
253 let mut possible_info = PossibleSigs::new();
254 let calldata = calldata.as_ref().trim_start_matches("0x");
255
256 let selector =
257 calldata.get(..8).ok_or_else(|| eyre::eyre!("calldata cannot be less that 4 bytes"))?;
258
259 let sigs = if offline {
260 vec![]
261 } else {
262 let selector = selector.parse()?;
263 self.decode_function_selector(selector).await.unwrap_or_default().into_iter().collect()
264 };
265 let (_, data) = calldata.split_at(8);
266
267 if data.len() % 64 != 0 {
268 eyre::bail!("\nInvalid calldata size")
269 }
270
271 let row_length = data.len() / 64;
272
273 for row in 0..row_length {
274 possible_info.data.push(data[64 * row..64 * (row + 1)].to_string());
275 }
276 if sigs.is_empty() {
277 possible_info.method = SelectorOrSig::Selector(selector.to_string());
278 } else {
279 possible_info.method = SelectorOrSig::Sig(sigs);
280 }
281 Ok(possible_info)
282 }
283
284 pub async fn import_selectors(
286 &self,
287 data: SelectorImportData,
288 ) -> eyre::Result<SelectorImportResponse> {
289 self.ensure_not_spurious()?;
290
291 let request = match data {
292 SelectorImportData::Abi(abis) => {
293 let functions_and_errors: OpenChainSignatures = abis
294 .iter()
295 .flat_map(|abi| {
296 abi.functions()
297 .map(|func| func.signature())
298 .chain(abi.errors().map(|error| error.signature()))
299 .collect::<Vec<_>>()
300 })
301 .collect();
302
303 let events = abis
304 .iter()
305 .flat_map(|abi| abi.events().map(|event| event.signature()))
306 .collect::<Vec<_>>();
307
308 SelectorImportRequest { function: functions_and_errors, event: events }
309 }
310 SelectorImportData::Raw(raw) => {
311 let function_and_error =
312 raw.function.iter().chain(raw.error.iter()).cloned().collect::<Vec<_>>();
313 SelectorImportRequest { function: function_and_error, event: raw.event }
314 }
315 };
316
317 Ok(self.post_json(SELECTOR_IMPORT_URL, &request).await?)
318 }
319}
320
321pub enum SelectorOrSig {
322 Selector(String),
323 Sig(OpenChainSignatures),
324}
325
326pub struct PossibleSigs {
327 method: SelectorOrSig,
328 data: OpenChainSignatures,
329}
330
331impl PossibleSigs {
332 fn new() -> Self {
333 Self { method: SelectorOrSig::Selector("0x00000000".to_string()), data: vec![] }
334 }
335}
336
337impl fmt::Display for PossibleSigs {
338 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339 match &self.method {
340 SelectorOrSig::Selector(selector) => {
341 writeln!(f, "\n Method: {selector}")?;
342 }
343 SelectorOrSig::Sig(sigs) => {
344 writeln!(f, "\n Possible methods:")?;
345 for sig in sigs {
346 writeln!(f, " - {sig}")?;
347 }
348 }
349 }
350
351 writeln!(f, " ------------")?;
352 for (i, row) in self.data.iter().enumerate() {
353 let row_label_decimal = i * 32;
354 let row_label_hex = format!("{row_label_decimal:03x}");
355 writeln!(f, " [{row_label_hex}]: {row}")?;
356 }
357 Ok(())
358 }
359}
360
361#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
363pub enum SelectorKind {
364 Function(Selector),
366 Error(Selector),
368 Event(B256),
370}
371
372impl SelectorKind {
373 pub fn as_function(&self) -> Option<Selector> {
375 match *self {
376 Self::Function(selector) | Self::Error(selector) => Some(selector),
377 _ => None,
378 }
379 }
380
381 pub fn as_event(&self) -> Option<B256> {
383 match *self {
384 Self::Event(hash) => Some(hash),
385 _ => None,
386 }
387 }
388}
389
390pub async fn decode_selector(selector: SelectorKind) -> eyre::Result<OpenChainSignatures> {
392 OpenChainClient::new()?.decode_selector(selector).await
393}
394
395pub async fn decode_selectors(
397 selectors: &[SelectorKind],
398) -> eyre::Result<Vec<OpenChainSignatures>> {
399 OpenChainClient::new()?.decode_selectors(selectors).await
400}
401
402pub async fn decode_function_selector(selector: Selector) -> eyre::Result<OpenChainSignatures> {
404 OpenChainClient::new()?.decode_function_selector(selector).await
405}
406
407pub async fn decode_calldata(calldata: &str) -> eyre::Result<OpenChainSignatures> {
409 OpenChainClient::new()?.decode_calldata(calldata).await
410}
411
412pub async fn decode_event_topic(topic: B256) -> eyre::Result<OpenChainSignatures> {
414 OpenChainClient::new()?.decode_event_topic(topic).await
415}
416
417pub async fn pretty_calldata(
433 calldata: impl AsRef<str>,
434 offline: bool,
435) -> eyre::Result<PossibleSigs> {
436 OpenChainClient::new()?.pretty_calldata(calldata, offline).await
437}
438
439#[derive(Debug, Default, PartialEq, Eq, Serialize)]
440pub struct RawSelectorImportData {
441 pub function: OpenChainSignatures,
442 pub event: OpenChainSignatures,
443 pub error: OpenChainSignatures,
444}
445
446impl RawSelectorImportData {
447 pub fn is_empty(&self) -> bool {
448 self.function.is_empty() && self.event.is_empty() && self.error.is_empty()
449 }
450}
451
452#[derive(Serialize)]
453#[serde(untagged)]
454pub enum SelectorImportData {
455 Abi(Vec<JsonAbi>),
456 Raw(RawSelectorImportData),
457}
458
459#[derive(Debug, Default, Serialize)]
460struct SelectorImportRequest {
461 function: OpenChainSignatures,
462 event: OpenChainSignatures,
463}
464
465#[derive(Debug, Deserialize)]
466struct SelectorImportEffect {
467 imported: HashMap<String, String>,
468 duplicated: HashMap<String, String>,
469}
470
471#[derive(Debug, Deserialize)]
472struct SelectorImportResult {
473 function: SelectorImportEffect,
474 event: SelectorImportEffect,
475}
476
477#[derive(Debug, Deserialize)]
478pub struct SelectorImportResponse {
479 result: SelectorImportResult,
480}
481
482impl SelectorImportResponse {
483 pub fn describe(&self) {
485 self.result.function.imported.iter().for_each(|(k, v)| {
486 let _ = sh_println!("Imported: Function {k}: {v}");
487 });
488 self.result.event.imported.iter().for_each(|(k, v)| {
489 let _ = sh_println!("Imported: Event {k}: {v}");
490 });
491 self.result.function.duplicated.iter().for_each(|(k, v)| {
492 let _ = sh_println!("Duplicated: Function {k}: {v}");
493 });
494 self.result.event.duplicated.iter().for_each(|(k, v)| {
495 let _ = sh_println!("Duplicated: Event {k}: {v}");
496 });
497
498 let _ = sh_println!("Selectors successfully uploaded to OpenChain");
499 }
500}
501
502pub async fn import_selectors(data: SelectorImportData) -> eyre::Result<SelectorImportResponse> {
504 OpenChainClient::new()?.import_selectors(data).await
505}
506
507#[derive(Debug, Default, PartialEq, Eq)]
508pub struct ParsedSignatures {
509 pub signatures: RawSelectorImportData,
510 pub abis: Vec<JsonAbi>,
511}
512
513#[derive(Deserialize)]
514struct Artifact {
515 abi: JsonAbi,
516}
517
518pub fn parse_signatures(tokens: Vec<String>) -> ParsedSignatures {
522 let abis = tokens
525 .iter()
526 .filter(|sig| sig.ends_with(".json"))
527 .filter_map(|filename| std::fs::read_to_string(filename).ok())
528 .filter_map(|file| serde_json::from_str(file.as_str()).ok())
529 .map(|artifact: Artifact| artifact.abi)
530 .collect();
531
532 let signatures = tokens.iter().filter(|sig| !sig.ends_with(".json")).fold(
535 RawSelectorImportData::default(),
536 |mut data, signature| {
537 let mut split = signature.split(' ');
538 match split.next() {
539 Some("function") => {
540 if let Some(sig) = split.next() {
541 data.function.push(sig.to_string())
542 }
543 }
544 Some("event") => {
545 if let Some(sig) = split.next() {
546 data.event.push(sig.to_string())
547 }
548 }
549 Some("error") => {
550 if let Some(sig) = split.next() {
551 data.error.push(sig.to_string())
552 }
553 }
554 Some(signature) => {
555 data.function.push(signature.to_string());
557 }
558 None => {}
559 }
560 data
561 },
562 );
563
564 ParsedSignatures { signatures, abis }
565}
566
567#[derive(Deserialize)]
569struct SignatureResponse {
570 ok: bool,
571 result: SignatureResult,
572}
573
574#[derive(Deserialize)]
575struct SignatureResult {
576 event: HashMap<B256, Option<Vec<Signature>>>,
577 function: HashMap<Selector, Option<Vec<Signature>>>,
578}
579
580#[derive(Deserialize)]
581struct Signature {
582 name: String,
583}
584
585#[cfg(test)]
586mod tests {
587 use super::*;
588
589 #[test]
590 fn test_parse_signatures() {
591 let result = parse_signatures(vec!["transfer(address,uint256)".to_string()]);
592 assert_eq!(
593 result,
594 ParsedSignatures {
595 signatures: RawSelectorImportData {
596 function: vec!["transfer(address,uint256)".to_string()],
597 ..Default::default()
598 },
599 ..Default::default()
600 }
601 );
602
603 let result = parse_signatures(vec![
604 "transfer(address,uint256)".to_string(),
605 "function approve(address,uint256)".to_string(),
606 ]);
607 assert_eq!(
608 result,
609 ParsedSignatures {
610 signatures: RawSelectorImportData {
611 function: vec![
612 "transfer(address,uint256)".to_string(),
613 "approve(address,uint256)".to_string()
614 ],
615 ..Default::default()
616 },
617 ..Default::default()
618 }
619 );
620
621 let result = parse_signatures(vec![
622 "transfer(address,uint256)".to_string(),
623 "event Approval(address,address,uint256)".to_string(),
624 "error ERC20InsufficientBalance(address,uint256,uint256)".to_string(),
625 ]);
626 assert_eq!(
627 result,
628 ParsedSignatures {
629 signatures: RawSelectorImportData {
630 function: vec!["transfer(address,uint256)".to_string()],
631 event: vec!["Approval(address,address,uint256)".to_string()],
632 error: vec!["ERC20InsufficientBalance(address,uint256,uint256)".to_string()]
633 },
634 ..Default::default()
635 }
636 );
637
638 let result = parse_signatures(vec!["event".to_string()]);
640 assert_eq!(
641 result,
642 ParsedSignatures { signatures: Default::default(), ..Default::default() }
643 );
644 }
645}