1use crate::generator_interpreter::generate_interpreter_impl;
2use crate::signature::{FunctionBinding, SimplifiedBindings};
3use crate::types::get_rust_type;
4use convert_case::{Case::Pascal, Casing};
5use itertools::Itertools;
6use proc_macro2::{Ident, Literal, Span, TokenStream};
7use quote::quote;
8
9pub fn generate_program_module(simplified: &SimplifiedBindings) -> TokenStream {
10 let program_name_pascal = simplified.program_name.to_case(Pascal);
11
12 let program_module = Ident::new(&simplified.program_name, Span::call_site());
13 let program_trait = Ident::new(&format!("{}Aleo", program_name_pascal), Span::call_site());
14 let program_struct = Ident::new(
15 &format!("{}Network", program_name_pascal),
16 Span::call_site(),
17 );
18
19 let network_aliases = generate_network_aliases(&program_name_pascal, &program_struct);
20
21 let records = generate_records(&simplified.records);
22 let structs = generate_structs(&simplified.structs);
23
24 let function_types = generate_function_types(&simplified.functions);
25 let mapping_types = generate_mapping_types(&simplified.mappings);
26
27 let trait_definition = generate_trait(&function_types, &mapping_types, &program_trait);
28
29 let network_impl = generate_network_impl(
30 simplified,
31 &function_types,
32 &mapping_types,
33 &program_trait,
34 &program_struct,
35 );
36
37 let interpreter_impl =
38 generate_interpreter_impl(simplified, &function_types, &mapping_types, &program_trait);
39
40 let type_imports = generate_type_imports(&simplified.imports);
41
42 quote! {
43 pub mod #program_module {
44 use leo_bindings::{anyhow, snarkvm, indexmap};
45 use anyhow::{anyhow, Result};
46 use snarkvm::prelude::*;
47 use snarkvm::prelude::Network;
48 use indexmap::IndexMap;
49 use leo_bindings::{ToValue, FromValue};
50 use leo_bindings::utils::Account;
51
52 #type_imports
53
54 #network_aliases
55
56 #(#structs)*
57
58 #(#records)*
59
60 #trait_definition
61
62 pub mod network {
66 use super::*;
67 #network_impl
68 }
69
70 #interpreter_impl
71 }
72 }
73}
74
75fn generate_trait(
76 function_types: &[FunctionTypes],
77 mapping_types: &[MappingTypes],
78 program_trait: &Ident,
79) -> TokenStream {
80 let function_signatures: Vec<TokenStream> = function_types
81 .iter()
82 .map(|types| {
83 let name = &types.name;
84 let input_params = &types.input_params;
85 let return_type = &types.return_type;
86 quote! { fn #name (&self, account: &Account<N>, #input_params) -> #return_type; }
87 })
88 .collect();
89
90 let mapping_signatures: Vec<TokenStream> = mapping_types
91 .iter()
92 .map(|types| {
93 let getter_name = &types.getter_name;
94 let key_type = &types.key_type;
95 let value_type = &types.value_type;
96 quote! { fn #getter_name(&self, key: #key_type) -> Option<#value_type>; }
97 })
98 .collect();
99
100 quote! {
101 pub trait #program_trait<N: snarkvm::prelude::Network> {
103 fn new(deployer: &Account<N>, endpoint: &str) -> Result<Self, anyhow::Error> where Self: Sized;
104 #(#function_signatures)*
105 #(#mapping_signatures)*
106 }
107 }
108}
109
110fn generate_network_impl(
111 simplified: &SimplifiedBindings,
112 function_types: &[FunctionTypes],
113 mapping_types: &[MappingTypes],
114 program_trait: &Ident,
115 program_struct: &Ident,
116) -> TokenStream {
117 let program_id = Literal::string(&format!("{}.aleo", &simplified.program_name));
118
119 let (deployment_calls, trait_imports, dependency_additions): (Vec<_>, Vec<_>, Vec<_>) = simplified
120 .imports
121 .iter()
122 .map(|import| {
123 let import_pascal = import.to_case(Pascal);
124 let import_module = Ident::new(import, Span::call_site());
125 let import_struct = Ident::new(&format!("{}Network", import_pascal), Span::call_site());
126 let import_trait = Ident::new(&format!("{}Aleo", import_pascal), Span::call_site());
127 let dependency_id = format!("{}.aleo", import);
128 let import_crate_name = Ident::new(&format!("{}_bindings", import), Span::call_site());
129
130 let deployment = quote! { #import_crate_name::#import_module::network::#import_struct::<N>::new(deployer, endpoint)?; };
131 let trait_import = quote! { use #import_crate_name::#import_module::#import_trait; };
132 let dependency_addition = quote! {
133 let dependency_id = ProgramID::<N>::from_str(#dependency_id)?;
134 let api_endpoint = format!("{}/v2", endpoint);
135 wait_for_program_availability(&dependency_id.to_string(), &api_endpoint, N::SHORT_NAME, 60).map_err(|e| anyhow!(e.to_string()))?;
136 let dependency_program: Program<N> = {
137 let mut response = ureq::get(&format!("{}/{}/program/{}", api_endpoint, N::SHORT_NAME, dependency_id)).call().unwrap();
138 let json_text = response.body_mut().read_to_string().unwrap();
139 let json_response: serde_json::Value = serde_json::from_str(&json_text).unwrap();
140 json_response.as_str().unwrap().to_string().parse().unwrap()
141 };
142 vm.process().write().add_program(&dependency_program)?;
143 };
144 (deployment, trait_import, dependency_addition)
145 })
146 .multiunzip();
147 let dependency_additions = quote! { #(#dependency_additions)* };
148
149 let function_implementations: Vec<TokenStream> = function_types
150 .iter()
151 .map(|types| generate_function(&dependency_additions, types, &program_id))
152 .collect();
153
154 let mapping_implementations: Vec<TokenStream> = mapping_types
155 .iter()
156 .map(|types| generate_mapping(types, &program_id))
157 .collect();
158
159 let new_implementation = generate_new(
160 &deployment_calls,
161 &dependency_additions,
162 &trait_imports,
163 &simplified.program_name,
164 );
165
166 quote! {
167 use leo_bindings::{serde_json, leo_package, leo_ast, leo_span, aleo_std, http, ureq, rand, print_execution_stats, print_deployment_stats};
168 use leo_bindings::utils::*;
169 use anyhow::ensure;
170 use snarkvm::ledger::query::*;
171 use snarkvm::ledger::store::helpers::memory::{ConsensusMemory, BlockMemory};
172 use snarkvm::ledger::store::ConsensusStore;
173 use snarkvm::ledger::block::Transaction;
174 use snarkvm::console::program::{Record, Plaintext};
175 use snarkvm::synthesizer::VM;
176 use snarkvm::synthesizer::process::execution_cost;
177 use snarkvm::prelude::ConsensusVersion;
178 use snarkvm::ledger::query::{QueryTrait, Query};
179 use leo_package::Package;
180 use leo_ast::NetworkName;
181 use leo_span::create_session_if_not_set_then;
182 use aleo_std::StorageMode;
183 use std::str::FromStr;
184
185 #[derive(Debug)]
186 pub struct #program_struct<N: Network> {
187 pub package: Package,
188 pub endpoint: String,
189 pub delegated_proving_config: Option<leo_bindings::DelegatedProvingConfig>,
190 _network: std::marker::PhantomData<N>,
191 }
192
193 impl<N: Network> #program_trait<N> for #program_struct<N> {
194 #new_implementation
195
196 #(#function_implementations)*
197
198 #(#mapping_implementations)*
199 }
200
201 impl<N: Network> #program_struct<N> {
202 pub fn configure_delegation(mut self, config: leo_bindings::DelegatedProvingConfig) -> Self {
203 self.delegated_proving_config = Some(config);
204 self
205 }
206 pub fn enable_delegation(mut self) -> Self {
207 if let Some(config) = &mut self.delegated_proving_config {
208 config.enabled = true;
209 log::info!("â
Delegated proving enabled");
210 }
211 self
212 }
213 pub fn disable_delegation(mut self) -> Self {
214 if let Some(config) = &mut self.delegated_proving_config {
215 config.enabled = false;
216 log::info!("âšī¸ Delegated proving disabled");
217 }
218 self
219 }
220 }
221 }
222}
223
224pub fn generate_records(records: &[crate::signature::StructBinding]) -> Vec<TokenStream> {
225 records.iter().map(|record| {
226 let record_name = Ident::new(&record.name.to_case(Pascal), Span::call_site());
227 let member_definitions = record.members.iter().map(|member| {
228 let member_name = Ident::new(&member.name, Span::call_site());
229 if member.name == "owner" {
230 quote! { #member_name: Owner<N, Plaintext<N>> }
231 } else {
232 let member_type = get_rust_type(&member.type_name);
233 quote! { #member_name: #member_type }
234 }
235 });
236 let extra_record_fields = quote! { __nonce: Group<N>, __version: U8<N> };
237
238 let member_conversions = record.members.iter().filter(|member| member.name != "owner").map(|member| {
239 let member_name = Ident::new(&member.name, Span::call_site());
240 let mode = &member.mode;
241
242 let entry_creation = match mode.to_lowercase().as_str() {
243 "public" => quote! { Entry::Public(plaintext_value) },
244 "private" | "none" => quote! { Entry::Private(plaintext_value) },
245 _ => panic!("Unsupported mode '{}' for field '{}'. Only 'Private' and 'Public' modes are supported.", mode, member.name),
246 };
247
248 quote! {
249 (
250 Identifier::try_from(stringify!(#member_name)).unwrap(),
251 {
252 let plaintext_value = match self.#member_name.to_value() {
253 Value::Plaintext(p) => p,
254 _ => panic!("Expected plaintext value from record member"),
255 };
256 #entry_creation
257 }
258 )
259 }
260 });
261
262 let (member_extractions, struct_member_extractions): (Vec<_>, Vec<_>) = record.members
263 .iter()
264 .map(|member| {
265 let member_name = Ident::new(&member.name, Span::call_site());
266 let member_type = get_rust_type(&member.type_name);
267 let field_name = &member.name;
268
269 let record_extraction = if field_name == "owner" {
270 quote! {
271 let #member_name = record.owner().clone();
272 }
273 } else {
274 quote! {
275 let #member_name = {
276 let member_id = &Identifier::try_from(#field_name).unwrap();
277 let entry = record.data().get(member_id)
278 .expect(&format!("Field '{}' not found in record data", #field_name));
279 let plaintext = match entry {
280 Entry::Public(p) | Entry::Private(p) | Entry::Constant(p) => p,
281 };
282 let value = Value::Plaintext(plaintext.clone());
283 <#member_type>::from_value(value)
284 };
285 }
286 };
287
288 let struct_extraction = if field_name == "owner" {
290 quote! {
291 let #member_name = {
292 let member_id = &Identifier::try_from(#field_name).unwrap();
293 let plaintext = struct_members.get(member_id)
294 .expect("Owner field not found in record struct");
295 match plaintext {
296 Plaintext::Literal(Literal::Address(addr), _) => Owner::Public(*addr),
297 _ => panic!("Expected address for owner field"),
298 }
299 };
300 }
301 } else {
302 quote! {
303 let #member_name = {
304 let member_id = &Identifier::try_from(#field_name).unwrap();
305 let plaintext = struct_members.get(member_id)
306 .expect(&format!("Field '{}' not found in record data", #field_name));
307 <#member_type>::from_value(Value::Plaintext(plaintext.clone()))
308 };
309 }
310 };
311
312 (record_extraction, struct_extraction)
313 })
314 .unzip();
315
316 let member_names: Vec<_> = record.members.iter().map(|member| {
317 Ident::new(&member.name, Span::call_site())
318 }).collect();
319 let extra_member_inits = quote! { __nonce: record.nonce().clone(), __version: record.version().clone() };
320
321 let getter_methods = record.members.iter().map(|member| {
322 let member_name = Ident::new(&member.name, Span::call_site());
323
324 if member.name == "owner" {
325 quote! {
326 pub fn #member_name(&self) -> Address<N> {
327 match &self.#member_name {
328 Owner::Public(addr) => *addr,
329 Owner::Private(plaintext) => {
330 match plaintext {
331 Plaintext::Literal(Literal::Address(addr), _) => *addr,
332 _ => panic!("Expected address in private owner field"),
333 }
334 }
335 }
336 }
337 }
338 } else {
339 let member_type = get_rust_type(&member.type_name);
340 quote! {
341 pub fn #member_name(&self) -> &#member_type {
342 &self.#member_name
343 }
344 }
345 }
346 });
347
348 quote! {
349 #[derive(Debug, Clone)]
351 pub struct #record_name<N: Network> {
352 #(#member_definitions),*,
353 #extra_record_fields
354 }
355
356 impl<N: Network> ToValue<N> for #record_name<N> {
358 fn to_value(&self) -> Value<N> {
359 match self.to_record() {
360 Ok(rec) => Value::Record(rec),
361 Err(e) => panic!("Failed to convert to Record: {}", e),
362 }
363 }
364 }
365
366 impl<N: Network> FromValue<N> for #record_name<N> {
368 fn from_value(value: Value<N>) -> Self {
369 match value {
370 Value::Record(record) => {
371 #(#member_extractions)*
372 Self {
373 #(#member_names),*,
374 #extra_member_inits
375 }
376 },
377 Value::Plaintext(Plaintext::Struct(struct_members, _)) => {
379 #(#struct_member_extractions)*
380
381 Self {
382 #(#member_names),*,
383 __nonce: Group::zero(),
384 __version: U8::new(0)
385 }
386 },
387 _ => panic!("Expected record or struct value"),
388 }
389 }
390 }
391
392 impl<N: Network> #record_name<N> {
393 pub fn to_record(&self) -> Result<Record<N, Plaintext<N>>, anyhow::Error> {
395 let data = IndexMap::from([
396 #(#member_conversions),*
397 ]);
398 let owner = self.owner.clone();
399 let nonce = self.__nonce.clone();
400 let version = self.__version.clone();
401
402 Record::<N, Plaintext<N>>::from_plaintext(
403 owner,
404 data,
405 nonce,
406 version
407 ).map_err(|e| anyhow::anyhow!("Failed to create record: {}", e))
408 }
409
410 #(#getter_methods)*
411 }
412 }
413 }).collect()
414}
415
416pub fn generate_structs(structs: &[crate::signature::StructBinding]) -> Vec<TokenStream> {
417 structs
418 .iter()
419 .map(|struct_def| {
420 let struct_name = Ident::new(&struct_def.name.to_case(Pascal), Span::call_site());
421 let (definitions, extractions, names, constructor_definitions, conversions): (Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>) = struct_def
422 .members
423 .iter()
424 .map(|member| {
425 let member_name = Ident::new(&member.name, Span::call_site());
426 let member_type = get_rust_type(&member.type_name);
427
428 let definition = quote! { pub #member_name: #member_type, };
429
430 let extraction = quote! {
431 let #member_name = {
432 let member_id = &Identifier::try_from(stringify!(#member_name)).unwrap();
433 let entry = struct_members.get(member_id).unwrap();
434 <#member_type>::from_value(Value::Plaintext(entry.clone()))
435 };
436 };
437
438 let name = member_name.clone();
439
440 let constructor_definition = quote! { #member_name: #member_type, };
441
442 let conversion = quote! {
443 (
444 Identifier::try_from(stringify!(#member_name)).unwrap(),
445 match self.#member_name.to_value() {
446 Value::Plaintext(p) => p,
447 _ => panic!("Expected plaintext value"),
448 }
449 )
450 };
451
452 (definition, extraction, name, constructor_definition, conversion)
453 })
454 .multiunzip();
455
456 quote! {
457 #[derive(Debug, Clone, Copy)]
459 pub struct #struct_name<N: Network> {
460 #(#definitions)*
461 _network: std::marker::PhantomData<N>
462 }
463
464 impl<N: Network> ToValue<N> for #struct_name<N> {
466 fn to_value(&self) -> Value<N> {
467 let members = IndexMap::from([
468 #(#conversions),*
469 ]);
470 Value::Plaintext(Plaintext::Struct(members, std::sync::OnceLock::new()))
471 }
472 }
473
474 impl<N: Network> FromValue<N> for #struct_name<N> {
476 fn from_value(value: Value<N>) -> Self {
477 match value {
478 Value::Plaintext(Plaintext::Struct(struct_members, _)) => {
479 #(#extractions)*
480 Self {
481 #(#names,)*
482 _network: std::marker::PhantomData
483 }
484 },
485 _ => panic!("Expected struct type"),
486 }
487 }
488 }
489
490 impl<N: Network> #struct_name<N> {
491 pub fn new(#(#constructor_definitions)*) -> Self {
492 Self {
493 #(#names,)*
494 _network: std::marker::PhantomData
495 }
496 }
497 }
498 }
499 })
500 .collect()
501}
502
503pub(crate) struct FunctionTypes {
504 pub(crate) name: Ident,
505 pub(crate) input_params: TokenStream,
506 pub(crate) input_conversions: TokenStream,
507 pub(crate) return_type: TokenStream,
508 pub(crate) return_conversions: TokenStream,
509}
510
511pub(crate) struct MappingTypes {
512 pub(crate) getter_name: Ident,
513 pub(crate) mapping_name_literal: String,
514 pub(crate) key_type: TokenStream,
515 pub(crate) value_type: TokenStream,
516}
517
518fn generate_function_types(functions: &[FunctionBinding]) -> Vec<FunctionTypes> {
519 functions.iter().map(|function| {
520 let name = Ident::new(&function.name, Span::call_site());
521
522 let (input_params, input_conversions): (Vec<_>, Vec<_>) = function.inputs.iter().map(|input| {
523 let param_name = Ident::new(&input.name, Span::call_site());
524 let param_type = get_rust_type(&input.type_name);
525 let param = quote! { #param_name: #param_type };
526 let conversion = quote! { (#param_name).to_value() };
527 (param, conversion)
528 }).unzip();
529 let input_params = quote! { #(#input_params),* };
530 let input_conversions = quote! { #(#input_conversions),* };
531
532 let (return_type, return_conversions) = match function.outputs.len() {
533 0 => (
534 quote! { Result<(), anyhow::Error> },
535 quote! { Ok(()) }
536 ),
537 1 => {
538 let output_type = get_rust_type(&function.outputs[0].type_name);
539 let conversion = quote! {
540 match function_outputs.get(0) {
541 Some(snarkvm_value) => <#output_type>::from_value(snarkvm_value.clone()),
542 None => return Err(anyhow!("Missing output at index 0")),
543 }
544 };
545 (
546 quote! { Result<#output_type, anyhow::Error> },
547 quote! { Ok(#conversion) }
548 )
549 },
550 _ => {
551 let (output_types, output_conversions): (Vec<_>, Vec<_>) = function.outputs.iter()
552 .enumerate()
553 .map(|(i, output)| {
554 let output_type = get_rust_type(&output.type_name);
555 let conversion = quote! {
556 match function_outputs.get(#i) {
557 Some(snarkvm_value) => <#output_type>::from_value(snarkvm_value.clone()),
558 None => return Err(anyhow!("Missing output at index {}", #i)),
559 }
560 };
561 (output_type, conversion)
562 })
563 .unzip();
564 (
565 quote! { Result<(#(#output_types),*), anyhow::Error> },
566 quote! { Ok((#(#output_conversions),*)) }
567 )
568 }
569 };
570 FunctionTypes {
571 name,
572 input_params,
573 input_conversions,
574 return_type,
575 return_conversions,
576 }
577 }).collect()
578}
579
580fn generate_mapping_types(mappings: &[crate::signature::MappingBinding]) -> Vec<MappingTypes> {
581 mappings
582 .iter()
583 .map(|mapping| {
584 let getter_name = Ident::new(&format!("get_{}", mapping.name), Span::call_site());
585 let mapping_name_literal = mapping.name.clone();
586 let key_type = get_rust_type(&mapping.key_type);
587 let value_type = get_rust_type(&mapping.value_type);
588
589 MappingTypes {
590 getter_name,
591 mapping_name_literal,
592 key_type,
593 value_type,
594 }
595 })
596 .collect()
597}
598
599fn generate_new(
600 deployment_calls: &[TokenStream],
601 dependency_additions: &TokenStream,
602 trait_imports: &[TokenStream],
603 program_name: &str,
604) -> TokenStream {
605 quote! {
606 fn new(deployer: &Account<N>, endpoint: &str) -> Result<Self, anyhow::Error> {
607 use leo_package::Package;
608 use leo_span::create_session_if_not_set_then;
609 use std::path::Path;
610 #(#trait_imports)*
611
612 let result = create_session_if_not_set_then(|_| {
613 let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
614
615 let package = Package::from_directory(
616 crate_dir,
617 crate_dir,
618 false,
619 false,
620 Some(NetworkName::from_str(N::SHORT_NAME).unwrap()),
621 Some(endpoint),
622 )?;
623
624 let program_id = ProgramID::<N>::from_str(concat!(#program_name, ".aleo"))?;
625 let api_endpoint = format!("{}/v2", endpoint);
626
627 #(#deployment_calls)*
628
629 let program_exists = {
630 let check_response = ureq::get(&format!("{}/{}/program/{}", api_endpoint, N::SHORT_NAME, program_id))
631 .call();
632 match check_response {
633 Ok(_) => {
634 log::info!("â
Found '{}', skipping deployment", program_id);
635 true
636 },
637 Err(_) => {
638 log::info!("đĻ Deploying '{}'", program_id);
639 false
640 }
641 }
642 };
643
644 if !program_exists {
645 let program_symbol = leo_span::Symbol::intern(#program_name);
646 let target_program = package.programs.iter()
647 .find(|p| p.name == program_symbol)
648 .ok_or_else(|| anyhow!("Program '{}' not found in package", #program_name))?;
649
650 let bytecode = match &target_program.data {
651 leo_package::ProgramData::Bytecode(bytecode) => {
652 bytecode.clone()
653 },
654 leo_package::ProgramData::SourcePath { directory, source: _ } => {
655 let aleo_path = directory.join("build").join("main.aleo");
656 std::fs::read_to_string(&aleo_path)
657 .map_err(|e| anyhow!("Failed to read bytecode from {}: {}", aleo_path.display(), e))?
658 }
659 };
660
661 let program: Program<N> = bytecode.parse()
662 .map_err(|e| anyhow!("Failed to parse program: {}", e))?;
663
664 log::info!("đĻ Creating deployment tx for '{}'...", program_id);
665 let rng = &mut rand::thread_rng();
666 let vm = VM::from(ConsensusStore::<N, ConsensusMemory<N>>::open(StorageMode::Production)?)?;
667 let query = Query::<N, BlockMemory<N>>::from(endpoint.parse::<http::uri::Uri>()?);
668
669 #dependency_additions
670
671 let transaction = vm.deploy(
672 deployer.private_key(),
673 &program,
674 None,
675 0,
676 Some(&query),
677 rng,
678 ).map_err(|e| anyhow!("Failed to generate deployment transaction: {}", e))?;
679
680 match &transaction {
681 Transaction::Deploy(_, _, _, deployment, fee) => {
682 print_deployment_stats(&vm, &program_id.to_string(), deployment, None, ConsensusVersion::V10)?;
683 },
684 _ => panic!("Expected a deployment transaction."),
685 };
686
687 log::info!("đĄ Broadcasting deployment tx: {} to {}",transaction.id(), endpoint);
688
689 broadcast_transaction(transaction.clone(), &api_endpoint, N::SHORT_NAME)?;
690
691 wait_for_transaction_confirmation::<N>(&transaction.id(), &api_endpoint, N::SHORT_NAME, 120)?;
692 wait_for_program_availability(&program_id.to_string(), &api_endpoint, N::SHORT_NAME, 60).map_err(|e| anyhow!(e.to_string()))?;
693 }
694
695 Ok(Self {
696 package,
697 endpoint: endpoint.to_string(),
698 delegated_proving_config: None,
699 _network: std::marker::PhantomData,
700 })
701 });
702 result
703 }
704 }
705}
706
707fn generate_function(
708 dependency_additions: &TokenStream,
709 types: &FunctionTypes,
710 program_id: &Literal,
711) -> TokenStream {
712 let FunctionTypes {
713 name,
714 input_params,
715 input_conversions,
716 return_type,
717 return_conversions,
718 } = types;
719
720 quote! {
721 fn #name(&self, account: &Account<N>, #input_params) -> #return_type {
722 let endpoint = &self.endpoint;
723 let api_endpoint = format!("{}/v2", endpoint);
724 let program_id = ProgramID::try_from(#program_id).unwrap();
725 let function_id = Identifier::try_from(stringify!(#name)).unwrap();
726 let function_args: Vec<Value<N>> = vec![#input_conversions];
727
728 let rng = &mut rand::thread_rng();
729 let locator = Locator::<N>::new(program_id, function_id);
730
731 log::info!("Creating tx: {}.{}({})", #program_id, stringify!(#name), stringify!(#input_params));
732 let vm = VM::from(ConsensusStore::<N, ConsensusMemory<N>>::open(StorageMode::Production)?)?;
733 let query = Query::<N, BlockMemory<N>>::from(endpoint.parse::<http::uri::Uri>()?);
734
735 wait_for_program_availability(&program_id.to_string(), &api_endpoint, N::SHORT_NAME, 60).map_err(|e| anyhow!(e.to_string()))?;
736 let program: Program<N> = {
737 let mut response = ureq::get(&format!("{}/{}/program/{}", api_endpoint, N::SHORT_NAME, program_id))
738 .call().unwrap();
739 let json_text = response.body_mut().read_to_string().unwrap();
740 let json_response: serde_json::Value = serde_json::from_str(&json_text).unwrap();
741 json_response.as_str().unwrap().parse().unwrap()
742 };
743
744 #dependency_additions
745
746 vm.process().write().add_programs_with_editions(&vec![(program, 1u16)])
747 .map_err(|e| anyhow!("Failed to add program '{}' to VM: {}", program_id, e))?;
748
749 let delegated_result = self.delegated_proving_config.as_ref()
750 .filter(|config| config.enabled)
751 .and_then(|config| {
752 let authorization = vm
753 .authorize(account.private_key(), program_id, function_id, function_args.iter(), rng)
754 .map_err(|e| log::warn!("Failed to create authorization: {}", e))
755 .ok()?;
756
757 let function_outputs = extract_outputs_from_authorization(&authorization, account.view_key())
758 .map_err(|e| log::warn!("Failed to extract outputs: {}", e))
759 .ok()?;
760
761 let transaction = execute_with_delegated_proving(config, authorization).ok()?;
762
763 Some((transaction, function_outputs))
764 });
765
766 let (transaction, function_outputs): (Transaction<N>, Vec<Value<N>>) = match delegated_result {
767 Some(result) => result,
768 None => {
769 let (transaction, response) = vm.execute_with_response(
770 account.private_key(),
771 (program_id, function_id),
772 function_args.iter(),
773 None,
774 0,
775 Some(&query as &dyn QueryTrait<N>),
776 rng,
777 ).map_err(|e| anyhow!("Failed to execute function '{}' in program '{}': {}", function_id, program_id, e))?;
778 (transaction, response.outputs().to_vec())
779 }
780 };
781
782 let public_balance = get_public_balance(&account.address(), &api_endpoint, N::SHORT_NAME);
783 let execution = transaction.execution().ok_or_else(|| anyhow!("Missing execution"))?;
784 let (total_cost, _) = execution_cost(&vm.process().read(), execution, ConsensusVersion::V10)?;
785
786 match &transaction {
787 Transaction::Execute(_, _, execution, fee) => {
788 print_execution_stats(&vm, &program_id.to_string(), &execution, None, ConsensusVersion::V10)?;
789 },
790 _ => panic!("Expected an execution transaction."),
791 };
792
793 ensure!(public_balance >= total_cost,
794 "â Insufficient balance {} for total cost {} on `{}`", public_balance, total_cost, locator);
795
796 log::info!("đĄ Broadcasting tx: {}",transaction.id());
797 broadcast_transaction(transaction.clone(), &api_endpoint, N::SHORT_NAME)?;
798 wait_for_transaction_confirmation::<N>(&transaction.id(), &api_endpoint, N::SHORT_NAME, 30)?;
799
800 #return_conversions
801 }
802 }
803}
804
805fn generate_mapping(types: &MappingTypes, program_id: &Literal) -> TokenStream {
806 let MappingTypes {
807 getter_name,
808 mapping_name_literal,
809 key_type,
810 value_type,
811 } = types;
812
813 quote! {
814 fn #getter_name(&self, key: #key_type) -> Option<#value_type> {
815 let program_id = #program_id;
816 let mapping_name = #mapping_name_literal;
817 let api_endpoint = format!("{}/v2", self.endpoint);
818
819 let key_value: Value<N> = key.to_value();
820 let url = format!("{}/{}/program/{}/mapping/{}/{}",
821 &api_endpoint, N::SHORT_NAME, program_id, mapping_name,
822 key_value.to_string().replace("\"", ""));
823
824 match leo_bindings::utils::fetch_mapping_value(&url) {
825 Ok(Some(json_text)) => {
826 let value: Option<Value<N>> = serde_json::from_str(&json_text)
827 .expect("Failed to parse mapping value JSON");
828 value.map(|val| <#value_type>::from_value(val))
829 }
830 Ok(None) => None,
831 Err(e) => panic!("Failed to fetch mapping value: {}", e),
832 }
833 }
834 }
835}
836
837fn generate_network_aliases(program_name_pascal: &str, program_struct: &Ident) -> TokenStream {
838 let testnet_struct = Ident::new(
839 &format!("{}Testnet", program_name_pascal),
840 Span::call_site(),
841 );
842 let mainnet_struct = Ident::new(
843 &format!("{}Mainnet", program_name_pascal),
844 Span::call_site(),
845 );
846 let canary_struct = Ident::new(&format!("{}Canary", program_name_pascal), Span::call_site());
847 let interpreter_struct = Ident::new(
848 &format!("{}Interpreter", program_name_pascal),
849 Span::call_site(),
850 );
851
852 quote! {
853 pub type #testnet_struct = network::#program_struct<snarkvm::prelude::TestnetV0>;
854
855 pub type #mainnet_struct = network::#program_struct<snarkvm::prelude::MainnetV0>;
856
857 pub type #canary_struct = network::#program_struct<snarkvm::prelude::CanaryV0>;
858
859 pub type #interpreter_struct = interpreter::#interpreter_struct<snarkvm::prelude::TestnetV0>;
860 }
861}
862
863fn generate_type_imports(imports: &[String]) -> TokenStream {
864 let import_statements: Vec<TokenStream> = imports
865 .iter()
866 .map(|import| {
867 let import_crate_name = Ident::new(&format!("{}_bindings", import), Span::call_site());
868 let import_module = Ident::new(import, Span::call_site());
869 quote! {
870 pub use #import_crate_name::#import_module::*;
871 }
872 })
873 .collect();
874
875 quote! {
876 #(#import_statements)*
877 }
878}