cli/commands/wallet/
status.rs1use anyhow::{Context, Result};
2use serde_json::Value;
3
4#[derive(Debug, clap::Args)]
5pub struct Status {
6 #[arg(long)]
8 pub hash: String,
9
10 #[arg(long, default_value = "http://localhost:3000")]
12 pub node: String,
13
14 #[arg(long)]
16 pub check_mempool: bool,
17}
18
19impl Status {
20 pub fn run(self) -> Result<()> {
21 println!("Checking transaction status...");
22 println!("Transaction hash: {}", self.hash);
23
24 let client = reqwest::blocking::Client::builder()
25 .timeout(std::time::Duration::from_secs(30))
26 .build()
27 .context("Failed to create HTTP client")?;
28
29 let url = format!("{}/graphql", self.node);
30
31 let query = serde_json::json!({
33 "query": format!(
34 r#"query {{
35 transactionStatus(payment: "{}")
36 }}"#,
37 self.hash
38 )
39 });
40
41 let response = client
42 .post(&url)
43 .json(&query)
44 .send()
45 .context("Failed to query transaction status")?;
46
47 if !response.status().is_success() {
48 anyhow::bail!("Failed to query node: HTTP {}", response.status());
49 }
50
51 let response_json: Value = response
52 .json()
53 .context("Failed to parse GraphQL response")?;
54
55 if let Some(_errors) = response_json.get("errors") {
57 println!("\nTransaction not found in blockchain, checking mempool...");
60 return self.check_pooled_transactions(&client, &url);
61 }
62
63 if let Some(status) = response_json["data"]["transactionStatus"].as_str() {
65 println!("\nTransaction Status: {}", status);
66
67 match status {
68 "INCLUDED" => {
69 println!("ā Transaction has been included in a block");
70 }
71 "PENDING" => {
72 println!("ā³ Transaction is pending inclusion");
73 }
74 "UNKNOWN" => {
75 println!("? Transaction status is unknown");
76 if !self.check_mempool {
77 println!(
78 "\nTry using --check-mempool to check if it's in the transaction pool"
79 );
80 }
81 }
82 _ => {
83 println!("Status: {}", status);
84 }
85 }
86 } else {
87 println!("Unable to determine transaction status");
88 }
89
90 Ok(())
91 }
92
93 fn check_pooled_transactions(
94 &self,
95 client: &reqwest::blocking::Client,
96 url: &str,
97 ) -> Result<()> {
98 let query = serde_json::json!({
99 "query": r#"query {
100 pooledUserCommands {
101 hash
102 from
103 to
104 amount
105 fee
106 nonce
107 }
108 }"#
109 });
110
111 let response = client
112 .post(url)
113 .json(&query)
114 .send()
115 .context("Failed to query pooled transactions")?;
116
117 if !response.status().is_success() {
118 anyhow::bail!("Failed to query mempool: HTTP {}", response.status());
119 }
120
121 let response_json: Value = response
122 .json()
123 .context("Failed to parse GraphQL response")?;
124
125 if let Some(pooled) = response_json["data"]["pooledUserCommands"].as_array() {
126 for tx in pooled {
128 if let Some(hash) = tx["hash"].as_str() {
129 if hash == self.hash {
130 println!("\nā Transaction found in mempool!");
131 println!("\nTransaction Details:");
132 println!(" Hash: {}", hash);
133 if let Some(from) = tx["from"].as_str() {
134 println!(" From: {}", from);
135 }
136 if let Some(to) = tx["to"].as_str() {
137 println!(" To: {}", to);
138 }
139 if let Some(amount) = tx["amount"].as_str() {
140 println!(" Amount: {} nanomina", amount);
141 }
142 if let Some(fee) = tx["fee"].as_str() {
143 println!(" Fee: {} nanomina", fee);
144 }
145 if let Some(nonce) = tx["nonce"].as_u64() {
147 println!(" Nonce: {}", nonce);
148 } else if let Some(nonce) = tx["nonce"].as_str() {
149 println!(" Nonce: {}", nonce);
150 }
151
152 println!("\nStatus: PENDING (waiting to be included in a block)");
153 return Ok(());
154 }
155 }
156 }
157
158 println!("\nā Transaction not found in mempool");
159 println!("\nThe transaction may have:");
160 println!(" - Already been included in a block");
161 println!(" - Been rejected by the network");
162 println!(" - Not yet propagated to this node");
163 }
164
165 Ok(())
166 }
167}