From 97385c7e4ed1809b871c4162e875e3ee91828338 Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Sat, 21 Sep 2024 01:50:52 +0300 Subject: [PATCH 01/18] isCoinbase on tx_record --- wallet/core/src/storage/transaction/record.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wallet/core/src/storage/transaction/record.rs b/wallet/core/src/storage/transaction/record.rs index 05be3b69f2..93062d1741 100644 --- a/wallet/core/src/storage/transaction/record.rs +++ b/wallet/core/src/storage/transaction/record.rs @@ -818,6 +818,12 @@ impl TransactionRecord { self.transaction_data.has_address(address) } + /// Check if the transaction record is coinbase sourced. + #[wasm_bindgen(getter, js_name = "isCoinbase")] + pub fn is_coinbase_sourced(&self) -> bool { + self.is_coinbase() + } + /// Serialize the transaction record to a JavaScript object. pub fn serialize(&self) -> JsValue { serde_wasm_bindgen::to_value(&self).unwrap() From 8e71af0a94ad467764abd27732bebbebd5dce8c3 Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Sat, 21 Sep 2024 16:30:21 +0300 Subject: [PATCH 02/18] Allow passing ``decimals`` to low-level Kaspa-parsing functions --- cli/src/extensions/transaction.rs | 19 +++++++------- cli/src/modules/account.rs | 8 +++--- cli/src/modules/pskb.rs | 2 +- cli/src/modules/rpc.rs | 2 +- cli/src/modules/send.rs | 2 +- wallet/core/src/utils.rs | 43 ++++++++++++++++++++----------- wallet/core/src/utxo/balance.rs | 4 +-- wallet/core/src/wasm/utils.rs | 8 +++--- 8 files changed, 51 insertions(+), 37 deletions(-) diff --git a/cli/src/extensions/transaction.rs b/cli/src/extensions/transaction.rs index 415aa7a34b..3b85ea6906 100644 --- a/cli/src/extensions/transaction.rs +++ b/cli/src/extensions/transaction.rs @@ -132,7 +132,7 @@ impl TransactionExtension for TransactionRecord { | TransactionData::External { utxo_entries, aggregate_input_value } | TransactionData::Change { utxo_entries, aggregate_input_value, .. } => { let aggregate_input_value = - transaction_type.style_with_sign(sompi_to_kaspa_string(*aggregate_input_value).as_str(), history); + transaction_type.style_with_sign(sompi_to_kaspa_string(*aggregate_input_value, None).as_str(), history); lines.push(format!("{:>4}UTXOs: {} Total: {}", "", utxo_entries.len(), aggregate_input_value)); if include_utxos { for utxo_entry in utxo_entries { @@ -145,7 +145,8 @@ impl TransactionExtension for TransactionRecord { } else { style(format!("standard utxo [{index}]")).dim() }; - let amount = transaction_type.style_with_sign(sompi_to_kaspa_string(utxo_entry.amount).as_str(), history); + let amount = + transaction_type.style_with_sign(sompi_to_kaspa_string(utxo_entry.amount, None).as_str(), history); lines.push(format!("{:>4}{address}", "")); lines.push(format!("{:>4}{amount} {suffix} {is_coinbase}", "")); @@ -160,10 +161,10 @@ impl TransactionExtension for TransactionRecord { lines.push(format!( "{:>4}Payment: {} Used: {} Fees: {} Change: {} UTXOs: [{}↠{}]", "", - style(sompi_to_kaspa_string(*payment_value)).red(), - style(sompi_to_kaspa_string(*aggregate_input_value)).blue(), - style(sompi_to_kaspa_string(*fees)).red(), - style(sompi_to_kaspa_string(*change_value)).green(), + style(sompi_to_kaspa_string(*payment_value, None)).red(), + style(sompi_to_kaspa_string(*aggregate_input_value, None)).blue(), + style(sompi_to_kaspa_string(*fees, None)).red(), + style(sompi_to_kaspa_string(*change_value, None)).green(), transaction.inputs.len(), transaction.outputs.len(), )); @@ -171,9 +172,9 @@ impl TransactionExtension for TransactionRecord { lines.push(format!( "{:>4}Sweep: {} Fees: {} Change: {} UTXOs: [{}↠{}]", "", - style(sompi_to_kaspa_string(*aggregate_input_value)).blue(), - style(sompi_to_kaspa_string(*fees)).red(), - style(sompi_to_kaspa_string(*change_value)).green(), + style(sompi_to_kaspa_string(*aggregate_input_value, None)).blue(), + style(sompi_to_kaspa_string(*fees, None)).red(), + style(sompi_to_kaspa_string(*change_value, None)).green(), transaction.inputs.len(), transaction.outputs.len(), )); diff --git a/cli/src/modules/account.rs b/cli/src/modules/account.rs index 9d423e46d1..458d24b563 100644 --- a/cli/src/modules/account.rs +++ b/cli/src/modules/account.rs @@ -122,7 +122,7 @@ impl Account { tprintln!( ctx_, "Scan detected {} KAS at index {}; transfer txid: {}", - sompi_to_kaspa_string(balance), + sompi_to_kaspa_string(balance, None), processed, txid ); @@ -131,7 +131,7 @@ impl Account { ctx_, "Scanned {} derivations, found {} KAS", processed, - sompi_to_kaspa_string(balance) + sompi_to_kaspa_string(balance, None) ); } else { tprintln!(ctx_, "Please wait... scanning for account UTXOs..."); @@ -302,12 +302,12 @@ impl Account { tprintln!( ctx_, "Scan detected {} KAS at index {}; transfer txid: {}", - sompi_to_kaspa_string(balance), + sompi_to_kaspa_string(balance, None), processed, txid ); } else { - tprintln!(ctx_, "Scanned {} derivations, found {} KAS", processed, sompi_to_kaspa_string(balance)); + tprintln!(ctx_, "Scanned {} derivations, found {} KAS", processed, sompi_to_kaspa_string(balance, None)); } })), ) diff --git a/cli/src/modules/pskb.rs b/cli/src/modules/pskb.rs index 3757f939ac..bcfdfd0f67 100644 --- a/cli/src/modules/pskb.rs +++ b/cli/src/modules/pskb.rs @@ -138,7 +138,7 @@ impl Pskb { "{} locked UTXO{} found with total amount of {} KAS", spend_utxos.len(), if spend_utxos.len() == 1 { "" } else { "s" }, - sompi_to_kaspa(total_locked_sompi) + sompi_to_kaspa(total_locked_sompi, None) ); // Sweep UTXO set. diff --git a/cli/src/modules/rpc.rs b/cli/src/modules/rpc.rs index f32523c4a3..8502a841ed 100644 --- a/cli/src/modules/rpc.rs +++ b/cli/src/modules/rpc.rs @@ -164,7 +164,7 @@ impl Rpc { let addresses = argv.iter().map(|s| Address::try_from(s.as_str())).collect::, _>>()?; for address in addresses { let result = rpc.get_balance_by_address_call(None, GetBalanceByAddressRequest { address }).await?; - self.println(&ctx, sompi_to_kaspa(result.balance)); + self.println(&ctx, sompi_to_kaspa(result.balance, None)); } } RpcApiOps::GetBalancesByAddresses => { diff --git a/cli/src/modules/send.rs b/cli/src/modules/send.rs index 8c28679a99..465b357714 100644 --- a/cli/src/modules/send.rs +++ b/cli/src/modules/send.rs @@ -42,7 +42,7 @@ impl Send { .await?; tprintln!(ctx, "Send - {summary}"); - tprintln!(ctx, "\nSending {} KAS to {address}, tx ids:", sompi_to_kaspa_string(amount_sompi)); + tprintln!(ctx, "\nSending {} KAS to {address}, tx ids:", sompi_to_kaspa_string(amount_sompi, None)); // tprintln!(ctx, "{}\n", ids.into_iter().map(|a| a.to_string()).collect::>().join("\n")); Ok(()) diff --git a/wallet/core/src/utils.rs b/wallet/core/src/utils.rs index c732e3f68d..838eab323a 100644 --- a/wallet/core/src/utils.rs +++ b/wallet/core/src/utils.rs @@ -9,14 +9,14 @@ use kaspa_consensus_core::network::NetworkType; use separator::{separated_float, separated_int, separated_uint_with_output, Separatable}; use workflow_log::style; -pub fn try_kaspa_str_to_sompi>(s: S) -> Result> { +pub fn try_kaspa_str_to_sompi>(s: S, decimals: Option) -> Result> { let s: String = s.into(); let amount = s.trim(); if amount.is_empty() { return Ok(None); } - Ok(Some(str_to_sompi(amount)?)) + Ok(Some(str_to_sompi(amount, decimals)?)) } pub fn try_kaspa_str_to_sompi_i64>(s: S) -> Result> { @@ -31,8 +31,11 @@ pub fn try_kaspa_str_to_sompi_i64>(s: S) -> Result> } #[inline] -pub fn sompi_to_kaspa(sompi: u64) -> f64 { - sompi as f64 / SOMPI_PER_KASPA as f64 +pub fn sompi_to_kaspa(sompi: u64, decimals: Option) -> f64 { + let decimals = decimals.unwrap_or(8); + let sompi_per_unit = 10u64.pow(decimals); + + sompi as f64 / sompi_per_unit as f64 } #[inline] @@ -41,13 +44,13 @@ pub fn kaspa_to_sompi(kaspa: f64) -> u64 { } #[inline] -pub fn sompi_to_kaspa_string(sompi: u64) -> String { - sompi_to_kaspa(sompi).separated_string() +pub fn sompi_to_kaspa_string(sompi: u64, decimals: Option) -> String { + sompi_to_kaspa(sompi, decimals).separated_string() } #[inline] pub fn sompi_to_kaspa_string_with_trailing_zeroes(sompi: u64) -> String { - separated_float!(format!("{:.8}", sompi_to_kaspa(sompi))) + separated_float!(format!("{:.8}", sompi_to_kaspa(sompi, None))) } pub fn kaspa_suffix(network_type: &NetworkType) -> &'static str { @@ -61,7 +64,7 @@ pub fn kaspa_suffix(network_type: &NetworkType) -> &'static str { #[inline] pub fn sompi_to_kaspa_string_with_suffix(sompi: u64, network_type: &NetworkType) -> String { - let kas = sompi_to_kaspa_string(sompi); + let kas = sompi_to_kaspa_string(sompi, None); let suffix = kaspa_suffix(network_type); format!("{kas} {suffix}") } @@ -90,21 +93,31 @@ pub fn format_address_colors(address: &Address, range: Option) -> String format!("{prefix}:{left}:{center}:{right}") } -fn str_to_sompi(amount: &str) -> Result { +fn str_to_sompi(amount: &str, decimals: Option) -> Result { + let decimals = decimals.unwrap_or(8); + let sompi_per_unit = 10u64.pow(decimals); + + // Check if the amount contains a decimal point, if doesn't return value directly. let Some(dot_idx) = amount.find('.') else { - return Ok(amount.parse::()? * SOMPI_PER_KASPA); + return Ok(amount.parse::()? * sompi_per_unit); }; - let integer = amount[..dot_idx].parse::()? * SOMPI_PER_KASPA; + + // Parse the integer part of the amount + let integer = amount[..dot_idx].parse::()? * sompi_per_unit; + let decimal = &amount[dot_idx + 1..]; let decimal_len = decimal.len(); let decimal = if decimal_len == 0 { + // If there are no digits after the decimal point, the fractional value is 0. 0 - } else if decimal_len <= 8 { - decimal.parse::()? * 10u64.pow(8 - decimal_len as u32) + } else if decimal_len <= decimals as usize { + // If its within allowed decimals range, parse it as u64 and pad with zeros to the right to reach the correct precision. + decimal.parse::()? * 10u64.pow(decimals - decimal_len as u32) } else { - // TODO - discuss how to handle values longer than 8 decimal places + // Truncate values longer than allowed decimal places. + // TODO - discuss how to handle values longer than supplied decimal places. // (reject, truncate, ceil(), etc.) - decimal[..8].parse::()? + decimal[..decimals as usize].parse::()? }; Ok(integer + decimal) } diff --git a/wallet/core/src/utxo/balance.rs b/wallet/core/src/utxo/balance.rs index f16ce94ff6..3686ce2d26 100644 --- a/wallet/core/src/utxo/balance.rs +++ b/wallet/core/src/utxo/balance.rs @@ -190,8 +190,8 @@ impl From<(Option<&Balance>, &NetworkType, Option)> for BalanceStrings { fn from((balance, network_type, padding): (Option<&Balance>, &NetworkType, Option)) -> Self { let suffix = utils::kaspa_suffix(network_type); if let Some(balance) = balance { - let mut mature = utils::sompi_to_kaspa_string(balance.mature); - let mut pending = if balance.pending > 0 { Some(utils::sompi_to_kaspa_string(balance.pending)) } else { None }; + let mut mature = utils::sompi_to_kaspa_string(balance.mature, None); + let mut pending = if balance.pending > 0 { Some(utils::sompi_to_kaspa_string(balance.pending, None)) } else { None }; if let Some(padding) = padding { mature = mature.pad_to_width(padding); pending = pending.map(|pending| pending.pad_to_width(padding)); diff --git a/wallet/core/src/wasm/utils.rs b/wallet/core/src/wasm/utils.rs index a06c6136a3..8336698ce6 100644 --- a/wallet/core/src/wasm/utils.rs +++ b/wallet/core/src/wasm/utils.rs @@ -16,8 +16,8 @@ extern "C" { /// can be used to parse user input. /// @category Wallet SDK #[wasm_bindgen(js_name = "kaspaToSompi")] -pub fn kaspa_to_sompi(kaspa: String) -> Option { - crate::utils::try_kaspa_str_to_sompi(kaspa).ok().flatten().map(Into::into) +pub fn kaspa_to_sompi(kaspa: String, decimals: Option) -> Option { + crate::utils::try_kaspa_str_to_sompi(kaspa, decimals).ok().flatten().map(Into::into) } /// @@ -26,9 +26,9 @@ pub fn kaspa_to_sompi(kaspa: String) -> Option { /// @category Wallet SDK /// #[wasm_bindgen(js_name = "sompiToKaspaString")] -pub fn sompi_to_kaspa_string(sompi: ISompiToKaspa) -> Result { +pub fn sompi_to_kaspa_string(sompi: ISompiToKaspa, decimals: Option) -> Result { let sompi = sompi.try_as_u64()?; - Ok(crate::utils::sompi_to_kaspa_string(sompi)) + Ok(crate::utils::sompi_to_kaspa_string(sompi, decimals)) } /// From 255a23583dca5d5d117be2ce8cd037b98e69b50a Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Wed, 25 Sep 2024 00:17:53 +0300 Subject: [PATCH 03/18] Revert decimals overriding existing functs --- cli/src/extensions/transaction.rs | 19 +++++++------- cli/src/modules/account.rs | 8 +++--- cli/src/modules/pskb.rs | 2 +- cli/src/modules/rpc.rs | 2 +- cli/src/modules/send.rs | 2 +- wallet/core/src/utils.rs | 43 +++++++++++-------------------- wallet/core/src/utxo/balance.rs | 4 +-- wallet/core/src/wasm/utils.rs | 8 +++--- 8 files changed, 37 insertions(+), 51 deletions(-) diff --git a/cli/src/extensions/transaction.rs b/cli/src/extensions/transaction.rs index 3b85ea6906..415aa7a34b 100644 --- a/cli/src/extensions/transaction.rs +++ b/cli/src/extensions/transaction.rs @@ -132,7 +132,7 @@ impl TransactionExtension for TransactionRecord { | TransactionData::External { utxo_entries, aggregate_input_value } | TransactionData::Change { utxo_entries, aggregate_input_value, .. } => { let aggregate_input_value = - transaction_type.style_with_sign(sompi_to_kaspa_string(*aggregate_input_value, None).as_str(), history); + transaction_type.style_with_sign(sompi_to_kaspa_string(*aggregate_input_value).as_str(), history); lines.push(format!("{:>4}UTXOs: {} Total: {}", "", utxo_entries.len(), aggregate_input_value)); if include_utxos { for utxo_entry in utxo_entries { @@ -145,8 +145,7 @@ impl TransactionExtension for TransactionRecord { } else { style(format!("standard utxo [{index}]")).dim() }; - let amount = - transaction_type.style_with_sign(sompi_to_kaspa_string(utxo_entry.amount, None).as_str(), history); + let amount = transaction_type.style_with_sign(sompi_to_kaspa_string(utxo_entry.amount).as_str(), history); lines.push(format!("{:>4}{address}", "")); lines.push(format!("{:>4}{amount} {suffix} {is_coinbase}", "")); @@ -161,10 +160,10 @@ impl TransactionExtension for TransactionRecord { lines.push(format!( "{:>4}Payment: {} Used: {} Fees: {} Change: {} UTXOs: [{}↠{}]", "", - style(sompi_to_kaspa_string(*payment_value, None)).red(), - style(sompi_to_kaspa_string(*aggregate_input_value, None)).blue(), - style(sompi_to_kaspa_string(*fees, None)).red(), - style(sompi_to_kaspa_string(*change_value, None)).green(), + style(sompi_to_kaspa_string(*payment_value)).red(), + style(sompi_to_kaspa_string(*aggregate_input_value)).blue(), + style(sompi_to_kaspa_string(*fees)).red(), + style(sompi_to_kaspa_string(*change_value)).green(), transaction.inputs.len(), transaction.outputs.len(), )); @@ -172,9 +171,9 @@ impl TransactionExtension for TransactionRecord { lines.push(format!( "{:>4}Sweep: {} Fees: {} Change: {} UTXOs: [{}↠{}]", "", - style(sompi_to_kaspa_string(*aggregate_input_value, None)).blue(), - style(sompi_to_kaspa_string(*fees, None)).red(), - style(sompi_to_kaspa_string(*change_value, None)).green(), + style(sompi_to_kaspa_string(*aggregate_input_value)).blue(), + style(sompi_to_kaspa_string(*fees)).red(), + style(sompi_to_kaspa_string(*change_value)).green(), transaction.inputs.len(), transaction.outputs.len(), )); diff --git a/cli/src/modules/account.rs b/cli/src/modules/account.rs index 458d24b563..9d423e46d1 100644 --- a/cli/src/modules/account.rs +++ b/cli/src/modules/account.rs @@ -122,7 +122,7 @@ impl Account { tprintln!( ctx_, "Scan detected {} KAS at index {}; transfer txid: {}", - sompi_to_kaspa_string(balance, None), + sompi_to_kaspa_string(balance), processed, txid ); @@ -131,7 +131,7 @@ impl Account { ctx_, "Scanned {} derivations, found {} KAS", processed, - sompi_to_kaspa_string(balance, None) + sompi_to_kaspa_string(balance) ); } else { tprintln!(ctx_, "Please wait... scanning for account UTXOs..."); @@ -302,12 +302,12 @@ impl Account { tprintln!( ctx_, "Scan detected {} KAS at index {}; transfer txid: {}", - sompi_to_kaspa_string(balance, None), + sompi_to_kaspa_string(balance), processed, txid ); } else { - tprintln!(ctx_, "Scanned {} derivations, found {} KAS", processed, sompi_to_kaspa_string(balance, None)); + tprintln!(ctx_, "Scanned {} derivations, found {} KAS", processed, sompi_to_kaspa_string(balance)); } })), ) diff --git a/cli/src/modules/pskb.rs b/cli/src/modules/pskb.rs index bcfdfd0f67..3757f939ac 100644 --- a/cli/src/modules/pskb.rs +++ b/cli/src/modules/pskb.rs @@ -138,7 +138,7 @@ impl Pskb { "{} locked UTXO{} found with total amount of {} KAS", spend_utxos.len(), if spend_utxos.len() == 1 { "" } else { "s" }, - sompi_to_kaspa(total_locked_sompi, None) + sompi_to_kaspa(total_locked_sompi) ); // Sweep UTXO set. diff --git a/cli/src/modules/rpc.rs b/cli/src/modules/rpc.rs index 8502a841ed..f32523c4a3 100644 --- a/cli/src/modules/rpc.rs +++ b/cli/src/modules/rpc.rs @@ -164,7 +164,7 @@ impl Rpc { let addresses = argv.iter().map(|s| Address::try_from(s.as_str())).collect::, _>>()?; for address in addresses { let result = rpc.get_balance_by_address_call(None, GetBalanceByAddressRequest { address }).await?; - self.println(&ctx, sompi_to_kaspa(result.balance, None)); + self.println(&ctx, sompi_to_kaspa(result.balance)); } } RpcApiOps::GetBalancesByAddresses => { diff --git a/cli/src/modules/send.rs b/cli/src/modules/send.rs index 465b357714..8c28679a99 100644 --- a/cli/src/modules/send.rs +++ b/cli/src/modules/send.rs @@ -42,7 +42,7 @@ impl Send { .await?; tprintln!(ctx, "Send - {summary}"); - tprintln!(ctx, "\nSending {} KAS to {address}, tx ids:", sompi_to_kaspa_string(amount_sompi, None)); + tprintln!(ctx, "\nSending {} KAS to {address}, tx ids:", sompi_to_kaspa_string(amount_sompi)); // tprintln!(ctx, "{}\n", ids.into_iter().map(|a| a.to_string()).collect::>().join("\n")); Ok(()) diff --git a/wallet/core/src/utils.rs b/wallet/core/src/utils.rs index 838eab323a..c732e3f68d 100644 --- a/wallet/core/src/utils.rs +++ b/wallet/core/src/utils.rs @@ -9,14 +9,14 @@ use kaspa_consensus_core::network::NetworkType; use separator::{separated_float, separated_int, separated_uint_with_output, Separatable}; use workflow_log::style; -pub fn try_kaspa_str_to_sompi>(s: S, decimals: Option) -> Result> { +pub fn try_kaspa_str_to_sompi>(s: S) -> Result> { let s: String = s.into(); let amount = s.trim(); if amount.is_empty() { return Ok(None); } - Ok(Some(str_to_sompi(amount, decimals)?)) + Ok(Some(str_to_sompi(amount)?)) } pub fn try_kaspa_str_to_sompi_i64>(s: S) -> Result> { @@ -31,11 +31,8 @@ pub fn try_kaspa_str_to_sompi_i64>(s: S) -> Result> } #[inline] -pub fn sompi_to_kaspa(sompi: u64, decimals: Option) -> f64 { - let decimals = decimals.unwrap_or(8); - let sompi_per_unit = 10u64.pow(decimals); - - sompi as f64 / sompi_per_unit as f64 +pub fn sompi_to_kaspa(sompi: u64) -> f64 { + sompi as f64 / SOMPI_PER_KASPA as f64 } #[inline] @@ -44,13 +41,13 @@ pub fn kaspa_to_sompi(kaspa: f64) -> u64 { } #[inline] -pub fn sompi_to_kaspa_string(sompi: u64, decimals: Option) -> String { - sompi_to_kaspa(sompi, decimals).separated_string() +pub fn sompi_to_kaspa_string(sompi: u64) -> String { + sompi_to_kaspa(sompi).separated_string() } #[inline] pub fn sompi_to_kaspa_string_with_trailing_zeroes(sompi: u64) -> String { - separated_float!(format!("{:.8}", sompi_to_kaspa(sompi, None))) + separated_float!(format!("{:.8}", sompi_to_kaspa(sompi))) } pub fn kaspa_suffix(network_type: &NetworkType) -> &'static str { @@ -64,7 +61,7 @@ pub fn kaspa_suffix(network_type: &NetworkType) -> &'static str { #[inline] pub fn sompi_to_kaspa_string_with_suffix(sompi: u64, network_type: &NetworkType) -> String { - let kas = sompi_to_kaspa_string(sompi, None); + let kas = sompi_to_kaspa_string(sompi); let suffix = kaspa_suffix(network_type); format!("{kas} {suffix}") } @@ -93,31 +90,21 @@ pub fn format_address_colors(address: &Address, range: Option) -> String format!("{prefix}:{left}:{center}:{right}") } -fn str_to_sompi(amount: &str, decimals: Option) -> Result { - let decimals = decimals.unwrap_or(8); - let sompi_per_unit = 10u64.pow(decimals); - - // Check if the amount contains a decimal point, if doesn't return value directly. +fn str_to_sompi(amount: &str) -> Result { let Some(dot_idx) = amount.find('.') else { - return Ok(amount.parse::()? * sompi_per_unit); + return Ok(amount.parse::()? * SOMPI_PER_KASPA); }; - - // Parse the integer part of the amount - let integer = amount[..dot_idx].parse::()? * sompi_per_unit; - + let integer = amount[..dot_idx].parse::()? * SOMPI_PER_KASPA; let decimal = &amount[dot_idx + 1..]; let decimal_len = decimal.len(); let decimal = if decimal_len == 0 { - // If there are no digits after the decimal point, the fractional value is 0. 0 - } else if decimal_len <= decimals as usize { - // If its within allowed decimals range, parse it as u64 and pad with zeros to the right to reach the correct precision. - decimal.parse::()? * 10u64.pow(decimals - decimal_len as u32) + } else if decimal_len <= 8 { + decimal.parse::()? * 10u64.pow(8 - decimal_len as u32) } else { - // Truncate values longer than allowed decimal places. - // TODO - discuss how to handle values longer than supplied decimal places. + // TODO - discuss how to handle values longer than 8 decimal places // (reject, truncate, ceil(), etc.) - decimal[..decimals as usize].parse::()? + decimal[..8].parse::()? }; Ok(integer + decimal) } diff --git a/wallet/core/src/utxo/balance.rs b/wallet/core/src/utxo/balance.rs index 3686ce2d26..f16ce94ff6 100644 --- a/wallet/core/src/utxo/balance.rs +++ b/wallet/core/src/utxo/balance.rs @@ -190,8 +190,8 @@ impl From<(Option<&Balance>, &NetworkType, Option)> for BalanceStrings { fn from((balance, network_type, padding): (Option<&Balance>, &NetworkType, Option)) -> Self { let suffix = utils::kaspa_suffix(network_type); if let Some(balance) = balance { - let mut mature = utils::sompi_to_kaspa_string(balance.mature, None); - let mut pending = if balance.pending > 0 { Some(utils::sompi_to_kaspa_string(balance.pending, None)) } else { None }; + let mut mature = utils::sompi_to_kaspa_string(balance.mature); + let mut pending = if balance.pending > 0 { Some(utils::sompi_to_kaspa_string(balance.pending)) } else { None }; if let Some(padding) = padding { mature = mature.pad_to_width(padding); pending = pending.map(|pending| pending.pad_to_width(padding)); diff --git a/wallet/core/src/wasm/utils.rs b/wallet/core/src/wasm/utils.rs index 8336698ce6..a06c6136a3 100644 --- a/wallet/core/src/wasm/utils.rs +++ b/wallet/core/src/wasm/utils.rs @@ -16,8 +16,8 @@ extern "C" { /// can be used to parse user input. /// @category Wallet SDK #[wasm_bindgen(js_name = "kaspaToSompi")] -pub fn kaspa_to_sompi(kaspa: String, decimals: Option) -> Option { - crate::utils::try_kaspa_str_to_sompi(kaspa, decimals).ok().flatten().map(Into::into) +pub fn kaspa_to_sompi(kaspa: String) -> Option { + crate::utils::try_kaspa_str_to_sompi(kaspa).ok().flatten().map(Into::into) } /// @@ -26,9 +26,9 @@ pub fn kaspa_to_sompi(kaspa: String, decimals: Option) -> Option { /// @category Wallet SDK /// #[wasm_bindgen(js_name = "sompiToKaspaString")] -pub fn sompi_to_kaspa_string(sompi: ISompiToKaspa, decimals: Option) -> Result { +pub fn sompi_to_kaspa_string(sompi: ISompiToKaspa) -> Result { let sompi = sompi.try_as_u64()?; - Ok(crate::utils::sompi_to_kaspa_string(sompi, decimals)) + Ok(crate::utils::sompi_to_kaspa_string(sompi)) } /// From 49ec7f531ef75eb114a83a77e852fbf708a6dd38 Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Wed, 25 Sep 2024 01:59:10 +0300 Subject: [PATCH 04/18] Allow passing decimals on low-level Kaspa parsing utilities --- wallet/core/src/utils.rs | 36 ++++++++++++++++++++++++++--------- wallet/core/src/wasm/utils.rs | 4 ++-- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/wallet/core/src/utils.rs b/wallet/core/src/utils.rs index c732e3f68d..ef60c5b322 100644 --- a/wallet/core/src/utils.rs +++ b/wallet/core/src/utils.rs @@ -9,14 +9,14 @@ use kaspa_consensus_core::network::NetworkType; use separator::{separated_float, separated_int, separated_uint_with_output, Separatable}; use workflow_log::style; -pub fn try_kaspa_str_to_sompi>(s: S) -> Result> { +pub fn try_kaspa_str_to_sompi>(s: S, decimals: Option) -> Result> { let s: String = s.into(); let amount = s.trim(); if amount.is_empty() { return Ok(None); } - Ok(Some(str_to_sompi(amount)?)) + Ok(Some(str_to_sompi(amount, decimals)?)) } pub fn try_kaspa_str_to_sompi_i64>(s: S) -> Result> { @@ -35,6 +35,14 @@ pub fn sompi_to_kaspa(sompi: u64) -> f64 { sompi as f64 / SOMPI_PER_KASPA as f64 } +#[inline] +pub fn sompi_to_kaspa_with_decimals(sompi: u64, decimals: Option) -> f64 { + let decimals = decimals.unwrap_or(8); + let sompi_per_unit = 10u64.pow(decimals); + + sompi as f64 / sompi_per_unit as f64 +} + #[inline] pub fn kaspa_to_sompi(kaspa: f64) -> u64 { (kaspa * SOMPI_PER_KASPA as f64) as u64 @@ -90,21 +98,31 @@ pub fn format_address_colors(address: &Address, range: Option) -> String format!("{prefix}:{left}:{center}:{right}") } -fn str_to_sompi(amount: &str) -> Result { +fn str_to_sompi(amount: &str, decimals: Option) -> Result { + let decimals = decimals.unwrap_or(8); + let sompi_per_unit = 10u64.pow(decimals); + + // Check if the amount contains a decimal point, if doesn't return value directly. let Some(dot_idx) = amount.find('.') else { - return Ok(amount.parse::()? * SOMPI_PER_KASPA); + return Ok(amount.parse::()? * sompi_per_unit); }; - let integer = amount[..dot_idx].parse::()? * SOMPI_PER_KASPA; + + // Parse the integer part of the amount + let integer = amount[..dot_idx].parse::()? * sompi_per_unit; + let decimal = &amount[dot_idx + 1..]; let decimal_len = decimal.len(); let decimal = if decimal_len == 0 { + // If there are no digits after the decimal point, the fractional value is 0. 0 - } else if decimal_len <= 8 { - decimal.parse::()? * 10u64.pow(8 - decimal_len as u32) + } else if decimal_len <= decimals as usize { + // If its within allowed decimals range, parse it as u64 and pad with zeros to the right to reach the correct precision. + decimal.parse::()? * 10u64.pow(decimals - decimal_len as u32) } else { - // TODO - discuss how to handle values longer than 8 decimal places + // Truncate values longer than allowed decimal places. + // TODO - discuss how to handle values longer than supplied decimal places. // (reject, truncate, ceil(), etc.) - decimal[..8].parse::()? + decimal[..decimals as usize].parse::()? }; Ok(integer + decimal) } diff --git a/wallet/core/src/wasm/utils.rs b/wallet/core/src/wasm/utils.rs index a06c6136a3..26653846c2 100644 --- a/wallet/core/src/wasm/utils.rs +++ b/wallet/core/src/wasm/utils.rs @@ -16,8 +16,8 @@ extern "C" { /// can be used to parse user input. /// @category Wallet SDK #[wasm_bindgen(js_name = "kaspaToSompi")] -pub fn kaspa_to_sompi(kaspa: String) -> Option { - crate::utils::try_kaspa_str_to_sompi(kaspa).ok().flatten().map(Into::into) +pub fn kaspa_to_sompi(kaspa: String, decimals: Option) -> Option { + crate::utils::try_kaspa_str_to_sompi(kaspa, decimals).ok().flatten().map(Into::into) } /// From b3eb1d5f10452eb620c1231d95f0e8131079c349 Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Sat, 28 Sep 2024 20:35:13 +0300 Subject: [PATCH 05/18] allow `hasAddress` to accept string(via AddressT) --- wallet/core/src/storage/transaction/record.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wallet/core/src/storage/transaction/record.rs b/wallet/core/src/storage/transaction/record.rs index 93062d1741..65266fcae0 100644 --- a/wallet/core/src/storage/transaction/record.rs +++ b/wallet/core/src/storage/transaction/record.rs @@ -6,6 +6,7 @@ use super::*; use crate::imports::*; use crate::storage::{Binding, BindingT}; use crate::tx::PendingTransactionInner; +use kaspa_addresses::AddressT; use workflow_core::time::{unixtime_as_millis_u64, unixtime_to_locale_string}; use workflow_wasm::utils::try_get_js_value_prop; @@ -814,8 +815,9 @@ impl TransactionRecord { /// Check if the transaction record has the given address within the associated UTXO set. #[wasm_bindgen(js_name = hasAddress)] - pub fn has_address(&self, address: &Address) -> bool { - self.transaction_data.has_address(address) + pub fn has_address(&self, address: &AddressT) -> Result { + let address = Address::try_cast_from(address)?; + Ok(self.transaction_data.has_address(address.as_ref())) } /// Check if the transaction record is coinbase sourced. From 89dc469945125199ec7667d10e2f3a92f8da90f1 Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Sat, 28 Sep 2024 20:44:46 +0300 Subject: [PATCH 06/18] try_kaspa_str_to_sompi_with_decimals --- wallet/core/src/utils.rs | 35 ++++++++++++++++++++++++++++++++--- wallet/core/src/wasm/utils.rs | 2 +- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/wallet/core/src/utils.rs b/wallet/core/src/utils.rs index ef60c5b322..22d6017cac 100644 --- a/wallet/core/src/utils.rs +++ b/wallet/core/src/utils.rs @@ -9,14 +9,24 @@ use kaspa_consensus_core::network::NetworkType; use separator::{separated_float, separated_int, separated_uint_with_output, Separatable}; use workflow_log::style; -pub fn try_kaspa_str_to_sompi>(s: S, decimals: Option) -> Result> { +pub fn try_kaspa_str_to_sompi>(s: S) -> Result> { let s: String = s.into(); let amount = s.trim(); if amount.is_empty() { return Ok(None); } - Ok(Some(str_to_sompi(amount, decimals)?)) + Ok(Some(str_to_sompi(amount)?)) +} + +pub fn try_kaspa_str_to_sompi_with_decimals>(s: S, decimals: Option) -> Result> { + let s: String = s.into(); + let amount = s.trim(); + if amount.is_empty() { + return Ok(None); + } + + Ok(Some(str_to_sompi_with_decimals(amount, decimals)?)) } pub fn try_kaspa_str_to_sompi_i64>(s: S) -> Result> { @@ -98,7 +108,26 @@ pub fn format_address_colors(address: &Address, range: Option) -> String format!("{prefix}:{left}:{center}:{right}") } -fn str_to_sompi(amount: &str, decimals: Option) -> Result { +fn str_to_sompi(amount: &str) -> Result { + let Some(dot_idx) = amount.find('.') else { + return Ok(amount.parse::()? * SOMPI_PER_KASPA); + }; + let integer = amount[..dot_idx].parse::()? * SOMPI_PER_KASPA; + let decimal = &amount[dot_idx + 1..]; + let decimal_len = decimal.len(); + let decimal = if decimal_len == 0 { + 0 + } else if decimal_len <= 8 { + decimal.parse::()? * 10u64.pow(8 - decimal_len as u32) + } else { + // TODO - discuss how to handle values longer than 8 decimal places + // (reject, truncate, ceil(), etc.) + decimal[..8].parse::()? + }; + Ok(integer + decimal) +} + +fn str_to_sompi_with_decimals(amount: &str, decimals: Option) -> Result { let decimals = decimals.unwrap_or(8); let sompi_per_unit = 10u64.pow(decimals); diff --git a/wallet/core/src/wasm/utils.rs b/wallet/core/src/wasm/utils.rs index 26653846c2..6ec66e62e1 100644 --- a/wallet/core/src/wasm/utils.rs +++ b/wallet/core/src/wasm/utils.rs @@ -17,7 +17,7 @@ extern "C" { /// @category Wallet SDK #[wasm_bindgen(js_name = "kaspaToSompi")] pub fn kaspa_to_sompi(kaspa: String, decimals: Option) -> Option { - crate::utils::try_kaspa_str_to_sompi(kaspa, decimals).ok().flatten().map(Into::into) + crate::utils::try_kaspa_str_to_sompi_with_decimals(kaspa, decimals).ok().flatten().map(Into::into) } /// From 5b4e97299c1d5b4a8871f611adfd3f44c929bd7d Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Sat, 28 Sep 2024 21:00:04 +0300 Subject: [PATCH 07/18] kaspaToUnit and sompiToUnitString --- wallet/core/src/utils.rs | 15 +++++++++------ wallet/core/src/wasm/utils.rs | 24 ++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/wallet/core/src/utils.rs b/wallet/core/src/utils.rs index 22d6017cac..e433bebf1b 100644 --- a/wallet/core/src/utils.rs +++ b/wallet/core/src/utils.rs @@ -19,14 +19,14 @@ pub fn try_kaspa_str_to_sompi>(s: S) -> Result> { Ok(Some(str_to_sompi(amount)?)) } -pub fn try_kaspa_str_to_sompi_with_decimals>(s: S, decimals: Option) -> Result> { +pub fn try_kaspa_str_to_unit>(s: S, decimals: u32) -> Result> { let s: String = s.into(); let amount = s.trim(); if amount.is_empty() { return Ok(None); } - Ok(Some(str_to_sompi_with_decimals(amount, decimals)?)) + Ok(Some(str_to_unit(amount, decimals)?)) } pub fn try_kaspa_str_to_sompi_i64>(s: S) -> Result> { @@ -46,8 +46,7 @@ pub fn sompi_to_kaspa(sompi: u64) -> f64 { } #[inline] -pub fn sompi_to_kaspa_with_decimals(sompi: u64, decimals: Option) -> f64 { - let decimals = decimals.unwrap_or(8); +pub fn sompi_to_unit(sompi: u64, decimals: u32) -> f64 { let sompi_per_unit = 10u64.pow(decimals); sompi as f64 / sompi_per_unit as f64 @@ -63,6 +62,11 @@ pub fn sompi_to_kaspa_string(sompi: u64) -> String { sompi_to_kaspa(sompi).separated_string() } +#[inline] +pub fn sompi_to_unit_string(sompi: u64, decimals: u32) -> String { + sompi_to_unit(sompi, decimals).separated_string() +} + #[inline] pub fn sompi_to_kaspa_string_with_trailing_zeroes(sompi: u64) -> String { separated_float!(format!("{:.8}", sompi_to_kaspa(sompi))) @@ -127,8 +131,7 @@ fn str_to_sompi(amount: &str) -> Result { Ok(integer + decimal) } -fn str_to_sompi_with_decimals(amount: &str, decimals: Option) -> Result { - let decimals = decimals.unwrap_or(8); +fn str_to_unit(amount: &str, decimals: u32) -> Result { let sompi_per_unit = 10u64.pow(decimals); // Check if the amount contains a decimal point, if doesn't return value directly. diff --git a/wallet/core/src/wasm/utils.rs b/wallet/core/src/wasm/utils.rs index 6ec66e62e1..c5cad8ac88 100644 --- a/wallet/core/src/wasm/utils.rs +++ b/wallet/core/src/wasm/utils.rs @@ -16,8 +16,17 @@ extern "C" { /// can be used to parse user input. /// @category Wallet SDK #[wasm_bindgen(js_name = "kaspaToSompi")] -pub fn kaspa_to_sompi(kaspa: String, decimals: Option) -> Option { - crate::utils::try_kaspa_str_to_sompi_with_decimals(kaspa, decimals).ok().flatten().map(Into::into) +pub fn kaspa_to_sompi(kaspa: String) -> Option { + crate::utils::try_kaspa_str_to_sompi(kaspa).ok().flatten().map(Into::into) +} + +/// Convert a Kaspa string to a specific unit represented by bigint. +/// This function provides correct precision handling and +/// can be used to parse user input. +/// @category Wallet SDK +#[wasm_bindgen(js_name = "kaspaToUnit")] +pub fn kaspa_to_unit(kaspa: String, decimals: u32) -> Option { + crate::utils::try_kaspa_str_to_unit(kaspa, decimals).ok().flatten().map(Into::into) } /// @@ -31,6 +40,17 @@ pub fn sompi_to_kaspa_string(sompi: ISompiToKaspa) -> Result { Ok(crate::utils::sompi_to_kaspa_string(sompi)) } +/// +/// Convert Sompi to a string representation of an unit in Kaspa. +/// +/// @category Wallet SDK +/// +#[wasm_bindgen(js_name = "sompiToUnitString")] +pub fn sompi_to_unit_string(sompi: ISompiToKaspa, decimals: u32) -> Result { + let sompi = sompi.try_as_u64()?; + Ok(crate::utils::sompi_to_unit_string(sompi, decimals)) +} + /// /// Format a Sompi amount to a string representation of the amount in Kaspa with a suffix /// based on the network type (e.g. `KAS` for mainnet, `TKAS` for testnet, From 964ab1fff6e74f130ee59ea079dde243b364a4a0 Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Sat, 28 Sep 2024 21:06:07 +0300 Subject: [PATCH 08/18] Move function to somewhere which makes more sense --- wallet/core/src/utils.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/wallet/core/src/utils.rs b/wallet/core/src/utils.rs index e433bebf1b..a5890d047a 100644 --- a/wallet/core/src/utils.rs +++ b/wallet/core/src/utils.rs @@ -45,13 +45,6 @@ pub fn sompi_to_kaspa(sompi: u64) -> f64 { sompi as f64 / SOMPI_PER_KASPA as f64 } -#[inline] -pub fn sompi_to_unit(sompi: u64, decimals: u32) -> f64 { - let sompi_per_unit = 10u64.pow(decimals); - - sompi as f64 / sompi_per_unit as f64 -} - #[inline] pub fn kaspa_to_sompi(kaspa: f64) -> u64 { (kaspa * SOMPI_PER_KASPA as f64) as u64 @@ -62,6 +55,13 @@ pub fn sompi_to_kaspa_string(sompi: u64) -> String { sompi_to_kaspa(sompi).separated_string() } +#[inline] +pub fn sompi_to_unit(sompi: u64, decimals: u32) -> f64 { + let sompi_per_unit = 10u64.pow(decimals); + + sompi as f64 / sompi_per_unit as f64 +} + #[inline] pub fn sompi_to_unit_string(sompi: u64, decimals: u32) -> String { sompi_to_unit(sompi, decimals).separated_string() From 8beea2060ae53f2cb882b67ddf71edae7bd922d2 Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Mon, 30 Sep 2024 02:45:13 +0300 Subject: [PATCH 09/18] Try removing whitespace? --- wallet/keys/src/prelude.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/keys/src/prelude.rs b/wallet/keys/src/prelude.rs index 1aed7c5353..439f4c701d 100644 --- a/wallet/keys/src/prelude.rs +++ b/wallet/keys/src/prelude.rs @@ -7,4 +7,4 @@ pub use crate::publickey::*; pub use crate::secret::*; pub use crate::types::*; pub use crate::xprv::*; -pub use crate::xpub::*; +pub use crate::xpub::*; \ No newline at end of file From 163cb3e0ce8d0799bf8a0eef5983d17aa2d2fcc1 Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Mon, 30 Sep 2024 02:46:55 +0300 Subject: [PATCH 10/18] Add whitespace back --- wallet/keys/src/prelude.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/keys/src/prelude.rs b/wallet/keys/src/prelude.rs index 439f4c701d..1aed7c5353 100644 --- a/wallet/keys/src/prelude.rs +++ b/wallet/keys/src/prelude.rs @@ -7,4 +7,4 @@ pub use crate::publickey::*; pub use crate::secret::*; pub use crate::types::*; pub use crate::xprv::*; -pub use crate::xpub::*; \ No newline at end of file +pub use crate::xpub::*; From 037144d9f31689809e675ca5db2d1a5f9730868a Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Sat, 5 Oct 2024 21:30:43 +0300 Subject: [PATCH 11/18] Address.validate with network (prefix) --- crypto/addresses/src/lib.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/crypto/addresses/src/lib.rs b/crypto/addresses/src/lib.rs index 8aca863866..13f3a005b7 100644 --- a/crypto/addresses/src/lib.rs +++ b/crypto/addresses/src/lib.rs @@ -230,8 +230,19 @@ impl Address { } #[wasm_bindgen(js_name=validate)] - pub fn validate(address: &str) -> bool { - Self::try_from(address).is_ok() + pub fn validate(address: &str, network: Option) -> bool { + let address = Self::try_from(address).ok(); + + if let Some(address) = address { + if let Some(network) = network { + if let Ok(prefix) = Prefix::try_from(network.as_str()) { + return address.prefix == prefix; + } + } + return true; + } + + false } /// Convert an address to a string. From 13dd2eefc339f125230662564b9dd2ffb1f9f635 Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Sun, 6 Oct 2024 21:21:58 +0300 Subject: [PATCH 12/18] Panic if network is invalid on validate --- crypto/addresses/src/lib.rs | 5 ++--- wallet/keys/src/prelude.rs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crypto/addresses/src/lib.rs b/crypto/addresses/src/lib.rs index 13f3a005b7..e34cdbfbe0 100644 --- a/crypto/addresses/src/lib.rs +++ b/crypto/addresses/src/lib.rs @@ -235,9 +235,8 @@ impl Address { if let Some(address) = address { if let Some(network) = network { - if let Ok(prefix) = Prefix::try_from(network.as_str()) { - return address.prefix == prefix; - } + let prefix = Prefix::try_from(network.as_str()).unwrap(); + return address.prefix == prefix; } return true; } diff --git a/wallet/keys/src/prelude.rs b/wallet/keys/src/prelude.rs index 1aed7c5353..439f4c701d 100644 --- a/wallet/keys/src/prelude.rs +++ b/wallet/keys/src/prelude.rs @@ -7,4 +7,4 @@ pub use crate::publickey::*; pub use crate::secret::*; pub use crate::types::*; pub use crate::xprv::*; -pub use crate::xpub::*; +pub use crate::xpub::*; \ No newline at end of file From b84046dce5960405d96d56e8b5b4e2decb69a1d5 Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Sun, 6 Oct 2024 22:33:42 +0300 Subject: [PATCH 13/18] Add whitespace back... again --- wallet/keys/src/prelude.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/keys/src/prelude.rs b/wallet/keys/src/prelude.rs index 439f4c701d..1aed7c5353 100644 --- a/wallet/keys/src/prelude.rs +++ b/wallet/keys/src/prelude.rs @@ -7,4 +7,4 @@ pub use crate::publickey::*; pub use crate::secret::*; pub use crate::types::*; pub use crate::xprv::*; -pub use crate::xpub::*; \ No newline at end of file +pub use crate::xpub::*; From ea230c52ec06c15749f3649838d08345511c724a Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Wed, 9 Oct 2024 23:09:16 +0300 Subject: [PATCH 14/18] Use expect on prefix check --- crypto/addresses/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crypto/addresses/src/lib.rs b/crypto/addresses/src/lib.rs index e34cdbfbe0..df98ab3998 100644 --- a/crypto/addresses/src/lib.rs +++ b/crypto/addresses/src/lib.rs @@ -230,12 +230,12 @@ impl Address { } #[wasm_bindgen(js_name=validate)] - pub fn validate(address: &str, network: Option) -> bool { + pub fn validate(address: &str, prefix: Option) -> bool { let address = Self::try_from(address).ok(); if let Some(address) = address { - if let Some(network) = network { - let prefix = Prefix::try_from(network.as_str()).unwrap(); + if let Some(prefix) = prefix { + let prefix = Prefix::try_from(prefix.as_str()).expect("Address::verify() - invalid network prefix"); return address.prefix == prefix; } return true; From fb61ea9c692a0d2628b2b6cbb8420a40fb875f09 Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Sat, 19 Oct 2024 22:37:55 +0300 Subject: [PATCH 15/18] Revert "Address.validate with network (prefix)" --- crypto/addresses/src/lib.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/crypto/addresses/src/lib.rs b/crypto/addresses/src/lib.rs index df98ab3998..8aca863866 100644 --- a/crypto/addresses/src/lib.rs +++ b/crypto/addresses/src/lib.rs @@ -230,18 +230,8 @@ impl Address { } #[wasm_bindgen(js_name=validate)] - pub fn validate(address: &str, prefix: Option) -> bool { - let address = Self::try_from(address).ok(); - - if let Some(address) = address { - if let Some(prefix) = prefix { - let prefix = Prefix::try_from(prefix.as_str()).expect("Address::verify() - invalid network prefix"); - return address.prefix == prefix; - } - return true; - } - - false + pub fn validate(address: &str) -> bool { + Self::try_from(address).is_ok() } /// Convert an address to a string. From bc1ddae5153c4152a6959bd9da8d659d5a53f740 Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Wed, 23 Oct 2024 02:11:53 +0300 Subject: [PATCH 16/18] =?UTF-8?q?Add=20`=C3=ACd``=20getter=20to=20UtxoCont?= =?UTF-8?q?ext?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wallet/core/src/wasm/utxo/context.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/wallet/core/src/wasm/utxo/context.rs b/wallet/core/src/wasm/utxo/context.rs index 3298a4829e..6fd7b37846 100644 --- a/wallet/core/src/wasm/utxo/context.rs +++ b/wallet/core/src/wasm/utxo/context.rs @@ -8,6 +8,7 @@ use kaspa_addresses::AddressOrStringArrayT; use kaspa_consensus_client::UtxoEntryReferenceArrayT; use kaspa_hashes::Hash; use kaspa_wallet_macros::declare_typescript_wasm_interface as declare; +use kaspa_wasm_core::types::HexString; declare! { IUtxoContextArgs, @@ -147,6 +148,12 @@ impl UtxoContext { self.inner().clear().await } + /// Deterministic ID of the context, allows differentiation across event notifications. + #[wasm_bindgen(getter, js_name = "id")] + pub fn id(&self) -> HexString { + self.inner.id().to_hex().into() + } + #[wasm_bindgen(getter, js_name = "isActive")] pub fn active(&self) -> bool { let processor = self.inner().processor(); From 477819576af7df7435f6209b9f7a421c98661fd9 Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Sat, 26 Oct 2024 04:01:42 +0300 Subject: [PATCH 17/18] Random privateKey generation --- wallet/keys/src/privatekey.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/wallet/keys/src/privatekey.rs b/wallet/keys/src/privatekey.rs index 554bdf36e3..3f83052c1a 100644 --- a/wallet/keys/src/privatekey.rs +++ b/wallet/keys/src/privatekey.rs @@ -5,6 +5,7 @@ use crate::imports::*; use crate::keypair::Keypair; use js_sys::{Array, Uint8Array}; +use rand::thread_rng; /// Data structure that envelops a Private Key. /// @category Wallet SDK @@ -39,6 +40,11 @@ impl PrivateKey { pub fn try_new(key: &str) -> Result { Ok(Self { inner: secp256k1::SecretKey::from_str(key)? }) } + + #[wasm_bindgen(js_name = random)] + pub fn create_new() -> PrivateKey { + Self { inner: secp256k1::SecretKey::new(&mut thread_rng()) } + } } impl PrivateKey { @@ -49,13 +55,6 @@ impl PrivateKey { #[wasm_bindgen] impl PrivateKey { - /// Returns the [`PrivateKey`] key encoded as a hex string. - #[wasm_bindgen(js_name = toString)] - pub fn to_hex(&self) -> String { - use kaspa_utils::hex::ToHex; - self.secret_bytes().to_vec().to_hex() - } - /// Generate a [`Keypair`] from this [`PrivateKey`]. #[wasm_bindgen(js_name = toKeypair)] pub fn to_keypair(&self) -> Result { @@ -91,6 +90,13 @@ impl PrivateKey { let address = Address::new(network.try_into()?, AddressVersion::PubKeyECDSA, &payload); Ok(address) } + + /// Returns the [`PrivateKey`] key encoded as a hex string. + #[wasm_bindgen(js_name = toString)] + pub fn to_hex(&self) -> String { + use kaspa_utils::hex::ToHex; + self.secret_bytes().to_vec().to_hex() + } } impl TryCastFromJs for PrivateKey { From 3f993d8e6df64f8983823ab4f20622020534f9d2 Mon Sep 17 00:00:00 2001 From: KaffinPX Date: Mon, 28 Oct 2024 17:12:55 +0300 Subject: [PATCH 18/18] Allow address as string on fromAddress of XOnlyPublicKey --- wallet/keys/src/error.rs | 3 +++ wallet/keys/src/publickey.rs | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/wallet/keys/src/error.rs b/wallet/keys/src/error.rs index 0059a09420..d599d6c1e8 100644 --- a/wallet/keys/src/error.rs +++ b/wallet/keys/src/error.rs @@ -66,6 +66,9 @@ pub enum Error { #[error("Invalid UTF-8 sequence")] Utf8(#[from] std::str::Utf8Error), + + #[error(transparent)] + AddressError(#[from] kaspa_addresses::AddressError), } impl Error { diff --git a/wallet/keys/src/publickey.rs b/wallet/keys/src/publickey.rs index 235eb80804..423969238f 100644 --- a/wallet/keys/src/publickey.rs +++ b/wallet/keys/src/publickey.rs @@ -19,6 +19,7 @@ use crate::imports::*; +use kaspa_addresses::AddressT; use kaspa_consensus_core::network::NetworkType; use ripemd::{Digest, Ripemd160}; use sha2::Sha256; @@ -236,8 +237,9 @@ impl XOnlyPublicKey { } #[wasm_bindgen(js_name = fromAddress)] - pub fn from_address(address: &Address) -> Result { - Ok(secp256k1::XOnlyPublicKey::from_slice(&address.payload)?.into()) + pub fn from_address(address: &AddressT) -> Result { + let address = Address::try_cast_from(address)?; + Ok(secp256k1::XOnlyPublicKey::from_slice(&address.as_ref().payload)?.into()) } }