leo_lang/cli/commands/common/
query.rs1use crate::cli::query::{LeoBlock, LeoProgram};
18use indexmap::IndexMap;
19use snarkvm::prelude::{Program, ProgramID};
20
21use super::*;
22
23use leo_package::{NetworkName, ProgramData};
24use leo_span::Symbol;
25use ureq::Response;
26
27pub fn get_public_balance<N: Network>(
29 private_key: &PrivateKey<N>,
30 endpoint: &str,
31 network: &str,
32 context: &Context,
33) -> Result<u64> {
34 let address = Address::<N>::try_from(ViewKey::try_from(private_key)?)?;
36 let mut public_balance = LeoQuery {
38 endpoint: Some(endpoint.to_string()),
39 network: Some(network.to_string()),
40 command: QueryCommands::Program {
41 command: LeoProgram {
42 name: "credits".to_string(),
43 mappings: false,
44 mapping_value: Some(vec!["account".to_string(), address.to_string()]),
45 },
46 },
47 }
48 .execute(Context::new(context.path.clone(), context.home.clone(), true)?)?;
49 public_balance.truncate(public_balance.len() - 3);
51 public_balance.parse::<u64>().map_err(|_| CliError::invalid_balance(address).into())
53}
54
55#[allow(dead_code)]
57pub fn get_latest_block_height(endpoint: &str, network: NetworkName, context: &Context) -> Result<u32> {
58 let height = LeoQuery {
60 endpoint: Some(endpoint.to_string()),
61 network: Some(network.to_string()),
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>(endpoint: &str, transaction: &Transaction<N>, operation: &str) -> Result<Response> {
82 let response = ureq::AgentBuilder::new()
84 .redirects(0)
85 .build()
86 .post(endpoint)
87 .set("X-Leo-Version", env!("CARGO_PKG_VERSION"))
88 .send_json(transaction)
89 .map_err(|err| CliError::broadcast_error(err.to_string()))?;
90 match response.status() {
91 200..=299 => {
92 println!(
93 "✉️ Broadcasted transaction with:\n - transaction ID: '{}'\n - fee ID: '{}'",
94 transaction.id().to_string().bold().yellow(),
95 transaction.fee_transition().expect("Expected a fee in transactions").id().to_string().bold().yellow()
96 );
97 Ok(response)
98 }
99 301 => {
100 let msg = format!(
101 "⚠️ The endpoint `{endpoint}` has been permanently moved. Try using `https://api.explorer.provable.com/v1` in your `.env` file or via the `--endpoint` flag."
102 );
103 Err(CliError::broadcast_error(msg).into())
104 }
105 _ => {
106 let code = response.status();
107 let error_message = match response.into_string() {
108 Ok(response) => format!("(status code {code}: {:?})", response),
109 Err(err) => format!("({err})"),
110 };
111
112 let msg = match transaction {
113 Transaction::Deploy(..) => {
114 format!("❌ Failed to deploy '{}' to {}: {}", operation.bold(), &endpoint, error_message)
115 }
116 Transaction::Execute(..) => {
117 format!(
118 "❌ Failed to broadcast execution '{}' to {}: {}",
119 operation.bold(),
120 &endpoint,
121 error_message
122 )
123 }
124 Transaction::Fee(..) => {
125 format!("❌ Failed to broadcast fee '{}' to {}: {}", operation.bold(), &endpoint, error_message)
126 }
127 };
128
129 Err(CliError::broadcast_error(msg).into())
130 }
131 }
132}
133
134pub fn load_programs_from_network<N: Network>(
136 context: &Context,
137 program_id: ProgramID<N>,
138 network: NetworkName,
139 endpoint: &str,
140) -> Result<Vec<(ProgramID<N>, Program<N>)>> {
141 use snarkvm::prelude::Program;
142 use std::collections::HashSet;
143
144 let mut visited = HashSet::new();
146 let mut programs = IndexMap::new();
148 let mut stack = vec![program_id];
150
151 while let Some(current_id) = stack.pop() {
153 if !visited.insert(current_id) {
155 continue;
156 }
157
158 let ProgramData::Bytecode(program_src) = leo_package::Program::fetch(
160 Symbol::intern(¤t_id.name().to_string()),
161 &context.home()?,
162 network,
163 endpoint,
164 true,
165 )
166 .map_err(|_| CliError::custom(format!("Failed to fetch program source for ID: {current_id}")))?
167 .data
168 else {
169 panic!("Expected bytecode when fetching a remote program");
170 };
171
172 let program = Program::<N>::from_str(&program_src)
174 .map_err(|_| CliError::custom(format!("Failed to parse program source for ID: {current_id}")))?;
175
176 for import_id in program.imports().keys() {
178 stack.push(*import_id);
179 }
180
181 programs.insert(current_id, program);
183 }
184
185 Ok(programs.into_iter().rev().collect())
187}