snarkos_cli/commands/developer/
transfer_private.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkOS library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use super::{DEFAULT_ENDPOINT, Developer};
17use crate::{
18    commands::StoreFormat,
19    helpers::args::{parse_private_key, prepare_endpoint},
20};
21use snarkvm::{
22    console::network::Network,
23    ledger::store::helpers::memory::BlockMemory,
24    prelude::{
25        Address,
26        Locator,
27        VM,
28        Value,
29        query::Query,
30        store::{ConsensusStore, helpers::memory::ConsensusMemory},
31    },
32};
33
34use aleo_std::StorageMode;
35use anyhow::Result;
36use clap::{Parser, builder::NonEmptyStringValueParser};
37use std::{path::PathBuf, str::FromStr};
38use ureq::http::Uri;
39use zeroize::Zeroize;
40
41/// Executes the `transfer_private` function in the `credits.aleo` program.
42#[derive(Debug, Parser)]
43#[clap(
44    group(clap::ArgGroup::new("mode").required(true).multiple(false))
45)]
46pub struct TransferPrivate {
47    /// The input record used to craft the transfer.
48    #[clap(long)]
49    input_record: String,
50    /// The recipient address.
51    #[clap(long)]
52    recipient: String,
53    /// The number of microcredits to transfer.
54    #[clap(long)]
55    amount: u64,
56    /// The private key used to generate the deployment.
57    #[clap(short = 'p', long, group = "key", value_parser=NonEmptyStringValueParser::default())]
58    private_key: Option<String>,
59    /// Specify the path to a file containing the account private key of the node
60    #[clap(long, group = "key", value_parser=NonEmptyStringValueParser::default())]
61    private_key_file: Option<String>,
62    /// Use a developer validator key tok generate the deployment.
63    #[clap(long, group = "key")]
64    dev_key: Option<u16>,
65    /// The endpoint to query node state from and broadcast to (if set to broadcast).
66    #[clap(short, long, default_value=DEFAULT_ENDPOINT)]
67    endpoint: Uri,
68    /// The priority fee in microcredits.
69    #[clap(long)]
70    priority_fee: u64,
71    /// The record to spend the fee from.
72    #[clap(long)]
73    fee_record: String,
74    /// Set the URL used to broadcast the transaction (if no value is given, the query endpoint is used).
75    #[clap(short, long, group = "mode")]
76    broadcast: Option<Option<Uri>>,
77    /// Performs a dry-run of transaction generation.
78    #[clap(short, long, group = "mode")]
79    dry_run: bool,
80    /// Store generated deployment transaction to a local file.
81    #[clap(long, group = "mode")]
82    store: Option<String>,
83    /// If --store is specified, the format in which the transaction should be stored : string or
84    /// bytes, by default : bytes.
85    #[clap(long, value_enum, default_value_t = StoreFormat::Bytes, requires="store")]
86    store_format: StoreFormat,
87    /// Wait for the transaction to be accepted by the network. Requires --broadcast.
88    #[clap(long, requires = "broadcast")]
89    wait: bool,
90    /// Timeout in seconds when waiting for transaction confirmation. Default is 60 seconds.
91    #[clap(long, default_value_t = 60, requires = "wait")]
92    timeout: u64,
93    /// Specify the path to a directory containing the ledger. Overrides the default path.
94    #[clap(long = "storage_path")]
95    storage_path: Option<PathBuf>,
96}
97
98impl Drop for TransferPrivate {
99    /// Zeroize the private key when the `TransferPrivate` struct goes out of scope.
100    fn drop(&mut self) {
101        self.private_key.zeroize();
102    }
103}
104
105impl TransferPrivate {
106    /// Creates an Aleo transfer with the provided inputs.
107    pub fn parse<N: Network>(self) -> Result<String> {
108        let endpoint = prepare_endpoint(self.endpoint.clone())?;
109
110        // Specify the query
111        let query = Query::<N, BlockMemory<N>>::from(endpoint.clone());
112
113        // Retrieve the recipient.
114        let recipient = Address::<N>::from_str(&self.recipient)?;
115
116        // Retrieve the private key.
117        let private_key = parse_private_key(self.private_key.clone(), self.private_key_file.clone(), self.dev_key)?;
118        println!("📦 Creating private transfer of {} microcredits to {}...\n", self.amount, recipient);
119
120        // Generate the transfer_private transaction.
121        let transaction = {
122            // Initialize an RNG.
123            let rng = &mut rand::thread_rng();
124
125            // Initialize the storage.
126            let storage_mode = match &self.storage_path {
127                Some(path) => StorageMode::Custom(path.clone()),
128                None => StorageMode::Production,
129            };
130            let store = ConsensusStore::<N, ConsensusMemory<N>>::open(storage_mode)?;
131
132            // Initialize the VM.
133            let vm = VM::from(store)?;
134
135            // Prepare the fee.
136            let fee_record = Developer::parse_record(&private_key, &self.fee_record)?;
137            let priority_fee = self.priority_fee;
138
139            // Prepare the inputs for a transfer.
140            let input_record = Developer::parse_record(&private_key, &self.input_record)?;
141            let inputs = [
142                Value::Record(input_record),
143                Value::from_str(&format!("{recipient}"))?,
144                Value::from_str(&format!("{}u64", self.amount))?,
145            ];
146
147            // Create a new transaction.
148            vm.execute(
149                &private_key,
150                ("credits.aleo", "transfer_private"),
151                inputs.iter(),
152                Some(fee_record),
153                priority_fee,
154                Some(&query),
155                rng,
156            )?
157        };
158        let locator = Locator::<N>::from_str("credits.aleo/transfer_private")?;
159        println!("✅ Created private transfer of {} microcredits to {}\n", &self.amount, recipient);
160
161        // Determine if the transaction should be broadcast, stored, or displayed to the user.
162        Developer::handle_transaction(
163            &endpoint,
164            &self.broadcast,
165            self.dry_run,
166            &self.store,
167            self.store_format,
168            self.wait,
169            self.timeout,
170            transaction,
171            locator.to_string(),
172        )
173    }
174}