1use serde::Serialize;
20
21use indexmap::IndexMap;
22use snarkvm::prelude::{Network, ProgramID, block::Transaction};
23use std::fmt;
24
25#[derive(Serialize, Default)]
27pub struct BuildOutput {
28 pub program: String,
29 pub program_checksum: Vec<u8>,
30 pub function_checksums: IndexMap<String, Vec<u8>>,
31}
32
33pub(crate) fn microcredits_to_credits(microcredits: u64) -> f64 {
35 microcredits as f64 / 1_000_000.0
36}
37
38#[derive(Serialize, Clone, Default)]
40pub struct FunctionCostStats {
41 pub name: String,
42 pub finalize_cost: u64,
43 #[serde(skip_serializing_if = "Option::is_none")]
46 pub storage_cost: Option<u64>,
47 #[serde(skip_serializing_if = "Option::is_none")]
50 pub execution_cost: Option<u64>,
51 #[serde(skip_serializing_if = "std::ops::Not::not")]
53 pub has_dynamic_calls: bool,
54}
55
56#[derive(Serialize, Clone, Default)]
58pub struct DeploymentStats {
59 pub program_size_bytes: usize,
60 pub max_program_size_bytes: usize,
61 #[serde(skip_serializing_if = "Option::is_none")]
64 pub total_variables: Option<u64>,
65 #[serde(skip_serializing_if = "Option::is_none")]
68 pub total_constraints: Option<u64>,
69 #[serde(skip_serializing_if = "Option::is_none")]
72 pub max_variables: Option<u64>,
73 #[serde(skip_serializing_if = "Option::is_none")]
76 pub max_constraints: Option<u64>,
77 pub storage_cost: u64,
78 pub namespace_cost: u64,
79 pub synthesis_cost: u64,
80 pub constructor_cost: u64,
81 pub priority_fee: u64,
82 pub total_cost: u64,
83 #[serde(skip_serializing_if = "Vec::is_empty")]
84 pub function_costs: Vec<FunctionCostStats>,
85}
86
87impl fmt::Display for DeploymentStats {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 use colored::*;
90 use num_format::{Locale, ToFormattedString};
91
92 writeln!(
93 f,
94 " {:22}{:.2} KB / {:.2} KB",
95 "Program Size:".cyan(),
96 self.program_size_bytes as f64 / 1024.0,
97 self.max_program_size_bytes as f64 / 1024.0
98 )?;
99 if let Some(total_variables) = self.total_variables {
100 writeln!(
101 f,
102 " {:22}{}",
103 "Total Variables:".cyan(),
104 total_variables.to_formatted_string(&Locale::en).yellow()
105 )?;
106 }
107 if let Some(total_constraints) = self.total_constraints {
108 writeln!(
109 f,
110 " {:22}{}",
111 "Total Constraints:".cyan(),
112 total_constraints.to_formatted_string(&Locale::en).yellow()
113 )?;
114 }
115 if let Some(max_variables) = self.max_variables {
116 writeln!(f, " {:22}{}", "Max Variables:".cyan(), max_variables.to_formatted_string(&Locale::en).green())?;
117 }
118 if let Some(max_constraints) = self.max_constraints {
119 writeln!(
120 f,
121 " {:22}{}",
122 "Max Constraints:".cyan(),
123 max_constraints.to_formatted_string(&Locale::en).green()
124 )?;
125 }
126 if self.total_variables.is_some() || self.total_constraints.is_some() {
127 writeln!(f)?;
128 }
129 writeln!(f, "{}", "💰 Cost Breakdown (credits)".bold())?;
130 writeln!(f, " {:22}{:.6}", "Transaction Storage:".cyan(), microcredits_to_credits(self.storage_cost))?;
131 writeln!(f, " {:22}{:.6}", "Program Synthesis:".cyan(), microcredits_to_credits(self.synthesis_cost))?;
132 writeln!(f, " {:22}{:.6}", "Namespace:".cyan(), microcredits_to_credits(self.namespace_cost))?;
133 writeln!(f, " {:22}{:.6}", "Constructor:".cyan(), microcredits_to_credits(self.constructor_cost))?;
134 writeln!(f, " {:22}{:.6}", "Priority Fee:".cyan(), microcredits_to_credits(self.priority_fee))?;
135 writeln!(f, " {:22}{:.6}", "Total Fee:".cyan(), microcredits_to_credits(self.total_cost))?;
136
137 for fc in &self.function_costs {
138 writeln!(f, "{}", format!(" Function '{}'", fc.name).bold())?;
139 let bound = if fc.has_dynamic_calls { " (lower bound)" } else { "" };
140 if let Some(execution_cost) = fc.execution_cost {
141 writeln!(
142 f,
143 " {:24}{:.6}{}",
144 "Total Execution Cost:".cyan(),
145 microcredits_to_credits(execution_cost),
146 bound.dimmed()
147 )?;
148 if let Some(storage_cost) = fc.storage_cost {
149 writeln!(
150 f,
151 " {:24}{:.6}{}",
152 "|- Storage Cost:".cyan(),
153 microcredits_to_credits(storage_cost),
154 bound.dimmed()
155 )?;
156 }
157 writeln!(
158 f,
159 " {:24}{:.6}{}",
160 "|- Finalize Cost:".cyan(),
161 microcredits_to_credits(fc.finalize_cost),
162 bound.dimmed()
163 )?;
164 } else {
165 writeln!(
166 f,
167 " {:24}{:.6} (lower bound)",
168 "Total Execution Cost:".cyan(),
169 microcredits_to_credits(fc.finalize_cost),
170 )?;
171 writeln!(f, " {:24}{}", "|- Storage Cost:".cyan(), "N/A (dynamic call)".dimmed())?;
172 writeln!(
173 f,
174 " {:24}{:.6}{}",
175 "|- Finalize Cost:".cyan(),
176 microcredits_to_credits(fc.finalize_cost),
177 bound.dimmed()
178 )?;
179 }
180 }
181 Ok(())
182 }
183}
184
185#[derive(Serialize, Clone, Default)]
187pub struct ExecutionStats {
188 pub storage_cost: u64,
189 pub execution_cost: u64,
190 pub priority_fee: u64,
191 pub total_cost: u64,
192}
193
194impl fmt::Display for ExecutionStats {
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 use colored::*;
197 writeln!(f, "{}", "💰 Cost Breakdown (credits)".bold())?;
198 writeln!(f, " {:22}{:.6}", "Transaction Storage:".cyan(), microcredits_to_credits(self.storage_cost))?;
199 writeln!(f, " {:22}{:.6}", "On-chain Execution:".cyan(), microcredits_to_credits(self.execution_cost))?;
200 writeln!(f, " {:22}{:.6}", "Priority Fee:".cyan(), microcredits_to_credits(self.priority_fee))?;
201 writeln!(f, " {:22}{:.6}", "Total Fee:".cyan(), microcredits_to_credits(self.total_cost))
202 }
203}
204
205#[derive(Serialize, Clone, Default)]
207pub struct BroadcastStats {
208 pub fee_id: String,
209 pub fee_transaction_id: String,
210 pub confirmed: bool,
211}
212
213#[derive(Serialize, Clone, Default)]
215pub struct Config {
216 pub address: String,
217 pub network: String,
218 #[serde(skip_serializing_if = "Option::is_none")]
219 pub endpoint: Option<String>,
220 #[serde(skip_serializing_if = "Option::is_none")]
221 pub consensus_version: Option<u8>,
222}
223
224#[derive(Serialize, Default)]
226pub struct DeployOutput {
227 #[serde(skip_serializing_if = "Option::is_none")]
228 pub config: Option<Config>,
229 pub deployments: Vec<DeployedProgram>,
230}
231
232#[derive(Serialize)]
234pub struct DeployedProgram {
235 pub program_id: String,
236 pub transaction_id: String,
237 #[serde(skip_serializing_if = "Option::is_none")]
238 pub stats: Option<DeploymentStats>,
239 #[serde(skip_serializing_if = "Option::is_none")]
240 pub broadcast: Option<BroadcastStats>,
241}
242
243#[derive(Serialize)]
245pub struct RunOutput {
246 pub program: String,
247 pub function: String,
248 pub outputs: Vec<String>,
249}
250
251#[derive(Serialize, Default)]
253pub struct ExecuteOutput {
254 #[serde(skip_serializing_if = "Option::is_none")]
255 pub config: Option<Config>,
256 pub program: String,
257 pub function: String,
258 pub outputs: Vec<String>,
259 pub transaction_id: String,
260 #[serde(skip_serializing_if = "Option::is_none")]
261 pub stats: Option<ExecutionStats>,
262 #[serde(skip_serializing_if = "Option::is_none")]
263 pub broadcast: Option<BroadcastStats>,
264}
265
266#[derive(Serialize, Default)]
268pub struct TestOutput {
269 pub passed: usize,
270 pub failed: usize,
271 pub tests: Vec<TestResult>,
272}
273
274#[derive(Serialize)]
276pub struct TestResult {
277 pub name: String,
278 pub passed: bool,
279 #[serde(skip_serializing_if = "Option::is_none")]
280 pub error: Option<String>,
281}
282
283#[derive(Serialize, Default)]
285pub struct SynthesizeOutput {
286 pub program: String,
287 pub functions: Vec<SynthesizedFunction>,
288}
289
290#[derive(Serialize, Clone, Default)]
292pub struct Metadata {
293 pub prover_checksum: String,
294 pub prover_size: usize,
295 pub verifier_checksum: String,
296 pub verifier_size: usize,
297}
298
299#[derive(Serialize, Clone, Default)]
301pub struct CircuitInfo {
302 pub num_public_inputs: u64,
303 pub num_variables: u64,
304 pub num_constraints: u64,
305 pub num_non_zero_a: u64,
306 pub num_non_zero_b: u64,
307 pub num_non_zero_c: u64,
308 pub circuit_id: String,
309}
310
311#[derive(Serialize)]
313pub struct SynthesizedFunction {
314 pub name: String,
315 pub circuit_info: CircuitInfo,
316 #[serde(flatten)]
317 pub metadata: Metadata,
318}
319
320pub fn build_deploy_output<N: Network>(
322 config: Option<Config>,
323 transactions: &[(ProgramID<N>, Transaction<N>)],
324 stats: &[DeploymentStats],
325 broadcasts: &[BroadcastStats],
326) -> DeployOutput {
327 DeployOutput {
328 config,
329 deployments: transactions
330 .iter()
331 .zip(stats.iter().map(Some).chain(std::iter::repeat(None)))
332 .zip(broadcasts.iter().map(Some).chain(std::iter::repeat(None)))
333 .map(|(((program_id, transaction), stats), broadcast)| DeployedProgram {
334 program_id: program_id.to_string(),
335 transaction_id: transaction.id().to_string(),
336 stats: stats.cloned(),
337 broadcast: broadcast.cloned(),
338 })
339 .collect(),
340 }
341}