leo_lang/cli/commands/common/
query.rs1use super::*;
18use crate::cli::query::{LeoBlock, LeoProgram};
19
20use leo_ast::NetworkName;
21use leo_package::{ProgramData, create_http_agent};
22use leo_span::Symbol;
23use snarkvm::prelude::{Program, ProgramID};
24
25use indexmap::IndexSet;
26use std::collections::HashMap;
27
28pub fn get_public_balance<N: Network>(
30 private_key: &PrivateKey<N>,
31 endpoint: &str,
32 network: NetworkName,
33 context: &Context,
34) -> Result<u64> {
35 let address = Address::<N>::try_from(ViewKey::try_from(private_key)?)?;
37 let mut public_balance = LeoQuery {
39 env_override: EnvOptions { endpoint: Some(endpoint.to_string()), network: Some(network), ..Default::default() },
40 command: QueryCommands::Program {
41 command: LeoProgram {
42 name: "credits".to_string(),
43 edition: None,
44 mappings: false,
45 mapping_value: Some(vec!["account".to_string(), address.to_string()]),
46 },
47 },
48 }
49 .execute(Context::new(context.path.clone(), context.home.clone(), true, None)?)?;
50 public_balance.truncate(public_balance.len() - 3);
52 public_balance.parse::<u64>().map_err(|_| crate::errors::invalid_balance(address).into())
54}
55
56#[allow(dead_code)]
58pub fn get_latest_block_height(
59 endpoint: &str,
60 network: NetworkName,
61 context: &Context,
62 network_retries: u32,
63) -> Result<u32> {
64 let height = LeoQuery {
66 env_override: EnvOptions { endpoint: Some(endpoint.to_string()), network: Some(network), network_retries },
67 command: QueryCommands::Block {
68 command: LeoBlock {
69 id: None,
70 latest: false,
71 latest_hash: false,
72 latest_height: true,
73 range: None,
74 transactions: false,
75 to_height: false,
76 },
77 },
78 }
79 .execute(Context::new(context.path.clone(), context.home.clone(), true, None)?)?;
80 let height = height.parse::<u32>().map_err(crate::errors::string_parse_error)?;
82 Ok(height)
83}
84
85pub fn handle_broadcast<N: Network>(
89 endpoint: &str,
90 transaction: &Transaction<N>,
91 operation: &str,
92) -> Result<(String, u16)> {
93 let mut response = create_http_agent()
95 .post(endpoint)
96 .query("check_transaction", "true")
97 .header("X-Leo-Version", env!("CARGO_PKG_VERSION"))
98 .send_json(transaction)
99 .map_err(|err| crate::errors::broadcast_error(err.to_string()))?;
100 match response.status().as_u16() {
101 200..=299 => {
102 println!(
103 "✉️ Broadcasted transaction with:\n - transaction ID: '{}'",
104 transaction.id().to_string().bold().yellow(),
105 );
106 if let Some(fee) = transaction.fee_transition() {
107 println!(" - fee ID: '{}'", fee.id().to_string().bold().yellow());
109 println!(" - fee transaction ID: '{}'", Transaction::from_fee(fee)?.id().to_string().bold().yellow());
111 println!(" (use this to check for rejected transactions)\n");
112 }
113 Ok((response.body_mut().read_to_string().unwrap(), response.status().as_u16()))
114 }
115 301 => {
116 let msg = format!(
117 "⚠️ The endpoint `{endpoint}` has been permanently moved. Try using `https://api.explorer.provable.com/v1` in your `.env` file or via the `--endpoint` flag."
118 );
119 Err(crate::errors::broadcast_error(msg).into())
120 }
121 _ => {
122 let code = response.status();
123 let response_body = response.body_mut().read_to_string().unwrap_or_default();
124
125 let action = match transaction {
126 Transaction::Deploy(..) => format!("deploy '{}'", operation.bold()),
127 Transaction::Execute(..) => format!("broadcast execution '{}'", operation.bold()),
128 Transaction::Fee(..) => format!("broadcast fee '{}'", operation.bold()),
129 };
130
131 let msg = format!(
132 " Failed to {action}\n Endpoint: {endpoint}\n Status: {code}\n Response: {response_body}"
133 );
134
135 Err(crate::errors::broadcast_error(msg).into())
136 }
137 }
138}
139
140pub fn load_latest_programs_from_network<N: Network>(
142 context: &Context,
143 program_id: ProgramID<N>,
144 network: NetworkName,
145 endpoint: &str,
146 network_retries: u32,
147) -> Result<Vec<(Program<N>, Option<u16>)>> {
148 use snarkvm::prelude::Program;
149 use std::collections::HashSet;
150
151 let mut programs = HashMap::new();
153 let mut ordered_programs = IndexSet::new();
155 let mut stack = vec![(program_id, false)];
157
158 while let Some((current_id, seen)) = stack.pop() {
160 if seen {
163 ordered_programs.insert(current_id);
164 }
165 else {
167 if programs.contains_key(¤t_id) {
169 continue;
170 }
171 let program = leo_package::CompilationUnit::fetch(
173 Symbol::intern(¤t_id.name().to_string()),
174 None,
175 &context.home()?,
176 network,
177 endpoint,
178 true,
179 network_retries,
180 )
181 .map_err(|_| crate::errors::custom(format!("Failed to fetch program source for ID: {current_id}")))?;
182 let ProgramData::Bytecode(program_src) = program.data else {
183 panic!("Expected bytecode when fetching a remote program");
184 };
185
186 let bytecode = Program::<N>::from_str(&program_src)
188 .map_err(|_| crate::errors::custom(format!("Failed to parse program source for ID: {current_id}")))?;
189
190 let imports = bytecode.imports().keys().cloned().collect::<HashSet<_>>();
192
193 programs.insert(current_id, (bytecode, program.edition));
195
196 stack.push((current_id, true));
198
199 for import_id in imports {
201 stack.push((import_id, false));
202 }
203 }
204 }
205
206 Ok(ordered_programs
208 .iter()
209 .map(|program_id| programs.remove(program_id).expect("Program not found in cache"))
210 .collect())
211}