snarkos_cli/commands/developer/
mod.rs1mod decrypt;
17pub use decrypt::*;
18
19mod deploy;
20pub use deploy::*;
21
22mod execute;
23pub use execute::*;
24
25mod scan;
26pub use scan::*;
27
28mod transfer_private;
29pub use transfer_private::*;
30
31use snarkvm::{
32 console::network::Network,
33 package::Package,
34 prelude::{
35 Address,
36 Ciphertext,
37 Identifier,
38 Literal,
39 Plaintext,
40 PrivateKey,
41 Program,
42 ProgramID,
43 Record,
44 ToBytes,
45 Value,
46 ViewKey,
47 block::Transaction,
48 },
49};
50
51use anyhow::{Result, bail, ensure};
52use clap::Parser;
53use colored::Colorize;
54use std::{path::PathBuf, str::FromStr};
55
56#[derive(Debug, Parser)]
58pub enum Developer {
59 Decrypt(Decrypt),
61 Deploy(Deploy),
63 Execute(Execute),
65 Scan(Scan),
67 TransferPrivate(TransferPrivate),
69}
70
71impl Developer {
72 pub fn parse(self) -> Result<String> {
73 match self {
74 Self::Decrypt(decrypt) => decrypt.parse(),
75 Self::Deploy(deploy) => deploy.parse(),
76 Self::Execute(execute) => execute.parse(),
77 Self::Scan(scan) => scan.parse(),
78 Self::TransferPrivate(transfer_private) => transfer_private.parse(),
79 }
80 }
81
82 fn parse_package<N: Network>(program_id: ProgramID<N>, path: &Option<String>) -> Result<Package<N>> {
84 let directory = match path {
86 Some(path) => PathBuf::from_str(path)?,
87 None => std::env::current_dir()?,
88 };
89
90 let package = Package::open(&directory)?;
92
93 ensure!(
94 package.program_id() == &program_id,
95 "The program name in the package does not match the specified program name"
96 );
97
98 Ok(package)
100 }
101
102 fn parse_record<N: Network>(private_key: &PrivateKey<N>, record: &str) -> Result<Record<N, Plaintext<N>>> {
104 match record.starts_with("record1") {
105 true => {
106 let ciphertext = Record::<N, Ciphertext<N>>::from_str(record)?;
108 let view_key = ViewKey::try_from(private_key)?;
110 ciphertext.decrypt(&view_key)
112 }
113 false => Record::<N, Plaintext<N>>::from_str(record),
114 }
115 }
116
117 fn fetch_program<N: Network>(program_id: &ProgramID<N>, endpoint: &str) -> Result<Program<N>> {
119 let network = match N::ID {
121 snarkvm::console::network::MainnetV0::ID => "mainnet",
122 snarkvm::console::network::TestnetV0::ID => "testnet",
123 snarkvm::console::network::CanaryV0::ID => "canary",
124 unknown_id => bail!("Unknown network ID ({unknown_id})"),
125 };
126
127 let response = ureq::get(&format!("{endpoint}/{network}/program/{program_id}")).call();
129
130 match response {
132 Ok(response) => response.into_json().map_err(|err| err.into()),
133 Err(err) => match err {
134 ureq::Error::Status(_status, response) => {
135 bail!("Failed to fetch program {program_id}: {response:?}")
137 }
138 err => bail!(err),
139 },
140 }
141 }
142
143 fn get_public_balance<N: Network>(address: &Address<N>, endpoint: &str) -> Result<u64> {
145 let credits = ProgramID::<N>::from_str("credits.aleo")?;
147 let account_mapping = Identifier::<N>::from_str("account")?;
148
149 let network = match N::ID {
151 snarkvm::console::network::MainnetV0::ID => "mainnet",
152 snarkvm::console::network::TestnetV0::ID => "testnet",
153 snarkvm::console::network::CanaryV0::ID => "canary",
154 unknown_id => bail!("Unknown network ID ({unknown_id})"),
155 };
156
157 let response =
159 ureq::get(&format!("{endpoint}/{network}/program/{credits}/mapping/{account_mapping}/{address}")).call();
160
161 let balance: Result<Option<Value<N>>> = match response {
163 Ok(response) => response.into_json().map_err(|err| err.into()),
164 Err(err) => match err {
165 ureq::Error::Status(_status, response) => {
166 bail!(response.into_string().unwrap_or("Response too large!".to_owned()))
167 }
168 err => bail!(err),
169 },
170 };
171
172 match balance {
174 Ok(Some(Value::Plaintext(Plaintext::Literal(Literal::<N>::U64(amount), _)))) => Ok(*amount),
175 Ok(None) => Ok(0),
176 Ok(Some(..)) => bail!("Failed to deserialize balance for {address}"),
177 Err(err) => bail!("Failed to fetch balance for {address}: {err}"),
178 }
179 }
180
181 fn handle_transaction<N: Network>(
183 broadcast: &Option<String>,
184 dry_run: bool,
185 store: &Option<String>,
186 transaction: Transaction<N>,
187 operation: String,
188 ) -> Result<String> {
189 let transaction_id = transaction.id();
191
192 ensure!(!transaction.is_fee(), "The transaction is a fee transaction and cannot be broadcast");
194
195 if let Some(path) = store {
197 match PathBuf::from_str(path) {
198 Ok(file_path) => {
199 let transaction_bytes = transaction.to_bytes_le()?;
200 std::fs::write(&file_path, transaction_bytes)?;
201 println!("Transaction {transaction_id} was stored to {}", file_path.display());
202 }
203 Err(err) => {
204 println!("The transaction was unable to be stored due to: {err}");
205 }
206 }
207 };
208
209 if let Some(endpoint) = broadcast {
211 match ureq::post(endpoint).send_json(&transaction) {
213 Ok(id) => {
214 let response_string = id.into_string()?.trim_matches('\"').to_string();
216 ensure!(
217 response_string == transaction_id.to_string(),
218 "The response does not match the transaction id. ({response_string} != {transaction_id})"
219 );
220
221 match transaction {
222 Transaction::Deploy(..) => {
223 println!(
224 "⌛ Deployment {transaction_id} ('{}') has been broadcast to {}.",
225 operation.bold(),
226 endpoint
227 )
228 }
229 Transaction::Execute(..) => {
230 println!(
231 "⌛ Execution {transaction_id} ('{}') has been broadcast to {}.",
232 operation.bold(),
233 endpoint
234 )
235 }
236 Transaction::Fee(..) => {
237 println!("❌ Failed to broadcast fee '{}' to the {}.", operation.bold(), endpoint)
238 }
239 }
240 }
241 Err(error) => {
242 let error_message = match error {
243 ureq::Error::Status(code, response) => {
244 format!("(status code {code}: {:?})", response.into_string()?)
245 }
246 ureq::Error::Transport(err) => format!("({err})"),
247 };
248
249 match transaction {
250 Transaction::Deploy(..) => {
251 bail!("❌ Failed to deploy '{}' to {}: {}", operation.bold(), &endpoint, error_message)
252 }
253 Transaction::Execute(..) => {
254 bail!(
255 "❌ Failed to broadcast execution '{}' to {}: {}",
256 operation.bold(),
257 &endpoint,
258 error_message
259 )
260 }
261 Transaction::Fee(..) => {
262 bail!(
263 "❌ Failed to broadcast fee '{}' to {}: {}",
264 operation.bold(),
265 &endpoint,
266 error_message
267 )
268 }
269 }
270 }
271 };
272
273 Ok(transaction_id.to_string())
275 } else if dry_run {
276 Ok(transaction.to_string())
278 } else {
279 Ok("".to_string())
280 }
281 }
282}