diff --git a/asset_sources/default_themes/stack_duo/dark.zip b/asset_sources/default_themes/stack_duo/dark.zip
index c6d417500..1e5f6136e 100644
Binary files a/asset_sources/default_themes/stack_duo/dark.zip and b/asset_sources/default_themes/stack_duo/dark.zip differ
diff --git a/asset_sources/default_themes/stack_duo/light.zip b/asset_sources/default_themes/stack_duo/light.zip
index d94ce2ac8..bc7a95771 100644
Binary files a/asset_sources/default_themes/stack_duo/light.zip and b/asset_sources/default_themes/stack_duo/light.zip differ
diff --git a/asset_sources/default_themes/stack_wallet/dark.zip b/asset_sources/default_themes/stack_wallet/dark.zip
index c6d417500..1e5f6136e 100644
Binary files a/asset_sources/default_themes/stack_wallet/dark.zip and b/asset_sources/default_themes/stack_wallet/dark.zip differ
diff --git a/asset_sources/default_themes/stack_wallet/light.zip b/asset_sources/default_themes/stack_wallet/light.zip
index d94ce2ac8..bc7a95771 100644
Binary files a/asset_sources/default_themes/stack_wallet/light.zip and b/asset_sources/default_themes/stack_wallet/light.zip differ
diff --git a/lib/pages/send_view/sub_widgets/sending_transaction_dialog.dart b/lib/pages/send_view/sub_widgets/sending_transaction_dialog.dart
index 710cbfa1c..c16792d11 100644
--- a/lib/pages/send_view/sub_widgets/sending_transaction_dialog.dart
+++ b/lib/pages/send_view/sub_widgets/sending_transaction_dialog.dart
@@ -13,6 +13,7 @@ import 'dart:io';
 import 'package:flutter/material.dart';
 import 'package:flutter_riverpod/flutter_riverpod.dart';
 import 'package:lottie/lottie.dart';
+
 import '../../../themes/coin_image_provider.dart';
 import '../../../themes/stack_colors.dart';
 import '../../../utilities/assets.dart';
@@ -64,6 +65,7 @@ class _RestoringDialogState extends ConsumerState<SendingTransactionDialog> {
 
     if (Util.isDesktop) {
       return DesktopDialog(
+        maxHeight: assetPath.endsWith(".gif") ? double.infinity : null,
         child: Padding(
           padding: const EdgeInsets.all(40),
           child: Column(
@@ -77,8 +79,10 @@ class _RestoringDialogState extends ConsumerState<SendingTransactionDialog> {
                 height: 40,
               ),
               assetPath.endsWith(".gif")
-                  ? Image.file(
-                      File(assetPath),
+                  ? Flexible(
+                      child: Image.file(
+                        File(assetPath),
+                      ),
                     )
                   : ProgressAndSuccess(
                       controller: _progressAndSuccessController!,
diff --git a/lib/pages/settings_views/global_settings_view/currency_view.dart b/lib/pages/settings_views/global_settings_view/currency_view.dart
index 18c8a6a12..2f85dc445 100644
--- a/lib/pages/settings_views/global_settings_view/currency_view.dart
+++ b/lib/pages/settings_views/global_settings_view/currency_view.dart
@@ -48,18 +48,17 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
   final _searchFocusNode = FocusNode();
 
   void onTap(int index) {
+    if (currenciesWithoutSelected[index] == current || current.isEmpty) {
+      // ignore if already selected currency
+      return;
+    }
+    current = currenciesWithoutSelected[index];
+    currenciesWithoutSelected.remove(current);
+    currenciesWithoutSelected.insert(0, current);
+
     if (Util.isDesktop) {
-      setState(() {
-        current = currenciesWithoutSelected[index];
-      });
+      setState(() {});
     } else {
-      if (currenciesWithoutSelected[index] == current || current.isEmpty) {
-        // ignore if already selected currency
-        return;
-      }
-      current = currenciesWithoutSelected[index];
-      currenciesWithoutSelected.remove(current);
-      currenciesWithoutSelected.insert(0, current);
       ref.read(prefsChangeNotifierProvider).currency = current;
 
       if (ref.read(prefsChangeNotifierProvider).externalCalls) {
@@ -104,13 +103,7 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
   void initState() {
     _searchController = TextEditingController();
     if (Util.isDesktop) {
-      currenciesWithoutSelected =
-          ref.read(baseCurrenciesProvider).map.keys.toList();
       current = ref.read(prefsChangeNotifierProvider).currency;
-      if (current.isNotEmpty) {
-        currenciesWithoutSelected.remove(current);
-        currenciesWithoutSelected.insert(0, current);
-      }
     }
     super.initState();
   }
@@ -129,16 +122,16 @@ class _CurrencyViewState extends ConsumerState<BaseCurrencySettingsView> {
     if (!isDesktop) {
       current = ref
           .watch(prefsChangeNotifierProvider.select((value) => value.currency));
+    }
 
-      currenciesWithoutSelected = ref
-          .watch(baseCurrenciesProvider.select((value) => value.map))
-          .keys
-          .toList();
+    currenciesWithoutSelected = ref
+        .watch(baseCurrenciesProvider.select((value) => value.map))
+        .keys
+        .toList();
 
-      if (current.isNotEmpty) {
-        currenciesWithoutSelected.remove(current);
-        currenciesWithoutSelected.insert(0, current);
-      }
+    if (current.isNotEmpty) {
+      currenciesWithoutSelected.remove(current);
+      currenciesWithoutSelected.insert(0, current);
     }
 
     currenciesWithoutSelected = _filtered();
diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/coin_nodes_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/coin_nodes_view.dart
index 26f54fc27..1cacd431e 100644
--- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/coin_nodes_view.dart
+++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/coin_nodes_view.dart
@@ -13,8 +13,8 @@ import 'dart:io';
 import 'package:flutter/material.dart';
 import 'package:flutter_riverpod/flutter_riverpod.dart';
 import 'package:flutter_svg/svg.dart';
-import 'add_edit_node_view.dart';
-import '../../sub_widgets/nodes_list.dart';
+import 'package:tuple/tuple.dart';
+
 import '../../../../themes/coin_icon_provider.dart';
 import '../../../../themes/stack_colors.dart';
 import '../../../../utilities/assets.dart';
@@ -26,7 +26,8 @@ import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
 import '../../../../widgets/custom_buttons/blue_text_button.dart';
 import '../../../../widgets/desktop/desktop_dialog.dart';
 import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
-import 'package:tuple/tuple.dart';
+import '../../sub_widgets/nodes_list.dart';
+import 'add_edit_node_view.dart';
 
 class CoinNodesView extends ConsumerStatefulWidget {
   const CoinNodesView({
@@ -59,7 +60,10 @@ class _CoinNodesViewState extends ConsumerState<CoinNodesView> {
   Widget build(BuildContext context) {
     if (Util.isDesktop) {
       return DesktopDialog(
+        maxHeight: null,
+        maxWidth: 580,
         child: Column(
+          mainAxisSize: MainAxisSize.min,
           children: [
             Row(
               children: [
@@ -129,11 +133,15 @@ class _CoinNodesViewState extends ConsumerState<CoinNodesView> {
             const SizedBox(
               width: 12,
             ),
-            Padding(
-              padding: const EdgeInsets.all(20),
-              child: NodesList(
-                coin: widget.coin,
-                popBackToRoute: CoinNodesView.routeName,
+            Flexible(
+              child: Padding(
+                padding: const EdgeInsets.all(20),
+                child: SingleChildScrollView(
+                  child: NodesList(
+                    coin: widget.coin,
+                    popBackToRoute: CoinNodesView.routeName,
+                  ),
+                ),
               ),
             ),
           ],
diff --git a/lib/services/price.dart b/lib/services/price.dart
index 83470c125..dc4f3d8d8 100644
--- a/lib/services/price.dart
+++ b/lib/services/price.dart
@@ -30,6 +30,7 @@ class PriceAPI {
     BitcoinFrost: "bitcoin",
     Litecoin: "litecoin",
     Bitcoincash: "bitcoin-cash",
+    Dash: "dash",
     Dogecoin: "dogecoin",
     Epiccash: "epic-cash",
     Ecash: "ecash",
diff --git a/lib/wallets/crypto_currency/coins/dash.dart b/lib/wallets/crypto_currency/coins/dash.dart
new file mode 100644
index 000000000..366bbbcc8
--- /dev/null
+++ b/lib/wallets/crypto_currency/coins/dash.dart
@@ -0,0 +1,247 @@
+import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib;
+
+import '../../../models/isar/models/blockchain_data/address.dart';
+import '../../../models/node_model.dart';
+import '../../../utilities/amount/amount.dart';
+import '../../../utilities/default_nodes.dart';
+import '../../../utilities/enums/derive_path_type_enum.dart';
+import '../crypto_currency.dart';
+import '../interfaces/electrumx_currency_interface.dart';
+import '../intermediate/bip39_hd_currency.dart';
+
+class Dash extends Bip39HDCurrency with ElectrumXCurrencyInterface {
+  Dash(super.network) {
+    _idMain = "dash";
+    _uriScheme = "dash";
+    switch (network) {
+      case CryptoCurrencyNetwork.main:
+        _id = _idMain;
+        _name = "Dash";
+        _ticker = "DASH";
+      // case CryptoCurrencyNetwork.test:
+      //   _id = "dashTestNet";
+      //   _name = "tDash";
+      //   _ticker = "tDASH";
+      default:
+        throw Exception("Unsupported network: $network");
+    }
+  }
+
+  late final String _id;
+  @override
+  String get identifier => _id;
+
+  late final String _idMain;
+  @override
+  String get mainNetId => _idMain;
+
+  late final String _name;
+  @override
+  String get prettyName => _name;
+
+  late final String _uriScheme;
+  @override
+  String get uriScheme => _uriScheme;
+
+  late final String _ticker;
+  @override
+  String get ticker => _ticker;
+
+  @override
+  bool get torSupport => true;
+
+  @override
+  List<DerivePathType> get supportedDerivationPathTypes => [
+        DerivePathType.bip44,
+      ];
+
+  @override
+  String constructDerivePath({
+    required DerivePathType derivePathType,
+    int account = 0,
+    required int chain,
+    required int index,
+  }) {
+    String coinType;
+
+    switch (networkParams.wifPrefix) {
+      case 204: // dash mainnet wif
+        coinType = "5"; // dash mainnet
+        break;
+      // case 239: // dash testnet wif
+      //   coinType = "1"; // dash testnet
+      //   break;
+      default:
+        throw Exception("Invalid Dash network wif used!");
+    }
+
+    int purpose;
+    switch (derivePathType) {
+      case DerivePathType.bip44:
+        purpose = 44;
+        break;
+
+      default:
+        throw Exception("DerivePathType $derivePathType not supported");
+    }
+
+    return "m/$purpose'/$coinType'/$account'/$chain/$index";
+  }
+
+  @override
+  Amount get dustLimit => Amount(
+        rawValue: BigInt.from(1000000),
+        fractionDigits: fractionDigits,
+      );
+
+  @override
+  String get genesisHash {
+    switch (network) {
+      case CryptoCurrencyNetwork.main:
+        return "00000ffd590b1485b3caadc19b22e6379c733355108f107a430458cdf3407ab6";
+      // case CryptoCurrencyNetwork.test:
+      //   return "00000bafbc94add76cb75e2ec92894837288a481e5c005f6563d91623bf8bc2c";
+      default:
+        throw Exception("Unsupported network: $network");
+    }
+  }
+
+  @override
+  ({
+    coinlib.Address address,
+    AddressType addressType,
+  }) getAddressForPublicKey({
+    required coinlib.ECPublicKey publicKey,
+    required DerivePathType derivePathType,
+  }) {
+    switch (derivePathType) {
+      case DerivePathType.bip44:
+        final addr = coinlib.P2PKHAddress.fromPublicKey(
+          publicKey,
+          version: networkParams.p2pkhPrefix,
+        );
+
+        return (address: addr, addressType: AddressType.p2pkh);
+
+      default:
+        throw Exception("DerivePathType $derivePathType not supported");
+    }
+  }
+
+  @override
+  int get minConfirms => 6;
+
+  @override
+  coinlib.Network get networkParams {
+    switch (network) {
+      case CryptoCurrencyNetwork.main:
+        return coinlib.Network(
+          p2pkhPrefix: 76,
+          p2shPrefix: 16,
+          wifPrefix: 204,
+          pubHDPrefix: 0x0488B21E,
+          privHDPrefix: 0x0488ADE4,
+          bech32Hrp: "dash", // TODO ?????
+          messagePrefix: '\x18Dash Signed Message:\n', // TODO ?????
+          minFee: BigInt.from(1), // Not used in stack wallet currently
+          minOutput: dustLimit.raw, // Not used in stack wallet currently
+          feePerKb: BigInt.from(1), // Not used in stack wallet currently
+        );
+      // case CryptoCurrencyNetwork.test:
+      //   return coinlib.Network(
+      //     p2pkhPrefix: 140,
+      //     p2shPrefix: 19,
+      //     wifPrefix: 239,
+      //     pubHDPrefix: 0x043587CF,
+      //     privHDPrefix: 0x04358394,
+      //     bech32Hrp: "tdash", // TODO ?????
+      //     messagePrefix: '\x18Dash Signed Message:\n', // TODO ?????
+      //     minFee: BigInt.from(1), // Not used in stack wallet currently
+      //     minOutput: dustLimit.raw, // Not used in stack wallet currently
+      //     feePerKb: BigInt.from(1), // Not used in stack wallet currently
+      //   );
+      default:
+        throw Exception("Unsupported network: $network");
+    }
+  }
+
+  @override
+  bool validateAddress(String address) {
+    try {
+      coinlib.Address.fromString(address, networkParams);
+      return true;
+    } catch (_) {
+      return false;
+    }
+  }
+
+  @override
+  NodeModel get defaultNode {
+    switch (network) {
+      case CryptoCurrencyNetwork.main:
+        return NodeModel(
+          host: "dash.stackwallet.com",
+          port: 60002,
+          name: DefaultNodes.defaultName,
+          id: DefaultNodes.buildId(this),
+          useSSL: true,
+          enabled: true,
+          coinName: identifier,
+          isFailover: true,
+          isDown: false,
+        );
+
+      default:
+        throw UnimplementedError();
+    }
+  }
+
+  @override
+  int get defaultSeedPhraseLength => 12;
+
+  @override
+  int get fractionDigits => 8;
+
+  @override
+  bool get hasBuySupport => true;
+
+  @override
+  bool get hasMnemonicPassphraseSupport => true;
+
+  @override
+  List<int> get possibleMnemonicLengths => [defaultSeedPhraseLength, 24];
+
+  @override
+  AddressType get defaultAddressType => defaultDerivePathType.getAddressType();
+
+  @override
+  BigInt get satsPerCoin => BigInt.from(100000000);
+
+  @override
+  int get targetBlockTimeSeconds => 150;
+
+  @override
+  DerivePathType get defaultDerivePathType => DerivePathType.bip44;
+
+  @override
+  Uri defaultBlockExplorer(String txid) {
+    switch (network) {
+      case CryptoCurrencyNetwork.main:
+        return Uri.parse("https://insight.dash.org/insight/tx/$txid");
+      // case CryptoCurrencyNetwork.test:
+      //   return Uri.parse(
+      //     "https://insight.testnet.networks.dash.org:3002/insight/tx/$txid",
+      //   );
+      default:
+        throw Exception(
+          "Unsupported network for defaultBlockExplorer(): $network",
+        );
+    }
+  }
+
+  @override
+  int get transactionVersion => 2;
+
+  @override
+  BigInt get defaultFeeRate => BigInt.from(1000); // TODO check for dash?
+}
diff --git a/lib/wallets/crypto_currency/crypto_currency.dart b/lib/wallets/crypto_currency/crypto_currency.dart
index a498aa53d..788c87b4f 100644
--- a/lib/wallets/crypto_currency/crypto_currency.dart
+++ b/lib/wallets/crypto_currency/crypto_currency.dart
@@ -6,6 +6,7 @@ export 'coins/banano.dart';
 export 'coins/bitcoin.dart';
 export 'coins/bitcoin_frost.dart';
 export 'coins/bitcoincash.dart';
+export 'coins/dash.dart';
 export 'coins/dogecoin.dart';
 export 'coins/ecash.dart';
 export 'coins/epiccash.dart';
diff --git a/lib/wallets/wallet/impl/dash_wallet.dart b/lib/wallets/wallet/impl/dash_wallet.dart
new file mode 100644
index 000000000..8138b684b
--- /dev/null
+++ b/lib/wallets/wallet/impl/dash_wallet.dart
@@ -0,0 +1,314 @@
+import 'package:isar/isar.dart';
+
+import '../../../models/isar/models/blockchain_data/address.dart';
+import '../../../models/isar/models/blockchain_data/transaction.dart';
+import '../../../models/isar/models/blockchain_data/v2/input_v2.dart';
+import '../../../models/isar/models/blockchain_data/v2/output_v2.dart';
+import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart';
+import '../../../utilities/amount/amount.dart';
+import '../../../utilities/logger.dart';
+import '../../crypto_currency/crypto_currency.dart';
+import '../../crypto_currency/interfaces/electrumx_currency_interface.dart';
+import '../intermediate/bip39_hd_wallet.dart';
+import '../wallet_mixin_interfaces/coin_control_interface.dart';
+import '../wallet_mixin_interfaces/electrumx_interface.dart';
+
+class DashWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
+    with ElectrumXInterface<T>, CoinControlInterface {
+  DashWallet(CryptoCurrencyNetwork network) : super(Dash(network) as T);
+
+  @override
+  int get maximumFeerate => 2500;
+
+  @override
+  int get isarTransactionVersion => 2;
+
+  @override
+  FilterOperation? get changeAddressFilterOperation =>
+      FilterGroup.and(standardChangeAddressFilters);
+
+  @override
+  FilterOperation? get receivingAddressFilterOperation =>
+      FilterGroup.and(standardReceivingAddressFilters);
+
+  // ===========================================================================
+
+  @override
+  Future<List<Address>> fetchAddressesForElectrumXScan() async {
+    final allAddresses = await mainDB
+        .getAddresses(walletId)
+        .filter()
+        .not()
+        .group(
+          (q) => q
+              .typeEqualTo(AddressType.nonWallet)
+              .or()
+              .subTypeEqualTo(AddressSubType.nonWallet),
+        )
+        .findAll();
+    return allAddresses;
+  }
+
+  // ===========================================================================
+
+  @override
+  Future<void> updateTransactions() async {
+    // Get all addresses.
+    final List<Address> allAddressesOld =
+        await fetchAddressesForElectrumXScan();
+
+    // Separate receiving and change addresses.
+    final Set<String> receivingAddresses = allAddressesOld
+        .where((e) => e.subType == AddressSubType.receiving)
+        .map((e) => e.value)
+        .toSet();
+    final Set<String> changeAddresses = allAddressesOld
+        .where((e) => e.subType == AddressSubType.change)
+        .map((e) => e.value)
+        .toSet();
+
+    // Remove duplicates.
+    final allAddressesSet = {...receivingAddresses, ...changeAddresses};
+
+    // Fetch history from ElectrumX.
+    final List<Map<String, dynamic>> allTxHashes =
+        await fetchHistory(allAddressesSet);
+
+    // Only parse new txs (not in db yet).
+    final List<Map<String, dynamic>> allTransactions = [];
+    for (final txHash in allTxHashes) {
+      // Check for duplicates by searching for tx by tx_hash in db.
+      final storedTx = await mainDB.isar.transactionV2s
+          .where()
+          .txidWalletIdEqualTo(txHash["tx_hash"] as String, walletId)
+          .findFirst();
+
+      if (storedTx == null ||
+          storedTx.height == null ||
+          (storedTx.height != null && storedTx.height! <= 0)) {
+        // Tx not in db yet.
+        final tx = await electrumXCachedClient.getTransaction(
+          txHash: txHash["tx_hash"] as String,
+          verbose: true,
+          cryptoCurrency: cryptoCurrency,
+        );
+
+        // Only tx to list once.
+        if (allTransactions
+                .indexWhere((e) => e["txid"] == tx["txid"] as String) ==
+            -1) {
+          tx["height"] = txHash["height"];
+          allTransactions.add(tx);
+        }
+      }
+    }
+
+    // Parse all new txs.
+    final List<TransactionV2> txns = [];
+    for (final txData in allTransactions) {
+      bool wasSentFromThisWallet = false;
+      // Set to true if any inputs were detected as owned by this wallet.
+
+      bool wasReceivedInThisWallet = false;
+      // Set to true if any outputs were detected as owned by this wallet.
+
+      // Parse inputs.
+      BigInt amountReceivedInThisWallet = BigInt.zero;
+      BigInt changeAmountReceivedInThisWallet = BigInt.zero;
+      final List<InputV2> inputs = [];
+      for (final jsonInput in txData["vin"] as List) {
+        final map = Map<String, dynamic>.from(jsonInput as Map);
+
+        final List<String> addresses = [];
+        String valueStringSats = "0";
+        OutpointV2? outpoint;
+
+        final coinbase = map["coinbase"] as String?;
+
+        if (coinbase == null) {
+          // Not a coinbase (ie a typical input).
+          final txid = map["txid"] as String;
+          final vout = map["vout"] as int;
+
+          final inputTx = await electrumXCachedClient.getTransaction(
+            txHash: txid,
+            cryptoCurrency: cryptoCurrency,
+          );
+
+          final prevOutJson = Map<String, dynamic>.from(
+            (inputTx["vout"] as List).firstWhere((e) => e["n"] == vout) as Map,
+          );
+
+          final prevOut = OutputV2.fromElectrumXJson(
+            prevOutJson,
+            decimalPlaces: cryptoCurrency.fractionDigits,
+            isFullAmountNotSats: true,
+            walletOwns: false, // Doesn't matter here as this is not saved.
+          );
+
+          outpoint = OutpointV2.isarCantDoRequiredInDefaultConstructor(
+            txid: txid,
+            vout: vout,
+          );
+          valueStringSats = prevOut.valueStringSats;
+          addresses.addAll(prevOut.addresses);
+        }
+
+        InputV2 input = InputV2.isarCantDoRequiredInDefaultConstructor(
+          scriptSigHex: map["scriptSig"]?["hex"] as String?,
+          scriptSigAsm: map["scriptSig"]?["asm"] as String?,
+          sequence: map["sequence"] as int?,
+          outpoint: outpoint,
+          valueStringSats: valueStringSats,
+          addresses: addresses,
+          witness: map["witness"] as String?,
+          coinbase: coinbase,
+          innerRedeemScriptAsm: map["innerRedeemscriptAsm"] as String?,
+          // Need addresses before we can know if the wallet owns this input.
+          walletOwns: false,
+        );
+
+        // Check if input was from this wallet.
+        if (allAddressesSet.intersection(input.addresses.toSet()).isNotEmpty) {
+          wasSentFromThisWallet = true;
+          input = input.copyWith(walletOwns: true);
+        }
+
+        inputs.add(input);
+      }
+
+      // Parse outputs.
+      final List<OutputV2> outputs = [];
+      for (final outputJson in txData["vout"] as List) {
+        OutputV2 output = OutputV2.fromElectrumXJson(
+          Map<String, dynamic>.from(outputJson as Map),
+          decimalPlaces: cryptoCurrency.fractionDigits,
+          isFullAmountNotSats: true,
+          // Need addresses before we can know if the wallet owns this input.
+          walletOwns: false,
+        );
+
+        // If output was to my wallet, add value to amount received.
+        if (receivingAddresses
+            .intersection(output.addresses.toSet())
+            .isNotEmpty) {
+          wasReceivedInThisWallet = true;
+          amountReceivedInThisWallet += output.value;
+          output = output.copyWith(walletOwns: true);
+        } else if (changeAddresses
+            .intersection(output.addresses.toSet())
+            .isNotEmpty) {
+          wasReceivedInThisWallet = true;
+          changeAmountReceivedInThisWallet += output.value;
+          output = output.copyWith(walletOwns: true);
+        }
+
+        outputs.add(output);
+      }
+
+      final totalOut = outputs
+          .map((e) => e.value)
+          .fold(BigInt.zero, (value, element) => value + element);
+
+      TransactionType type;
+      final TransactionSubType subType = TransactionSubType.none;
+
+      // At least one input was owned by this wallet.
+      if (wasSentFromThisWallet) {
+        type = TransactionType.outgoing;
+
+        if (wasReceivedInThisWallet) {
+          if (changeAmountReceivedInThisWallet + amountReceivedInThisWallet ==
+              totalOut) {
+            // Definitely sent all to self.
+            type = TransactionType.sentToSelf;
+          } else if (amountReceivedInThisWallet == BigInt.zero) {
+            // Most likely just a typical send, do nothing here yet.
+          }
+
+          // This is where we would check for them.
+          // TODO: [prio=high] Check for special Dash outputs.
+        }
+      } else if (wasReceivedInThisWallet) {
+        // Only found outputs owned by this wallet.
+        type = TransactionType.incoming;
+      } else {
+        Logging.instance.log(
+          "Unexpected tx found (ignoring it): $txData",
+          level: LogLevel.Error,
+        );
+        continue;
+      }
+
+      final tx = TransactionV2(
+        walletId: walletId,
+        blockHash: txData["blockhash"] as String?,
+        hash: txData["txid"] as String,
+        txid: txData["txid"] as String,
+        height: txData["height"] as int?,
+        version: txData["version"] as int,
+        timestamp: txData["blocktime"] as int? ??
+            DateTime.timestamp().millisecondsSinceEpoch ~/ 1000,
+        inputs: List.unmodifiable(inputs),
+        outputs: List.unmodifiable(outputs),
+        type: type,
+        subType: subType,
+        otherData: null,
+      );
+
+      txns.add(tx);
+    }
+
+    await mainDB.updateOrPutTransactionV2s(txns);
+  }
+
+  @override
+  Future<({String? blockedReason, bool blocked, String? utxoLabel})>
+      checkBlockUTXO(
+    Map<String, dynamic> jsonUTXO,
+    String? scriptPubKeyHex,
+    Map<String, dynamic> jsonTX,
+    String? utxoOwnerAddress,
+  ) async {
+    bool blocked = false;
+    String? blockedReason;
+
+    // // check for bip47 notification
+    // final outputs = jsonTX["vout"] as List;
+    // for (final output in outputs) {
+    //   final List<String>? scriptChunks =
+    //   (output['scriptPubKey']?['asm'] as String?)?.split(" ");
+    //   if (scriptChunks?.length == 2 && scriptChunks?[0] == "OP_RETURN") {
+    //     final blindedPaymentCode = scriptChunks![1];
+    //     final bytes = blindedPaymentCode.toUint8ListFromHex;
+    //
+    //     // https://en.bitcoin.it/wiki/BIP_0047#Sending
+    //     if (bytes.length == 80 && bytes.first == 1) {
+    //       blocked = true;
+    //       blockedReason = "Paynym notification output. Incautious "
+    //           "handling of outputs from notification transactions "
+    //           "may cause unintended loss of privacy.";
+    //       break;
+    //     }
+    //   }
+    // }
+
+    return (blockedReason: blockedReason, blocked: blocked, utxoLabel: null);
+  }
+
+  @override
+  Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
+    return Amount(
+      rawValue: BigInt.from(
+        ((181 * inputCount) + (34 * outputCount) + 10) *
+            (feeRatePerKB / 1000).ceil(),
+      ),
+      fractionDigits: cryptoCurrency.fractionDigits,
+    );
+  }
+
+  @override
+  int estimateTxFee({required int vSize, required int feeRatePerKB}) {
+    return vSize * (feeRatePerKB / 1000).ceil();
+  }
+}
diff --git a/lib/wallets/wallet/wallet.dart b/lib/wallets/wallet/wallet.dart
index 7b0851ee5..1bca3c858 100644
--- a/lib/wallets/wallet/wallet.dart
+++ b/lib/wallets/wallet/wallet.dart
@@ -28,6 +28,7 @@ import 'impl/banano_wallet.dart';
 import 'impl/bitcoin_frost_wallet.dart';
 import 'impl/bitcoin_wallet.dart';
 import 'impl/bitcoincash_wallet.dart';
+import 'impl/dash_wallet.dart';
 import 'impl/dogecoin_wallet.dart';
 import 'impl/ecash_wallet.dart';
 import 'impl/epiccash_wallet.dart';
@@ -323,6 +324,9 @@ abstract class Wallet<T extends CryptoCurrency> {
       case const (Bitcoincash):
         return BitcoincashWallet(net);
 
+      case const (Dash):
+        return DashWallet(net);
+
       case const (Dogecoin):
         return DogecoinWallet(net);
 
diff --git a/lib/widgets/node_options_sheet.dart b/lib/widgets/node_options_sheet.dart
index bb5692b1f..553f60c05 100644
--- a/lib/widgets/node_options_sheet.dart
+++ b/lib/widgets/node_options_sheet.dart
@@ -13,26 +13,19 @@ import 'dart:async';
 import 'package:flutter/material.dart';
 import 'package:flutter_riverpod/flutter_riverpod.dart';
 import 'package:flutter_svg/svg.dart';
-import 'package:solana/solana.dart';
 import 'package:tuple/tuple.dart';
 
-import '../models/node_model.dart';
-import '../notifications/show_flush_bar.dart';
 import '../pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart';
 import '../pages/settings_views/global_settings_view/manage_nodes_views/node_details_view.dart';
 import '../providers/global/active_wallet_provider.dart';
+import '../providers/global/secure_store_provider.dart';
 import '../providers/providers.dart';
-import '../services/tor_service.dart';
 import '../themes/stack_colors.dart';
 import '../utilities/assets.dart';
-import '../utilities/connection_check/electrum_connection_check.dart';
 import '../utilities/constants.dart';
 import '../utilities/default_nodes.dart';
 import '../utilities/enums/sync_type_enum.dart';
-import '../utilities/logger.dart';
-import '../utilities/test_epic_box_connection.dart';
-import '../utilities/test_eth_node_connection.dart';
-import '../utilities/test_monero_node_connection.dart';
+import '../utilities/test_node_connection.dart';
 import '../utilities/text_styles.dart';
 import '../wallets/crypto_currency/crypto_currency.dart';
 import 'rounded_white_container.dart';
@@ -82,150 +75,6 @@ class NodeOptionsSheet extends ConsumerWidget {
     }
   }
 
-  Future<bool> _testConnection(
-    NodeModel node,
-    BuildContext context,
-    WidgetRef ref,
-  ) async {
-    bool testPassed = false;
-
-    switch (coin.runtimeType) {
-      case const (Epiccash):
-        try {
-          testPassed = await testEpicNodeConnection(
-                NodeFormData()
-                  ..host = node.host
-                  ..useSSL = node.useSSL
-                  ..port = node.port,
-              ) !=
-              null;
-        } catch (e, s) {
-          Logging.instance.log("$e\n$s", level: LogLevel.Warning);
-        }
-        break;
-
-      case const (Monero):
-      case const (Wownero):
-        try {
-          final uri = Uri.parse(node.host);
-          if (uri.scheme.startsWith("http")) {
-            final String path = uri.path.isEmpty ? "/json_rpc" : uri.path;
-
-            final String uriString =
-                "${uri.scheme}://${uri.host}:${node.port}$path";
-
-            final response = await testMoneroNodeConnection(
-              Uri.parse(uriString),
-              false,
-              proxyInfo: ref.read(prefsChangeNotifierProvider).useTor
-                  ? ref.read(pTorService).getProxyInfo()
-                  : null,
-            );
-
-            if (response.cert != null && context.mounted) {
-              // if (mounted) {
-              final shouldAllowBadCert = await showBadX509CertificateDialog(
-                response.cert!,
-                response.url!,
-                response.port!,
-                context,
-              );
-
-              if (shouldAllowBadCert) {
-                final response = await testMoneroNodeConnection(
-                  Uri.parse(uriString),
-                  true,
-                  proxyInfo: ref.read(prefsChangeNotifierProvider).useTor
-                      ? ref.read(pTorService).getProxyInfo()
-                      : null,
-                );
-                testPassed = response.success;
-              }
-              // }
-            } else {
-              testPassed = response.success;
-            }
-          }
-        } catch (e, s) {
-          Logging.instance.log("$e\n$s", level: LogLevel.Warning);
-        }
-
-        break;
-
-      case const (Bitcoin):
-      case const (Litecoin):
-      case const (Dogecoin):
-      case const (Firo):
-      case const (Particl):
-      case const (Bitcoincash):
-      case const (Namecoin):
-      case const (Ecash):
-      case const (BitcoinFrost):
-      case const (Peercoin):
-        try {
-          testPassed = await checkElectrumServer(
-            host: node.host,
-            port: node.port,
-            useSSL: node.useSSL,
-            overridePrefs: ref.read(prefsChangeNotifierProvider),
-            overrideTorService: ref.read(pTorService),
-          );
-        } catch (_) {
-          testPassed = false;
-        }
-
-        break;
-
-      case const (Ethereum):
-        try {
-          testPassed = await testEthNodeConnection(node.host);
-        } catch (_) {
-          testPassed = false;
-        }
-        break;
-
-      case const (Nano):
-      case const (Banano):
-      case const (Tezos):
-      case const (Stellar):
-        throw UnimplementedError();
-      //TODO: check network/node
-
-      case const (Solana):
-        try {
-          RpcClient rpcClient;
-          if (node.host.startsWith("http") || node.host.startsWith("https")) {
-            rpcClient = RpcClient("${node.host}:${node.port}");
-          } else {
-            rpcClient = RpcClient("http://${node.host}:${node.port}");
-          }
-          await rpcClient.getEpochInfo().then((value) => testPassed = true);
-        } catch (_) {
-          testPassed = false;
-        }
-        break;
-    }
-
-    if (testPassed) {
-      // showFloatingFlushBar(
-      //   type: FlushBarType.success,
-      //   message: "Server ping success",
-      //   context: context,
-      // );
-    } else {
-      unawaited(
-        showFloatingFlushBar(
-          type: FlushBarType.warning,
-          iconAsset: Assets.svg.circleAlert,
-          message: "Could not connect to node",
-          context: context,
-        ),
-      );
-    }
-
-    return testPassed;
-  }
-
   @override
   Widget build(BuildContext context, WidgetRef ref) {
     final maxHeight = MediaQuery.of(context).size.height * 0.60;
@@ -403,21 +252,38 @@ class NodeOptionsSheet extends ConsumerWidget {
                         onPressed: status == "Connected"
                             ? null
                             : () async {
-                                final canConnect =
-                                    await _testConnection(node, context, ref);
-                                if (!canConnect) {
-                                  return;
+                                final pw = await node.getPassword(
+                                  ref.read(secureStoreProvider),
+                                );
+                                if (context.mounted) {
+                                  final canConnect = await testNodeConnection(
+                                    context: context,
+                                    nodeFormData: NodeFormData()
+                                      ..name = node.name
+                                      ..host = node.host
+                                      ..login = node.loginName
+                                      ..password = pw
+                                      ..port = node.port
+                                      ..useSSL = node.useSSL
+                                      ..isFailover = node.isFailover
+                                      ..trusted = node.trusted,
+                                    cryptoCurrency: coin,
+                                    ref: ref,
+                                  );
+                                  if (!canConnect) {
+                                    return;
+                                  }
+
+                                  await ref
+                                      .read(nodeServiceChangeNotifierProvider)
+                                      .setPrimaryNodeFor(
+                                        coin: coin,
+                                        node: node,
+                                        shouldNotifyListeners: true,
+                                      );
+
+                                  await _notifyWalletsOfUpdatedNode(ref);
                                 }
-
-                                await ref
-                                    .read(nodeServiceChangeNotifierProvider)
-                                    .setPrimaryNodeFor(
-                                      coin: coin,
-                                      node: node,
-                                      shouldNotifyListeners: true,
-                                    );
-
-                                await _notifyWalletsOfUpdatedNode(ref);
                               },
                         child: Text(
                           // status == "Connected" ? "Disconnect" : "Connect",
diff --git a/scripts/app_config/configure_stack_wallet.sh b/scripts/app_config/configure_stack_wallet.sh
index cb9bbf46c..2c798e53f 100755
--- a/scripts/app_config/configure_stack_wallet.sh
+++ b/scripts/app_config/configure_stack_wallet.sh
@@ -55,6 +55,7 @@ final List<CryptoCurrency> _supportedCoins = List.unmodifiable([
   Banano(CryptoCurrencyNetwork.main),
   Bitcoincash(CryptoCurrencyNetwork.main),
   BitcoinFrost(CryptoCurrencyNetwork.main),
+  Dash(CryptoCurrencyNetwork.main),
   Dogecoin(CryptoCurrencyNetwork.main),
   Ecash(CryptoCurrencyNetwork.main),
   Epiccash(CryptoCurrencyNetwork.main),