leo_lang/cli/commands/common/
query.rs1use super::*;
18use crate::cli::query::{LeoBlock, LeoProgram};
19
20use leo_ast::NetworkName;
21use leo_package::ProgramData;
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)?)?;
50 public_balance.truncate(public_balance.len() - 3);
52 public_balance.parse::<u64>().map_err(|_| CliError::invalid_balance(address).into())
54}
55
56#[allow(dead_code)]
58pub fn get_latest_block_height(endpoint: &str, network: NetworkName, context: &Context) -> Result<u32> {
59 let height = LeoQuery {
61 env_override: EnvOptions { endpoint: Some(endpoint.to_string()), network: Some(network), ..Default::default() },
62 command: QueryCommands::Block {
63 command: LeoBlock {
64 id: None,
65 latest: false,
66 latest_hash: false,
67 latest_height: true,
68 range: None,
69 transactions: false,
70 to_height: false,
71 },
72 },
73 }
74 .execute(Context::new(context.path.clone(), context.home.clone(), true)?)?;
75 let height = height.parse::<u32>().map_err(CliError::string_parse_error)?;
77 Ok(height)
78}
79
80pub fn handle_broadcast<N: Network>(
84 endpoint: &str,
85 transaction: &Transaction<N>,
86 operation: &str,
87) -> Result<(String, u16)> {
88 let mut response = ureq::Agent::config_builder()
90 .max_redirects(0)
91 .build()
92 .new_agent()
93 .post(endpoint)
94 .query("check_transaction", "true")
95 .header("X-Leo-Version", env!("CARGO_PKG_VERSION"))
96 .send_json(transaction)
97 .map_err(|err| CliError::broadcast_error(err.to_string()))?;
98 match response.status().as_u16() {
99 200..=299 => {
100 println!(
101 "✉️ Broadcasted transaction with:\n - transaction ID: '{}'",
102 transaction.id().to_string().bold().yellow(),
103 );
104 if let Some(fee) = transaction.fee_transition() {
105 println!(" - fee ID: '{}'", fee.id().to_string().bold().yellow());
107 println!(" - fee transaction ID: '{}'", Transaction::from_fee(fee)?.id().to_string().bold().yellow());
109 println!(" (use this to check for rejected transactions)\n");
110 }
111 Ok((response.body_mut().read_to_string().unwrap(), response.status().as_u16()))
112 }
113 301 => {
114 let msg = format!(
115 "⚠️ The endpoint `{endpoint}` has been permanently moved. Try using `https://api.explorer.provable.com/v1` in your `.env` file or via the `--endpoint` flag."
116 );
117 Err(CliError::broadcast_error(msg).into())
118 }
119 _ => {
120 let code = response.status();
121 let error_message = match response.body_mut().read_to_string() {
122 Ok(response) => format!("(status code {code}: {response:?})"),
123 Err(err) => format!("({err})"),
124 };
125
126 let msg = match transaction {
127 Transaction::Deploy(..) => {
128 format!("❌ Failed to deploy '{}' to {}: {}", operation.bold(), &endpoint, error_message)
129 }
130 Transaction::Execute(..) => {
131 format!(
132 "❌ Failed to broadcast execution '{}' to {}: {}",
133 operation.bold(),
134 &endpoint,
135 error_message
136 )
137 }
138 Transaction::Fee(..) => {
139 format!("❌ Failed to broadcast fee '{}' to {}: {}", operation.bold(), &endpoint, error_message)
140 }
141 };
142
143 Err(CliError::broadcast_error(msg).into())
144 }
145 }
146}
147
148pub fn load_latest_programs_from_network<N: Network>(
150 context: &Context,
151 program_id: ProgramID<N>,
152 network: NetworkName,
153 endpoint: &str,
154) -> Result<Vec<(Program<N>, Option<u16>)>> {
155 use snarkvm::prelude::Program;
156 use std::collections::HashSet;
157
158 let mut programs = HashMap::new();
160 let mut ordered_programs = IndexSet::new();
162 let mut stack = vec![(program_id, false)];
164
165 while let Some((current_id, seen)) = stack.pop() {
167 if seen {
170 ordered_programs.insert(current_id);
171 }
172 else {
174 if programs.contains_key(¤t_id) {
176 continue;
177 }
178 let program = leo_package::Program::fetch(
180 Symbol::intern(¤t_id.name().to_string()),
181 None,
182 &context.home()?,
183 network,
184 endpoint,
185 true,
186 )
187 .map_err(|_| CliError::custom(format!("Failed to fetch program source for ID: {current_id}")))?;
188 let ProgramData::Bytecode(program_src) = program.data else {
189 panic!("Expected bytecode when fetching a remote program");
190 };
191
192 let bytecode = Program::<N>::from_str(&program_src)
194 .map_err(|_| CliError::custom(format!("Failed to parse program source for ID: {current_id}")))?;
195
196 let imports = bytecode.imports().keys().cloned().collect::<HashSet<_>>();
198
199 programs.insert(current_id, (bytecode, program.edition));
201
202 stack.push((current_id, true));
204
205 for import_id in imports {
207 stack.push((import_id, false));
208 }
209 }
210 }
211
212 Ok(ordered_programs
214 .iter()
215 .map(|program_id| programs.remove(program_id).expect("Program not found in cache"))
216 .collect())
217}