quantus_cli/cli/
common.rs1use crate::{error::Result, log_error, log_verbose};
3use colored::Colorize;
4use sp_core::crypto::{AccountId32, Ss58Codec};
5
6pub fn resolve_address(address_or_wallet_name: &str) -> Result<String> {
9 if AccountId32::from_ss58check_with_version(address_or_wallet_name).is_ok() {
11 return Ok(address_or_wallet_name.to_string());
13 }
14
15 let wallet_manager = crate::wallet::WalletManager::new()?;
17 if let Some(wallet_address) = wallet_manager.find_wallet_address(address_or_wallet_name)? {
18 log_verbose!(
19 "🔍 Found wallet '{}' with address: {}",
20 address_or_wallet_name.bright_cyan(),
21 wallet_address.bright_green()
22 );
23 return Ok(wallet_address);
24 }
25
26 Err(crate::error::QuantusError::Generic(format!(
28 "Invalid destination: '{address_or_wallet_name}' is neither a valid SS58 address nor a known wallet name"
29 )))
30}
31
32pub async fn get_fresh_nonce_with_client(
36 quantus_client: &crate::chain::client::QuantusClient,
37 from_keypair: &crate::wallet::QuantumKeyPair,
38) -> Result<u64> {
39 let (from_account_id, _version) =
40 AccountId32::from_ss58check_with_version(&from_keypair.to_account_id_ss58check()).map_err(
41 |e| crate::error::QuantusError::NetworkError(format!("Invalid from address: {e:?}")),
42 )?;
43
44 let latest_nonce = quantus_client
46 .get_account_nonce_from_best_block(&from_account_id)
47 .await
48 .map_err(|e| {
49 crate::error::QuantusError::NetworkError(format!(
50 "Failed to get account nonce from best block: {e:?}"
51 ))
52 })?;
53
54 log_verbose!("🔢 Using fresh nonce from latest block: {}", latest_nonce);
55
56 let finalized_nonce = quantus_client
58 .client()
59 .tx()
60 .account_nonce(&from_account_id)
61 .await
62 .map_err(|e| {
63 crate::error::QuantusError::NetworkError(format!(
64 "Failed to get account nonce from finalized block: {e:?}"
65 ))
66 })?;
67
68 if latest_nonce != finalized_nonce {
69 log_verbose!(
70 "⚠️ Nonce difference detected! Latest: {}, Finalized: {}",
71 latest_nonce,
72 finalized_nonce
73 );
74 }
75
76 Ok(latest_nonce)
77}
78
79pub async fn get_incremented_nonce_with_client(
82 quantus_client: &crate::chain::client::QuantusClient,
83 from_keypair: &crate::wallet::QuantumKeyPair,
84 base_nonce: u64,
85) -> Result<u64> {
86 let (from_account_id, _version) =
87 AccountId32::from_ss58check_with_version(&from_keypair.to_account_id_ss58check()).map_err(
88 |e| crate::error::QuantusError::NetworkError(format!("Invalid from address: {e:?}")),
89 )?;
90
91 let current_nonce = quantus_client
93 .get_account_nonce_from_best_block(&from_account_id)
94 .await
95 .map_err(|e| {
96 crate::error::QuantusError::NetworkError(format!(
97 "Failed to get account nonce from best block: {e:?}"
98 ))
99 })?;
100
101 let incremented_nonce = std::cmp::max(current_nonce, base_nonce + 1);
103 log_verbose!(
104 "🔢 Using incremented nonce: {} (base: {}, current from latest block: {})",
105 incremented_nonce,
106 base_nonce,
107 current_nonce
108 );
109 Ok(incremented_nonce)
110}
111
112pub async fn submit_transaction<Call>(
115 quantus_client: &crate::chain::client::QuantusClient,
116 from_keypair: &crate::wallet::QuantumKeyPair,
117 call: Call,
118 tip: Option<u128>,
119) -> crate::error::Result<subxt::utils::H256>
120where
121 Call: subxt::tx::Payload,
122{
123 let signer = from_keypair.to_subxt_signer().map_err(|e| {
124 crate::error::QuantusError::NetworkError(format!("Failed to convert keypair: {e:?}"))
125 })?;
126
127 let mut attempt = 0;
129 let mut current_nonce = None;
130
131 loop {
132 attempt += 1;
133
134 let nonce = if let Some(prev_nonce) = current_nonce {
136 let incremented_nonce =
138 get_incremented_nonce_with_client(quantus_client, from_keypair, prev_nonce).await?;
139 log_verbose!(
140 "🔢 Using incremented nonce from best block: {} (previous: {})",
141 incremented_nonce,
142 prev_nonce
143 );
144 incremented_nonce
145 } else {
146 let fresh_nonce = get_fresh_nonce_with_client(quantus_client, from_keypair).await?;
148 log_verbose!("🔢 Using fresh nonce from best block: {}", fresh_nonce);
149 fresh_nonce
150 };
151 current_nonce = Some(nonce);
152
153 let latest_block_hash = quantus_client.get_latest_block().await.map_err(|e| {
155 crate::error::QuantusError::NetworkError(format!("Failed to get latest block: {e:?}"))
156 })?;
157
158 log_verbose!("🔗 Latest block hash: {:?}", latest_block_hash);
159
160 use subxt::config::DefaultExtrinsicParamsBuilder;
162 let mut params_builder = DefaultExtrinsicParamsBuilder::new()
163 .mortal(256) .nonce(nonce);
165
166 if let Some(tip_amount) = tip {
167 params_builder = params_builder.tip(tip_amount);
168 log_verbose!("💰 Using tip: {} to increase priority", tip_amount);
169 } else {
170 log_verbose!("💰 No tip specified, using default priority");
171 }
172
173 let genesis_hash = quantus_client.get_genesis_hash().await?;
175 let (spec_version, transaction_version) = quantus_client.get_runtime_version().await?;
176
177 log_verbose!("🔍 Chain parameters:");
178 log_verbose!(" Genesis hash: {:?}", genesis_hash);
179 log_verbose!(" Spec version: {}", spec_version);
180 log_verbose!(" Transaction version: {}", transaction_version);
181
182 let params = params_builder.build();
184
185 log_verbose!("🔍 Transaction parameters:");
187 log_verbose!(" Nonce: {}", nonce);
188 log_verbose!(" Tip: {:?}", tip);
189 log_verbose!(" Latest block hash: {:?}", latest_block_hash);
190
191 log_verbose!(" Era: Using default era from SubXT");
193 log_verbose!(" Genesis hash: Using default from SubXT");
194 log_verbose!(" Spec version: Using default from SubXT");
195
196 log_verbose!("🔍 Additional debugging:");
198 log_verbose!(" Call type: {:?}", std::any::type_name::<Call>());
199
200 match quantus_client.client().tx().sign_and_submit(&call, &signer, params).await {
202 Ok(tx_hash) => {
203 crate::log_verbose!("📋 Transaction submitted: {:?}", tx_hash);
204 return Ok(tx_hash);
205 },
206 Err(e) => {
207 let error_msg = format!("{e:?}");
208
209 let is_retryable = error_msg.contains("Priority is too low") ||
211 error_msg.contains("Transaction is outdated") ||
212 error_msg.contains("Transaction is temporarily banned") ||
213 error_msg.contains("Transaction has a bad signature") ||
214 error_msg.contains("Invalid Transaction");
215
216 if is_retryable && attempt < 5 {
217 log_verbose!(
218 "⚠️ Transaction error detected (attempt {}/5): {}",
219 attempt,
220 error_msg
221 );
222
223 let delay = std::cmp::min(2u64.pow(attempt as u32), 16);
225 log_verbose!("⏳ Waiting {} seconds before retry...", delay);
226 tokio::time::sleep(tokio::time::Duration::from_secs(delay)).await;
227 continue;
228 } else {
229 log_verbose!("❌ Final error after {} attempts: {}", attempt, error_msg);
230 return Err(crate::error::QuantusError::NetworkError(format!(
231 "Failed to submit transaction: {e:?}"
232 )));
233 }
234 },
235 }
236 }
237}
238
239pub async fn submit_transaction_with_nonce<Call>(
241 quantus_client: &crate::chain::client::QuantusClient,
242 from_keypair: &crate::wallet::QuantumKeyPair,
243 call: Call,
244 tip: Option<u128>,
245 nonce: u32,
246) -> crate::error::Result<subxt::utils::H256>
247where
248 Call: subxt::tx::Payload,
249{
250 let signer = from_keypair.to_subxt_signer().map_err(|e| {
251 crate::error::QuantusError::NetworkError(format!("Failed to convert keypair: {e:?}"))
252 })?;
253
254 let latest_block_hash = quantus_client.get_latest_block().await.map_err(|e| {
256 crate::error::QuantusError::NetworkError(format!("Failed to get latest block: {e:?}"))
257 })?;
258
259 log_verbose!("🔗 Latest block hash: {:?}", latest_block_hash);
260
261 use subxt::config::DefaultExtrinsicParamsBuilder;
263 let mut params_builder = DefaultExtrinsicParamsBuilder::new()
264 .mortal(256) .nonce(nonce.into());
266
267 if let Some(tip_amount) = tip {
268 params_builder = params_builder.tip(tip_amount);
269 log_verbose!("💰 Using tip: {}", tip_amount);
270 }
271
272 let params = params_builder.build();
273
274 log_verbose!("🔢 Using manual nonce: {}", nonce);
275 log_verbose!("📤 Submitting transaction with manual nonce...");
276
277 match quantus_client.client().tx().sign_and_submit(&call, &signer, params).await {
279 Ok(tx_hash) => {
280 log_verbose!("✅ Transaction submitted successfully: {:?}", tx_hash);
281 Ok(tx_hash)
282 },
283 Err(e) => {
284 log_error!("❌ Failed to submit transaction with manual nonce {}: {e:?}", nonce);
285 Err(crate::error::QuantusError::NetworkError(format!(
286 "Failed to submit transaction with nonce {nonce}: {e:?}"
287 )))
288 },
289 }
290}