From b1cc8d7e3b6d413870fdd8a2d85638e9d0055e8d Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 22 May 2025 15:30:23 -0500 Subject: [PATCH 01/22] chore: add cs_salvium to pubspec and autogenerated plugin registrants etc --- linux/flutter/generated_plugin_registrant.cc | 4 ++++ linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 ++ scripts/app_config/templates/pubspec.template | 5 +++++ windows/flutter/generated_plugin_registrant.cc | 3 +++ windows/flutter/generated_plugins.cmake | 1 + 6 files changed, 16 insertions(+) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 88c196c5e..02b019f4d 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include #include @@ -21,6 +22,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) cs_monero_flutter_libs_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "CsMoneroFlutterLibsLinuxPlugin"); cs_monero_flutter_libs_linux_plugin_register_with_registrar(cs_monero_flutter_libs_linux_registrar); + g_autoptr(FlPluginRegistrar) cs_salvium_flutter_libs_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "CsSalviumFlutterLibsLinuxPlugin"); + cs_salvium_flutter_libs_linux_plugin_register_with_registrar(cs_salvium_flutter_libs_linux_registrar); g_autoptr(FlPluginRegistrar) desktop_drop_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin"); desktop_drop_plugin_register_with_registrar(desktop_drop_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index aa67f97ac..8bca8e723 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST cs_monero_flutter_libs_linux + cs_salvium_flutter_libs_linux desktop_drop devicelocale flutter_libepiccash diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 245400e1a..8b28c002f 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,6 +8,7 @@ import Foundation import camera_macos import connectivity_plus import cs_monero_flutter_libs_macos +import cs_salvium_flutter_libs_macos import desktop_drop import device_info_plus import devicelocale @@ -31,6 +32,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { CameraMacosPlugin.register(with: registry.registrar(forPlugin: "CameraMacosPlugin")) ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) CsMoneroFlutterLibsMacosPlugin.register(with: registry.registrar(forPlugin: "CsMoneroFlutterLibsMacosPlugin")) + CsSalviumFlutterLibsMacosPlugin.register(with: registry.registrar(forPlugin: "CsSalviumFlutterLibsMacosPlugin")) DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) diff --git a/scripts/app_config/templates/pubspec.template b/scripts/app_config/templates/pubspec.template index b8d87d85a..2171e8cda 100644 --- a/scripts/app_config/templates/pubspec.template +++ b/scripts/app_config/templates/pubspec.template @@ -221,6 +221,11 @@ dependencies: drift: ^2.22.1 drift_flutter: ^0.2.3 path: ^1.9.1 +# cs_salvium: 1.0.0-pre.1 + cs_salvium: + path: ../cs_salvium/packages/cs_salvium + cs_salvium_flutter_libs: + path: ../cs_salvium/packages/cs_salvium_flutter_libs dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 74ef33c4e..63c954ce1 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); CsMoneroFlutterLibsWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("CsMoneroFlutterLibsWindowsPluginCApi")); + CsSalviumFlutterLibsWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("CsSalviumFlutterLibsWindowsPluginCApi")); DesktopDropPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("DesktopDropPlugin")); FlutterLibepiccashPluginCApiRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 55c2cc622..b7bea9e3f 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST camera_windows connectivity_plus cs_monero_flutter_libs_windows + cs_salvium_flutter_libs_windows desktop_drop flutter_libepiccash flutter_secure_storage_windows From 1369d8ffcac60f942bedf8b7b924b07e1d267188 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 22 May 2025 16:57:04 -0500 Subject: [PATCH 02/22] feat: add wip salvium wallet files --- .../crypto_currency/coins/salvium.dart | 124 ++++++++++++++++ .../crypto_currency/crypto_currency.dart | 1 + lib/wallets/wallet/impl/salvium_wallet.dart | 134 ++++++++++++++++++ 3 files changed, 259 insertions(+) create mode 100644 lib/wallets/crypto_currency/coins/salvium.dart create mode 100644 lib/wallets/wallet/impl/salvium_wallet.dart diff --git a/lib/wallets/crypto_currency/coins/salvium.dart b/lib/wallets/crypto_currency/coins/salvium.dart new file mode 100644 index 000000000..fe4d8f64d --- /dev/null +++ b/lib/wallets/crypto_currency/coins/salvium.dart @@ -0,0 +1,124 @@ +import 'package:cs_salvium/src/ffi_bindings/salvium_wallet_bindings.dart' + as sal_wallet_ffi; + +import '../../../models/node_model.dart'; +import '../../../utilities/default_nodes.dart'; +import '../../../utilities/enums/derive_path_type_enum.dart'; +import '../crypto_currency.dart'; +import '../intermediate/cryptonote_currency.dart'; + +class Salvium extends CryptonoteCurrency { + Salvium(super.network) { + _idMain = "salvium"; + _uriScheme = "salvium"; + switch (network) { + case CryptoCurrencyNetwork.main: + _id = _idMain; + _name = "Salvium"; + _ticker = "SAL"; + 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 + int get minConfirms => 10; + + @override + bool get torSupport => true; + + @override + bool validateAddress(String address) { + if (address.contains("111")) { + return false; + } + switch (network) { + case CryptoCurrencyNetwork.main: + return sal_wallet_ffi.validateAddress(address, 0); + default: + throw Exception("Unsupported network: $network"); + } + } + + @override + NodeModel get defaultNode { + switch (network) { + case CryptoCurrencyNetwork.main: + return NodeModel( + host: "https://salvium.stackwallet.com", + port: 19081, + name: DefaultNodes.defaultName, + id: DefaultNodes.buildId(this), + useSSL: true, + enabled: true, + coinName: identifier, + isFailover: true, + isDown: false, + trusted: true, + torEnabled: true, + clearnetEnabled: true, + ); + + default: + throw UnimplementedError(); + } + } + + @override + int get defaultSeedPhraseLength => 25; + + @override + int get fractionDigits => 12; + + @override + bool get hasBuySupport => false; + + @override + bool get hasMnemonicPassphraseSupport => false; + + @override + List get possibleMnemonicLengths => [defaultSeedPhraseLength]; + + @override + BigInt get satsPerCoin => BigInt.from(1000000000000); + + @override + int get targetBlockTimeSeconds => 120; + + @override + DerivePathType get defaultDerivePathType => throw UnsupportedError( + "$runtimeType does not use bitcoin style derivation paths", + ); + + @override + Uri defaultBlockExplorer(String txid) { + switch (network) { + case CryptoCurrencyNetwork.main: + return Uri.parse("https://explorer.salvium.io//tx/$txid"); + default: + throw Exception( + "Unsupported network for defaultBlockExplorer(): $network", + ); + } + } +} diff --git a/lib/wallets/crypto_currency/crypto_currency.dart b/lib/wallets/crypto_currency/crypto_currency.dart index d5553ceca..773128bbb 100644 --- a/lib/wallets/crypto_currency/crypto_currency.dart +++ b/lib/wallets/crypto_currency/crypto_currency.dart @@ -19,6 +19,7 @@ export 'coins/namecoin.dart'; export 'coins/nano.dart'; export 'coins/particl.dart'; export 'coins/peercoin.dart'; +export 'coins/salvium.dart'; export 'coins/solana.dart'; export 'coins/stellar.dart'; export 'coins/tezos.dart'; diff --git a/lib/wallets/wallet/impl/salvium_wallet.dart b/lib/wallets/wallet/impl/salvium_wallet.dart new file mode 100644 index 000000000..c04baed27 --- /dev/null +++ b/lib/wallets/wallet/impl/salvium_wallet.dart @@ -0,0 +1,134 @@ +import 'dart:async'; + +import 'package:compat/compat.dart' as lib_salvium_compat; +import 'package:cs_salvium/cs_salvium.dart' as lib_salvium; + +import '../../../utilities/amount/amount.dart'; +import '../../crypto_currency/crypto_currency.dart'; +import '../intermediate/lib_monero_wallet.dart'; + +class SalviumWallet extends LibMoneroWallet { + SalviumWallet(CryptoCurrencyNetwork network) + : super(Salvium(network), lib_salvium_compat.WalletType.salvium); + + @override + Future estimateFeeFor(Amount amount, BigInt feeRate) async { + if (libMoneroWallet == null || + syncStatus is! lib_salvium_compat.SyncedSyncStatus) { + return Amount.zeroWith(fractionDigits: cryptoCurrency.fractionDigits); + } + + lib_salvium.TransactionPriority priority; + switch (feeRate.toInt()) { + case 1: + priority = lib_salvium.TransactionPriority.low; + break; + case 2: + priority = lib_salvium.TransactionPriority.medium; + break; + case 3: + priority = lib_salvium.TransactionPriority.high; + break; + case 4: + priority = lib_salvium.TransactionPriority.last; + break; + case 0: + default: + priority = lib_salvium.TransactionPriority.normal; + break; + } + + int approximateFee = 0; + await estimateFeeMutex.protect(() async { + approximateFee = await libMoneroWallet!.estimateFee( + priority, + amount.raw.toInt(), + ); + }); + + return Amount( + rawValue: BigInt.from(approximateFee), + fractionDigits: cryptoCurrency.fractionDigits, + ); + } + + @override + bool walletExists(String path) => lib_salvium.SalviumWallet.isWalletExist(path); + + @override + Future loadWallet({ + required String path, + required String password, + }) async { + libMoneroWallet = await lib_salvium.SalviumWallet.loadWallet( + path: path, + password: password, + ); + } + + @override + Future getCreatedWallet({ + required String path, + required String password, + required int wordCount, + required String seedOffset, + }) async { + final lib_salvium.SalviumSeedType type; + switch (wordCount) { + case 16: + type = lib_salvium.SalviumSeedType.sixteen; + break; + + case 25: + type = lib_salvium.SalviumSeedType.twentyFive; + break; + + default: + throw Exception("Invalid mnemonic word count: $wordCount"); + } + + return await lib_salvium.SalviumWallet.create( + path: path, + password: password, + seedType: type, + seedOffset: seedOffset, + ); + } + + @override + Future getRestoredWallet({ + required String path, + required String password, + required String mnemonic, + required String seedOffset, + int height = 0, + }) async => await lib_salvium.SalviumWallet.restoreWalletFromSeed( + path: path, + password: password, + seed: mnemonic, + restoreHeight: height, + seedOffset: seedOffset, + ); + + @override + Future getRestoredFromViewKeyWallet({ + required String path, + required String password, + required String address, + required String privateViewKey, + int height = 0, + }) async => lib_salvium.SalviumWallet.createViewOnlyWallet( + path: path, + password: password, + address: address, + viewKey: privateViewKey, + restoreHeight: height, + ); + + @override + void invalidSeedLengthCheck(int length) { + if (length != 25 && length != 16) { + throw Exception("Invalid salvium mnemonic length found: $length"); + } + } +} From b8cbffdb7f00514c2b92be5d462bdff0822dbce9 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 22 May 2025 16:57:20 -0500 Subject: [PATCH 03/22] chore: ignore working theme dir(s) --- asset_sources/default_themes/stack_wallet/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 asset_sources/default_themes/stack_wallet/.gitignore diff --git a/asset_sources/default_themes/stack_wallet/.gitignore b/asset_sources/default_themes/stack_wallet/.gitignore new file mode 100644 index 000000000..49f4a6294 --- /dev/null +++ b/asset_sources/default_themes/stack_wallet/.gitignore @@ -0,0 +1,2 @@ +light/ +dark/ From b570d6ed1686814f7ba22b7308e02d402c86a28b Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 23 May 2025 13:37:01 -0500 Subject: [PATCH 04/22] feat: add libSalviumWallet workaround --- lib/wallets/wallet/impl/salvium_wallet.dart | 20 +- .../intermediate/lib_salvium_wallet.dart | 1507 +++++++++++++++++ 2 files changed, 1515 insertions(+), 12 deletions(-) create mode 100644 lib/wallets/wallet/intermediate/lib_salvium_wallet.dart diff --git a/lib/wallets/wallet/impl/salvium_wallet.dart b/lib/wallets/wallet/impl/salvium_wallet.dart index c04baed27..29555ef62 100644 --- a/lib/wallets/wallet/impl/salvium_wallet.dart +++ b/lib/wallets/wallet/impl/salvium_wallet.dart @@ -1,20 +1,20 @@ import 'dart:async'; -import 'package:compat/compat.dart' as lib_salvium_compat; +import 'package:compat/compat.dart' as lib_monero_compat; import 'package:cs_salvium/cs_salvium.dart' as lib_salvium; import '../../../utilities/amount/amount.dart'; import '../../crypto_currency/crypto_currency.dart'; -import '../intermediate/lib_monero_wallet.dart'; +import '../intermediate/lib_salvium_wallet.dart'; -class SalviumWallet extends LibMoneroWallet { +class SalviumWallet extends LibSalviumWallet { SalviumWallet(CryptoCurrencyNetwork network) - : super(Salvium(network), lib_salvium_compat.WalletType.salvium); + : super(Salvium(network), lib_monero_compat.WalletType.salvium); @override Future estimateFeeFor(Amount amount, BigInt feeRate) async { - if (libMoneroWallet == null || - syncStatus is! lib_salvium_compat.SyncedSyncStatus) { + if (libSalviumWallet == null || + syncStatus is! lib_monero_compat.SyncedSyncStatus) { return Amount.zeroWith(fractionDigits: cryptoCurrency.fractionDigits); } @@ -40,7 +40,7 @@ class SalviumWallet extends LibMoneroWallet { int approximateFee = 0; await estimateFeeMutex.protect(() async { - approximateFee = await libMoneroWallet!.estimateFee( + approximateFee = await libSalviumWallet!.estimateFee( priority, amount.raw.toInt(), ); @@ -60,7 +60,7 @@ class SalviumWallet extends LibMoneroWallet { required String path, required String password, }) async { - libMoneroWallet = await lib_salvium.SalviumWallet.loadWallet( + libSalviumWallet = await lib_salvium.SalviumWallet.loadWallet( path: path, password: password, ); @@ -75,10 +75,6 @@ class SalviumWallet extends LibMoneroWallet { }) async { final lib_salvium.SalviumSeedType type; switch (wordCount) { - case 16: - type = lib_salvium.SalviumSeedType.sixteen; - break; - case 25: type = lib_salvium.SalviumSeedType.twentyFive; break; diff --git a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart new file mode 100644 index 000000000..be26f5b22 --- /dev/null +++ b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart @@ -0,0 +1,1507 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; + +import 'package:compat/compat.dart' as lib_monero_compat; +import 'package:cs_salvium/cs_salvium.dart' as lib_salvium; +import 'package:isar/isar.dart'; +import 'package:mutex/mutex.dart'; +import 'package:stack_wallet_backup/generate_password.dart'; + +import '../../../db/hive/db.dart'; +import '../../../models/balance.dart'; +import '../../../models/isar/models/blockchain_data/address.dart'; +import '../../../models/isar/models/blockchain_data/transaction.dart'; +import '../../../models/isar/models/blockchain_data/utxo.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 '../../../models/keys/cw_key_data.dart'; +import '../../../models/keys/view_only_wallet_data.dart'; +import '../../../models/paymint/fee_object_model.dart'; +import '../../../services/event_bus/events/global/blocks_remaining_event.dart'; +import '../../../services/event_bus/events/global/refresh_percent_changed_event.dart'; +import '../../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; +import '../../../services/event_bus/events/global/tor_status_changed_event.dart'; +import '../../../services/event_bus/events/global/updated_in_background_event.dart'; +import '../../../services/event_bus/events/global/wallet_sync_status_changed_event.dart'; +import '../../../services/event_bus/global_event_bus.dart'; +import '../../../services/tor_service.dart'; +import '../../../utilities/amount/amount.dart'; +import '../../../utilities/enums/fee_rate_type_enum.dart'; +import '../../../utilities/logger.dart'; +import '../../../utilities/stack_file_system.dart'; +import '../../crypto_currency/intermediate/cryptonote_currency.dart'; +import '../../isar/models/wallet_info.dart'; +import '../../models/tx_data.dart'; +import '../wallet.dart'; +import '../wallet_mixin_interfaces/multi_address_interface.dart'; +import '../wallet_mixin_interfaces/view_only_option_interface.dart'; +import 'cryptonote_wallet.dart'; + +abstract class LibSalviumWallet + extends CryptonoteWallet + with ViewOnlyOptionInterface + implements MultiAddressInterface { + @override + int get isarTransactionVersion => 2; + + LibSalviumWallet(super.currency, this.compatType) { + final bus = GlobalEventBus.instance; + + // Listen for tor status changes. + _torStatusListener = bus.on().listen(( + event, + ) async { + switch (event.newStatus) { + case TorConnectionStatus.connecting: + if (!_torConnectingLock.isLocked) { + await _torConnectingLock.acquire(); + } + _requireMutex = true; + break; + + case TorConnectionStatus.connected: + case TorConnectionStatus.disconnected: + if (_torConnectingLock.isLocked) { + _torConnectingLock.release(); + } + _requireMutex = false; + break; + } + }); + + // Listen for tor preference changes. + _torPreferenceListener = bus.on().listen(( + event, + ) async { + await updateNode(); + }); + + // Potentially dangerous hack. See comments in _startInit() + _startInit(); + } + // cw based wallet listener to handle synchronization of utxo frozen states + late final StreamSubscription> _streamSub; + Future _startInit() async { + // Delay required as `mainDB` is not initialized in constructor. + // This is a hack and could lead to a race condition. + Future.delayed(const Duration(seconds: 2), () { + _streamSub = mainDB.isar.utxos + .where() + .walletIdEqualTo(walletId) + .watch(fireImmediately: true) + .listen((utxos) async { + try { + await onUTXOsChanged(utxos); + await updateBalance(shouldUpdateUtxos: false); + } catch (e, s) { + lib_salvium.Logging.log?.i("_startInit", error: e, stackTrace: s); + } + }); + }); + } + + final lib_monero_compat.WalletType compatType; + lib_salvium.Wallet? libSalviumWallet; + + lib_monero_compat.SyncStatus? get syncStatus => _syncStatus; + lib_monero_compat.SyncStatus? _syncStatus; + int _syncedCount = 0; + void _setSyncStatus(lib_monero_compat.SyncStatus status) { + if (status is lib_monero_compat.SyncedSyncStatus) { + if (_syncStatus is lib_monero_compat.SyncedSyncStatus) { + _syncedCount++; + } + } else { + _syncedCount = 0; + } + + if (_syncedCount < 3) { + _syncStatus = status; + syncStatusChanged(); + } + } + + final prepareSendMutex = Mutex(); + final estimateFeeMutex = Mutex(); + + bool _txRefreshLock = false; + int _lastCheckedHeight = -1; + int _txCount = 0; + int currentKnownChainHeight = 0; + double highestPercentCached = 0; + + Future loadWallet({required String path, required String password}); + + Future getCreatedWallet({ + required String path, + required String password, + required int wordCount, + required String seedOffset, + }); + + Future getRestoredWallet({ + required String path, + required String password, + required String mnemonic, + required String seedOffset, + int height = 0, + }); + + Future getRestoredFromViewKeyWallet({ + required String path, + required String password, + required String address, + required String privateViewKey, + int height = 0, + }); + + void invalidSeedLengthCheck(int length); + + bool walletExists(String path); + + String getTxKeyFor({required String txid}) { + if (libSalviumWallet == null) { + throw Exception("Cannot get tx key in uninitialized libSalviumWallet"); + } + return libSalviumWallet!.getTxKey(txid); + } + + void _setListener() { + if (libSalviumWallet != null && libSalviumWallet!.getListeners().isEmpty) { + libSalviumWallet?.addListener( + lib_salvium.WalletListener( + onSyncingUpdate: onSyncingUpdate, + onNewBlock: onNewBlock, + onBalancesChanged: onBalancesChanged, + onError: (e, s) { + Logging.instance.w("$e\n$s", error: e, stackTrace: s); + }, + ), + ); + } + } + + @override + Future open() async { + bool wasNull = false; + + if (libSalviumWallet == null) { + wasNull = true; + // libSalviumWalletT?.close(); + final path = await pathForWallet(name: walletId, type: compatType); + + final String password; + try { + password = + (await secureStorageInterface.read( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + ))!; + } catch (e, s) { + throw Exception("Password not found $e, $s"); + } + + await loadWallet(path: path, password: password); + + _setListener(); + + await updateNode(); + } + + Address? currentAddress = await getCurrentReceivingAddress(); + if (currentAddress == null) { + currentAddress = addressFor(index: 0); + await mainDB.updateOrPutAddresses([currentAddress]); + } + if (info.cachedReceivingAddress != currentAddress.value) { + await info.updateReceivingAddress( + newAddress: currentAddress.value, + isar: mainDB.isar, + ); + } + + if (wasNull) { + try { + _setSyncStatus(lib_monero_compat.ConnectingSyncStatus()); + libSalviumWallet?.startSyncing(); + } catch (_) { + _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + // TODO log + } + } + _setListener(); + libSalviumWallet?.startListeners(); + libSalviumWallet?.startAutoSaving(); + + unawaited(refresh()); + } + + @Deprecated("Only used in the case of older wallets") + lib_monero_compat.WalletInfo? getLibSalviumWalletInfo(String walletId) { + try { + return DB.instance.moneroWalletInfoBox.values.firstWhere( + (info) => info.id == lib_monero_compat.hiveIdFor(walletId, compatType), + ); + } catch (_) { + return null; + } + } + + Future save() async { + if (!Platform.isWindows) { + final appRoot = await StackFileSystem.applicationRootDirectory(); + await lib_monero_compat.backupWalletFiles( + name: walletId, + type: compatType, + appRoot: appRoot, + ); + } + await libSalviumWallet!.save(); + } + + Address addressFor({required int index, int account = 0}) { + final address = libSalviumWallet!.getAddress( + accountIndex: account, + addressIndex: index, + ); + + if (address.value.contains("111")) { + throw Exception("111 address found!"); + } + + final newReceivingAddress = Address( + walletId: walletId, + derivationIndex: index, + derivationPath: null, + value: address.value, + publicKey: [], + type: AddressType.cryptonote, + subType: AddressSubType.receiving, + ); + + return newReceivingAddress; + } + + Future getKeys() async { + final base = libSalviumWallet; + + final oldInfo = getLibSalviumWalletInfo(walletId); + if (base == null || (oldInfo != null && oldInfo.name != walletId)) { + return null; + } + try { + return CWKeyData( + walletId: walletId, + publicViewKey: base.getPublicViewKey(), + privateViewKey: base.getPrivateViewKey(), + publicSpendKey: base.getPublicSpendKey(), + privateSpendKey: base.getPrivateSpendKey(), + ); + } catch (e, s) { + Logging.instance.f("getKeys failed: ", error: e, stackTrace: s); + return CWKeyData( + walletId: walletId, + publicViewKey: "ERROR", + privateViewKey: "ERROR", + publicSpendKey: "ERROR", + privateSpendKey: "ERROR", + ); + } + } + + Future<(String, String)> + hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing() async { + final path = await pathForWallet(name: walletId, type: compatType); + final String password; + try { + password = + (await secureStorageInterface.read( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + ))!; + } catch (e, s) { + throw Exception("Password not found $e, $s"); + } + await loadWallet(path: path, password: password); + final wallet = libSalviumWallet!; + return (wallet.getAddress().value, wallet.getPrivateViewKey()); + } + + @override + Future init({bool? isRestore, int? wordCount}) async { + final path = await pathForWallet(name: walletId, type: compatType); + if (!(walletExists(path)) && isRestore != true) { + if (wordCount == null) { + throw Exception("Missing word count for new xmr/wow wallet!"); + } + try { + final password = generatePassword(); + await secureStorageInterface.write( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + value: password, + ); + final wallet = await getCreatedWallet( + path: path, + password: password, + wordCount: wordCount, + seedOffset: "", // default for non restored wallets for now + ); + + final height = wallet.getRefreshFromBlockHeight(); + + await info.updateRestoreHeight( + newRestoreHeight: height, + isar: mainDB.isar, + ); + + // special case for xmr/wow. Normally mnemonic + passphrase is saved + // before wallet.init() is called + await secureStorageInterface.write( + key: Wallet.mnemonicKey(walletId: walletId), + value: wallet.getSeed().trim(), + ); + await secureStorageInterface.write( + key: Wallet.mnemonicPassphraseKey(walletId: walletId), + value: "", + ); + } catch (e, s) { + Logging.instance.f("", error: e, stackTrace: s); + } + await updateNode(); + } + + return super.init(); + } + + @override + Future recover({required bool isRescan}) async { + if (isRescan) { + await refreshMutex.protect(() async { + // clear blockchain info + await mainDB.deleteWalletBlockchainData(walletId); + + highestPercentCached = 0; + unawaited(libSalviumWallet?.rescanBlockchain()); + libSalviumWallet?.startSyncing(); + // unawaited(save()); + }); + unawaited(refresh()); + return; + } + + if (isViewOnly) { + await recoverViewOnly(); + return; + } + + await refreshMutex.protect(() async { + final mnemonic = await getMnemonic(); + final seedOffset = await getMnemonicPassphrase(); + final seedLength = mnemonic.trim().split(" ").length; + + invalidSeedLengthCheck(seedLength); + + try { + final height = max(info.restoreHeight, 0); + + await info.updateRestoreHeight( + newRestoreHeight: height, + isar: mainDB.isar, + ); + + final String name = walletId; + + final path = await pathForWallet(name: name, type: compatType); + + try { + final password = generatePassword(); + await secureStorageInterface.write( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + value: password, + ); + final wallet = await getRestoredWallet( + path: path, + password: password, + mnemonic: mnemonic, + height: height, + seedOffset: seedOffset, + ); + + if (libSalviumWallet != null) { + await exit(); + } + + libSalviumWallet = wallet; + + _setListener(); + + final newReceivingAddress = + await getCurrentReceivingAddress() ?? + Address( + walletId: walletId, + derivationIndex: 0, + derivationPath: null, + value: wallet.getAddress().value, + publicKey: [], + type: AddressType.cryptonote, + subType: AddressSubType.receiving, + ); + + await mainDB.updateOrPutAddresses([newReceivingAddress]); + await info.updateReceivingAddress( + newAddress: newReceivingAddress.value, + isar: mainDB.isar, + ); + } catch (e, s) { + Logging.instance.f("", error: e, stackTrace: s); + rethrow; + } + await updateNode(); + _setListener(); + + // libSalviumWallet?.setRecoveringFromSeed(isRecovery: true); + unawaited(libSalviumWallet?.rescanBlockchain()); + libSalviumWallet?.startSyncing(); + + // await save(); + libSalviumWallet?.startListeners(); + libSalviumWallet?.startAutoSaving(); + } catch (e, s) { + Logging.instance.e( + "Exception rethrown from recoverFromMnemonic(): ", + error: e, + stackTrace: s, + ); + rethrow; + } + }); + } + + @override + Future pingCheck() async { + return (await libSalviumWallet?.isConnectedToDaemon()) ?? false; + } + + @override + Future updateNode() async { + final node = getCurrentNode(); + + final host = + node.host.endsWith(".onion") ? node.host : Uri.parse(node.host).host; + ({InternetAddress host, int port})? proxy; + if (prefs.useTor) { + if (node.clearnetEnabled && !node.torEnabled) { + libSalviumWallet?.stopAutoSaving(); + libSalviumWallet?.stopListeners(); + libSalviumWallet?.stopSyncing(); + _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + throw Exception("TOR - clearnet mismatch"); + } + proxy = TorService.sharedInstance.getProxyInfo(); + } else { + if (!node.clearnetEnabled && node.torEnabled) { + libSalviumWallet?.stopAutoSaving(); + libSalviumWallet?.stopListeners(); + libSalviumWallet?.stopSyncing(); + _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + throw Exception("TOR - clearnet mismatch"); + } + } + + _setSyncStatus(lib_monero_compat.ConnectingSyncStatus()); + try { + if (_requireMutex) { + await _torConnectingLock.protect(() async { + await libSalviumWallet?.connect( + daemonAddress: "$host:${node.port}", + daemonUsername: node.loginName, + daemonPassword: await node.getPassword(secureStorageInterface), + trusted: node.trusted ?? false, + useSSL: node.useSSL, + socksProxyAddress: + node.forceNoTor + ? null + : proxy == null + ? null + : "${proxy.host.address}:${proxy.port}", + ); + }); + } else { + await libSalviumWallet?.connect( + daemonAddress: "$host:${node.port}", + daemonUsername: node.loginName, + daemonPassword: await node.getPassword(secureStorageInterface), + trusted: node.trusted ?? false, + useSSL: node.useSSL, + socksProxyAddress: + node.forceNoTor + ? null + : proxy == null + ? null + : "${proxy.host.address}:${proxy.port}", + ); + } + libSalviumWallet?.startSyncing(); + libSalviumWallet?.startListeners(); + libSalviumWallet?.startAutoSaving(); + + _setSyncStatus(lib_monero_compat.ConnectedSyncStatus()); + } catch (e, s) { + _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + Logging.instance.e( + "Exception caught in $runtimeType.updateNode(): ", + error: e, + stackTrace: s, + ); + } + + return; + } + + @override + Future updateTransactions() async { + final base = libSalviumWallet; + + if (base == null) { + return; + } + + final localTxids = + await mainDB.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) + .filter() + .heightGreaterThan(0) + .txidProperty() + .findAll(); + + final allTxids = await base.getAllTxids(refresh: true); + + final txidsToFetch = allTxids.toSet().difference(localTxids.toSet()); + + if (txidsToFetch.isEmpty) { + return; + } + + final transactions = await base.getTxs(txids: txidsToFetch, refresh: false); + + final allOutputs = await base.getOutputs(includeSpent: true, refresh: true); + + // final cachedTransactions = + // DB.instance.get(boxName: walletId, key: 'latest_tx_model') + // as TransactionData?; + // int latestTxnBlockHeight = + // DB.instance.get(boxName: walletId, key: "storedTxnDataHeight") + // as int? ?? + // 0; + // + // final txidsList = DB.instance + // .get(boxName: walletId, key: "cachedTxids") as List? ?? + // []; + // + // final Set cachedTxids = Set.from(txidsList); + + // TODO: filter to skip cached + confirmed txn processing in next step + // final unconfirmedCachedTransactions = + // cachedTransactions?.getAllTransactions() ?? {}; + // unconfirmedCachedTransactions + // .removeWhere((key, value) => value.confirmedStatus); + // + // if (cachedTransactions != null) { + // for (final tx in allTxHashes.toList(growable: false)) { + // final txHeight = tx["height"] as int; + // if (txHeight > 0 && + // txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS) { + // if (unconfirmedCachedTransactions[tx["tx_hash"] as String] == null) { + // allTxHashes.remove(tx); + // } + // } + // } + // } + + final List txns = []; + + for (final tx in transactions) { + final associatedOutputs = allOutputs.where((e) => e.hash == tx.hash); + final List inputs = []; + final List outputs = []; + TransactionType type; + if (!tx.isSpend) { + type = TransactionType.incoming; + for (final output in associatedOutputs) { + outputs.add( + OutputV2.isarCantDoRequiredInDefaultConstructor( + scriptPubKeyHex: "", + valueStringSats: output.value.toString(), + addresses: [output.address], + walletOwns: true, + ), + ); + } + } else { + type = TransactionType.outgoing; + for (final output in associatedOutputs) { + inputs.add( + InputV2.isarCantDoRequiredInDefaultConstructor( + scriptSigHex: null, + scriptSigAsm: null, + sequence: null, + outpoint: null, + addresses: [output.address], + valueStringSats: output.value.toString(), + witness: null, + innerRedeemScriptAsm: null, + coinbase: null, + walletOwns: true, + ), + ); + } + } + + final txn = TransactionV2( + walletId: walletId, + blockHash: null, // not exposed via current cs_salvium + hash: tx.hash, + txid: tx.hash, + timestamp: (tx.timeStamp.millisecondsSinceEpoch ~/ 1000), + height: tx.blockHeight, + inputs: inputs, + outputs: outputs, + version: -1, // not exposed via current cs_salvium + type: type, + subType: TransactionSubType.none, + otherData: jsonEncode({ + TxV2OdKeys.overrideFee: + Amount( + rawValue: tx.fee, + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), + TxV2OdKeys.moneroAmount: + Amount( + rawValue: tx.amount, + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), + TxV2OdKeys.moneroAccountIndex: tx.accountIndex, + TxV2OdKeys.isMoneroTransaction: true, + }), + ); + + txns.add(txn); + } + + await mainDB.updateOrPutTransactionV2s(txns); + } + + Future get availableBalance async { + try { + return Amount( + rawValue: libSalviumWallet!.getUnlockedBalance(), + fractionDigits: cryptoCurrency.fractionDigits, + ); + } catch (_) { + return info.cachedBalance.spendable; + } + } + + Future get totalBalance async { + try { + final full = libSalviumWallet?.getBalance(); + if (full != null) { + return Amount( + rawValue: full, + fractionDigits: cryptoCurrency.fractionDigits, + ); + } else { + final transactions = await libSalviumWallet!.getAllTxs(refresh: true); + BigInt transactionBalance = BigInt.zero; + for (final tx in transactions) { + if (!tx.isSpend) { + transactionBalance += tx.amount; + } else { + transactionBalance += -tx.amount - tx.fee; + } + } + + return Amount( + rawValue: transactionBalance, + fractionDigits: cryptoCurrency.fractionDigits, + ); + } + } catch (_) { + return info.cachedBalance.total; + } + } + + @override + Future exit() async { + Logging.instance.i("exit called on $walletId"); + libSalviumWallet?.stopAutoSaving(); + libSalviumWallet?.stopListeners(); + libSalviumWallet?.stopSyncing(); + await libSalviumWallet?.save(); + } + + Future pathForWalletDir({ + required String name, + required lib_monero_compat.WalletType type, + }) async { + final Directory root = await StackFileSystem.applicationRootDirectory(); + return lib_monero_compat.pathForWalletDir( + name: name, + type: type.name.toLowerCase(), + appRoot: root, + ); + } + + Future pathForWallet({ + required String name, + required lib_monero_compat.WalletType type, + }) async => await pathForWalletDir( + name: name, + type: type, + ).then((path) => '$path/$name'); + + void onSyncingUpdate({ + required int syncHeight, + required int nodeHeight, + String? message, + }) { + if (nodeHeight > 0 && syncHeight >= 0) { + currentKnownChainHeight = nodeHeight; + updateChainHeight(); + final blocksLeft = nodeHeight - syncHeight; + final lib_monero_compat.SyncStatus status; + if (blocksLeft < 100) { + status = lib_monero_compat.SyncedSyncStatus(); + + // if (!_hasSyncAfterStartup) { + // _hasSyncAfterStartup = true; + // await save(); + // } + // + // if (walletInfo.isRecovery!) { + // await setAsRecovered(); + // } + } else { + final percent = syncHeight / currentKnownChainHeight; + + status = lib_monero_compat.SyncingSyncStatus( + blocksLeft, + percent, + currentKnownChainHeight, + ); + } + + _setSyncStatus(status); + _refreshTxDataHelper(); + } + } + + void onBalancesChanged({ + required BigInt newBalance, + required BigInt newUnlockedBalance, + }) async { + try { + await updateBalance(); + await updateTransactions(); + } catch (e, s) { + Logging.instance.w("onBalancesChanged(): ", error: e, stackTrace: s); + } + } + + void onNewBlock(int nodeHeight) async { + try { + await updateTransactions(); + } catch (e, s) { + Logging.instance.w("onNewBlock(): ", error: e, stackTrace: s); + } + } + + final _utxosUpdateLock = Mutex(); + Future onUTXOsChanged(List utxos) async { + await _utxosUpdateLock.protect(() async { + final cwUtxos = await libSalviumWallet?.getOutputs(refresh: true) ?? []; + + // bool changed = false; + + for (final cw in cwUtxos) { + final match = utxos.where( + (e) => + e.keyImage != null && + e.keyImage!.isNotEmpty && + e.keyImage == cw.keyImage, + ); + + if (match.isNotEmpty) { + final u = match.first; + + if (u.isBlocked) { + if (!cw.isFrozen) { + await libSalviumWallet?.freezeOutput(cw.keyImage); + // changed = true; + } + } else { + if (cw.isFrozen) { + await libSalviumWallet?.thawOutput(cw.keyImage); + // changed = true; + } + } + } + } + + // if (changed) { + // await libSalviumWallet?.updateUTXOs(); + // } + }); + } + + void onNewTransaction() { + // TODO: [prio=low] get rid of UpdatedInBackgroundEvent and move to + // adding the v2 tx to the db which would update ui automagically since the + // db is watched by the ui + // call this here? + GlobalEventBus.instance.fire( + UpdatedInBackgroundEvent( + "New data found in $walletId ${info.name} in background!", + walletId, + ), + ); + } + + void syncStatusChanged() async { + final _syncStatus = syncStatus; + + if (_syncStatus != null) { + if (_syncStatus.progress() == 1 && refreshMutex.isLocked) { + refreshMutex.release(); + } + + WalletSyncStatus? status; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(true); + + if (_syncStatus is lib_monero_compat.SyncingSyncStatus) { + final int blocksLeft = _syncStatus.blocksLeft; + + // ensure at least 1 to prevent math errors + final int height = max(1, _syncStatus.height); + + final nodeHeight = height + blocksLeft; + currentKnownChainHeight = nodeHeight; + + // final percent = height / nodeHeight; + final percent = _syncStatus.ptc; + + final highest = max(highestPercentCached, percent); + + final unchanged = highest == highestPercentCached; + if (unchanged) { + return; + } + + // update cached + if (highestPercentCached < percent) { + highestPercentCached = percent; + } + + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent(highest, walletId), + ); + GlobalEventBus.instance.fire( + BlocksRemainingEvent(blocksLeft, walletId), + ); + } else if (_syncStatus is lib_monero_compat.SyncedSyncStatus) { + status = WalletSyncStatus.synced; + } else if (_syncStatus is lib_monero_compat.NotConnectedSyncStatus) { + status = WalletSyncStatus.unableToSync; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + } else if (_syncStatus is lib_monero_compat.StartingSyncStatus) { + status = WalletSyncStatus.syncing; + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent(highestPercentCached, walletId), + ); + } else if (_syncStatus is lib_monero_compat.FailedSyncStatus) { + status = WalletSyncStatus.unableToSync; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + } else if (_syncStatus is lib_monero_compat.ConnectingSyncStatus) { + status = WalletSyncStatus.syncing; + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent(highestPercentCached, walletId), + ); + } else if (_syncStatus is lib_monero_compat.ConnectedSyncStatus) { + status = WalletSyncStatus.syncing; + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent(highestPercentCached, walletId), + ); + } else if (_syncStatus is lib_monero_compat.LostConnectionSyncStatus) { + status = WalletSyncStatus.unableToSync; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + } + + if (status != null) { + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent(status, walletId, info.coin), + ); + } + } + } + + @override + Future checkSaveInitialReceivingAddress() async { + // this doesn't work without opening the wallet first which takes a while + } + + // ============ Private ====================================================== + Future _refreshTxDataHelper() async { + if (_txRefreshLock) return; + _txRefreshLock = true; + + final _syncStatus = syncStatus; + + if (_syncStatus != null && + _syncStatus is lib_monero_compat.SyncingSyncStatus) { + final int blocksLeft = _syncStatus.blocksLeft; + final tenKChange = blocksLeft ~/ 10000; + + // only refresh transactions periodically during a sync + if (_lastCheckedHeight == -1 || tenKChange < _lastCheckedHeight) { + _lastCheckedHeight = tenKChange; + await _refreshTxData(); + } + } else { + await _refreshTxData(); + } + + _txRefreshLock = false; + } + + Future _refreshTxData() async { + await updateTransactions(); + final count = await mainDB.getTransactions(walletId).count(); + + if (count > _txCount) { + _txCount = count; + await updateBalance(); + GlobalEventBus.instance.fire( + UpdatedInBackgroundEvent( + "New transaction data found in $walletId ${info.name}!", + walletId, + ), + ); + } + } + + // ============ Overrides ==================================================== + + @override + FilterOperation? get changeAddressFilterOperation => null; + + @override + FilterOperation? get receivingAddressFilterOperation => null; + + @override + Future updateUTXOs() async { + final List outputArray = []; + final utxos = + await libSalviumWallet?.getOutputs(refresh: true) ?? + []; + for (final rawUTXO in utxos) { + if (!rawUTXO.spent) { + final current = + await mainDB.isar.utxos + .where() + .walletIdEqualTo(walletId) + .filter() + .voutEqualTo(rawUTXO.vout) + .and() + .txidEqualTo(rawUTXO.hash) + .findFirst(); + final tx = + await mainDB.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) + .filter() + .txidEqualTo(rawUTXO.hash) + .findFirst(); + + final otherDataMap = { + UTXOOtherDataKeys.keyImage: rawUTXO.keyImage, + UTXOOtherDataKeys.spent: rawUTXO.spent, + }; + + final utxo = UTXO( + address: rawUTXO.address, + walletId: walletId, + txid: rawUTXO.hash, + vout: rawUTXO.vout, + value: rawUTXO.value.toInt(), + name: current?.name ?? "", + isBlocked: current?.isBlocked ?? rawUTXO.isFrozen, + blockedReason: current?.blockedReason ?? "", + isCoinbase: rawUTXO.coinbase, + blockHash: "", + blockHeight: + tx?.height ?? (rawUTXO.height > 0 ? rawUTXO.height : null), + blockTime: tx?.timestamp, + otherData: jsonEncode(otherDataMap), + ); + + outputArray.add(utxo); + } + } + + await mainDB.updateUTXOs(walletId, outputArray); + + return true; + } + + @override + Future updateBalance({bool shouldUpdateUtxos = true}) async { + if (shouldUpdateUtxos) { + await updateUTXOs(); + } + + final total = await totalBalance; + final available = await availableBalance; + + final balance = Balance( + total: total, + spendable: available, + blockedTotal: Amount( + rawValue: BigInt.zero, + fractionDigits: cryptoCurrency.fractionDigits, + ), + pendingSpendable: total - available, + ); + + await info.updateBalance(newBalance: balance, isar: mainDB.isar); + } + + @override + Future refresh() async { + // Awaiting this lock could be dangerous. + // Since refresh is periodic (generally) + if (refreshMutex.isLocked) { + return; + } + + final node = getCurrentNode(); + + if (prefs.useTor) { + if (node.clearnetEnabled && !node.torEnabled) { + libSalviumWallet?.stopAutoSaving(); + libSalviumWallet?.stopListeners(); + libSalviumWallet?.stopSyncing(); + _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + throw Exception("TOR - clearnet mismatch"); + } + } else { + if (!node.clearnetEnabled && node.torEnabled) { + libSalviumWallet?.stopAutoSaving(); + libSalviumWallet?.stopListeners(); + libSalviumWallet?.stopSyncing(); + _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + throw Exception("TOR - clearnet mismatch"); + } + } + + // this acquire should be almost instant due to above check. + // Slight possibility of race but should be irrelevant + await refreshMutex.acquire(); + + libSalviumWallet?.startSyncing(); + _setSyncStatus(lib_monero_compat.StartingSyncStatus()); + + await updateTransactions(); + await updateBalance(); + + if (info.otherData[WalletInfoKeys.reuseAddress] != true) { + await checkReceivingAddressForTransactions(); + } + + if (refreshMutex.isLocked) { + refreshMutex.release(); + } + + final synced = await libSalviumWallet?.isSynced(); + + if (synced == true) { + _setSyncStatus(lib_monero_compat.SyncedSyncStatus()); + } + } + + @override + Future generateNewReceivingAddress() async { + try { + final currentReceiving = await getCurrentReceivingAddress(); + + final newReceivingIndex = + currentReceiving == null ? 0 : currentReceiving.derivationIndex + 1; + + final newReceivingAddress = addressFor(index: newReceivingIndex); + + // Add that new receiving address + await mainDB.putAddress(newReceivingAddress); + await info.updateReceivingAddress( + newAddress: newReceivingAddress.value, + isar: mainDB.isar, + ); + } catch (e, s) { + Logging.instance.e( + "Exception in generateNewAddress(): ", + error: e, + stackTrace: s, + ); + } + } + + @override + Future checkReceivingAddressForTransactions() async { + if (info.otherData[WalletInfoKeys.reuseAddress] == true) { + try { + throw Exception(); + } catch (_, s) { + Logging.instance.e( + "checkReceivingAddressForTransactions called but reuse address flag set: $s", + error: e, + stackTrace: s, + ); + } + } + + try { + int highestIndex = -1; + final entries = await libSalviumWallet?.getAllTxs(refresh: true); + if (entries != null) { + for (final element in entries) { + if (!element.isSpend) { + final int curAddressIndex = + element.addressIndexes.isEmpty + ? 0 + : element.addressIndexes.reduce(max); + if (curAddressIndex > highestIndex) { + highestIndex = curAddressIndex; + } + } + } + } + + // Check the new receiving index + final currentReceiving = await getCurrentReceivingAddress(); + final curIndex = currentReceiving?.derivationIndex ?? -1; + + if (highestIndex >= curIndex) { + // First increment the receiving index + final newReceivingIndex = curIndex + 1; + + // Use new index to derive a new receiving address + final newReceivingAddress = addressFor(index: newReceivingIndex); + + final existing = + await mainDB + .getAddresses(walletId) + .filter() + .valueEqualTo(newReceivingAddress.value) + .findFirst(); + if (existing == null) { + // Add that new change address + await mainDB.putAddress(newReceivingAddress); + } else { + // we need to update the address + await mainDB.updateAddress(existing, newReceivingAddress); + } + if (info.otherData[WalletInfoKeys.reuseAddress] != true) { + // keep checking until address with no tx history is set as current + await checkReceivingAddressForTransactions(); + } + } + } on SocketException catch (se, s) { + Logging.instance.e( + "SocketException caught in _checkReceivingAddressForTransactions(): $se\n$s", + error: e, + stackTrace: s, + ); + return; + } catch (e, s) { + Logging.instance.e( + "Exception rethrown from _checkReceivingAddressForTransactions(): ", + error: e, + stackTrace: s, + ); + rethrow; + } + } + + // TODO: this needs some work. Prio's may need to be changed as well as estimated blocks + @override + Future get fees async => FeeObject( + numberOfBlocksFast: 10, + numberOfBlocksAverage: 15, + numberOfBlocksSlow: 20, + fast: BigInt.from(lib_salvium.TransactionPriority.high.value), + medium: BigInt.from(lib_salvium.TransactionPriority.medium.value), + slow: BigInt.from(lib_salvium.TransactionPriority.normal.value), + ); + + @override + Future updateChainHeight() async { + await info.updateCachedChainHeight( + newHeight: currentKnownChainHeight, + isar: mainDB.isar, + ); + } + + @override + Future checkChangeAddressForTransactions() async { + // do nothing + } + + @override + Future generateNewChangeAddress() async { + // do nothing + } + + @override + Future prepareSend({required TxData txData}) async { + try { + final feeRate = txData.feeRateType; + if (feeRate is FeeRateType) { + lib_salvium.TransactionPriority feePriority; + switch (feeRate) { + case FeeRateType.fast: + feePriority = lib_salvium.TransactionPriority.high; + break; + case FeeRateType.average: + feePriority = lib_salvium.TransactionPriority.medium; + break; + case FeeRateType.slow: + feePriority = lib_salvium.TransactionPriority.normal; + break; + default: + throw ArgumentError("Invalid use of custom fee"); + } + + try { + final bool sweep; + + if (txData.utxos == null) { + final balance = await availableBalance; + sweep = txData.amount! == balance; + } else { + final totalInputsValue = txData.utxos! + .map((e) => e.value) + .fold(BigInt.zero, (p, e) => p + BigInt.from(e)); + sweep = txData.amount!.raw == totalInputsValue; + } + + // TODO: test this one day + // cs_salvium may not support this yet properly + if (sweep && txData.recipients!.length > 1) { + throw Exception("Send all not supported with multiple recipients"); + } + + final List outputs = []; + for (final recipient in txData.recipients!) { + final output = lib_salvium.Recipient( + address: recipient.address, + amount: recipient.amount.raw, + ); + + outputs.add(output); + } + + if (outputs.isEmpty) { + throw Exception("No recipients provided"); + } + + final height = await chainHeight; + final inputs = + txData.utxos + ?.map( + (e) => lib_salvium.Output( + address: e.address!, + hash: e.txid, + keyImage: e.keyImage!, + value: BigInt.from(e.value), + isFrozen: e.isBlocked, + isUnlocked: + e.blockHeight != null && + (height - (e.blockHeight ?? 0)) >= + cryptoCurrency.minConfirms, + height: e.blockHeight ?? 0, + vout: e.vout, + spent: e.used ?? false, + spentHeight: null, // doesn't matter here + coinbase: e.isCoinbase, + ), + ) + .toList(); + + return await prepareSendMutex.protect(() async { + final lib_salvium.PendingTransaction pendingTransaction; + if (outputs.length == 1) { + pendingTransaction = await libSalviumWallet!.createTx( + output: outputs.first, + paymentId: "", + sweep: sweep, + priority: feePriority, + preferredInputs: inputs, + accountIndex: 0, // sw only uses account 0 at this time + ); + } else { + pendingTransaction = await libSalviumWallet!.createTxMultiDest( + outputs: outputs, + paymentId: "", + priority: feePriority, + preferredInputs: inputs, + sweep: sweep, + accountIndex: 0, // sw only uses account 0 at this time + ); + } + + final realFee = Amount( + rawValue: pendingTransaction.fee, + fractionDigits: cryptoCurrency.fractionDigits, + ); + + return txData.copyWith( + fee: realFee, + pendingTransaction: pendingTransaction, + ); + }); + } catch (e) { + rethrow; + } + } else { + throw ArgumentError("Invalid fee rate argument provided!"); + } + } catch (e, s) { + Logging.instance.i( + "Exception rethrown from prepare send(): ", + error: e, + stackTrace: s, + ); + + if (e.toString().contains("Incorrect unlocked balance")) { + throw Exception("Insufficient balance!"); + } else { + throw Exception("Transaction failed with error: $e"); + } + } + } + + @override + Future confirmSend({required TxData txData}) async { + try { + try { + await libSalviumWallet!.commitTx(txData.pendingTransaction!); + + Logging.instance.d( + "transaction ${txData.pendingTransaction!.txid} has been sent", + ); + return txData.copyWith(txid: txData.pendingTransaction!.txid); + } catch (e, s) { + Logging.instance.e( + "${info.name} ${compatType.name.toLowerCase()} confirmSend: ", + error: e, + stackTrace: s, + ); + rethrow; + } + } catch (e, s) { + Logging.instance.e( + "Exception rethrown from confirmSend(): ", + error: e, + stackTrace: s, + ); + rethrow; + } + } + + // ============== View only ================================================== + + @override + Future recoverViewOnly() async { + await refreshMutex.protect(() async { + final data = + await getViewOnlyWalletData() as CryptonoteViewOnlyWalletData; + + try { + final height = max(info.restoreHeight, 0); + + await info.updateRestoreHeight( + newRestoreHeight: height, + isar: mainDB.isar, + ); + + final String name = walletId; + + final path = await pathForWallet(name: name, type: compatType); + + final password = generatePassword(); + await secureStorageInterface.write( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + value: password, + ); + final wallet = await getRestoredFromViewKeyWallet( + path: path, + password: password, + address: data.address, + privateViewKey: data.privateViewKey, + height: height, + ); + + if (libSalviumWallet != null) { + await exit(); + } + + libSalviumWallet = wallet; + + _setListener(); + + final newReceivingAddress = + await getCurrentReceivingAddress() ?? + Address( + walletId: walletId, + derivationIndex: 0, + derivationPath: null, + value: wallet.getAddress().value, + publicKey: [], + type: AddressType.cryptonote, + subType: AddressSubType.receiving, + ); + + await mainDB.updateOrPutAddresses([newReceivingAddress]); + await info.updateReceivingAddress( + newAddress: newReceivingAddress.value, + isar: mainDB.isar, + ); + + await updateNode(); + _setListener(); + + unawaited(libSalviumWallet?.rescanBlockchain()); + libSalviumWallet?.startSyncing(); + + // await save(); + libSalviumWallet?.startListeners(); + libSalviumWallet?.startAutoSaving(); + } catch (e, s) { + Logging.instance.e( + "Exception rethrown from recoverViewOnly(): ", + error: e, + stackTrace: s, + ); + rethrow; + } + }); + } + + // ============== Private ==================================================== + + StreamSubscription? _torStatusListener; + StreamSubscription? _torPreferenceListener; + + final Mutex _torConnectingLock = Mutex(); + bool _requireMutex = false; +} From 8029116a2b85a9f87843275b0329987722ddc446 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 23 May 2025 13:45:51 -0500 Subject: [PATCH 05/22] fix: add optional pendingSalviumTransaction to TxData --- lib/wallets/models/tx_data.dart | 23 +++++++++++++++++++ .../intermediate/lib_salvium_wallet.dart | 8 +++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/wallets/models/tx_data.dart b/lib/wallets/models/tx_data.dart index c95fad163..96882d428 100644 --- a/lib/wallets/models/tx_data.dart +++ b/lib/wallets/models/tx_data.dart @@ -1,4 +1,5 @@ import 'package:cs_monero/cs_monero.dart' as lib_monero; +import 'package:cs_salvium/cs_salvium.dart' as lib_salvium; import 'package:tezart/tezart.dart' as tezart; import 'package:web3dart/web3dart.dart' as web3dart; @@ -51,6 +52,7 @@ class TxData { final BigInt? chainId; // wownero and monero specific final lib_monero.PendingTransaction? pendingTransaction; + final lib_salvium.PendingTransaction? pendingSalviumTransaction; // firo lelantus specific final int? jMintValue; @@ -110,6 +112,7 @@ class TxData { this.nonce, this.chainId, this.pendingTransaction, + this.pendingSalviumTransaction, this.jMintValue, this.spendCoinIndexes, this.height, @@ -142,6 +145,14 @@ class TxData { ); } } + if (pendingSalviumTransaction?.amount != null && fee != null) { + if (pendingSalviumTransaction!.amount + fee!.raw == sum.raw) { + return Amount( + rawValue: pendingSalviumTransaction!.amount, + fractionDigits: recipients!.first.amount.fractionDigits, + ); + } + } return sum; } @@ -178,6 +189,14 @@ class TxData { ); } } + if (pendingSalviumTransaction?.amount != null && fee != null) { + if (pendingSalviumTransaction!.amount + fee!.raw == sum.raw) { + return Amount( + rawValue: pendingSalviumTransaction!.amount, + fractionDigits: recipients!.first.amount.fractionDigits, + ); + } + } return sum; } @@ -234,6 +253,7 @@ class TxData { int? nonce, BigInt? chainId, lib_monero.PendingTransaction? pendingTransaction, + lib_salvium.PendingTransaction? pendingSalviumTransaction, int? jMintValue, List? spendCoinIndexes, int? height, @@ -281,6 +301,8 @@ class TxData { nonce: nonce ?? this.nonce, chainId: chainId ?? this.chainId, pendingTransaction: pendingTransaction ?? this.pendingTransaction, + pendingSalviumTransaction: + pendingSalviumTransaction ?? this.pendingSalviumTransaction, jMintValue: jMintValue ?? this.jMintValue, spendCoinIndexes: spendCoinIndexes ?? this.spendCoinIndexes, height: height ?? this.height, @@ -324,6 +346,7 @@ class TxData { 'nonce: $nonce, ' 'chainId: $chainId, ' 'pendingTransaction: $pendingTransaction, ' + 'pendingSalviumTransaction: $pendingSalviumTransaction, ' 'jMintValue: $jMintValue, ' 'spendCoinIndexes: $spendCoinIndexes, ' 'height: $height, ' diff --git a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart index be26f5b22..4e9c9ac6c 100644 --- a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart +++ b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart @@ -1366,7 +1366,7 @@ abstract class LibSalviumWallet return txData.copyWith( fee: realFee, - pendingTransaction: pendingTransaction, + pendingSalviumTransaction: pendingTransaction, ); }); } catch (e) { @@ -1394,12 +1394,12 @@ abstract class LibSalviumWallet Future confirmSend({required TxData txData}) async { try { try { - await libSalviumWallet!.commitTx(txData.pendingTransaction!); + await libSalviumWallet!.commitTx(txData.pendingSalviumTransaction!); Logging.instance.d( - "transaction ${txData.pendingTransaction!.txid} has been sent", + "transaction ${txData.pendingSalviumTransaction!.txid} has been sent", ); - return txData.copyWith(txid: txData.pendingTransaction!.txid); + return txData.copyWith(txid: txData.pendingSalviumTransaction!.txid); } catch (e, s) { Logging.instance.e( "${info.name} ${compatType.name.toLowerCase()} confirmSend: ", From c6cba7eb4b9a7e81620a102d2af1399d1b5c33a1 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 23 May 2025 14:06:44 -0500 Subject: [PATCH 06/22] fix: HACK salvium wallets are "just" monero wallets--is probably bad --- lib/wallets/wallet/impl/salvium_wallet.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wallets/wallet/impl/salvium_wallet.dart b/lib/wallets/wallet/impl/salvium_wallet.dart index 29555ef62..484526554 100644 --- a/lib/wallets/wallet/impl/salvium_wallet.dart +++ b/lib/wallets/wallet/impl/salvium_wallet.dart @@ -9,7 +9,7 @@ import '../intermediate/lib_salvium_wallet.dart'; class SalviumWallet extends LibSalviumWallet { SalviumWallet(CryptoCurrencyNetwork network) - : super(Salvium(network), lib_monero_compat.WalletType.salvium); + : super(Salvium(network), lib_monero_compat.WalletType.monero); @override Future estimateFeeFor(Amount amount, BigInt feeRate) async { From 078a70981d0e1b35f7984bbdd9a7da1105d9677e Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 23 May 2025 14:07:13 -0500 Subject: [PATCH 07/22] fix: add Salvium to SW app config --- scripts/app_config/configure_stack_wallet.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/app_config/configure_stack_wallet.sh b/scripts/app_config/configure_stack_wallet.sh index e46420fa0..a0d83ee97 100755 --- a/scripts/app_config/configure_stack_wallet.sh +++ b/scripts/app_config/configure_stack_wallet.sh @@ -85,6 +85,7 @@ final List _supportedCoins = List.unmodifiable([ Peercoin(CryptoCurrencyNetwork.test), Stellar(CryptoCurrencyNetwork.test), Xelis(CryptoCurrencyNetwork.test), + Salvium(CryptoCurrencyNetwork.main), ]); final ({String from, String to}) _swapDefaults = (from: "BTC", to: "XMR"); From 604fd8c06f1e5524f841ee7bff413f650dc074ea Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 23 May 2025 14:37:03 -0500 Subject: [PATCH 08/22] fix: use monero wordlist for salvium, too --- .../restore_wallet_view/restore_wallet_view.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart index 90ef055e2..97e960baf 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart @@ -189,7 +189,8 @@ class _RestoreWalletViewState extends ConsumerState { // TODO: check for wownero wordlist? bool _isValidMnemonicWord(String word) { // TODO: get the actual language - if (widget.coin is Monero) { + if (widget.coin is Monero || widget.coin is Salvium) { + // Salvium use's Monero's wordlists. switch (widget.seedWordsLength) { case 25: return lib_monero.getMoneroWordList("English").contains(word); From fa262c1e00e95cad3a7393832b6cf73313020ddb Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 23 May 2025 14:39:33 -0500 Subject: [PATCH 09/22] fix: sort Salvium alphabetically in the coins list --- scripts/app_config/configure_stack_wallet.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/app_config/configure_stack_wallet.sh b/scripts/app_config/configure_stack_wallet.sh index a0d83ee97..8bec3a2a4 100755 --- a/scripts/app_config/configure_stack_wallet.sh +++ b/scripts/app_config/configure_stack_wallet.sh @@ -69,6 +69,7 @@ final List _supportedCoins = List.unmodifiable([ Namecoin(CryptoCurrencyNetwork.main), Particl(CryptoCurrencyNetwork.main), Peercoin(CryptoCurrencyNetwork.main), + Salvium(CryptoCurrencyNetwork.main), Solana(CryptoCurrencyNetwork.main), Stellar(CryptoCurrencyNetwork.main), Tezos(CryptoCurrencyNetwork.main), @@ -85,7 +86,6 @@ final List _supportedCoins = List.unmodifiable([ Peercoin(CryptoCurrencyNetwork.test), Stellar(CryptoCurrencyNetwork.test), Xelis(CryptoCurrencyNetwork.test), - Salvium(CryptoCurrencyNetwork.main), ]); final ({String from, String to}) _swapDefaults = (from: "BTC", to: "XMR"); From 5c418647eaa0d39c3de6cfa0bf5bde31566a0301 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 23 May 2025 15:06:45 -0500 Subject: [PATCH 10/22] fix: add Salvium wallet handling --- .../restore_wallet_view/restore_wallet_view.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart index 97e960baf..b70d9a0ef 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart @@ -45,6 +45,7 @@ import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/isar/models/wallet_info.dart'; import '../../../wallets/wallet/impl/epiccash_wallet.dart'; import '../../../wallets/wallet/impl/monero_wallet.dart'; +import '../../../wallets/wallet/impl/salvium_wallet.dart'; import '../../../wallets/wallet/impl/wownero_wallet.dart'; import '../../../wallets/wallet/impl/xelis_wallet.dart'; import '../../../wallets/wallet/intermediate/external_wallet.dart'; @@ -259,6 +260,7 @@ class _RestoreWalletViewState extends ConsumerState { if (bip39.validateMnemonic(mnemonic) == false && !(widget.coin is Monero || widget.coin is Wownero || + widget.coin is Salvium || widget.coin is Xelis)) { unawaited( showFloatingFlushBar( @@ -343,6 +345,10 @@ class _RestoreWalletViewState extends ConsumerState { await (wallet as WowneroWallet).init(isRestore: true); break; + case const (Salvium): + await (wallet as SalviumWallet).init(isRestore: true); + break; + case const (XelisWallet): await (wallet as XelisWallet).init(isRestore: true); break; From 27b6193a9d73ec32bf3f08daa2ab03414a254723 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 23 May 2025 15:09:10 -0500 Subject: [PATCH 11/22] fix: add Salvium wallet handling --- .../new_wallet_recovery_phrase_warning_view.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart index 2ad110fbd..e16ab5b0c 100644 --- a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart +++ b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart @@ -31,6 +31,7 @@ import '../../../utilities/util.dart'; import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/isar/models/wallet_info.dart'; import '../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../../../wallets/wallet/intermediate/lib_salvium_wallet.dart'; import '../../../wallets/wallet/wallet.dart'; import '../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -183,7 +184,7 @@ class _NewWalletRecoveryPhraseWarningViewState // TODO: Refactor these to generate each coin in their respective classes // This code should not be in a random view page file - if (coin is Monero || coin is Wownero || coin is Xelis) { + if (coin is Monero || coin is Wownero || coin is Xelis || coin is Salvium) { // currently a special case due to the // xmr/wow libraries handling their // own mnemonic generation @@ -230,6 +231,8 @@ class _NewWalletRecoveryPhraseWarningViewState if (wallet is LibMoneroWallet) { await wallet.init(wordCount: wordCount); + } else if (wallet is LibSalviumWallet) { + await wallet.init(wordCount: wordCount); } else { await wallet.init(); } From dfbbc33c77ccbea51fc70d6e1a79d0b41888014b Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 23 May 2025 15:11:18 -0500 Subject: [PATCH 12/22] fix: handle Salvium wallets --- lib/wallets/wallet/wallet.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/wallets/wallet/wallet.dart b/lib/wallets/wallet/wallet.dart index 9ed0c9c6e..595c1088e 100644 --- a/lib/wallets/wallet/wallet.dart +++ b/lib/wallets/wallet/wallet.dart @@ -42,6 +42,7 @@ import 'impl/namecoin_wallet.dart'; import 'impl/nano_wallet.dart'; import 'impl/particl_wallet.dart'; import 'impl/peercoin_wallet.dart'; +import 'impl/salvium_wallet.dart'; import 'impl/solana_wallet.dart'; import 'impl/stellar_wallet.dart'; import 'impl/sub_wallets/eth_token_wallet.dart'; @@ -378,6 +379,9 @@ abstract class Wallet { case const (Peercoin): return PeercoinWallet(net); + case const (Salvium): + return SalviumWallet(net); + case const (Solana): return SolanaWallet(net); From 7749c0f8819e783d306441ebc7aa695434f23fe8 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 23 May 2025 15:17:47 -0500 Subject: [PATCH 13/22] fix: only use 25 word salvium mnemonics --- lib/wallets/wallet/intermediate/lib_salvium_wallet.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart index 4e9c9ac6c..dc5373d3d 100644 --- a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart +++ b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart @@ -332,9 +332,7 @@ abstract class LibSalviumWallet Future init({bool? isRestore, int? wordCount}) async { final path = await pathForWallet(name: walletId, type: compatType); if (!(walletExists(path)) && isRestore != true) { - if (wordCount == null) { - throw Exception("Missing word count for new xmr/wow wallet!"); - } + wordCount ??= 25; try { final password = generatePassword(); await secureStorageInterface.write( From bb0401cca32d89c36a9c5a5b1ec7062dbba59492 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 23 May 2025 17:09:06 -0500 Subject: [PATCH 14/22] fix: rip compat out --- lib/wallets/wallet/impl/salvium_wallet.dart | 6 +- .../intermediate/lib_salvium_wallet.dart | 372 ++++++++++-------- 2 files changed, 216 insertions(+), 162 deletions(-) diff --git a/lib/wallets/wallet/impl/salvium_wallet.dart b/lib/wallets/wallet/impl/salvium_wallet.dart index 484526554..f340776be 100644 --- a/lib/wallets/wallet/impl/salvium_wallet.dart +++ b/lib/wallets/wallet/impl/salvium_wallet.dart @@ -9,12 +9,12 @@ import '../intermediate/lib_salvium_wallet.dart'; class SalviumWallet extends LibSalviumWallet { SalviumWallet(CryptoCurrencyNetwork network) - : super(Salvium(network), lib_monero_compat.WalletType.monero); + : super(Salvium(network)); @override Future estimateFeeFor(Amount amount, BigInt feeRate) async { - if (libSalviumWallet == null || - syncStatus is! lib_monero_compat.SyncedSyncStatus) { + if (libSalviumWallet == null /*|| + syncStatus is! lib_monero_compat.SyncedSyncStatus*/) { return Amount.zeroWith(fractionDigits: cryptoCurrency.fractionDigits); } diff --git a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart index dc5373d3d..bd402efe0 100644 --- a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart +++ b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'dart:io'; import 'dart:math'; -import 'package:compat/compat.dart' as lib_monero_compat; import 'package:cs_salvium/cs_salvium.dart' as lib_salvium; import 'package:isar/isar.dart'; import 'package:mutex/mutex.dart'; @@ -47,7 +46,7 @@ abstract class LibSalviumWallet @override int get isarTransactionVersion => 2; - LibSalviumWallet(super.currency, this.compatType) { + LibSalviumWallet(super.currency) { final bus = GlobalEventBus.instance; // Listen for tor status changes. @@ -103,26 +102,25 @@ abstract class LibSalviumWallet }); } - final lib_monero_compat.WalletType compatType; lib_salvium.Wallet? libSalviumWallet; - lib_monero_compat.SyncStatus? get syncStatus => _syncStatus; - lib_monero_compat.SyncStatus? _syncStatus; - int _syncedCount = 0; - void _setSyncStatus(lib_monero_compat.SyncStatus status) { - if (status is lib_monero_compat.SyncedSyncStatus) { - if (_syncStatus is lib_monero_compat.SyncedSyncStatus) { - _syncedCount++; - } - } else { - _syncedCount = 0; - } - - if (_syncedCount < 3) { - _syncStatus = status; - syncStatusChanged(); - } - } + // lib_monero_compat.SyncStatus? get syncStatus => _syncStatus; + // lib_monero_compat.SyncStatus? _syncStatus; + // int _syncedCount = 0; + // void _setSyncStatus(lib_monero_compat.SyncStatus status) { + // if (status is lib_monero_compat.SyncedSyncStatus) { + // if (_syncStatus is lib_monero_compat.SyncedSyncStatus) { + // _syncedCount++; + // } + // } else { + // _syncedCount = 0; + // } + // + // if (_syncedCount < 3) { + // _syncStatus = status; + // syncStatusChanged(); + // } + // } final prepareSendMutex = Mutex(); final estimateFeeMutex = Mutex(); @@ -191,13 +189,13 @@ abstract class LibSalviumWallet if (libSalviumWallet == null) { wasNull = true; // libSalviumWalletT?.close(); - final path = await pathForWallet(name: walletId, type: compatType); + final path = await pathForWallet(name: walletId); final String password; try { password = (await secureStorageInterface.read( - key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + key: _libSalviumWalletPasswordKey(walletId.toUpperCase()), ))!; } catch (e, s) { throw Exception("Password not found $e, $s"); @@ -224,10 +222,10 @@ abstract class LibSalviumWallet if (wasNull) { try { - _setSyncStatus(lib_monero_compat.ConnectingSyncStatus()); + // _setSyncStatus(lib_monero_compat.ConnectingSyncStatus()); libSalviumWallet?.startSyncing(); } catch (_) { - _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + // _setSyncStatus(lib_monero_compat.FailedSyncStatus()); // TODO log } } @@ -238,23 +236,22 @@ abstract class LibSalviumWallet unawaited(refresh()); } - @Deprecated("Only used in the case of older wallets") - lib_monero_compat.WalletInfo? getLibSalviumWalletInfo(String walletId) { - try { - return DB.instance.moneroWalletInfoBox.values.firstWhere( - (info) => info.id == lib_monero_compat.hiveIdFor(walletId, compatType), - ); - } catch (_) { - return null; - } - } + // @Deprecated("Only used in the case of older wallets") + // lib_monero_compat.WalletInfo? getLibSalviumWalletInfo(String walletId) { + // try { + // return DB.instance.moneroWalletInfoBox.values.firstWhere( + // (info) => info.id == lib_monero_compat.hiveIdFor(walletId, compatType), + // ); + // } catch (_) { + // return null; + // } + // } Future save() async { if (!Platform.isWindows) { final appRoot = await StackFileSystem.applicationRootDirectory(); - await lib_monero_compat.backupWalletFiles( + await _backupWalletFiles( name: walletId, - type: compatType, appRoot: appRoot, ); } @@ -287,8 +284,8 @@ abstract class LibSalviumWallet Future getKeys() async { final base = libSalviumWallet; - final oldInfo = getLibSalviumWalletInfo(walletId); - if (base == null || (oldInfo != null && oldInfo.name != walletId)) { + // final oldInfo = getLibSalviumWalletInfo(walletId); + if (base == null /*|| (oldInfo != null && oldInfo.name != walletId)*/) { return null; } try { @@ -313,12 +310,12 @@ abstract class LibSalviumWallet Future<(String, String)> hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing() async { - final path = await pathForWallet(name: walletId, type: compatType); + final path = await pathForWallet(name: walletId); final String password; try { password = (await secureStorageInterface.read( - key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + key: _libSalviumWalletPasswordKey(walletId), ))!; } catch (e, s) { throw Exception("Password not found $e, $s"); @@ -330,13 +327,13 @@ abstract class LibSalviumWallet @override Future init({bool? isRestore, int? wordCount}) async { - final path = await pathForWallet(name: walletId, type: compatType); + final path = await pathForWallet(name: walletId); if (!(walletExists(path)) && isRestore != true) { wordCount ??= 25; try { final password = generatePassword(); await secureStorageInterface.write( - key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + key: _libSalviumWalletPasswordKey(walletId), value: password, ); final wallet = await getCreatedWallet( @@ -410,12 +407,12 @@ abstract class LibSalviumWallet final String name = walletId; - final path = await pathForWallet(name: name, type: compatType); + final path = await pathForWallet(name: name); try { final password = generatePassword(); await secureStorageInterface.write( - key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + key: _libSalviumWalletPasswordKey(walletId), value: password, ); final wallet = await getRestoredWallet( @@ -493,7 +490,7 @@ abstract class LibSalviumWallet libSalviumWallet?.stopAutoSaving(); libSalviumWallet?.stopListeners(); libSalviumWallet?.stopSyncing(); - _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + // _setSyncStatus(lib_monero_compat.FailedSyncStatus()); throw Exception("TOR - clearnet mismatch"); } proxy = TorService.sharedInstance.getProxyInfo(); @@ -502,12 +499,12 @@ abstract class LibSalviumWallet libSalviumWallet?.stopAutoSaving(); libSalviumWallet?.stopListeners(); libSalviumWallet?.stopSyncing(); - _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + // _setSyncStatus(lib_monero_compat.FailedSyncStatus()); throw Exception("TOR - clearnet mismatch"); } } - _setSyncStatus(lib_monero_compat.ConnectingSyncStatus()); + // _setSyncStatus(lib_monero_compat.ConnectingSyncStatus()); try { if (_requireMutex) { await _torConnectingLock.protect(() async { @@ -544,9 +541,9 @@ abstract class LibSalviumWallet libSalviumWallet?.startListeners(); libSalviumWallet?.startAutoSaving(); - _setSyncStatus(lib_monero_compat.ConnectedSyncStatus()); + // _setSyncStatus(lib_monero_compat.ConnectedSyncStatus()); } catch (e, s) { - _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + // _setSyncStatus(lib_monero_compat.FailedSyncStatus()); Logging.instance.e( "Exception caught in $runtimeType.updateNode(): ", error: e, @@ -742,22 +739,18 @@ abstract class LibSalviumWallet Future pathForWalletDir({ required String name, - required lib_monero_compat.WalletType type, }) async { final Directory root = await StackFileSystem.applicationRootDirectory(); - return lib_monero_compat.pathForWalletDir( + return _pathForWalletDir( name: name, - type: type.name.toLowerCase(), appRoot: root, ); } Future pathForWallet({ required String name, - required lib_monero_compat.WalletType type, }) async => await pathForWalletDir( name: name, - type: type, ).then((path) => '$path/$name'); void onSyncingUpdate({ @@ -769,9 +762,8 @@ abstract class LibSalviumWallet currentKnownChainHeight = nodeHeight; updateChainHeight(); final blocksLeft = nodeHeight - syncHeight; - final lib_monero_compat.SyncStatus status; if (blocksLeft < 100) { - status = lib_monero_compat.SyncedSyncStatus(); + // status = lib_monero_compat.SyncedSyncStatus(); // if (!_hasSyncAfterStartup) { // _hasSyncAfterStartup = true; @@ -784,14 +776,14 @@ abstract class LibSalviumWallet } else { final percent = syncHeight / currentKnownChainHeight; - status = lib_monero_compat.SyncingSyncStatus( - blocksLeft, - percent, - currentKnownChainHeight, - ); + // status = lib_monero_compat.SyncingSyncStatus( + // blocksLeft, + // percent, + // currentKnownChainHeight, + // ); } - _setSyncStatus(status); + // _setSyncStatus(status); _refreshTxDataHelper(); } } @@ -867,82 +859,82 @@ abstract class LibSalviumWallet ); } - void syncStatusChanged() async { - final _syncStatus = syncStatus; - - if (_syncStatus != null) { - if (_syncStatus.progress() == 1 && refreshMutex.isLocked) { - refreshMutex.release(); - } - - WalletSyncStatus? status; - xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(true); - - if (_syncStatus is lib_monero_compat.SyncingSyncStatus) { - final int blocksLeft = _syncStatus.blocksLeft; - - // ensure at least 1 to prevent math errors - final int height = max(1, _syncStatus.height); - - final nodeHeight = height + blocksLeft; - currentKnownChainHeight = nodeHeight; - - // final percent = height / nodeHeight; - final percent = _syncStatus.ptc; - - final highest = max(highestPercentCached, percent); - - final unchanged = highest == highestPercentCached; - if (unchanged) { - return; - } - - // update cached - if (highestPercentCached < percent) { - highestPercentCached = percent; - } - - GlobalEventBus.instance.fire( - RefreshPercentChangedEvent(highest, walletId), - ); - GlobalEventBus.instance.fire( - BlocksRemainingEvent(blocksLeft, walletId), - ); - } else if (_syncStatus is lib_monero_compat.SyncedSyncStatus) { - status = WalletSyncStatus.synced; - } else if (_syncStatus is lib_monero_compat.NotConnectedSyncStatus) { - status = WalletSyncStatus.unableToSync; - xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); - } else if (_syncStatus is lib_monero_compat.StartingSyncStatus) { - status = WalletSyncStatus.syncing; - GlobalEventBus.instance.fire( - RefreshPercentChangedEvent(highestPercentCached, walletId), - ); - } else if (_syncStatus is lib_monero_compat.FailedSyncStatus) { - status = WalletSyncStatus.unableToSync; - xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); - } else if (_syncStatus is lib_monero_compat.ConnectingSyncStatus) { - status = WalletSyncStatus.syncing; - GlobalEventBus.instance.fire( - RefreshPercentChangedEvent(highestPercentCached, walletId), - ); - } else if (_syncStatus is lib_monero_compat.ConnectedSyncStatus) { - status = WalletSyncStatus.syncing; - GlobalEventBus.instance.fire( - RefreshPercentChangedEvent(highestPercentCached, walletId), - ); - } else if (_syncStatus is lib_monero_compat.LostConnectionSyncStatus) { - status = WalletSyncStatus.unableToSync; - xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); - } - - if (status != null) { - GlobalEventBus.instance.fire( - WalletSyncStatusChangedEvent(status, walletId, info.coin), - ); - } - } - } + // void syncStatusChanged() async { + // final _syncStatus = syncStatus; + // + // if (_syncStatus != null) { + // if (_syncStatus.progress() == 1 && refreshMutex.isLocked) { + // refreshMutex.release(); + // } + // + // WalletSyncStatus? status; + // xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(true); + // + // if (_syncStatus is lib_monero_compat.SyncingSyncStatus) { + // final int blocksLeft = _syncStatus.blocksLeft; + // + // // ensure at least 1 to prevent math errors + // final int height = max(1, _syncStatus.height); + // + // final nodeHeight = height + blocksLeft; + // currentKnownChainHeight = nodeHeight; + // + // // final percent = height / nodeHeight; + // final percent = _syncStatus.ptc; + // + // final highest = max(highestPercentCached, percent); + // + // final unchanged = highest == highestPercentCached; + // if (unchanged) { + // return; + // } + // + // // update cached + // if (highestPercentCached < percent) { + // highestPercentCached = percent; + // } + // + // GlobalEventBus.instance.fire( + // RefreshPercentChangedEvent(highest, walletId), + // ); + // GlobalEventBus.instance.fire( + // BlocksRemainingEvent(blocksLeft, walletId), + // ); + // } else if (_syncStatus is lib_monero_compat.SyncedSyncStatus) { + // status = WalletSyncStatus.synced; + // } else if (_syncStatus is lib_monero_compat.NotConnectedSyncStatus) { + // status = WalletSyncStatus.unableToSync; + // xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + // } else if (_syncStatus is lib_monero_compat.StartingSyncStatus) { + // status = WalletSyncStatus.syncing; + // GlobalEventBus.instance.fire( + // RefreshPercentChangedEvent(highestPercentCached, walletId), + // ); + // } else if (_syncStatus is lib_monero_compat.FailedSyncStatus) { + // status = WalletSyncStatus.unableToSync; + // xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + // } else if (_syncStatus is lib_monero_compat.ConnectingSyncStatus) { + // status = WalletSyncStatus.syncing; + // GlobalEventBus.instance.fire( + // RefreshPercentChangedEvent(highestPercentCached, walletId), + // ); + // } else if (_syncStatus is lib_monero_compat.ConnectedSyncStatus) { + // status = WalletSyncStatus.syncing; + // GlobalEventBus.instance.fire( + // RefreshPercentChangedEvent(highestPercentCached, walletId), + // ); + // } else if (_syncStatus is lib_monero_compat.LostConnectionSyncStatus) { + // status = WalletSyncStatus.unableToSync; + // xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + // } + // + // if (status != null) { + // GlobalEventBus.instance.fire( + // WalletSyncStatusChangedEvent(status, walletId, info.coin), + // ); + // } + // } + // } @override Future checkSaveInitialReceivingAddress() async { @@ -954,21 +946,21 @@ abstract class LibSalviumWallet if (_txRefreshLock) return; _txRefreshLock = true; - final _syncStatus = syncStatus; - - if (_syncStatus != null && - _syncStatus is lib_monero_compat.SyncingSyncStatus) { - final int blocksLeft = _syncStatus.blocksLeft; - final tenKChange = blocksLeft ~/ 10000; + // final _syncStatus = syncStatus; - // only refresh transactions periodically during a sync - if (_lastCheckedHeight == -1 || tenKChange < _lastCheckedHeight) { - _lastCheckedHeight = tenKChange; - await _refreshTxData(); - } - } else { - await _refreshTxData(); - } + // if (_syncStatus != null && + // _syncStatus is lib_monero_compat.SyncingSyncStatus) { + // final int blocksLeft = _syncStatus.blocksLeft; + // final tenKChange = blocksLeft ~/ 10000; + // + // // only refresh transactions periodically during a sync + // if (_lastCheckedHeight == -1 || tenKChange < _lastCheckedHeight) { + // _lastCheckedHeight = tenKChange; + // await _refreshTxData(); + // } + // } else { + // await _refreshTxData(); + // } _txRefreshLock = false; } @@ -1090,7 +1082,7 @@ abstract class LibSalviumWallet libSalviumWallet?.stopAutoSaving(); libSalviumWallet?.stopListeners(); libSalviumWallet?.stopSyncing(); - _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + // _setSyncStatus(lib_monero_compat.FailedSyncStatus()); throw Exception("TOR - clearnet mismatch"); } } else { @@ -1098,7 +1090,7 @@ abstract class LibSalviumWallet libSalviumWallet?.stopAutoSaving(); libSalviumWallet?.stopListeners(); libSalviumWallet?.stopSyncing(); - _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + // _setSyncStatus(lib_monero_compat.FailedSyncStatus()); throw Exception("TOR - clearnet mismatch"); } } @@ -1108,7 +1100,7 @@ abstract class LibSalviumWallet await refreshMutex.acquire(); libSalviumWallet?.startSyncing(); - _setSyncStatus(lib_monero_compat.StartingSyncStatus()); + // _setSyncStatus(lib_monero_compat.StartingSyncStatus()); await updateTransactions(); await updateBalance(); @@ -1124,7 +1116,7 @@ abstract class LibSalviumWallet final synced = await libSalviumWallet?.isSynced(); if (synced == true) { - _setSyncStatus(lib_monero_compat.SyncedSyncStatus()); + // _setSyncStatus(lib_monero_compat.SyncedSyncStatus()); } } @@ -1400,7 +1392,7 @@ abstract class LibSalviumWallet return txData.copyWith(txid: txData.pendingSalviumTransaction!.txid); } catch (e, s) { Logging.instance.e( - "${info.name} ${compatType.name.toLowerCase()} confirmSend: ", + "${info.name} confirmSend: ", error: e, stackTrace: s, ); @@ -1434,11 +1426,11 @@ abstract class LibSalviumWallet final String name = walletId; - final path = await pathForWallet(name: name, type: compatType); + final path = await pathForWallet(name: name); final password = generatePassword(); await secureStorageInterface.write( - key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + key: _libSalviumWalletPasswordKey(walletId.toUpperCase()), value: password, ); final wallet = await getRestoredFromViewKeyWallet( @@ -1503,3 +1495,65 @@ abstract class LibSalviumWallet final Mutex _torConnectingLock = Mutex(); bool _requireMutex = false; } + +String _libSalviumWalletPasswordKey(String walletName) => + "SALVIUM_WALLET_PASSWORD_${walletName.toUpperCase()}"; + +String _backupFileName(String originalPath) { + final pathParts = originalPath.split('/'); + final newName = '#_${pathParts.last}'; + pathParts.removeLast(); + pathParts.add(newName); + return pathParts.join('/'); +} + +Future _backupWalletFiles({ + required String name, + required Directory appRoot, +}) async { + final path = await _pathForWallet( + name: name, + appRoot: appRoot, + ); + final cacheFile = File(path); + final keysFile = File('$path.keys'); + final addressListFile = File('$path.address.txt'); + final newCacheFilePath = _backupFileName(cacheFile.path); + final newKeysFilePath = _backupFileName(keysFile.path); + final newAddressListFilePath = _backupFileName(addressListFile.path); + + if (cacheFile.existsSync()) { + await cacheFile.copy(newCacheFilePath); + } + + if (keysFile.existsSync()) { + await keysFile.copy(newKeysFilePath); + } + + if (addressListFile.existsSync()) { + await addressListFile.copy(newAddressListFilePath); + } +} + +Future _pathForWalletDir({ + required String name, + required Directory appRoot, +}) async { + final walletsDir = Directory('${appRoot.path}/wallets'); + final walletDire = Directory('${walletsDir.path}/salvium/$name'); + + if (!walletDire.existsSync()) { + walletDire.createSync(recursive: true); + } + + return walletDire.path; +} + +Future _pathForWallet({ + required String name, + required Directory appRoot, +}) async => + await _pathForWalletDir( + name: name, + appRoot: appRoot, + ).then((path) => '$path/$name'); From 3703127722ce2cd407dbe6f19909052bf1f6a62a Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 23 May 2025 17:21:21 -0500 Subject: [PATCH 15/22] fix: handle salvium wallets in more places and comment improvement --- .../verify_recovery_phrase_view.dart | 24 +++++++++++++++++++ .../helpers/restore_create_backup.dart | 3 ++- .../wallet_settings_wallet_settings_view.dart | 5 ++-- .../firo_rescan_recovery_error_dialog.dart | 3 +++ .../tx_v2/transaction_v2_details_view.dart | 3 ++- lib/pages/wallet_view/wallet_view.dart | 3 ++- .../sub_widgets/desktop_wallet_features.dart | 2 +- .../unlock_wallet_keys_desktop.dart | 3 +++ .../sub_widgets/wallet_options_button.dart | 3 ++- lib/services/wallets.dart | 9 +++---- .../intermediate/lib_salvium_wallet.dart | 2 +- 11 files changed, 48 insertions(+), 12 deletions(-) diff --git a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart index 97c467b34..06beb584a 100644 --- a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart +++ b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart @@ -14,6 +14,8 @@ import 'dart:math'; import 'package:cs_monero/src/deprecated/get_height_by_date.dart' as cs_monero_deprecated; +import 'package:cs_salvium/src/deprecated/get_height_by_date.dart' +as cs_salvium_deprecated; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -40,6 +42,7 @@ import '../../../wallets/wallet/impl/monero_wallet.dart'; import '../../../wallets/wallet/impl/wownero_wallet.dart'; import '../../../wallets/wallet/impl/xelis_wallet.dart'; import '../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../../../wallets/wallet/intermediate/lib_salvium_wallet.dart'; import '../../../wallets/wallet/wallet.dart'; import '../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; import '../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; @@ -132,6 +135,11 @@ class _VerifyRecoveryPhraseViewState date: DateTime.now().subtract(const Duration(days: 7)), ); } + if (widget.wallet.cryptoCurrency is Salvium) { + height = cs_salvium_deprecated.getSalviumHeightByDate( + date: DateTime.now().subtract(const Duration(days: 7)), + ); + } if (height < 0) height = 0; viewOnlyWalletType = ViewOnlyWalletType.cryptonote; @@ -188,6 +196,22 @@ class _VerifyRecoveryPhraseViewState await w.exit(); + viewOnlyData = CryptonoteViewOnlyWalletData( + walletId: voInfo.walletId, + address: address, + privateViewKey: privateViewKey, + ); + } else if (widget.wallet is LibSalviumWallet) { + final w = widget.wallet as LibSalviumWallet; + + final info = + await w + .hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing(); + final address = info.$1; + final privateViewKey = info.$2; + + await w.exit(); + viewOnlyData = CryptonoteViewOnlyWalletData( walletId: voInfo.walletId, address: address, diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart index 679ccc84c..57643c43b 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart @@ -57,6 +57,7 @@ import '../../../../../wallets/wallet/impl/monero_wallet.dart'; import '../../../../../wallets/wallet/impl/wownero_wallet.dart'; import '../../../../../wallets/wallet/impl/xelis_wallet.dart'; import '../../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../../../../../wallets/wallet/intermediate/lib_salvium_wallet.dart'; import '../../../../../wallets/wallet/wallet.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/private_key_interface.dart'; @@ -505,7 +506,7 @@ abstract class SWB { int restoreHeight = walletbackup['restoreHeight'] as int? ?? 0; if (restoreHeight <= 0) { - if (wallet is EpiccashWallet || wallet is LibMoneroWallet) { + if (wallet is EpiccashWallet || wallet is LibMoneroWallet || wallet is LibSalviumWallet) { restoreHeight = 0; } else { restoreHeight = walletbackup['storedChainHeight'] as int? ?? 0; diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart index 3917f743e..c2eca9000 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart @@ -21,6 +21,7 @@ import '../../../../utilities/text_styles.dart'; import '../../../../wallets/isar/models/wallet_info.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../../../../wallets/wallet/intermediate/lib_salvium_wallet.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/rbf_interface.dart'; @@ -416,8 +417,8 @@ class _WalletSettingsWalletSettingsViewState ), ), ), - if (wallet is LibMoneroWallet) const SizedBox(height: 8), - if (wallet is LibMoneroWallet) + if (wallet is LibMoneroWallet || wallet is LibSalviumWallet) const SizedBox(height: 8), + if (wallet is LibMoneroWallet || wallet is LibSalviumWallet) RoundedWhiteContainer( padding: const EdgeInsets.all(0), child: RawMaterialButton( diff --git a/lib/pages/special/firo_rescan_recovery_error_dialog.dart b/lib/pages/special/firo_rescan_recovery_error_dialog.dart index fa59d841a..2653db128 100644 --- a/lib/pages/special/firo_rescan_recovery_error_dialog.dart +++ b/lib/pages/special/firo_rescan_recovery_error_dialog.dart @@ -13,6 +13,7 @@ import '../../utilities/text_styles.dart'; import '../../utilities/util.dart'; import '../../wallets/isar/providers/wallet_info_provider.dart'; import '../../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../../wallets/wallet/intermediate/lib_salvium_wallet.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; import '../../widgets/background.dart'; @@ -272,6 +273,8 @@ class _FiroRescanRecoveryErrorViewState keyData = await wallet.getXPrivs(); } else if (wallet is LibMoneroWallet) { keyData = await wallet.getKeys(); + } else if (wallet is LibSalviumWallet) { + keyData = await wallet.getKeys(); } if (context.mounted) { diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart index 06469c92b..a2d4e8df1 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart @@ -44,6 +44,7 @@ import '../../../../wallets/isar/models/spark_coin.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../../wallets/wallet/impl/epiccash_wallet.dart'; import '../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../../../../wallets/wallet/intermediate/lib_salvium_wallet.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/rbf_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart'; import '../../../../widgets/background.dart'; @@ -187,7 +188,7 @@ class _TransactionV2DetailsViewState final wallet = ref.read(pWallets).getWallet(walletId); hasTxKeyProbably = - wallet is LibMoneroWallet && + (wallet is LibMoneroWallet || wallet is LibSalviumWallet) && (_transaction.type == TransactionType.outgoing || _transaction.type == TransactionType.sentToSelf); diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 7356e07e6..61bf31037 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -54,6 +54,7 @@ import '../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; import '../../wallets/wallet/impl/firo_wallet.dart'; import '../../wallets/wallet/impl/namecoin_wallet.dart'; import '../../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../../wallets/wallet/intermediate/lib_salvium_wallet.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart'; import '../../wallets/wallet/wallet_mixin_interfaces/ordinals_interface.dart'; @@ -1295,7 +1296,7 @@ class _WalletViewState extends ConsumerState { ); }, ), - if (wallet is LibMoneroWallet && !viewOnly) + if ((wallet is LibMoneroWallet || wallet is LibSalviumWallet) && !viewOnly) WalletNavigationBarItemData( label: "Churn", icon: const ChurnNavIcon(), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart index 814af0cd2..1f36c55b4 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart @@ -442,7 +442,7 @@ class _DesktopWalletFeaturesState extends ConsumerState { if (!isViewOnly && wallet is CashFusionInterface) (WalletFeature.fusion, Assets.svg.cashFusion, _onFusionPressed), - if (!isViewOnly && wallet is LibMoneroWallet) + if (!isViewOnly && (wallet is LibMoneroWallet || wallet is LibSalviumWallet)) (WalletFeature.churn, Assets.svg.churn, _onChurnPressed), if (wallet is NamecoinWallet) diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart index 938e9568a..3faf4ec80 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart @@ -24,6 +24,7 @@ import '../../../../utilities/constants.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; import '../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../../../../wallets/wallet/intermediate/lib_salvium_wallet.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; @@ -118,6 +119,8 @@ class _UnlockWalletKeysDesktopState keyData = await wallet.getXPrivs(); } else if (wallet is LibMoneroWallet) { keyData = await wallet.getKeys(); + } else if (wallet is LibSalviumWallet) { + keyData = await wallet.getKeys(); } if (mounted) { diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart index 47b590a61..8136133ca 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart @@ -30,6 +30,7 @@ import '../../../../wallets/crypto_currency/intermediate/frost_currency.dart'; import '../../../../wallets/crypto_currency/intermediate/nano_currency.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../../../../wallets/wallet/intermediate/lib_salvium_wallet.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; import '../../../addresses/desktop_wallet_addresses_view.dart'; @@ -296,7 +297,7 @@ class WalletOptionsPopupMenu extends ConsumerWidget { final bool canChangeRep = coin is NanoCurrency; final bool isFrost = coin is FrostCurrency; - final bool isMoneroWow = wallet is LibMoneroWallet; + final bool isMoneroWow = wallet is LibMoneroWallet || wallet is LibSalviumWallet; return Stack( children: [ diff --git a/lib/services/wallets.dart b/lib/services/wallets.dart index d47c7b66b..ce10f548b 100644 --- a/lib/services/wallets.dart +++ b/lib/services/wallets.dart @@ -25,6 +25,7 @@ import '../wallets/crypto_currency/crypto_currency.dart'; import '../wallets/isar/models/wallet_info.dart'; import '../wallets/wallet/impl/epiccash_wallet.dart'; import '../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../wallets/wallet/intermediate/lib_salvium_wallet.dart'; import '../wallets/wallet/wallet.dart'; import 'event_bus/events/wallet_added_event.dart'; import 'event_bus/global_event_bus.dart'; @@ -230,7 +231,7 @@ class Wallets { shouldAutoSyncAll || walletIdsToEnableAutoSync.contains(walletInfo.walletId); - if (wallet is LibMoneroWallet) { + if (wallet is LibMoneroWallet || wallet is LibSalviumWallet) { // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); } else { walletInitFutures.add( @@ -339,7 +340,7 @@ class Wallets { nodeService: nodeService, prefs: prefs, ).then((wallet) { - if (wallet is LibMoneroWallet) { + if (wallet is LibMoneroWallet || wallet is LibSalviumWallet) { // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); walletIdCompleter.complete("dummy_ignore"); @@ -477,7 +478,7 @@ class Wallets { nodeService: nodeService, prefs: prefs, ).then((wallet) { - if (wallet is LibMoneroWallet) { + if (wallet is LibMoneroWallet || wallet is LibSalviumWallet) { // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); walletIdCompleter.complete("dummy_ignore"); @@ -588,7 +589,7 @@ class Wallets { walletIdsToEnableAutoSync.contains(wallet.walletId); if (isDesktop) { - if (wallet is LibMoneroWallet) { + if (wallet is LibMoneroWallet || wallet is LibSalviumWallet) { // walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync)); } else { walletInitFutures.add( diff --git a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart index bd402efe0..aeff86381 100644 --- a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart +++ b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart @@ -188,7 +188,7 @@ abstract class LibSalviumWallet if (libSalviumWallet == null) { wasNull = true; - // libSalviumWalletT?.close(); + // await libSalviumWallet?.close(); final path = await pathForWallet(name: walletId); final String password; From 554e8984577a090a4dded32a0740eb07409d827e Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 23 May 2025 17:26:33 -0500 Subject: [PATCH 16/22] fix: add missing import --- .../wallet_view/sub_widgets/desktop_wallet_features.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart index 1f36c55b4..c5e40a234 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart @@ -40,6 +40,7 @@ import '../../../../wallets/crypto_currency/coins/firo.dart'; import '../../../../wallets/wallet/impl/firo_wallet.dart'; import '../../../../wallets/wallet/impl/namecoin_wallet.dart'; import '../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../../../../wallets/wallet/intermediate/lib_salvium_wallet.dart'; import '../../../../wallets/wallet/wallet.dart' show Wallet; import '../../../../wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart'; From 1cf6989e728229be06c7a4f416cf118f476c13b0 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Fri, 23 May 2025 17:48:28 -0500 Subject: [PATCH 17/22] fix: more salvium stuff --- .../manage_nodes_views/add_edit_node_view.dart | 7 ++++--- .../wallet_settings_view/wallet_settings_view.dart | 4 ++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart index 5703db754..f22070c5f 100644 --- a/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart +++ b/lib/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart @@ -36,6 +36,7 @@ import '../../../../utilities/util.dart'; import '../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../wallets/crypto_currency/intermediate/cryptonote_currency.dart'; import '../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../../../../wallets/wallet/intermediate/lib_salvium_wallet.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/conditional_parent.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -228,7 +229,7 @@ class _AddEditNodeViewState extends ConsumerState { // strip unused path String address = formData.host!; - if (coin is LibMoneroWallet) { + if (coin is LibMoneroWallet || coin is LibSalviumWallet) { if (address.startsWith("http")) { final uri = Uri.parse(address); address = "${uri.scheme}://${uri.host}"; @@ -1067,7 +1068,7 @@ class _NodeFormState extends ConsumerState { } else { enableSSLCheckbox = true; } - } else if (widget.coin is LibMoneroWallet) { + } else if (widget.coin is LibMoneroWallet || widget.coin is LibSalviumWallet) { if (newValue.startsWith("https://")) { _useSSL = true; } else if (newValue.startsWith("http://")) { @@ -1274,7 +1275,7 @@ class _NodeFormState extends ConsumerState { ), ], ), - if (widget.coin is LibMoneroWallet) + if (widget.coin is LibMoneroWallet || widget.coin is LibSalviumWallet) Row( children: [ GestureDetector( diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart index 68436c634..2c41c1c7b 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart @@ -38,6 +38,7 @@ import '../../../wallets/crypto_currency/intermediate/nano_currency.dart'; import '../../../wallets/wallet/impl/bitcoin_frost_wallet.dart'; import '../../../wallets/wallet/impl/epiccash_wallet.dart'; import '../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../../../wallets/wallet/intermediate/lib_salvium_wallet.dart'; import '../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; import '../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; import '../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; @@ -325,6 +326,9 @@ class _WalletSettingsViewState extends ConsumerState { } else if (wallet is LibMoneroWallet) { keyData = await wallet.getKeys(); + } else if (wallet + is LibSalviumWallet) { + keyData = await wallet.getKeys(); } if (context.mounted) { From 8d51d904d102f64756e9229efebaeb6e524af311 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 26 May 2025 13:42:33 -0600 Subject: [PATCH 18/22] autosave and organise --- .../restore_options_view.dart | 16 ++++---- .../verify_recovery_phrase_view.dart | 9 +++-- .../intermediate/lib_salvium_wallet.dart | 37 ++++++------------- 3 files changed, 23 insertions(+), 39 deletions(-) diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart index 40607b103..e09882a7c 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart @@ -416,8 +416,7 @@ class _SeedRestoreOptionState extends ConsumerState { final currentLength = ref.watch(mnemonicWordCountStateProvider); - final isMoneroAnd25 = widget.coin is Monero && currentLength == 25; - final isWowneroAnd25 = widget.coin is Wownero && currentLength == 25; + final isCnAnd25 = widget.coin is CryptonoteCurrency && currentLength == 25; final bool supportsPassphrase; if (widget.coin.hasMnemonicPassphraseSupport) { @@ -432,7 +431,7 @@ class _SeedRestoreOptionState extends ConsumerState { return Column( children: [ - if (isMoneroAnd25 || widget.coin is Epiccash || isWowneroAnd25) + if (isCnAnd25 || widget.coin is Epiccash) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -459,9 +458,9 @@ class _SeedRestoreOptionState extends ConsumerState { ), ], ), - if (isMoneroAnd25 || widget.coin is Epiccash || isWowneroAnd25) + if (isCnAnd25 || widget.coin is Epiccash) SizedBox(height: Util.isDesktop ? 16 : 8), - if (isMoneroAnd25 || widget.coin is Epiccash || isWowneroAnd25) + if (isCnAnd25 || widget.coin is Epiccash) ref.watch(_pIsUsingDate) ? RestoreFromDatePicker( onTap: widget.dateChooserFunction, @@ -518,9 +517,8 @@ class _SeedRestoreOptionState extends ConsumerState { ), ), ), - if (isMoneroAnd25 || widget.coin is Epiccash || isWowneroAnd25) - const SizedBox(height: 8), - if (isMoneroAnd25 || widget.coin is Epiccash || isWowneroAnd25) + if (isCnAnd25 || widget.coin is Epiccash) const SizedBox(height: 8), + if (isCnAnd25 || widget.coin is Epiccash) RoundedWhiteContainer( child: Center( child: Text( @@ -541,7 +539,7 @@ class _SeedRestoreOptionState extends ConsumerState { ), ), ), - if (isMoneroAnd25 || widget.coin is Epiccash || isWowneroAnd25) + if (isCnAnd25 || widget.coin is Epiccash) SizedBox(height: Util.isDesktop ? 24 : 16), Text( "Choose recovery phrase length", diff --git a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart index 06beb584a..ef959aa22 100644 --- a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart +++ b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart @@ -15,7 +15,7 @@ import 'dart:math'; import 'package:cs_monero/src/deprecated/get_height_by_date.dart' as cs_monero_deprecated; import 'package:cs_salvium/src/deprecated/get_height_by_date.dart' -as cs_salvium_deprecated; + as cs_salvium_deprecated; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -124,7 +124,8 @@ class _VerifyRecoveryPhraseViewState }); } viewOnlyWalletType = ViewOnlyWalletType.xPub; - } else if (widget.wallet is LibMoneroWallet) { + } else if (widget.wallet is LibMoneroWallet || + widget.wallet is LibSalviumWallet) { if (widget.wallet.cryptoCurrency is Monero) { height = cs_monero_deprecated.getMoneroHeightByDate( date: DateTime.now().subtract(const Duration(days: 7)), @@ -205,8 +206,8 @@ class _VerifyRecoveryPhraseViewState final w = widget.wallet as LibSalviumWallet; final info = - await w - .hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing(); + await w + .hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing(); final address = info.$1; final privateViewKey = info.$2; diff --git a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart index aeff86381..231aa721e 100644 --- a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart +++ b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart @@ -250,10 +250,7 @@ abstract class LibSalviumWallet Future save() async { if (!Platform.isWindows) { final appRoot = await StackFileSystem.applicationRootDirectory(); - await _backupWalletFiles( - name: walletId, - appRoot: appRoot, - ); + await _backupWalletFiles(name: walletId, appRoot: appRoot); } await libSalviumWallet!.save(); } @@ -285,7 +282,7 @@ abstract class LibSalviumWallet final base = libSalviumWallet; // final oldInfo = getLibSalviumWalletInfo(walletId); - if (base == null /*|| (oldInfo != null && oldInfo.name != walletId)*/) { + if (base == null /*|| (oldInfo != null && oldInfo.name != walletId)*/ ) { return null; } try { @@ -737,21 +734,13 @@ abstract class LibSalviumWallet await libSalviumWallet?.save(); } - Future pathForWalletDir({ - required String name, - }) async { + Future pathForWalletDir({required String name}) async { final Directory root = await StackFileSystem.applicationRootDirectory(); - return _pathForWalletDir( - name: name, - appRoot: root, - ); + return _pathForWalletDir(name: name, appRoot: root); } - Future pathForWallet({ - required String name, - }) async => await pathForWalletDir( - name: name, - ).then((path) => '$path/$name'); + Future pathForWallet({required String name}) async => + await pathForWalletDir(name: name).then((path) => '$path/$name'); void onSyncingUpdate({ required int syncHeight, @@ -1511,10 +1500,7 @@ Future _backupWalletFiles({ required String name, required Directory appRoot, }) async { - final path = await _pathForWallet( - name: name, - appRoot: appRoot, - ); + final path = await _pathForWallet(name: name, appRoot: appRoot); final cacheFile = File(path); final keysFile = File('$path.keys'); final addressListFile = File('$path.address.txt'); @@ -1552,8 +1538,7 @@ Future _pathForWalletDir({ Future _pathForWallet({ required String name, required Directory appRoot, -}) async => - await _pathForWalletDir( - name: name, - appRoot: appRoot, - ).then((path) => '$path/$name'); +}) async => await _pathForWalletDir( + name: name, + appRoot: appRoot, +).then((path) => '$path/$name'); From f24adaa6f282fbb54937562b97774a3464cf149f Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 26 May 2025 13:42:56 -0600 Subject: [PATCH 19/22] fix file exists error --- .../restore_wallet_view/restore_wallet_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart index b70d9a0ef..a8e0f8218 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart @@ -345,7 +345,7 @@ class _RestoreWalletViewState extends ConsumerState { await (wallet as WowneroWallet).init(isRestore: true); break; - case const (Salvium): + case const (SalviumWallet): await (wallet as SalviumWallet).init(isRestore: true); break; From 97f5995e442031384e2e0fc0e33755c4345263f7 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 26 May 2025 14:05:02 -0600 Subject: [PATCH 20/22] temporarily (lol) copy paste sync status stuff to quickly integrate syncing for salvium --- .../intermediate/lib_salvium_wallet.dart | 328 ++++++++++-------- 1 file changed, 189 insertions(+), 139 deletions(-) diff --git a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart index 231aa721e..a70c69d68 100644 --- a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart +++ b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart @@ -8,7 +8,6 @@ import 'package:isar/isar.dart'; import 'package:mutex/mutex.dart'; import 'package:stack_wallet_backup/generate_password.dart'; -import '../../../db/hive/db.dart'; import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; @@ -104,23 +103,23 @@ abstract class LibSalviumWallet lib_salvium.Wallet? libSalviumWallet; - // lib_monero_compat.SyncStatus? get syncStatus => _syncStatus; - // lib_monero_compat.SyncStatus? _syncStatus; - // int _syncedCount = 0; - // void _setSyncStatus(lib_monero_compat.SyncStatus status) { - // if (status is lib_monero_compat.SyncedSyncStatus) { - // if (_syncStatus is lib_monero_compat.SyncedSyncStatus) { - // _syncedCount++; - // } - // } else { - // _syncedCount = 0; - // } - // - // if (_syncedCount < 3) { - // _syncStatus = status; - // syncStatusChanged(); - // } - // } + SyncStatus? get syncStatus => _syncStatus; + SyncStatus? _syncStatus; + int _syncedCount = 0; + void _setSyncStatus(SyncStatus status) { + if (status is SyncedSyncStatus) { + if (_syncStatus is SyncedSyncStatus) { + _syncedCount++; + } + } else { + _syncedCount = 0; + } + + if (_syncedCount < 3) { + _syncStatus = status; + syncStatusChanged(); + } + } final prepareSendMutex = Mutex(); final estimateFeeMutex = Mutex(); @@ -222,10 +221,10 @@ abstract class LibSalviumWallet if (wasNull) { try { - // _setSyncStatus(lib_monero_compat.ConnectingSyncStatus()); + _setSyncStatus(ConnectingSyncStatus()); libSalviumWallet?.startSyncing(); } catch (_) { - // _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + _setSyncStatus(FailedSyncStatus()); // TODO log } } @@ -236,17 +235,6 @@ abstract class LibSalviumWallet unawaited(refresh()); } - // @Deprecated("Only used in the case of older wallets") - // lib_monero_compat.WalletInfo? getLibSalviumWalletInfo(String walletId) { - // try { - // return DB.instance.moneroWalletInfoBox.values.firstWhere( - // (info) => info.id == lib_monero_compat.hiveIdFor(walletId, compatType), - // ); - // } catch (_) { - // return null; - // } - // } - Future save() async { if (!Platform.isWindows) { final appRoot = await StackFileSystem.applicationRootDirectory(); @@ -281,8 +269,7 @@ abstract class LibSalviumWallet Future getKeys() async { final base = libSalviumWallet; - // final oldInfo = getLibSalviumWalletInfo(walletId); - if (base == null /*|| (oldInfo != null && oldInfo.name != walletId)*/ ) { + if (base == null) { return null; } try { @@ -487,7 +474,7 @@ abstract class LibSalviumWallet libSalviumWallet?.stopAutoSaving(); libSalviumWallet?.stopListeners(); libSalviumWallet?.stopSyncing(); - // _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + _setSyncStatus(FailedSyncStatus()); throw Exception("TOR - clearnet mismatch"); } proxy = TorService.sharedInstance.getProxyInfo(); @@ -496,12 +483,12 @@ abstract class LibSalviumWallet libSalviumWallet?.stopAutoSaving(); libSalviumWallet?.stopListeners(); libSalviumWallet?.stopSyncing(); - // _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + _setSyncStatus(FailedSyncStatus()); throw Exception("TOR - clearnet mismatch"); } } - // _setSyncStatus(lib_monero_compat.ConnectingSyncStatus()); + _setSyncStatus(ConnectingSyncStatus()); try { if (_requireMutex) { await _torConnectingLock.protect(() async { @@ -538,9 +525,9 @@ abstract class LibSalviumWallet libSalviumWallet?.startListeners(); libSalviumWallet?.startAutoSaving(); - // _setSyncStatus(lib_monero_compat.ConnectedSyncStatus()); + // _setSyncStatus(ConnectedSyncStatus()); } catch (e, s) { - // _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + // _setSyncStatus(FailedSyncStatus()); Logging.instance.e( "Exception caught in $runtimeType.updateNode(): ", error: e, @@ -751,8 +738,9 @@ abstract class LibSalviumWallet currentKnownChainHeight = nodeHeight; updateChainHeight(); final blocksLeft = nodeHeight - syncHeight; + final SyncStatus status; if (blocksLeft < 100) { - // status = lib_monero_compat.SyncedSyncStatus(); + status = SyncedSyncStatus(); // if (!_hasSyncAfterStartup) { // _hasSyncAfterStartup = true; @@ -765,14 +753,14 @@ abstract class LibSalviumWallet } else { final percent = syncHeight / currentKnownChainHeight; - // status = lib_monero_compat.SyncingSyncStatus( - // blocksLeft, - // percent, - // currentKnownChainHeight, - // ); + status = SyncingSyncStatus( + blocksLeft, + percent, + currentKnownChainHeight, + ); } - // _setSyncStatus(status); + _setSyncStatus(status); _refreshTxDataHelper(); } } @@ -848,82 +836,82 @@ abstract class LibSalviumWallet ); } - // void syncStatusChanged() async { - // final _syncStatus = syncStatus; - // - // if (_syncStatus != null) { - // if (_syncStatus.progress() == 1 && refreshMutex.isLocked) { - // refreshMutex.release(); - // } - // - // WalletSyncStatus? status; - // xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(true); - // - // if (_syncStatus is lib_monero_compat.SyncingSyncStatus) { - // final int blocksLeft = _syncStatus.blocksLeft; - // - // // ensure at least 1 to prevent math errors - // final int height = max(1, _syncStatus.height); - // - // final nodeHeight = height + blocksLeft; - // currentKnownChainHeight = nodeHeight; - // - // // final percent = height / nodeHeight; - // final percent = _syncStatus.ptc; - // - // final highest = max(highestPercentCached, percent); - // - // final unchanged = highest == highestPercentCached; - // if (unchanged) { - // return; - // } - // - // // update cached - // if (highestPercentCached < percent) { - // highestPercentCached = percent; - // } - // - // GlobalEventBus.instance.fire( - // RefreshPercentChangedEvent(highest, walletId), - // ); - // GlobalEventBus.instance.fire( - // BlocksRemainingEvent(blocksLeft, walletId), - // ); - // } else if (_syncStatus is lib_monero_compat.SyncedSyncStatus) { - // status = WalletSyncStatus.synced; - // } else if (_syncStatus is lib_monero_compat.NotConnectedSyncStatus) { - // status = WalletSyncStatus.unableToSync; - // xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); - // } else if (_syncStatus is lib_monero_compat.StartingSyncStatus) { - // status = WalletSyncStatus.syncing; - // GlobalEventBus.instance.fire( - // RefreshPercentChangedEvent(highestPercentCached, walletId), - // ); - // } else if (_syncStatus is lib_monero_compat.FailedSyncStatus) { - // status = WalletSyncStatus.unableToSync; - // xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); - // } else if (_syncStatus is lib_monero_compat.ConnectingSyncStatus) { - // status = WalletSyncStatus.syncing; - // GlobalEventBus.instance.fire( - // RefreshPercentChangedEvent(highestPercentCached, walletId), - // ); - // } else if (_syncStatus is lib_monero_compat.ConnectedSyncStatus) { - // status = WalletSyncStatus.syncing; - // GlobalEventBus.instance.fire( - // RefreshPercentChangedEvent(highestPercentCached, walletId), - // ); - // } else if (_syncStatus is lib_monero_compat.LostConnectionSyncStatus) { - // status = WalletSyncStatus.unableToSync; - // xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); - // } - // - // if (status != null) { - // GlobalEventBus.instance.fire( - // WalletSyncStatusChangedEvent(status, walletId, info.coin), - // ); - // } - // } - // } + void syncStatusChanged() async { + final _syncStatus = syncStatus; + + if (_syncStatus != null) { + if (_syncStatus.progress() == 1 && refreshMutex.isLocked) { + refreshMutex.release(); + } + + WalletSyncStatus? status; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(true); + + if (_syncStatus is SyncingSyncStatus) { + final int blocksLeft = _syncStatus.blocksLeft; + + // ensure at least 1 to prevent math errors + final int height = max(1, _syncStatus.height); + + final nodeHeight = height + blocksLeft; + currentKnownChainHeight = nodeHeight; + + // final percent = height / nodeHeight; + final percent = _syncStatus.ptc; + + final highest = max(highestPercentCached, percent); + + final unchanged = highest == highestPercentCached; + if (unchanged) { + return; + } + + // update cached + if (highestPercentCached < percent) { + highestPercentCached = percent; + } + + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent(highest, walletId), + ); + GlobalEventBus.instance.fire( + BlocksRemainingEvent(blocksLeft, walletId), + ); + } else if (_syncStatus is SyncedSyncStatus) { + status = WalletSyncStatus.synced; + } else if (_syncStatus is NotConnectedSyncStatus) { + status = WalletSyncStatus.unableToSync; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + } else if (_syncStatus is StartingSyncStatus) { + status = WalletSyncStatus.syncing; + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent(highestPercentCached, walletId), + ); + } else if (_syncStatus is FailedSyncStatus) { + status = WalletSyncStatus.unableToSync; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + } else if (_syncStatus is ConnectingSyncStatus) { + status = WalletSyncStatus.syncing; + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent(highestPercentCached, walletId), + ); + } else if (_syncStatus is ConnectedSyncStatus) { + status = WalletSyncStatus.syncing; + GlobalEventBus.instance.fire( + RefreshPercentChangedEvent(highestPercentCached, walletId), + ); + } else if (_syncStatus is LostConnectionSyncStatus) { + status = WalletSyncStatus.unableToSync; + xmrAndWowSyncSpecificFunctionThatShouldBeGottenRidOfInTheFuture(false); + } + + if (status != null) { + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent(status, walletId, info.coin), + ); + } + } + } @override Future checkSaveInitialReceivingAddress() async { @@ -935,21 +923,20 @@ abstract class LibSalviumWallet if (_txRefreshLock) return; _txRefreshLock = true; - // final _syncStatus = syncStatus; + final _syncStatus = syncStatus; - // if (_syncStatus != null && - // _syncStatus is lib_monero_compat.SyncingSyncStatus) { - // final int blocksLeft = _syncStatus.blocksLeft; - // final tenKChange = blocksLeft ~/ 10000; - // - // // only refresh transactions periodically during a sync - // if (_lastCheckedHeight == -1 || tenKChange < _lastCheckedHeight) { - // _lastCheckedHeight = tenKChange; - // await _refreshTxData(); - // } - // } else { - // await _refreshTxData(); - // } + if (_syncStatus != null && _syncStatus is SyncingSyncStatus) { + final int blocksLeft = _syncStatus.blocksLeft; + final tenKChange = blocksLeft ~/ 10000; + + // only refresh transactions periodically during a sync + if (_lastCheckedHeight == -1 || tenKChange < _lastCheckedHeight) { + _lastCheckedHeight = tenKChange; + await _refreshTxData(); + } + } else { + await _refreshTxData(); + } _txRefreshLock = false; } @@ -1071,7 +1058,7 @@ abstract class LibSalviumWallet libSalviumWallet?.stopAutoSaving(); libSalviumWallet?.stopListeners(); libSalviumWallet?.stopSyncing(); - // _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + _setSyncStatus(FailedSyncStatus()); throw Exception("TOR - clearnet mismatch"); } } else { @@ -1079,7 +1066,7 @@ abstract class LibSalviumWallet libSalviumWallet?.stopAutoSaving(); libSalviumWallet?.stopListeners(); libSalviumWallet?.stopSyncing(); - // _setSyncStatus(lib_monero_compat.FailedSyncStatus()); + _setSyncStatus(FailedSyncStatus()); throw Exception("TOR - clearnet mismatch"); } } @@ -1089,7 +1076,7 @@ abstract class LibSalviumWallet await refreshMutex.acquire(); libSalviumWallet?.startSyncing(); - // _setSyncStatus(lib_monero_compat.StartingSyncStatus()); + _setSyncStatus(StartingSyncStatus()); await updateTransactions(); await updateBalance(); @@ -1105,7 +1092,7 @@ abstract class LibSalviumWallet final synced = await libSalviumWallet?.isSynced(); if (synced == true) { - // _setSyncStatus(lib_monero_compat.SyncedSyncStatus()); + _setSyncStatus(SyncedSyncStatus()); } } @@ -1542,3 +1529,66 @@ Future _pathForWallet({ name: name, appRoot: appRoot, ).then((path) => '$path/$name'); + +// ============================================================================= +// The following sync status stuff copy pasted here for now to simplify the +// integration of salvium. +// TODO: eventually rework this (one day) + +abstract class SyncStatus { + const SyncStatus(); + double progress(); +} + +class SyncingSyncStatus extends SyncStatus { + SyncingSyncStatus(this.blocksLeft, this.ptc, this.height); + + final double ptc; + final int blocksLeft; + final int height; + + @override + double progress() => ptc; + + @override + String toString() => '$blocksLeft'; +} + +class SyncedSyncStatus extends SyncStatus { + @override + double progress() => 1.0; +} + +class NotConnectedSyncStatus extends SyncStatus { + const NotConnectedSyncStatus(); + + @override + double progress() => 0.0; +} + +class StartingSyncStatus extends SyncStatus { + @override + double progress() => 0.0; +} + +class FailedSyncStatus extends SyncStatus { + @override + double progress() => 1.0; +} + +class ConnectingSyncStatus extends SyncStatus { + @override + double progress() => 0.0; +} + +class ConnectedSyncStatus extends SyncStatus { + @override + double progress() => 0.0; +} + +class LostConnectionSyncStatus extends SyncStatus { + @override + double progress() => 1.0; +} + +// ============================================================================= From 0872e7245397c221f658accf494f10131b7600b9 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 26 May 2025 14:20:46 -0600 Subject: [PATCH 21/22] hook up salvium wallet files deletion --- lib/services/wallets.dart | 50 +++++++++++++------ .../intermediate/lib_salvium_wallet.dart | 5 ++ 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/lib/services/wallets.dart b/lib/services/wallets.dart index ce10f548b..7b5f9a376 100644 --- a/lib/services/wallets.dart +++ b/lib/services/wallets.dart @@ -9,6 +9,7 @@ */ import 'dart:async'; +import 'dart:io'; import 'package:compat/compat.dart' as lib_monero_compat; import 'package:isar/isar.dart'; @@ -22,6 +23,7 @@ import '../utilities/logger.dart'; import '../utilities/prefs.dart'; import '../utilities/stack_file_system.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; +import '../wallets/crypto_currency/intermediate/cryptonote_currency.dart'; import '../wallets/isar/models/wallet_info.dart'; import '../wallets/wallet/impl/epiccash_wallet.dart'; import '../wallets/wallet/intermediate/lib_monero_wallet.dart'; @@ -68,6 +70,38 @@ class Wallets { } } + Future _deleteCryptonoteWalletFilesHelper(WalletInfo info) async { + final walletId = info.walletId; + if (info.coin is Wownero) { + await lib_monero_compat.deleteWalletFiles( + name: walletId, + type: lib_monero_compat.WalletType.wownero, + appRoot: await StackFileSystem.applicationRootDirectory(), + ); + Logging.instance.d("Wownero wallet: $walletId deleted"); + } else if (info.coin is Monero) { + await lib_monero_compat.deleteWalletFiles( + name: walletId, + type: lib_monero_compat.WalletType.monero, + appRoot: await StackFileSystem.applicationRootDirectory(), + ); + Logging.instance.d("Monero wallet: $walletId deleted"); + } else if (info.coin is Salvium) { + final path = await salviumWalletDir( + walletId: walletId, + appRoot: await StackFileSystem.applicationRootDirectory(), + ); + final file = Directory(path); + final isExist = file.existsSync(); + + if (isExist) { + await file.delete(recursive: true); + } + } else { + throw Exception("Not a valid CN coin"); + } + } + Future deleteWallet( WalletInfo info, SecureStorageInterface secureStorage, @@ -88,20 +122,8 @@ class Wallets { key: Wallet.getViewOnlyWalletDataSecStoreKey(walletId: walletId), ); - if (info.coin is Wownero) { - await lib_monero_compat.deleteWalletFiles( - name: walletId, - type: lib_monero_compat.WalletType.wownero, - appRoot: await StackFileSystem.applicationRootDirectory(), - ); - Logging.instance.d("monero wallet: $walletId deleted"); - } else if (info.coin is Monero) { - await lib_monero_compat.deleteWalletFiles( - name: walletId, - type: lib_monero_compat.WalletType.monero, - appRoot: await StackFileSystem.applicationRootDirectory(), - ); - Logging.instance.d("monero wallet: $walletId deleted"); + if (info.coin is CryptonoteCurrency) { + await _deleteCryptonoteWalletFilesHelper(info); } else if (info.coin is Epiccash) { final deleteResult = await deleteEpicWallet( walletId: walletId, diff --git a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart index a70c69d68..fcb1cae60 100644 --- a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart +++ b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart @@ -1530,6 +1530,11 @@ Future _pathForWallet({ appRoot: appRoot, ).then((path) => '$path/$name'); +Future salviumWalletDir({ + required String walletId, + required Directory appRoot, +}) => _pathForWalletDir(name: walletId, appRoot: appRoot); + // ============================================================================= // The following sync status stuff copy pasted here for now to simplify the // integration of salvium. From aaac3e7688c5cd5e2d0ea22c3542ace9aa045d31 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 28 May 2025 16:46:05 -0600 Subject: [PATCH 22/22] use published salvium --- pubspec.lock | 88 +++++++++++++++++++ scripts/app_config/templates/pubspec.template | 7 +- 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index c5baa5ce0..6cb6be1ac 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -536,6 +536,94 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0-pre" + cs_salvium: + dependency: "direct main" + description: + name: cs_salvium + sha256: e25976e874dcf6bf4e4330b541b1028cffe8bacfffbcc51ab106f76c6686a56d + url: "https://pub.dev" + source: hosted + version: "1.1.0" + cs_salvium_flutter_libs: + dependency: "direct main" + description: + name: cs_salvium_flutter_libs + sha256: "4ab42fb499de4749e30de5cd4155aaaa44239229ec30a5c4ce661c6a2964ea96" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + cs_salvium_flutter_libs_android: + dependency: transitive + description: + name: cs_salvium_flutter_libs_android + sha256: "63603fc4c94d609e13c8e8064c742ac628ef006d3af9990e2c585489bde9b96d" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + cs_salvium_flutter_libs_android_arm64_v8a: + dependency: transitive + description: + name: cs_salvium_flutter_libs_android_arm64_v8a + sha256: "5ced9fe6d71dd22f90865600b8dff1ed07ce480db6c9de1a8d56e63318000e97" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + cs_salvium_flutter_libs_android_armeabi_v7a: + dependency: transitive + description: + name: cs_salvium_flutter_libs_android_armeabi_v7a + sha256: "2fb718dff22918e72b138c191dbd887d8d241f03a34add11e8d699c48b657b47" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + cs_salvium_flutter_libs_android_x86_64: + dependency: transitive + description: + name: cs_salvium_flutter_libs_android_x86_64 + sha256: "85134ab635a4dddec5735fdd8f3971a1c33e3aaa1c8aa88e54f0b3d5e0d0caab" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + cs_salvium_flutter_libs_ios: + dependency: transitive + description: + name: cs_salvium_flutter_libs_ios + sha256: "6e57590b054f420c79549d7059edfeca55e38cea19e34cf94fcf9fbd3de53d8b" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + cs_salvium_flutter_libs_linux: + dependency: transitive + description: + name: cs_salvium_flutter_libs_linux + sha256: ed24070da5c4bad5fd2f76c003a45a7378389d382b919cfa1ee88fad25d07fe3 + url: "https://pub.dev" + source: hosted + version: "1.0.1" + cs_salvium_flutter_libs_macos: + dependency: transitive + description: + name: cs_salvium_flutter_libs_macos + sha256: "9df0818299a5ddadd41eb4c94de2cd8e519d1e9f4aa6166657397200397f402f" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + cs_salvium_flutter_libs_platform_interface: + dependency: transitive + description: + name: cs_salvium_flutter_libs_platform_interface + sha256: "36ef1edd1481b92a95500fbdf397a371c1d624b58401a56638dc315f3c607dc0" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + cs_salvium_flutter_libs_windows: + dependency: transitive + description: + name: cs_salvium_flutter_libs_windows + sha256: "3cef0c1320ecc55afecbe5d0a97b9447d033d5994a2ebf93df64d0558e582daf" + url: "https://pub.dev" + source: hosted + version: "1.1.0" csslib: dependency: transitive description: diff --git a/scripts/app_config/templates/pubspec.template b/scripts/app_config/templates/pubspec.template index 2171e8cda..9b007143e 100644 --- a/scripts/app_config/templates/pubspec.template +++ b/scripts/app_config/templates/pubspec.template @@ -221,11 +221,8 @@ dependencies: drift: ^2.22.1 drift_flutter: ^0.2.3 path: ^1.9.1 -# cs_salvium: 1.0.0-pre.1 - cs_salvium: - path: ../cs_salvium/packages/cs_salvium - cs_salvium_flutter_libs: - path: ../cs_salvium/packages/cs_salvium_flutter_libs + cs_salvium: ^1.1.0 + cs_salvium_flutter_libs: ^1.0.1 dev_dependencies: flutter_test: