From a62d94a60d87523928469f73142313ddcde9baef Mon Sep 17 00:00:00 2001
From: sneurlax <sneurlax@gmail.com>
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<String, dynamic> 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 <sneurlax@gmail.com>
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<void> updateSentCachedTxData(Map<String, dynamic> txData);
+
+  // Certain outputs return address as an array/list of strings like List<String> ["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 <sneurlax@gmail.com>
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 <sneurlax@gmail.com>
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 <sneurlax@gmail.com>
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] = <String>[];
             }
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] = <String>[];
             }
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] = <String>[];
             }
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] = <String>[];
             }
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 <sneurlax@gmail.com>
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<String>
+  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<void> updateSentCachedTxData(Map<String, dynamic> 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] = <String>[];
@@ -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<Map<String, dynamic>> 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 <sneurlax@gmail.com>
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<String> ["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<String>
   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) {