pcli/command/
view.rs

1use anyhow::Result;
2
3use address::AddressCmd;
4use balance::BalanceCmd;
5use lps::LiquidityPositionsCmd;
6use noble_address::NobleAddressCmd;
7use staked::StakedCmd;
8use transaction_hashes::TransactionHashesCmd;
9use tx::TxCmd;
10use wallet_id::WalletIdCmd;
11
12use crate::App;
13
14use self::auction::AuctionCmd;
15
16mod address;
17mod auction;
18mod balance;
19mod lps;
20mod noble_address;
21mod staked;
22mod wallet_id;
23
24pub mod transaction_hashes;
25mod tx;
26
27#[derive(Debug, clap::Subcommand)]
28pub enum ViewCmd {
29    /// View your auction information
30    Auction(AuctionCmd),
31    /// View your wallet id
32    WalletId(WalletIdCmd),
33    /// View one of your addresses, either by numerical index, or a random ephemeral one.
34    Address(AddressCmd),
35    /// View the Noble forwarding address associated with one of your addresses, either by numerical index, or a random ephemeral one.
36    NobleAddress(NobleAddressCmd),
37    /// View your account balances.
38    Balance(BalanceCmd),
39    /// View your staked delegation tokens.
40    Staked(StakedCmd),
41    /// Deletes all scanned data and local state, while leaving keys untouched.
42    Reset(Reset),
43    /// Synchronizes the client, privately scanning the chain state.
44    ///
45    /// `pcli` syncs automatically prior to any action requiring chain state,
46    /// but this command can be used to "pre-sync" before interactive use.
47    Sync,
48    /// Get transaction hashes and block heights of spendable notes.
49    #[clap(visible_alias = "list-tx-hashes")]
50    ListTransactionHashes(TransactionHashesCmd),
51    /// Displays a transaction's details by hash.
52    Tx(TxCmd),
53    /// View information about the liquidity positions you control.
54    #[clap(visible_alias = "lps")]
55    LiquidityPositions(LiquidityPositionsCmd),
56}
57
58impl ViewCmd {
59    pub fn offline(&self) -> bool {
60        match self {
61            ViewCmd::Auction(auction_cmd) => auction_cmd.offline(),
62            ViewCmd::WalletId(wallet_id_cmd) => wallet_id_cmd.offline(),
63            ViewCmd::Address(address_cmd) => address_cmd.offline(),
64            ViewCmd::NobleAddress(address_cmd) => address_cmd.offline(),
65            ViewCmd::Balance(balance_cmd) => balance_cmd.offline(),
66            ViewCmd::Staked(staked_cmd) => staked_cmd.offline(),
67            ViewCmd::Reset(_) => true,
68            ViewCmd::Sync => false,
69            ViewCmd::ListTransactionHashes(transactions_cmd) => transactions_cmd.offline(),
70            ViewCmd::Tx(tx_cmd) => tx_cmd.offline(),
71            ViewCmd::LiquidityPositions(lps_cmd) => lps_cmd.offline(),
72        }
73    }
74
75    pub async fn exec(&self, app: &mut App) -> Result<()> {
76        // TODO: refactor view methods to take a single App
77        let full_viewing_key = app.config.full_viewing_key.clone();
78
79        match self {
80            ViewCmd::Auction(auction_cmd) => {
81                auction_cmd.exec(app.view(), &full_viewing_key).await?
82            }
83            ViewCmd::WalletId(wallet_id_cmd) => {
84                wallet_id_cmd.exec(&full_viewing_key)?;
85            }
86            ViewCmd::Tx(tx_cmd) => {
87                tx_cmd.exec(app).await?;
88            }
89            ViewCmd::ListTransactionHashes(transactions_cmd) => {
90                let view_client = app.view();
91                transactions_cmd
92                    .exec(&full_viewing_key, view_client)
93                    .await?;
94            }
95            ViewCmd::Sync => {
96                // We set needs_sync() -> true, so by this point, we have
97                // already synchronized the wallet above, so we can just return.
98            }
99            ViewCmd::Reset(_reset) => {
100                // The wallet has already been reset by a short-circuiting path.
101            }
102            ViewCmd::Address(address_cmd) => {
103                address_cmd.exec(&full_viewing_key)?;
104            }
105            ViewCmd::NobleAddress(noble_address_cmd) => {
106                noble_address_cmd.exec(&full_viewing_key)?;
107            }
108            ViewCmd::Balance(balance_cmd) => {
109                let view_client = app.view();
110                balance_cmd.exec(view_client).await?;
111            }
112            ViewCmd::Staked(staked_cmd) => {
113                let channel = app.pd_channel().await?;
114                let view_client = app.view();
115                staked_cmd
116                    .exec(&full_viewing_key, view_client, channel)
117                    .await?;
118            }
119            ViewCmd::LiquidityPositions(cmd) => cmd.exec(app).await?,
120        }
121
122        Ok(())
123    }
124}
125
126#[derive(Debug, clap::Parser)]
127pub struct Reset;
128
129impl Reset {
130    pub fn exec(&self, data_path: impl AsRef<camino::Utf8Path>) -> Result<()> {
131        tracing::info!("resetting client state");
132        let view_path = data_path.as_ref().join(crate::VIEW_FILE_NAME);
133        if view_path.is_file() {
134            std::fs::remove_file(&view_path)?;
135            println!("Deleted view data at {view_path}");
136        } else if view_path.exists() {
137            anyhow::bail!(
138                "Expected view data at {} but found something that is not a file; refusing to delete it",
139                view_path
140            );
141        } else {
142            anyhow::bail!(
143                "No view data exists at {}, so it cannot be deleted",
144                view_path
145            );
146        }
147
148        Ok(())
149    }
150}