leo_lang/cli/commands/query/
transaction.rs

1// Copyright (C) 2019-2025 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use super::*;
18
19use clap::Parser;
20
21/// Query transaction information.
22#[derive(Parser, Debug)]
23#[command(group(
24    // Ensure exactly one source is specified.
25    clap::ArgGroup::new("source").required(true).multiple(false)
26))]
27#[command(group(
28    // Ensure at most one mode is specified and ony if using "id" as a source.
29    // The `conflicts_with_all` here should not be required but it looks like Clap is getting a little confused
30    clap::ArgGroup::new("mode")
31        .required(false)
32        .multiple(false)
33        .requires("ID").conflicts_with_all(["from_io", "from_transition", "from_program"]
34    )
35))]
36pub struct LeoTransaction {
37    #[clap(name = "ID", help = "The ID of the transaction to fetch", group = "source")]
38    pub(crate) id: Option<String>,
39    #[arg(short, long, help = "Return more information about a confirmed transaction", group = "mode")]
40    pub(crate) confirmed: bool,
41    #[arg(short, long, help = "Get the original (unconfirmed) transaction", group = "mode")]
42    pub(crate) unconfirmed: bool,
43    #[arg(
44        value_name = "INPUT_OR_OUTPUT_ID",
45        long,
46        help = "Get the ID of the transaction that an input or output ID occurred in",
47        group = "source"
48    )]
49    pub(crate) from_io: Option<String>,
50    #[arg(
51        value_name = "TRANSITION_ID",
52        long,
53        help = "Get the ID of the transaction containing the specified transition",
54        group = "source"
55    )]
56    pub(crate) from_transition: Option<String>,
57    #[arg(
58        value_name = "PROGRAM",
59        long,
60        help = "Get the ID of the transaction that the specified program was deployed in",
61        group = "source"
62    )]
63    pub(crate) from_program: Option<String>,
64}
65
66impl Command for LeoTransaction {
67    type Input = ();
68    type Output = String;
69
70    fn log_span(&self) -> Span {
71        tracing::span!(tracing::Level::INFO, "Leo")
72    }
73
74    fn prelude(&self, _context: Context) -> Result<Self::Input> {
75        Ok(())
76    }
77
78    fn apply(self, _context: Context, _: Self::Input) -> Result<Self::Output> {
79        // Build custom url to fetch from based on the flags and user's input.
80        let url = if let Some(io_id) = self.from_io {
81            let field = is_valid_field(&io_id)?;
82            format!("find/transitionID/{field}")
83        } else if let Some(transition) = self.from_transition {
84            is_valid_transition_id(&transition)?;
85            format!("find/transactionID/{transition}")
86        } else if let Some(program) = self.from_program {
87            // Check that the program name is valid.
88            if !leo_package::is_valid_aleo_name(&program) {
89                return Err(CliError::invalid_program_name(program).into());
90            }
91            format!("find/transactionID/deployment/{}", program)
92        } else if let Some(id) = self.id {
93            is_valid_transaction_id(&id)?;
94            if self.confirmed {
95                format!("transaction/confirmed/{id}")
96            } else if self.unconfirmed {
97                format!("transaction/unconfirmed/{id}")
98            } else {
99                format!("transaction/{id}")
100            }
101        } else {
102            unreachable!("All command paths covered.")
103        };
104
105        Ok(url)
106    }
107}