scope_cli/cli/watch/
mod.rs1pub mod fetch;
11pub mod monitor;
12pub mod run;
13
14use crate::cli::output::Output;
15use crate::cli::resolve::{DexCandidates, ResolveContext, VenueKind, picker_for, resolve};
16use scope::chains::{ChainClientFactory, infer_chain_from_address};
17use scope::config::Config;
18use scope::error::Result;
19use scope::metrics::MetricGroup;
20use scope::tokens::TokenAliases;
21use scope::watch::{Target, TargetKind, Watchlist};
22
23pub use fetch::{TargetReading, Tick, tick};
24pub use run::{csv_header, csv_rows, report_text, run};
25
26#[derive(Clone, Copy)]
28pub struct Sources<'a> {
29 pub config: &'a Config,
30 pub clients: &'a dyn ChainClientFactory,
31}
32
33pub fn venue_ids() -> Vec<String> {
35 scope::market::VenueRegistry::load()
36 .map(|r| r.list().into_iter().map(str::to_string).collect())
37 .unwrap_or_default()
38}
39
40#[derive(Debug, Clone, PartialEq)]
42pub struct Ready {
43 pub list: Watchlist,
45 pub contracts: Vec<Option<String>>,
48}
49
50pub async fn prepare(w: &Watchlist, src: Sources<'_>, out: &Output) -> Result<Ready> {
60 let mut list = w.clone();
61 let mut contracts = Vec::with_capacity(list.targets.len());
62 for t in &mut list.targets {
63 if let Some((address, chain)) =
64 crate::cli::address_book::resolve_address_book_input(&t.id, src.config)?
65 {
66 t.id = address;
67 t.chain.get_or_insert(chain);
68 }
69 if t.chain.is_none() && t.kind != TargetKind::Pair {
70 t.chain = infer_chain_from_address(&t.id).map(str::to_string);
71 }
72 let contract = if t.kind == TargetKind::Token && t.needs(MetricGroup::Token) {
73 Some(resolve_token(t, src, out).await?)
74 } else {
75 None
76 };
77 contracts.push(contract);
78 }
79 Ok(Ready { list, contracts })
80}
81
82async fn resolve_token(t: &mut Target, src: Sources<'_>, out: &Output) -> Result<String> {
84 if TokenAliases::is_address(t.id.trim()) {
85 return Ok(t.id.trim().to_string());
86 }
87 let dex = src.clients.create_dex_client();
88 let ctx = ResolveContext {
89 chain: t.chain.clone(),
90 venue: VenueKind::Dex,
91 token_flag: None,
92 yes: false,
93 save: false,
94 };
95 let source = DexCandidates {
96 dex: dex.as_ref(),
97 out,
98 fallback: Some(src.clients.http()),
99 };
100 let picker = picker_for(out, false);
101 let mut aliases = TokenAliases::load();
102 let r = resolve(&t.id, &ctx, &source, picker.as_ref(), &mut aliases, out).await?;
103 if t.chain.is_none() && !r.chain.is_empty() {
104 t.chain = Some(r.chain);
105 }
106 Ok(r.address.unwrap_or_else(|| t.id.clone()))
107}