From a62d94a60d87523928469f73142313ddcde9baef Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 5 Jan 2023 10:49:55 -0600 Subject: [PATCH 1/7] handle 0 amounts --- lib/models/paymint/transactions_model.dart | 33 ++++------------------ 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/lib/models/paymint/transactions_model.dart b/lib/models/paymint/transactions_model.dart index 6eba877c4..0a1567105 100644 --- a/lib/models/paymint/transactions_model.dart +++ b/lib/models/paymint/transactions_model.dart @@ -362,12 +362,16 @@ class Input { class Output { // @HiveField(0) final String? scriptpubkey; + // @HiveField(1) final String? scriptpubkeyAsm; + // @HiveField(2) final String? scriptpubkeyType; + // @HiveField(3) final String scriptpubkeyAddress; + // @HiveField(4) final int value; @@ -381,9 +385,6 @@ class Output { factory Output.fromJson(Map json) { // TODO determine if any of this code is needed. try { - // Particl has different tx types that need to be detected and handled here - // if (json.containsKey('scriptPubKey') as bool) { - // output is transparent final address = json["scriptPubKey"]["addresses"] == null ? json['scriptPubKey']['type'] as String : json["scriptPubKey"]["addresses"][0] as String; @@ -392,35 +393,13 @@ class Output { scriptpubkeyAsm: json['scriptPubKey']['asm'] as String?, scriptpubkeyType: json['scriptPubKey']['type'] as String?, scriptpubkeyAddress: address, - value: (Decimal.parse(json["value"].toString()) * + value: (Decimal.parse( + (json["value"] != null ? json["value"] : 0).toString()) * Decimal.fromInt(Constants.satsPerCoin(Coin .firo))) // dirty hack but we need 8 decimal places here to keep consistent data structure .toBigInt() .toInt(), ); - // } /* else if (json.containsKey('ct_fee') as bool) { - // // or type: data - // // output is blinded (CT) - // } else if (json.containsKey('rangeproof') as bool) { - // // or valueCommitment or type: anon - // // output is private (RingCT) - // } */ - // else { - // // TODO detect staking - // // TODO handle CT, RingCT, and staking accordingly - // // print("transaction not supported: ${json}"); - // return Output( - // // Return output object with null values; allows wallet history to be built - // scriptpubkey: "", - // scriptpubkeyAsm: "", - // scriptpubkeyType: "", - // scriptpubkeyAddress: "", - // value: (Decimal.parse(0.toString()) * - // Decimal.fromInt(Constants.satsPerCoin(Coin - // .firo))) // dirty hack but we need 8 decimal places here to keep consistent data structure - // .toBigInt() - // .toInt()); - // } } catch (s, e) { return Output( // Return output object with null values; allows wallet history to be built From a902c7705780ea622ce2d90ad4906bf8be786024 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 5 Jan 2023 10:50:36 -0600 Subject: [PATCH 2/7] add getAddress helper func for transactions with odd outputs OP_RETURN and some other output types can cause addresses to be placed in a list of strings or as a string under a different key; this handles that case --- lib/services/coins/coin_service.dart | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/services/coins/coin_service.dart b/lib/services/coins/coin_service.dart index a690157ee..90a25a057 100644 --- a/lib/services/coins/coin_service.dart +++ b/lib/services/coins/coin_service.dart @@ -307,4 +307,24 @@ abstract class CoinServiceAPI { // used for electrumx coins Future updateSentCachedTxData(Map txData); + + // Certain outputs return address as an array/list of strings like List ["addresses"][0], some return it as a string like String ["address"] + String? getAddress(dynamic output) { + String? address; + if (output.containsKey('scriptPubKey') as bool) { + // Make sure the key exists before using it + if (output["scriptPubKey"].containsKey('address') as bool) { + address = output["scriptPubKey"]["address"] as String?; + } else if (output["scriptPubKey"].containsKey('addresses') as bool) { + address = output["scriptPubKey"]["addresses"][0] as String?; + // TODO determine cases in which there are multiple addresses in the array + } + } /*else { + // TODO detect cases in which no scriptPubKey exists + Logging.instance.log("output type not detected; output: ${output}", + level: LogLevel.Info); + }*/ + + return address; + } } From 4d107273604e38cdd9f193cdf8dacf4162f0ed44 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 5 Jan 2023 10:50:45 -0600 Subject: [PATCH 3/7] use getAddress with BCH --- .../coins/bitcoincash/bitcoincash_wallet.dart | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/services/coins/bitcoincash/bitcoincash_wallet.dart b/lib/services/coins/bitcoincash/bitcoincash_wallet.dart index b18a97186..c6dc8ca03 100644 --- a/lib/services/coins/bitcoincash/bitcoincash_wallet.dart +++ b/lib/services/coins/bitcoincash/bitcoincash_wallet.dart @@ -1174,7 +1174,8 @@ class BitcoinCashWallet extends CoinServiceAPI { final priceData = await _priceAPI.getPricesAnd24hChange(baseCurrency: _prefs.currency); Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero; - final locale = Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; + final locale = + Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; final String worthNow = Format.localizedStringAsFixed( value: ((currentPrice * Decimal.fromInt(txData["recipientAmt"] as int)) / @@ -2300,7 +2301,7 @@ class BitcoinCashWallet extends CoinServiceAPI { for (final out in tx["vout"] as List) { if (prevOut == out["n"]) { - final address = out["scriptPubKey"]["addresses"][0] as String?; + final address = getAddress(out); if (address != null) { sendersArray.add(address); } @@ -2311,7 +2312,7 @@ class BitcoinCashWallet extends CoinServiceAPI { Logging.instance.log("sendersArray: $sendersArray", level: LogLevel.Info); for (final output in txObject["vout"] as List) { - final address = output["scriptPubKey"]["addresses"][0] as String?; + final address = getAddress(output); if (address != null) { recipientsArray.add(address); } @@ -2352,7 +2353,7 @@ class BitcoinCashWallet extends CoinServiceAPI { int totalOutput = 0; for (final output in txObject["vout"] as List) { - final address = output["scriptPubKey"]["addresses"][0]; + final address = getAddress(output); final value = output["value"]; final _value = (Decimal.parse(value.toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) @@ -2377,7 +2378,7 @@ class BitcoinCashWallet extends CoinServiceAPI { // add up received tx value for (final output in txObject["vout"] as List) { - final address = output["scriptPubKey"]["addresses"][0]; + final address = getAddress(output); if (address != null) { final value = (Decimal.parse(output["value"].toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) @@ -2902,7 +2903,7 @@ class BitcoinCashWallet extends CoinServiceAPI { for (final output in tx["vout"] as List) { final n = output["n"]; if (n != null && n == utxosToUse[i].vout) { - String address = output["scriptPubKey"]["addresses"][0] as String; + String address = getAddress(output) as String; if (bitbox.Address.detectFormat(address) == bitbox.Address.formatCashAddr) { if (validateCashAddr(address)) { From fb7c58f60a0e8935e9fe3d80012ebfced7be5ebd Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 5 Jan 2023 12:12:38 -0600 Subject: [PATCH 4/7] handle 0 amounts use null operator where applicable --- lib/models/paymint/transactions_model.dart | 2 +- lib/services/coins/bitcoincash/bitcoincash_wallet.dart | 2 +- lib/services/coins/firo/firo_wallet.dart | 4 ++-- lib/services/coins/litecoin/litecoin_wallet.dart | 5 ++--- lib/services/coins/namecoin/namecoin_wallet.dart | 4 ++-- lib/services/coins/particl/particl_wallet.dart | 4 ++-- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/models/paymint/transactions_model.dart b/lib/models/paymint/transactions_model.dart index 0a1567105..1d40ac01c 100644 --- a/lib/models/paymint/transactions_model.dart +++ b/lib/models/paymint/transactions_model.dart @@ -394,7 +394,7 @@ class Output { scriptpubkeyType: json['scriptPubKey']['type'] as String?, scriptpubkeyAddress: address, value: (Decimal.parse( - (json["value"] != null ? json["value"] : 0).toString()) * + (json["value"] ?? 0).toString()) * Decimal.fromInt(Constants.satsPerCoin(Coin .firo))) // dirty hack but we need 8 decimal places here to keep consistent data structure .toBigInt() diff --git a/lib/services/coins/bitcoincash/bitcoincash_wallet.dart b/lib/services/coins/bitcoincash/bitcoincash_wallet.dart index c6dc8ca03..ebd2e2df8 100644 --- a/lib/services/coins/bitcoincash/bitcoincash_wallet.dart +++ b/lib/services/coins/bitcoincash/bitcoincash_wallet.dart @@ -2380,7 +2380,7 @@ class BitcoinCashWallet extends CoinServiceAPI { for (final output in txObject["vout"] as List) { final address = getAddress(output); if (address != null) { - final value = (Decimal.parse(output["value"].toString()) * + final value = (Decimal.parse((output["value"] ?? 0).toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) .toBigInt() .toInt(); diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 5d29bd0a9..58ac019b0 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -3320,7 +3320,7 @@ class FiroWallet extends CoinServiceAPI { for (final output in txObject["vout"] as List) { final addresses = output["scriptPubKey"]["addresses"] as List?; - final value = output["value"]; + final value = output["value"] ?? 0; if (addresses != null && addresses.isNotEmpty) { final address = addresses[0] as String; if (value != null) { @@ -3359,7 +3359,7 @@ class FiroWallet extends CoinServiceAPI { final addresses = output["scriptPubKey"]["addresses"] as List?; if (addresses != null && addresses.isNotEmpty) { final address = addresses[0] as String; - final value = output["value"]; + final value = output["value"] ?? 0; // Logging.instance.log(address + value.toString()); if (allAddresses.contains(address)) { diff --git a/lib/services/coins/litecoin/litecoin_wallet.dart b/lib/services/coins/litecoin/litecoin_wallet.dart index 269f59610..f027ab2db 100644 --- a/lib/services/coins/litecoin/litecoin_wallet.dart +++ b/lib/services/coins/litecoin/litecoin_wallet.dart @@ -2565,8 +2565,7 @@ class LitecoinWallet extends CoinServiceAPI { for (final output in txObject["vout"] as List) { final String address = - output["scriptPubKey"]!["addresses"][0] as String; - final value = output["value"]!; + final value = output["value"] ?? 0; final _value = (Decimal.parse(value.toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) .toBigInt() @@ -2592,7 +2591,7 @@ class LitecoinWallet extends CoinServiceAPI { for (final output in txObject["vout"] as List) { final address = output["scriptPubKey"]["addresses"][0]; if (address != null) { - final value = (Decimal.parse(output["value"].toString()) * + final value = (Decimal.parse((output["value"] ?? 0).toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) .toBigInt() .toInt(); diff --git a/lib/services/coins/namecoin/namecoin_wallet.dart b/lib/services/coins/namecoin/namecoin_wallet.dart index 85fea61ad..f20bfa36f 100644 --- a/lib/services/coins/namecoin/namecoin_wallet.dart +++ b/lib/services/coins/namecoin/namecoin_wallet.dart @@ -2554,7 +2554,7 @@ class NamecoinWallet extends CoinServiceAPI { for (final output in txObject["vout"] as List) { Logging.instance.log(output, level: LogLevel.Info); final address = output["scriptPubKey"]["address"]; - final value = output["value"]; + final value = output["value"] ?? 0; final _value = (Decimal.parse(value.toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) .toBigInt() @@ -2583,7 +2583,7 @@ class NamecoinWallet extends CoinServiceAPI { address = output["scriptPubKey"]["address"] as String?; } if (address != null) { - final value = (Decimal.parse(output["value"].toString()) * + final value = (Decimal.parse((output["value"] ?? 0).toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) .toBigInt() .toInt(); diff --git a/lib/services/coins/particl/particl_wallet.dart b/lib/services/coins/particl/particl_wallet.dart index fe535dbf0..897f5951f 100644 --- a/lib/services/coins/particl/particl_wallet.dart +++ b/lib/services/coins/particl/particl_wallet.dart @@ -2361,7 +2361,7 @@ class ParticlWallet extends CoinServiceAPI { try { final String address = output["scriptPubKey"]!["addresses"][0] as String; - final value = output["value"]!; + final value = output["value"] ?? 0; final _value = (Decimal.parse(value.toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) .toBigInt() @@ -2419,7 +2419,7 @@ class ParticlWallet extends CoinServiceAPI { try { final address = output["scriptPubKey"]["addresses"][0]; if (address != null) { - final value = (Decimal.parse(output["value"].toString()) * + final value = (Decimal.parse((output["value"] ?? 0).toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) .toBigInt() .toInt(); From 2dbd81fd4c19804dcae1cdc1c9efdcb9eca12732 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 5 Jan 2023 12:13:26 -0600 Subject: [PATCH 5/7] use getAddress with BTC, LTC, NMC, and PART --- .../coins/bitcoin/bitcoin_wallet.dart | 13 ++++++----- .../coins/dogecoin/dogecoin_wallet.dart | 13 ++++++----- .../coins/litecoin/litecoin_wallet.dart | 13 ++++++----- .../coins/namecoin/namecoin_wallet.dart | 23 ++++++------------- .../coins/particl/particl_wallet.dart | 9 ++++---- 5 files changed, 33 insertions(+), 38 deletions(-) diff --git a/lib/services/coins/bitcoin/bitcoin_wallet.dart b/lib/services/coins/bitcoin/bitcoin_wallet.dart index 1ed87681b..bfacb6b2a 100644 --- a/lib/services/coins/bitcoin/bitcoin_wallet.dart +++ b/lib/services/coins/bitcoin/bitcoin_wallet.dart @@ -1297,7 +1297,8 @@ class BitcoinWallet extends CoinServiceAPI { final priceData = await _priceAPI.getPricesAnd24hChange(baseCurrency: _prefs.currency); Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero; - final locale = Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; + final locale = + Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; final String worthNow = Format.localizedStringAsFixed( value: ((currentPrice * Decimal.fromInt(txData["recipientAmt"] as int)) / @@ -2502,7 +2503,7 @@ class BitcoinWallet extends CoinServiceAPI { for (final out in tx["vout"] as List) { if (prevOut == out["n"]) { - final address = out["scriptPubKey"]["address"] as String?; + final address = getAddress(out) as String?; if (address != null) { sendersArray.add(address); } @@ -2513,7 +2514,7 @@ class BitcoinWallet extends CoinServiceAPI { Logging.instance.log("sendersArray: $sendersArray", level: LogLevel.Info); for (final output in txObject["vout"] as List) { - final address = output["scriptPubKey"]["address"] as String?; + final address = getAddress(output); if (address != null) { recipientsArray.add(address); } @@ -2553,7 +2554,7 @@ class BitcoinWallet extends CoinServiceAPI { int totalOutput = 0; for (final output in txObject["vout"] as List) { - final String address = output["scriptPubKey"]!["address"] as String; + final String address = getAddress(output) as String; final value = output["value"]!; final _value = (Decimal.parse(value.toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) @@ -2578,7 +2579,7 @@ class BitcoinWallet extends CoinServiceAPI { // add up received tx value for (final output in txObject["vout"] as List) { - final address = output["scriptPubKey"]["address"]; + final address = getAddress(output); if (address != null) { final value = (Decimal.parse(output["value"].toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) @@ -3095,7 +3096,7 @@ class BitcoinWallet extends CoinServiceAPI { for (final output in tx["vout"] as List) { final n = output["n"]; if (n != null && n == utxosToUse[i].vout) { - final address = output["scriptPubKey"]["address"] as String; + final address = getAddress(output) as String; if (!addressTxid.containsKey(address)) { addressTxid[address] = []; } diff --git a/lib/services/coins/dogecoin/dogecoin_wallet.dart b/lib/services/coins/dogecoin/dogecoin_wallet.dart index 5b0da3fb3..f8a9809d1 100644 --- a/lib/services/coins/dogecoin/dogecoin_wallet.dart +++ b/lib/services/coins/dogecoin/dogecoin_wallet.dart @@ -1064,7 +1064,8 @@ class DogecoinWallet extends CoinServiceAPI { final priceData = await _priceAPI.getPricesAnd24hChange(baseCurrency: _prefs.currency); Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero; - final locale = Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; + final locale = + Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; final String worthNow = Format.localizedStringAsFixed( value: ((currentPrice * Decimal.fromInt(txData["recipientAmt"] as int)) / @@ -2113,7 +2114,7 @@ class DogecoinWallet extends CoinServiceAPI { for (final out in tx["vout"] as List) { if (prevOut == out["n"]) { - final address = out["scriptPubKey"]["addresses"][0] as String?; + final address = getAddress(out); if (address != null) { sendersArray.add(address); } @@ -2124,7 +2125,7 @@ class DogecoinWallet extends CoinServiceAPI { Logging.instance.log("sendersArray: $sendersArray", level: LogLevel.Info); for (final output in txObject["vout"] as List) { - final address = output["scriptPubKey"]["addresses"][0] as String?; + final address = getAddress(output); if (address != null) { recipientsArray.add(address); } @@ -2164,7 +2165,7 @@ class DogecoinWallet extends CoinServiceAPI { int totalOutput = 0; for (final output in txObject["vout"] as List) { - final address = output["scriptPubKey"]["addresses"][0]; + final address = getAddress(output); final value = output["value"]; final _value = (Decimal.parse(value.toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) @@ -2189,7 +2190,7 @@ class DogecoinWallet extends CoinServiceAPI { // add up received tx value for (final output in txObject["vout"] as List) { - final address = output["scriptPubKey"]["addresses"][0]; + final address = getAddress(output); if (address != null) { final value = (Decimal.parse(output["value"].toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) @@ -2712,7 +2713,7 @@ class DogecoinWallet extends CoinServiceAPI { for (final output in tx["vout"] as List) { final n = output["n"]; if (n != null && n == utxosToUse[i].vout) { - final address = output["scriptPubKey"]["addresses"][0] as String; + final address = getAddress(output) as String; if (!addressTxid.containsKey(address)) { addressTxid[address] = []; } diff --git a/lib/services/coins/litecoin/litecoin_wallet.dart b/lib/services/coins/litecoin/litecoin_wallet.dart index f027ab2db..6379ced7c 100644 --- a/lib/services/coins/litecoin/litecoin_wallet.dart +++ b/lib/services/coins/litecoin/litecoin_wallet.dart @@ -1299,7 +1299,8 @@ class LitecoinWallet extends CoinServiceAPI { final priceData = await _priceAPI.getPricesAnd24hChange(baseCurrency: _prefs.currency); Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero; - final locale = Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; + final locale = + Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; final String worthNow = Format.localizedStringAsFixed( value: ((currentPrice * Decimal.fromInt(txData["recipientAmt"] as int)) / @@ -2513,7 +2514,7 @@ class LitecoinWallet extends CoinServiceAPI { for (final out in tx["vout"] as List) { if (prevOut == out["n"]) { - final address = out["scriptPubKey"]["addresses"][0] as String?; + final address = getAddress(out) as String?; if (address != null) { sendersArray.add(address); } @@ -2524,7 +2525,7 @@ class LitecoinWallet extends CoinServiceAPI { Logging.instance.log("sendersArray: $sendersArray", level: LogLevel.Info); for (final output in txObject["vout"] as List) { - final address = output["scriptPubKey"]["addresses"][0] as String?; + final address = getAddress(output); if (address != null) { recipientsArray.add(address); } @@ -2564,7 +2565,7 @@ class LitecoinWallet extends CoinServiceAPI { int totalOutput = 0; for (final output in txObject["vout"] as List) { - final String address = + final String address = getAddress(output) as String; final value = output["value"] ?? 0; final _value = (Decimal.parse(value.toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) @@ -2589,7 +2590,7 @@ class LitecoinWallet extends CoinServiceAPI { // add up received tx value for (final output in txObject["vout"] as List) { - final address = output["scriptPubKey"]["addresses"][0]; + final address = getAddress(output); if (address != null) { final value = (Decimal.parse((output["value"] ?? 0).toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) @@ -3106,7 +3107,7 @@ class LitecoinWallet extends CoinServiceAPI { for (final output in tx["vout"] as List) { final n = output["n"]; if (n != null && n == utxosToUse[i].vout) { - final address = output["scriptPubKey"]["addresses"][0] as String; + final address = getAddress(output) as String; if (!addressTxid.containsKey(address)) { addressTxid[address] = []; } diff --git a/lib/services/coins/namecoin/namecoin_wallet.dart b/lib/services/coins/namecoin/namecoin_wallet.dart index f20bfa36f..eb0655b4f 100644 --- a/lib/services/coins/namecoin/namecoin_wallet.dart +++ b/lib/services/coins/namecoin/namecoin_wallet.dart @@ -1287,7 +1287,8 @@ class NamecoinWallet extends CoinServiceAPI { final priceData = await _priceAPI.getPricesAnd24hChange(baseCurrency: _prefs.currency); Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero; - final locale = Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; + final locale = + Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; final String worthNow = Format.localizedStringAsFixed( value: ((currentPrice * Decimal.fromInt(txData["recipientAmt"] as int)) / @@ -2494,11 +2495,7 @@ class NamecoinWallet extends CoinServiceAPI { for (final out in tx["vout"] as List) { if (prevOut == out["n"]) { - String? address = out["scriptPubKey"]["address"] as String?; - if (address == null && out["scriptPubKey"]["address"] != null) { - address = out["scriptPubKey"]["address"] as String?; - } - + String? address = getAddress(out); if (address != null) { sendersArray.add(address); } @@ -2509,10 +2506,7 @@ class NamecoinWallet extends CoinServiceAPI { Logging.instance.log("sendersArray: $sendersArray", level: LogLevel.Info); for (final output in txObject["vout"] as List) { - String? address = output["scriptPubKey"]["address"] as String?; - if (address == null && output["scriptPubKey"]["address"] != null) { - address = output["scriptPubKey"]["address"] as String?; - } + String? address = getAddress(output); if (address != null) { recipientsArray.add(address); } @@ -2553,7 +2547,7 @@ class NamecoinWallet extends CoinServiceAPI { for (final output in txObject["vout"] as List) { Logging.instance.log(output, level: LogLevel.Info); - final address = output["scriptPubKey"]["address"]; + final address = getAddress(output); final value = output["value"] ?? 0; final _value = (Decimal.parse(value.toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) @@ -2578,10 +2572,7 @@ class NamecoinWallet extends CoinServiceAPI { // add up received tx value for (final output in txObject["vout"] as List) { - String? address = output["scriptPubKey"]["address"] as String?; - if (address == null && output["scriptPubKey"]["address"] != null) { - address = output["scriptPubKey"]["address"] as String?; - } + String? address = getAddress(output); if (address != null) { final value = (Decimal.parse((output["value"] ?? 0).toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) @@ -3101,7 +3092,7 @@ class NamecoinWallet extends CoinServiceAPI { for (final output in tx["vout"] as List) { final n = output["n"]; if (n != null && n == utxosToUse[i].vout) { - final address = output["scriptPubKey"]["address"] as String; + final address = getAddress(output) as String; if (!addressTxid.containsKey(address)) { addressTxid[address] = []; } diff --git a/lib/services/coins/particl/particl_wallet.dart b/lib/services/coins/particl/particl_wallet.dart index 897f5951f..ff04d0fc4 100644 --- a/lib/services/coins/particl/particl_wallet.dart +++ b/lib/services/coins/particl/particl_wallet.dart @@ -1200,7 +1200,8 @@ class ParticlWallet extends CoinServiceAPI { final priceData = await _priceAPI.getPricesAnd24hChange(baseCurrency: _prefs.currency); Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero; - final locale = Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; + final locale = + Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; final String worthNow = Format.localizedStringAsFixed( value: ((currentPrice * Decimal.fromInt(txData["recipientAmt"] as int)) / @@ -2288,7 +2289,7 @@ class ParticlWallet extends CoinServiceAPI { for (final out in tx["vout"] as List) { if (prevOut == out["n"]) { - final address = out["scriptPubKey"]["addresses"][0] as String?; + final address = getAddress(out); if (address != null) { sendersArray.add(address); } @@ -2302,7 +2303,7 @@ class ParticlWallet extends CoinServiceAPI { // Particl has different tx types that need to be detected and handled here if (output.containsKey('scriptPubKey') as bool) { // Logging.instance.log("output is transparent", level: LogLevel.Info); - final address = output["scriptPubKey"]["addresses"][0] as String?; + final address = getAddress(output); if (address != null) { recipientsArray.add(address); } @@ -2417,7 +2418,7 @@ class ParticlWallet extends CoinServiceAPI { // add up received tx value for (final output in txObject["vout"] as List) { try { - final address = output["scriptPubKey"]["addresses"][0]; + final address = getAddress(output); if (address != null) { final value = (Decimal.parse((output["value"] ?? 0).toString()) * Decimal.fromInt(Constants.satsPerCoin(coin))) From 2495673f793bce0a9f74de0be42df7a19491cc4d Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 5 Jan 2023 12:13:54 -0600 Subject: [PATCH 6/7] add getAddresses helper func for Firo don't assume keys exist without checking them --- lib/services/coins/coin_service.dart | 18 ++++++++++++++++ lib/services/coins/firo/firo_wallet.dart | 26 ++++++++++++++---------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/lib/services/coins/coin_service.dart b/lib/services/coins/coin_service.dart index 90a25a057..7a43c71da 100644 --- a/lib/services/coins/coin_service.dart +++ b/lib/services/coins/coin_service.dart @@ -327,4 +327,22 @@ abstract class CoinServiceAPI { return address; } + + // Firo wants an array/list of address strings like List + List? getAddresses(dynamic output) { + List? addresses; + if (output.containsKey('scriptPubKey') as bool) { + if (output["scriptPubKey"].containsKey('addresses') as bool) { + addresses = output["scriptPubKey"]["addresses"] as List?; + } else if (output["scriptPubKey"].containsKey('address') as bool) { + addresses = [output["scriptPubKey"]["address"]]; + } + } /*else { + // TODO detect cases in which no scriptPubKey exists + Logging.instance.log("output type not detected; output: ${output}", + level: LogLevel.Info); + }*/ + + return addresses; + } } diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 58ac019b0..89d912fe1 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -883,7 +883,8 @@ class FiroWallet extends CoinServiceAPI { @override Future updateSentCachedTxData(Map txData) async { final currentPrice = await firoPrice; - final locale = Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; + final locale = + Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; final String worthNow = Format.localizedStringAsFixed( value: ((currentPrice * Decimal.fromInt(txData["recipientAmt"] as int)) / @@ -1691,7 +1692,7 @@ class FiroWallet extends CoinServiceAPI { for (final output in tx["vout"] as List) { final n = output["n"]; if (n != null && n == utxosToUse[i].vout) { - final address = output["scriptPubKey"]["addresses"][0] as String; + final address = getAddress(output) as String; if (!addressTxid.containsKey(address)) { addressTxid[address] = []; @@ -2654,8 +2655,7 @@ class FiroWallet extends CoinServiceAPI { final vouts = tx["vout"] as List?; if (vouts != null && outputIndex < vouts.length) { - final address = - vouts[outputIndex]["scriptPubKey"]["addresses"][0] as String?; + final address = getAddress(vouts[outputIndex]); if (address != null) { addressesToDerive.add(address); } @@ -2756,7 +2756,8 @@ class FiroWallet extends CoinServiceAPI { var price = await firoPrice; var builtHex = txb.build(); // return builtHex; - final locale =Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; + final locale = + Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; return { "transaction": builtHex, "txid": txId, @@ -2810,7 +2811,8 @@ class FiroWallet extends CoinServiceAPI { final currentPrice = await firoPrice; // Grab the most recent information on all the joinsplits - final locale = Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; + final locale = + Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; final updatedJSplit = await getJMintTransactions(cachedElectrumXClient, joinsplits, _prefs.currency, coin, currentPrice, locale!); @@ -3249,7 +3251,8 @@ class FiroWallet extends CoinServiceAPI { final currentPrice = await firoPrice; final List> midSortedArray = []; - final locale = Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; + final locale = + Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; Logging.instance.log("refresh the txs", level: LogLevel.Info); for (final txObject in allTransactions) { @@ -3275,7 +3278,7 @@ class FiroWallet extends CoinServiceAPI { // Logging.instance.log("sendersArray: $sendersArray"); for (final output in txObject["vout"] as List) { - final addresses = output["scriptPubKey"]["addresses"] as List?; + final addresses = getAddresses(output); if (addresses != null && addresses.isNotEmpty) { recipientsArray.add(addresses[0] as String); } @@ -3319,7 +3322,7 @@ class FiroWallet extends CoinServiceAPI { } for (final output in txObject["vout"] as List) { - final addresses = output["scriptPubKey"]["addresses"] as List?; + final addresses = getAddresses(output); final value = output["value"] ?? 0; if (addresses != null && addresses.isNotEmpty) { final address = addresses[0] as String; @@ -4375,7 +4378,8 @@ class FiroWallet extends CoinServiceAPI { final lelantusEntry = await _getLelantusEntry(); final anonymitySets = await fetchAnonymitySets(); final locktime = await getBlockHead(electrumXClient); - final locale = Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; + final locale = + Platform.isWindows ? "en_US" : await Devicelocale.currentLocale; ReceivePort receivePort = await getIsolate({ "function": "createJoinSplit", @@ -4742,7 +4746,7 @@ class FiroWallet extends CoinServiceAPI { } tx["amount"] = tx["vout"][sendIndex]["value"]; - tx["address"] = tx["vout"][sendIndex]["scriptPubKey"]["addresses"][0]; + tx["address"] = getAddress(tx["vout"][sendIndex]) as String; tx["fees"] = tx["vin"][0]["nFees"]; tx["inputSize"] = tx["vin"].length; From 3794b18ba63d561c3b2a367116584ff419bf31f4 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 6 Jan 2023 11:15:32 -0600 Subject: [PATCH 7/7] comment update --- lib/services/coins/coin_service.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/services/coins/coin_service.dart b/lib/services/coins/coin_service.dart index 7a43c71da..f0c154ba7 100644 --- a/lib/services/coins/coin_service.dart +++ b/lib/services/coins/coin_service.dart @@ -310,6 +310,8 @@ abstract class CoinServiceAPI { // Certain outputs return address as an array/list of strings like List ["addresses"][0], some return it as a string like String ["address"] String? getAddress(dynamic output) { + // Julian's code from https://github.com/cypherstack/stack_wallet/blob/35a8172d35f1b5cdbd22f0d56c4db02f795fd032/lib/services/coins/coin_paynym_extension.dart#L170 wins codegolf for this, I'd love to commit it now but need to retest this section ... should make unit tests for this case + // final String? address = output["scriptPubKey"]?["addresses"]?[0] as String? ?? output["scriptPubKey"]?["address"] as String?; String? address; if (output.containsKey('scriptPubKey') as bool) { // Make sure the key exists before using it @@ -330,6 +332,8 @@ abstract class CoinServiceAPI { // Firo wants an array/list of address strings like List List? getAddresses(dynamic output) { + // Inspired by Julian's code as referenced above, need to test before committing + // final List? addresses = output["scriptPubKey"]?["addresses"] as List? ?? [output["scriptPubKey"]?["address"]] as List?; List? addresses; if (output.containsKey('scriptPubKey') as bool) { if (output["scriptPubKey"].containsKey('addresses') as bool) {