diff --git a/.metadata b/.metadata index d1551205d..675f4a740 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled. version: - revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + revision: f92f44110e87bad5ff168335c36da6f6053036e6 channel: stable project_type: app @@ -13,17 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - - platform: linux - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + create_revision: f92f44110e87bad5ff168335c36da6f6053036e6 + base_revision: f92f44110e87bad5ff168335c36da6f6053036e6 - platform: macos - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - - platform: windows - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + create_revision: f92f44110e87bad5ff168335c36da6f6053036e6 + base_revision: f92f44110e87bad5ff168335c36da6f6053036e6 # User provided section diff --git a/assets/icon/macos-icon.png b/assets/icon/macos-icon.png new file mode 100644 index 000000000..e4a263022 Binary files /dev/null and b/assets/icon/macos-icon.png differ diff --git a/crypto_plugins/flutter_libepiccash b/crypto_plugins/flutter_libepiccash index e1df08873..f677dec0b 160000 --- a/crypto_plugins/flutter_libepiccash +++ b/crypto_plugins/flutter_libepiccash @@ -1 +1 @@ -Subproject commit e1df088733695ad06d377087d069eae1746d55a7 +Subproject commit f677dec0b34d3f9fe8fce2bc8ff5c508c3f3bb9a diff --git a/crypto_plugins/flutter_liblelantus b/crypto_plugins/flutter_liblelantus index ec3cf5e8e..9cd241b5e 160000 --- a/crypto_plugins/flutter_liblelantus +++ b/crypto_plugins/flutter_liblelantus @@ -1 +1 @@ -Subproject commit ec3cf5e8e1b90e006188aa8c323d4cd52dbfa9b9 +Subproject commit 9cd241b5ea142e21c01dd7639b42603281c43287 diff --git a/crypto_plugins/flutter_libmonero b/crypto_plugins/flutter_libmonero index 26a152fea..e48952185 160000 --- a/crypto_plugins/flutter_libmonero +++ b/crypto_plugins/flutter_libmonero @@ -1 +1 @@ -Subproject commit 26a152fea3ca4b8c3f1130392a02f579c2ff218c +Subproject commit e48952185556a10f182184fd572bcb04365f5831 diff --git a/docs/building.md b/docs/building.md index 63d67278a..b56a87899 100644 --- a/docs/building.md +++ b/docs/building.md @@ -134,10 +134,28 @@ flutter run linux Visual Studio is required for Windows development with the Flutter SDK. Download it at https://visualstudio.microsoft.com/downloads/ and install the "Desktop development with C++" workload, including all of its default components. ### Building libraries in WSL2 -Set up Ubuntu 20.04 in WSL2. Follow the entire Linux host section to get set up and build windows `dll` libraries. Copy the resulting `dll`s to their respective positions on the Windows host: +Set up Ubuntu 20.04 in WSL2. Follow the entire Linux host section in the WSL2 Ubuntu 20.04 host to get set up to build. You will also need to install Rust and MXE dependencies on the WSL2 Ubuntu 20.04 host: + - [Install Rust](https://rustup.rs/) + ```sh + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + ``` + - Install MXE by running `stack_wallet/scripts/windows/deps.sh` + ```sh + ./stack_wallet/scripts/windows/deps.sh + ``` + +The WSL2 host may optionally be navigated to the `stack_wallet` repository on the Windows host in order to build the plugins in-place and skip the next section in which you copy the `dll`s from WSL2 to Windows. Then build windows `dll` libraries by running the following script on the WSL2 Ubuntu 20.04 host: + +- `stack_wallet/scripts/windows/build_all.sh` + +Copy the resulting `dll`s to their respective positions on the Windows host: - `stack_wallet/crypto_plugins/flutter_libepiccash/scripts/windows/build/libepic_cash_wallet.dll` - `stack_wallet/crypto_plugins/flutter_liblelantus/scripts/windows/build/libmobileliblelantus.dll` + ### Install Flutter on Windows host diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 76dc00433..b2235ac27 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,6 +1,4 @@ PODS: - - "app_settings (3.0.0+1)": - - Flutter - barcode_scan2 (0.0.1): - Flutter - MTBBarcodeScanner @@ -112,7 +110,7 @@ PODS: - Flutter - flutter_native_splash (0.0.1): - Flutter - - flutter_secure_storage (3.3.1): + - flutter_secure_storage (6.0.0): - Flutter - integration_test (0.0.1): - Flutter @@ -149,7 +147,6 @@ PODS: - Flutter DEPENDENCIES: - - app_settings (from `.symlinks/plugins/app_settings/ios`) - barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - cw_monero (from `.symlinks/plugins/cw_monero/ios`) @@ -169,10 +166,10 @@ DEPENDENCIES: - lelantus (from `.symlinks/plugins/lelantus/ios`) - local_auth (from `.symlinks/plugins/local_auth/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - stack_wallet_backup (from `.symlinks/plugins/stack_wallet_backup/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - wakelock (from `.symlinks/plugins/wakelock/ios`) @@ -188,8 +185,6 @@ SPEC REPOS: - SwiftyGif EXTERNAL SOURCES: - app_settings: - :path: ".symlinks/plugins/app_settings/ios" barcode_scan2: :path: ".symlinks/plugins/barcode_scan2/ios" connectivity_plus: @@ -229,13 +224,13 @@ EXTERNAL SOURCES: package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: - :path: ".symlinks/plugins/path_provider_foundation/ios" + :path: ".symlinks/plugins/path_provider_foundation/darwin" permission_handler_apple: :path: ".symlinks/plugins/permission_handler_apple/ios" share_plus: :path: ".symlinks/plugins/share_plus/ios" shared_preferences_foundation: - :path: ".symlinks/plugins/shared_preferences_foundation/ios" + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" stack_wallet_backup: :path: ".symlinks/plugins/stack_wallet_backup/ios" url_launcher_ios: @@ -244,13 +239,12 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/wakelock/ios" SPEC CHECKSUMS: - app_settings: d103828c9f5d515c4df9ee754dabd443f7cedcf3 barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0 - connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e + connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a cw_monero: 9816991daff0e3ad0a8be140e31933b5526babd4 cw_shared_external: 2972d872b8917603478117c9957dfca611845a92 cw_wownero: ac53899fa5c6ff46b3fb490aa3b7ca36301fa832 - device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed + device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea devicelocale: b22617f40038496deffba44747101255cee005b0 DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 @@ -260,23 +254,23 @@ SPEC CHECKSUMS: flutter_libmonero: da68a616b73dd0374a8419c684fa6b6df2c44ffe flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef - flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec - integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5 + flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be + integration_test: 13825b8a9334a850581300559b8839134b124670 isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073 lelantus: 417f0221260013dfc052cae9cf4b741b6479edba local_auth: 1740f55d7af0a2e2a8684ce225fe79d8931e808c MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb - package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e - path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 + package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7 + path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866 - share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 - shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca + share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028 + shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c stack_wallet_backup: 5b8563aba5d8ffbf2ce1944331ff7294a0ec7c03 SwiftProtobuf: 6ef3f0e422ef90d6605ca20b21a94f6c1324d6b3 SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780 - url_launcher_ios: fb12c43172927bb5cf75aeebd073f883801f1993 + url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4 wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f PODFILE CHECKSUM: 57c8aed26fba39d3ec9424816221f294a07c58eb diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 4f2f20dc9..43e1486e6 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -242,6 +242,7 @@ files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -301,7 +302,6 @@ "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", "${BUILT_PRODUCTS_DIR}/SwiftProtobuf/SwiftProtobuf.framework", "${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework", - "${BUILT_PRODUCTS_DIR}/app_settings/app_settings.framework", "${BUILT_PRODUCTS_DIR}/barcode_scan2/barcode_scan2.framework", "${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework", "${BUILT_PRODUCTS_DIR}/cw_monero/cw_monero.framework", @@ -335,7 +335,6 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftProtobuf.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/app_settings.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/barcode_scan2.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cw_monero.framework", @@ -505,7 +504,10 @@ ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -692,7 +694,10 @@ ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -771,7 +776,10 @@ ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", diff --git a/lib/db/hive/db.dart b/lib/db/hive/db.dart index 931a1cee4..25efcdb3e 100644 --- a/lib/db/hive/db.dart +++ b/lib/db/hive/db.dart @@ -177,6 +177,9 @@ class DB { } Future> getTxCacheBox({required Coin coin}) async { + if (_txCacheBoxes[coin]?.isOpen != true) { + _txCacheBoxes.remove(coin); + } return _txCacheBoxes[coin] ??= await Hive.openBox(_boxNameTxCache(coin: coin)); } @@ -186,6 +189,9 @@ class DB { } Future> getAnonymitySetCacheBox({required Coin coin}) async { + if (_setCacheBoxes[coin]?.isOpen != true) { + _setCacheBoxes.remove(coin); + } return _setCacheBoxes[coin] ??= await Hive.openBox(_boxNameSetCache(coin: coin)); } @@ -195,6 +201,9 @@ class DB { } Future> getUsedSerialsCacheBox({required Coin coin}) async { + if (_usedSerialsCacheBoxes[coin]?.isOpen != true) { + _usedSerialsCacheBoxes.remove(coin); + } return _usedSerialsCacheBoxes[coin] ??= await Hive.openBox(_boxNameUsedSerialsCache(coin: coin)); } diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index bb6ea5e8d..c7a593d03 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -54,6 +54,7 @@ class MainDB { TransactionBlockExplorerSchema, StackThemeSchema, ContactEntrySchema, + LelantusCoinSchema, ], directory: (await StackFileSystem.applicationIsarDirectory()).path, // inspector: kDebugMode, @@ -370,6 +371,8 @@ class MainDB { final transactionCount = await getTransactions(walletId).count(); final addressCount = await getAddresses(walletId).count(); final utxoCount = await getUTXOs(walletId).count(); + final lelantusCoinCount = + await isar.lelantusCoins.where().walletIdEqualTo(walletId).count(); await isar.writeTxn(() async { const paginateLimit = 50; @@ -403,6 +406,18 @@ class MainDB { .findAll(); await isar.utxos.deleteAll(utxoIds); } + + // lelantusCoins + for (int i = 0; i < lelantusCoinCount; i += paginateLimit) { + final lelantusCoinIds = await isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .offset(i) + .limit(paginateLimit) + .idProperty() + .findAll(); + await isar.lelantusCoins.deleteAll(lelantusCoinIds); + } }); } @@ -497,4 +512,15 @@ class MainDB { isar.writeTxn(() async { await isar.ethContracts.putAll(contracts); }); + + // ========== Lelantus ======================================================= + + Future getHighestUsedMintIndex({required String walletId}) async { + return await isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .sortByMintIndexDesc() + .mintIndexProperty() + .findFirst(); + } } diff --git a/lib/dto/ethereum/eth_token_tx_extra_dto.dart b/lib/dto/ethereum/eth_token_tx_extra_dto.dart index 144194914..aab41bb76 100644 --- a/lib/dto/ethereum/eth_token_tx_extra_dto.dart +++ b/lib/dto/ethereum/eth_token_tx_extra_dto.dart @@ -46,7 +46,7 @@ class EthTokenTxExtraDTO { ), gas: _amountFromJsonNum(map['gas']), gasPrice: _amountFromJsonNum(map['gasPrice']), - nonce: map['nonce'] as int, + nonce: map['nonce'] as int?, input: map['input'] as String, gasCost: _amountFromJsonNum(map['gasCost']), gasUsed: _amountFromJsonNum(map['gasUsed']), @@ -63,7 +63,7 @@ class EthTokenTxExtraDTO { final Amount gas; final Amount gasPrice; final String input; - final int nonce; + final int? nonce; final Amount gasCost; final Amount gasUsed; diff --git a/lib/dto/ethereum/eth_tx_dto.dart b/lib/dto/ethereum/eth_tx_dto.dart index 7289cda15..260675cf9 100644 --- a/lib/dto/ethereum/eth_tx_dto.dart +++ b/lib/dto/ethereum/eth_tx_dto.dart @@ -127,16 +127,16 @@ class EthTxDTO { map['timestamp'] = timestamp; map['from'] = from; map['to'] = to; - map['value'] = value; - map['gas'] = gas; - map['gasPrice'] = gasPrice; - map['maxFeePerGas'] = maxFeePerGas; - map['maxPriorityFeePerGas'] = maxPriorityFeePerGas; + map['value'] = value.toString(); + map['gas'] = gas.toString(); + map['gasPrice'] = gasPrice.toString(); + map['maxFeePerGas'] = maxFeePerGas.toString(); + map['maxPriorityFeePerGas'] = maxPriorityFeePerGas.toString(); map['isError'] = isError; map['hasToken'] = hasToken; map['compressedTx'] = compressedTx; - map['gasCost'] = gasCost; - map['gasUsed'] = gasUsed; + map['gasCost'] = gasCost.toString(); + map['gasUsed'] = gasUsed.toString(); return map; } diff --git a/lib/electrumx_rpc/cached_electrumx.dart b/lib/electrumx_rpc/cached_electrumx.dart index 67f170bb4..91b7d1bc8 100644 --- a/lib/electrumx_rpc/cached_electrumx.dart +++ b/lib/electrumx_rpc/cached_electrumx.dart @@ -164,14 +164,16 @@ class CachedElectrumX { final _list = box.get("serials") as List?; - List cachedSerials = - _list == null ? [] : List.from(_list); + Set cachedSerials = + _list == null ? {} : List.from(_list).toSet(); - final startNumber = cachedSerials.length; + // startNumber is broken currently + final startNumber = 0; // cachedSerials.length; - final serials = - await electrumXClient.getUsedCoinSerials(startNumber: startNumber); - List newSerials = []; + final serials = await electrumXClient.getUsedCoinSerials( + startNumber: startNumber, + ); + Set newSerials = {}; for (final element in (serials["serials"] as List)) { if (!isHexadecimal(element as String)) { @@ -182,12 +184,14 @@ class CachedElectrumX { } cachedSerials.addAll(newSerials); + final resultingList = cachedSerials.toList(); + await box.put( "serials", - cachedSerials, + resultingList, ); - return cachedSerials; + return resultingList; } catch (e, s) { Logging.instance.log( "Failed to process CachedElectrumX.getTransaction(): $e\n$s", diff --git a/lib/electrumx_rpc/electrumx.dart b/lib/electrumx_rpc/electrumx.dart index e0a3118eb..804a1364c 100644 --- a/lib/electrumx_rpc/electrumx.dart +++ b/lib/electrumx_rpc/electrumx.dart @@ -159,8 +159,8 @@ class ElectrumX { throw Exception( "JSONRPC response\n" " command: $command\n" - " args: $args\n" - " error: ${response.data}", + " error: ${response.data}" + " args: $args\n", ); } diff --git a/lib/electrumx_rpc/rpc.dart b/lib/electrumx_rpc/rpc.dart index 0c3834ae8..e865356d6 100644 --- a/lib/electrumx_rpc/rpc.dart +++ b/lib/electrumx_rpc/rpc.dart @@ -79,7 +79,7 @@ class JsonRPC { // TODO different timeout length? req.initiateTimeout( - const Duration(seconds: 10), + Duration(seconds: connectionTimeout.inSeconds ~/ 2), onTimedOut: () { _requestQueue.remove(req); }, diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.dart b/lib/models/isar/models/firo_specific/lelantus_coin.dart new file mode 100644 index 000000000..ca4c11919 --- /dev/null +++ b/lib/models/isar/models/firo_specific/lelantus_coin.dart @@ -0,0 +1,81 @@ +import 'package:isar/isar.dart'; + +part 'lelantus_coin.g.dart'; + +@collection +class LelantusCoin { + Id id = Isar.autoIncrement; + + @Index() + final String walletId; + + final String txid; + + final String value; // can't use BigInt in isar :shrug: + + @Index( + unique: true, + replace: false, + composite: [ + CompositeIndex("walletId"), + ], + ) + final int mintIndex; + + final int anonymitySetId; + + final bool isUsed; + + final bool isJMint; + + final String? otherData; + + LelantusCoin({ + required this.walletId, + required this.txid, + required this.value, + required this.mintIndex, + required this.anonymitySetId, + required this.isUsed, + required this.isJMint, + required this.otherData, + }); + + LelantusCoin copyWith({ + String? walletId, + String? publicCoin, + String? txid, + String? value, + int? mintIndex, + int? anonymitySetId, + bool? isUsed, + bool? isJMint, + String? otherData, + }) { + return LelantusCoin( + walletId: walletId ?? this.walletId, + txid: txid ?? this.txid, + value: value ?? this.value, + mintIndex: mintIndex ?? this.mintIndex, + anonymitySetId: anonymitySetId ?? this.anonymitySetId, + isUsed: isUsed ?? this.isUsed, + isJMint: isJMint ?? this.isJMint, + otherData: otherData ?? this.otherData, + ); + } + + @override + String toString() { + return 'LelantusCoin{' + 'id: $id, ' + 'walletId: $walletId, ' + 'txid: $txid, ' + 'value: $value, ' + 'mintIndex: $mintIndex, ' + 'anonymitySetId: $anonymitySetId, ' + 'otherData: $otherData, ' + 'isJMint: $isJMint, ' + 'isUsed: $isUsed' + '}'; + } +} diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.g.dart b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart new file mode 100644 index 000000000..4b9214889 --- /dev/null +++ b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart @@ -0,0 +1,1629 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'lelantus_coin.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters + +extension GetLelantusCoinCollection on Isar { + IsarCollection get lelantusCoins => this.collection(); +} + +const LelantusCoinSchema = CollectionSchema( + name: r'LelantusCoin', + id: -6795633185033299066, + properties: { + r'anonymitySetId': PropertySchema( + id: 0, + name: r'anonymitySetId', + type: IsarType.long, + ), + r'isJMint': PropertySchema( + id: 1, + name: r'isJMint', + type: IsarType.bool, + ), + r'isUsed': PropertySchema( + id: 2, + name: r'isUsed', + type: IsarType.bool, + ), + r'mintIndex': PropertySchema( + id: 3, + name: r'mintIndex', + type: IsarType.long, + ), + r'otherData': PropertySchema( + id: 4, + name: r'otherData', + type: IsarType.string, + ), + r'txid': PropertySchema( + id: 5, + name: r'txid', + type: IsarType.string, + ), + r'value': PropertySchema( + id: 6, + name: r'value', + type: IsarType.string, + ), + r'walletId': PropertySchema( + id: 7, + name: r'walletId', + type: IsarType.string, + ) + }, + estimateSize: _lelantusCoinEstimateSize, + serialize: _lelantusCoinSerialize, + deserialize: _lelantusCoinDeserialize, + deserializeProp: _lelantusCoinDeserializeProp, + idName: r'id', + indexes: { + r'walletId': IndexSchema( + id: -1783113319798776304, + name: r'walletId', + unique: false, + replace: false, + properties: [ + IndexPropertySchema( + name: r'walletId', + type: IndexType.hash, + caseSensitive: true, + ) + ], + ), + r'mintIndex_walletId': IndexSchema( + id: -9147309777276196770, + name: r'mintIndex_walletId', + unique: true, + replace: false, + properties: [ + IndexPropertySchema( + name: r'mintIndex', + type: IndexType.value, + caseSensitive: false, + ), + IndexPropertySchema( + name: r'walletId', + type: IndexType.hash, + caseSensitive: true, + ) + ], + ) + }, + links: {}, + embeddedSchemas: {}, + getId: _lelantusCoinGetId, + getLinks: _lelantusCoinGetLinks, + attach: _lelantusCoinAttach, + version: '3.0.5', +); + +int _lelantusCoinEstimateSize( + LelantusCoin object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + { + final value = object.otherData; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } + bytesCount += 3 + object.txid.length * 3; + bytesCount += 3 + object.value.length * 3; + bytesCount += 3 + object.walletId.length * 3; + return bytesCount; +} + +void _lelantusCoinSerialize( + LelantusCoin object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeLong(offsets[0], object.anonymitySetId); + writer.writeBool(offsets[1], object.isJMint); + writer.writeBool(offsets[2], object.isUsed); + writer.writeLong(offsets[3], object.mintIndex); + writer.writeString(offsets[4], object.otherData); + writer.writeString(offsets[5], object.txid); + writer.writeString(offsets[6], object.value); + writer.writeString(offsets[7], object.walletId); +} + +LelantusCoin _lelantusCoinDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = LelantusCoin( + anonymitySetId: reader.readLong(offsets[0]), + isJMint: reader.readBool(offsets[1]), + isUsed: reader.readBool(offsets[2]), + mintIndex: reader.readLong(offsets[3]), + otherData: reader.readStringOrNull(offsets[4]), + txid: reader.readString(offsets[5]), + value: reader.readString(offsets[6]), + walletId: reader.readString(offsets[7]), + ); + object.id = id; + return object; +} + +P _lelantusCoinDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readLong(offset)) as P; + case 1: + return (reader.readBool(offset)) as P; + case 2: + return (reader.readBool(offset)) as P; + case 3: + return (reader.readLong(offset)) as P; + case 4: + return (reader.readStringOrNull(offset)) as P; + case 5: + return (reader.readString(offset)) as P; + case 6: + return (reader.readString(offset)) as P; + case 7: + return (reader.readString(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _lelantusCoinGetId(LelantusCoin object) { + return object.id; +} + +List> _lelantusCoinGetLinks(LelantusCoin object) { + return []; +} + +void _lelantusCoinAttach( + IsarCollection col, Id id, LelantusCoin object) { + object.id = id; +} + +extension LelantusCoinByIndex on IsarCollection { + Future getByMintIndexWalletId(int mintIndex, String walletId) { + return getByIndex(r'mintIndex_walletId', [mintIndex, walletId]); + } + + LelantusCoin? getByMintIndexWalletIdSync(int mintIndex, String walletId) { + return getByIndexSync(r'mintIndex_walletId', [mintIndex, walletId]); + } + + Future deleteByMintIndexWalletId(int mintIndex, String walletId) { + return deleteByIndex(r'mintIndex_walletId', [mintIndex, walletId]); + } + + bool deleteByMintIndexWalletIdSync(int mintIndex, String walletId) { + return deleteByIndexSync(r'mintIndex_walletId', [mintIndex, walletId]); + } + + Future> getAllByMintIndexWalletId( + List mintIndexValues, List walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return getAllByIndex(r'mintIndex_walletId', values); + } + + List getAllByMintIndexWalletIdSync( + List mintIndexValues, List walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return getAllByIndexSync(r'mintIndex_walletId', values); + } + + Future deleteAllByMintIndexWalletId( + List mintIndexValues, List walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return deleteAllByIndex(r'mintIndex_walletId', values); + } + + int deleteAllByMintIndexWalletIdSync( + List mintIndexValues, List walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return deleteAllByIndexSync(r'mintIndex_walletId', values); + } + + Future putByMintIndexWalletId(LelantusCoin object) { + return putByIndex(r'mintIndex_walletId', object); + } + + Id putByMintIndexWalletIdSync(LelantusCoin object, {bool saveLinks = true}) { + return putByIndexSync(r'mintIndex_walletId', object, saveLinks: saveLinks); + } + + Future> putAllByMintIndexWalletId(List objects) { + return putAllByIndex(r'mintIndex_walletId', objects); + } + + List putAllByMintIndexWalletIdSync(List objects, + {bool saveLinks = true}) { + return putAllByIndexSync(r'mintIndex_walletId', objects, + saveLinks: saveLinks); + } +} + +extension LelantusCoinQueryWhereSort + on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension LelantusCoinQueryWhere + on QueryBuilder { + QueryBuilder idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder idNotEqualTo( + Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder idGreaterThan( + Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder idLessThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder walletIdEqualTo( + String walletId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'walletId', + value: [walletId], + )); + }); + } + + QueryBuilder + walletIdNotEqualTo(String walletId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [], + upper: [walletId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [walletId], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [walletId], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [], + upper: [walletId], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + mintIndexEqualToAnyWalletId(int mintIndex) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'mintIndex_walletId', + value: [mintIndex], + )); + }); + } + + QueryBuilder + mintIndexNotEqualToAnyWalletId(int mintIndex) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [], + upper: [mintIndex], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [], + upper: [mintIndex], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + mintIndexGreaterThanAnyWalletId( + int mintIndex, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + includeLower: include, + upper: [], + )); + }); + } + + QueryBuilder + mintIndexLessThanAnyWalletId( + int mintIndex, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [], + upper: [mintIndex], + includeUpper: include, + )); + }); + } + + QueryBuilder + mintIndexBetweenAnyWalletId( + int lowerMintIndex, + int upperMintIndex, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [lowerMintIndex], + includeLower: includeLower, + upper: [upperMintIndex], + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + mintIndexWalletIdEqualTo(int mintIndex, String walletId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'mintIndex_walletId', + value: [mintIndex, walletId], + )); + }); + } + + QueryBuilder + mintIndexEqualToWalletIdNotEqualTo(int mintIndex, String walletId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + upper: [mintIndex, walletId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex, walletId], + includeLower: false, + upper: [mintIndex], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex, walletId], + includeLower: false, + upper: [mintIndex], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + upper: [mintIndex, walletId], + includeUpper: false, + )); + } + }); + } +} + +extension LelantusCoinQueryFilter + on QueryBuilder { + QueryBuilder + anonymitySetIdEqualTo(int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'anonymitySetId', + value: value, + )); + }); + } + + QueryBuilder + anonymitySetIdGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'anonymitySetId', + value: value, + )); + }); + } + + QueryBuilder + anonymitySetIdLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'anonymitySetId', + value: value, + )); + }); + } + + QueryBuilder + anonymitySetIdBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'anonymitySetId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder idEqualTo( + Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idBetween( + Id lower, + Id upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + isJMintEqualTo(bool value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'isJMint', + value: value, + )); + }); + } + + QueryBuilder isUsedEqualTo( + bool value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'isUsed', + value: value, + )); + }); + } + + QueryBuilder + mintIndexEqualTo(int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'mintIndex', + value: value, + )); + }); + } + + QueryBuilder + mintIndexGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'mintIndex', + value: value, + )); + }); + } + + QueryBuilder + mintIndexLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'mintIndex', + value: value, + )); + }); + } + + QueryBuilder + mintIndexBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'mintIndex', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + otherDataIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'otherData', + )); + }); + } + + QueryBuilder + otherDataIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'otherData', + )); + }); + } + + QueryBuilder + otherDataEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'otherData', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'otherData', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'otherData', + value: '', + )); + }); + } + + QueryBuilder + otherDataIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'otherData', + value: '', + )); + }); + } + + QueryBuilder txidEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'txid', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'txid', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'txid', + value: '', + )); + }); + } + + QueryBuilder + txidIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'txid', + value: '', + )); + }); + } + + QueryBuilder valueEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'value', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'value', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'value', + value: '', + )); + }); + } + + QueryBuilder + valueIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'value', + value: '', + )); + }); + } + + QueryBuilder + walletIdEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'walletId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'walletId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'walletId', + value: '', + )); + }); + } + + QueryBuilder + walletIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'walletId', + value: '', + )); + }); + } +} + +extension LelantusCoinQueryObject + on QueryBuilder {} + +extension LelantusCoinQueryLinks + on QueryBuilder {} + +extension LelantusCoinQuerySortBy + on QueryBuilder { + QueryBuilder + sortByAnonymitySetId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.asc); + }); + } + + QueryBuilder + sortByAnonymitySetIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.desc); + }); + } + + QueryBuilder sortByIsJMint() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isJMint', Sort.asc); + }); + } + + QueryBuilder sortByIsJMintDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isJMint', Sort.desc); + }); + } + + QueryBuilder sortByIsUsed() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.asc); + }); + } + + QueryBuilder sortByIsUsedDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.desc); + }); + } + + QueryBuilder sortByMintIndex() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.asc); + }); + } + + QueryBuilder sortByMintIndexDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.desc); + }); + } + + QueryBuilder sortByOtherData() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.asc); + }); + } + + QueryBuilder sortByOtherDataDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.desc); + }); + } + + QueryBuilder sortByTxid() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.asc); + }); + } + + QueryBuilder sortByTxidDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.desc); + }); + } + + QueryBuilder sortByValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.asc); + }); + } + + QueryBuilder sortByValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.desc); + }); + } + + QueryBuilder sortByWalletId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.asc); + }); + } + + QueryBuilder sortByWalletIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.desc); + }); + } +} + +extension LelantusCoinQuerySortThenBy + on QueryBuilder { + QueryBuilder + thenByAnonymitySetId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.asc); + }); + } + + QueryBuilder + thenByAnonymitySetIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.desc); + }); + } + + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder thenByIsJMint() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isJMint', Sort.asc); + }); + } + + QueryBuilder thenByIsJMintDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isJMint', Sort.desc); + }); + } + + QueryBuilder thenByIsUsed() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.asc); + }); + } + + QueryBuilder thenByIsUsedDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.desc); + }); + } + + QueryBuilder thenByMintIndex() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.asc); + }); + } + + QueryBuilder thenByMintIndexDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.desc); + }); + } + + QueryBuilder thenByOtherData() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.asc); + }); + } + + QueryBuilder thenByOtherDataDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.desc); + }); + } + + QueryBuilder thenByTxid() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.asc); + }); + } + + QueryBuilder thenByTxidDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.desc); + }); + } + + QueryBuilder thenByValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.asc); + }); + } + + QueryBuilder thenByValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.desc); + }); + } + + QueryBuilder thenByWalletId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.asc); + }); + } + + QueryBuilder thenByWalletIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.desc); + }); + } +} + +extension LelantusCoinQueryWhereDistinct + on QueryBuilder { + QueryBuilder + distinctByAnonymitySetId() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'anonymitySetId'); + }); + } + + QueryBuilder distinctByIsJMint() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'isJMint'); + }); + } + + QueryBuilder distinctByIsUsed() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'isUsed'); + }); + } + + QueryBuilder distinctByMintIndex() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'mintIndex'); + }); + } + + QueryBuilder distinctByOtherData( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'otherData', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByTxid( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'txid', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByValue( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'value', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByWalletId( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'walletId', caseSensitive: caseSensitive); + }); + } +} + +extension LelantusCoinQueryProperty + on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder anonymitySetIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'anonymitySetId'); + }); + } + + QueryBuilder isJMintProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'isJMint'); + }); + } + + QueryBuilder isUsedProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'isUsed'); + }); + } + + QueryBuilder mintIndexProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'mintIndex'); + }); + } + + QueryBuilder otherDataProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'otherData'); + }); + } + + QueryBuilder txidProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'txid'); + }); + } + + QueryBuilder valueProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'value'); + }); + } + + QueryBuilder walletIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'walletId'); + }); + } +} diff --git a/lib/models/isar/models/isar_models.dart b/lib/models/isar/models/isar_models.dart index ce7652a46..9de91fc84 100644 --- a/lib/models/isar/models/isar_models.dart +++ b/lib/models/isar/models/isar_models.dart @@ -15,5 +15,6 @@ export 'blockchain_data/output.dart'; export 'blockchain_data/transaction.dart'; export 'blockchain_data/utxo.dart'; export 'ethereum/eth_contract.dart'; +export 'firo_specific/lelantus_coin.dart'; export 'log.dart'; export 'transaction_note.dart'; diff --git a/lib/models/isar/stack_theme.dart b/lib/models/isar/stack_theme.dart index 557090584..32878366a 100644 --- a/lib/models/isar/stack_theme.dart +++ b/lib/models/isar/stack_theme.dart @@ -2341,8 +2341,6 @@ class ThemeAssetsV3 implements IThemeAssets { // Added some future proof params in case we want to add anything else // This should provide some buffer in stead of creating assetsV4 etc - @Name("otherStringParam1") - late final String? dummy1; @Name("otherStringParam2") late final String? dummy2; @Name("otherStringParam3") @@ -2388,6 +2386,19 @@ class ThemeAssetsV3 implements IThemeAssets { Map? _coinCardImages; late final String? coinCardImagesString; + @ignore + Map? get coinCardFavoritesImages => + _coinCardFavoritesImages ??= coinCardFavoritesImagesString == null + ? null + : parseCoinAssetsString( + coinCardFavoritesImagesString!, + placeHolder: coinPlaceholder, + ); + @ignore + Map? _coinCardFavoritesImages; + @Name("otherStringParam1") + late final String? coinCardFavoritesImagesString; + ThemeAssetsV3(); factory ThemeAssetsV3.fromJson({ @@ -2443,13 +2454,18 @@ class ThemeAssetsV3 implements IThemeAssets { Map.from(json["coins"]["cards"] as Map), ) : null + ..coinCardFavoritesImagesString = json["coins"]["favoriteCards"] is Map + ? createCoinAssetsString( + "$themeId/assets", + Map.from(json["coins"]["favoriteCards"] as Map), + ) + : null ..loadingGifRelative = json["loading_gif"] is String ? "$themeId/assets/${json["loading_gif"] as String}" : null ..backgroundRelative = json["background"] is String ? "$themeId/assets/${json["background"] as String}" : null - ..dummy1 = null ..dummy2 = null ..dummy3 = null; } @@ -2528,6 +2544,7 @@ class ThemeAssetsV3 implements IThemeAssets { 'coinImages: $coinImages, ' 'coinSecondaryImages: $coinSecondaryImages, ' 'coinCardImages: $coinCardImages' + 'coinCardFavoritesImages: $coinCardFavoritesImages' ')'; } } diff --git a/lib/models/isar/stack_theme.g.dart b/lib/models/isar/stack_theme.g.dart index bf38c461d..03c543ff0 100644 --- a/lib/models/isar/stack_theme.g.dart +++ b/lib/models/isar/stack_theme.g.dart @@ -29626,7 +29626,7 @@ int _themeAssetsV3EstimateSize( } } { - final value = object.dummy1; + final value = object.coinCardFavoritesImagesString; if (value != null) { bytesCount += 3 + value.length * 3; } @@ -29677,7 +29677,7 @@ void _themeAssetsV3Serialize( writer.writeString(offsets[7], object.coinSecondaryImagesString); writer.writeString(offsets[8], object.exchangeRelative); writer.writeString(offsets[9], object.loadingGifRelative); - writer.writeString(offsets[10], object.dummy1); + writer.writeString(offsets[10], object.coinCardFavoritesImagesString); writer.writeString(offsets[11], object.dummy2); writer.writeString(offsets[12], object.dummy3); writer.writeString(offsets[13], object.personaEasyRelative); @@ -29714,7 +29714,7 @@ ThemeAssetsV3 _themeAssetsV3Deserialize( object.coinSecondaryImagesString = reader.readString(offsets[7]); object.exchangeRelative = reader.readString(offsets[8]); object.loadingGifRelative = reader.readStringOrNull(offsets[9]); - object.dummy1 = reader.readStringOrNull(offsets[10]); + object.coinCardFavoritesImagesString = reader.readStringOrNull(offsets[10]); object.dummy2 = reader.readStringOrNull(offsets[11]); object.dummy3 = reader.readStringOrNull(offsets[12]); object.personaEasyRelative = reader.readString(offsets[13]); @@ -31224,7 +31224,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1IsNull() { + coinCardFavoritesImagesStringIsNull() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(const FilterCondition.isNull( property: r'otherStringParam1', @@ -31233,7 +31233,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1IsNotNull() { + coinCardFavoritesImagesStringIsNotNull() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(const FilterCondition.isNotNull( property: r'otherStringParam1', @@ -31242,7 +31242,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1EqualTo( + coinCardFavoritesImagesStringEqualTo( String? value, { bool caseSensitive = true, }) { @@ -31256,7 +31256,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1GreaterThan( + coinCardFavoritesImagesStringGreaterThan( String? value, { bool include = false, bool caseSensitive = true, @@ -31272,7 +31272,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1LessThan( + coinCardFavoritesImagesStringLessThan( String? value, { bool include = false, bool caseSensitive = true, @@ -31288,7 +31288,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1Between( + coinCardFavoritesImagesStringBetween( String? lower, String? upper, { bool includeLower = true, @@ -31308,7 +31308,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1StartsWith( + coinCardFavoritesImagesStringStartsWith( String value, { bool caseSensitive = true, }) { @@ -31322,7 +31322,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1EndsWith( + coinCardFavoritesImagesStringEndsWith( String value, { bool caseSensitive = true, }) { @@ -31336,7 +31336,8 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1Contains(String value, {bool caseSensitive = true}) { + coinCardFavoritesImagesStringContains(String value, + {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.contains( property: r'otherStringParam1', @@ -31347,7 +31348,8 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1Matches(String pattern, {bool caseSensitive = true}) { + coinCardFavoritesImagesStringMatches(String pattern, + {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.matches( property: r'otherStringParam1', @@ -31358,7 +31360,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1IsEmpty() { + coinCardFavoritesImagesStringIsEmpty() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( property: r'otherStringParam1', @@ -31368,7 +31370,7 @@ extension ThemeAssetsV3QueryFilter } QueryBuilder - dummy1IsNotEmpty() { + coinCardFavoritesImagesStringIsNotEmpty() { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.greaterThan( property: r'otherStringParam1', diff --git a/lib/models/lelantus_coin.dart b/lib/models/lelantus_coin.dart index 0e32d33bf..56557c1cd 100644 --- a/lib/models/lelantus_coin.dart +++ b/lib/models/lelantus_coin.dart @@ -12,6 +12,7 @@ import 'package:hive/hive.dart'; part 'type_adaptors/lelantus_coin.g.dart'; +@Deprecated("Use Isar object instead") // @HiveType(typeId: 9) class LelantusCoin { // @HiveField(0) @@ -27,6 +28,7 @@ class LelantusCoin { // @HiveField(5) bool isUsed; + @Deprecated("Use Isar object instead") LelantusCoin( this.index, this.value, diff --git a/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart b/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart index a3d297b8c..dd807976b 100644 --- a/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart +++ b/lib/pages/add_wallet_views/add_token_view/add_custom_token_view.dart @@ -142,7 +142,7 @@ class _AddCustomTokenViewState extends ConsumerState { context: context, message: "Looking up contract", ); - currentToken = response.value; + currentToken = response!.value; if (currentToken != null) { nameController.text = currentToken!.name; symbolController.text = currentToken!.symbol; @@ -157,7 +157,7 @@ class _AddCustomTokenViewState extends ConsumerState { context: context, builder: (context) => StackOkDialog( title: "Failed to look up token", - message: response.exception?.message, + message: response!.exception?.message, ), ), ); diff --git a/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart b/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart index e8c7c711c..36c914fd8 100644 --- a/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart +++ b/lib/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_wallet_button_group.dart @@ -8,6 +8,8 @@ * */ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:stackwallet/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart'; import 'package:stackwallet/themes/stack_colors.dart'; @@ -32,35 +34,37 @@ class CreateWalletButtonGroup extends StatelessWidget { crossAxisAlignment: isDesktop ? CrossAxisAlignment.center : CrossAxisAlignment.stretch, children: [ - ConstrainedBox( - constraints: BoxConstraints( - minHeight: isDesktop ? 70 : 0, - minWidth: isDesktop ? 480 : 0, - ), - child: TextButton( - style: Theme.of(context) - .extension()! - .getPrimaryEnabledButtonStyle(context), - onPressed: () { - Navigator.of(context).pushNamed( - NameYourWalletView.routeName, - arguments: Tuple2( - AddWalletType.New, - coin, - ), - ); - }, - child: Text( - "Create new wallet", - style: isDesktop - ? STextStyles.desktopButtonEnabled(context) - : STextStyles.button(context), + if (Platform.isAndroid || coin != Coin.wownero) + ConstrainedBox( + constraints: BoxConstraints( + minHeight: isDesktop ? 70 : 0, + minWidth: isDesktop ? 480 : 0, + ), + child: TextButton( + style: Theme.of(context) + .extension()! + .getPrimaryEnabledButtonStyle(context), + onPressed: () { + Navigator.of(context).pushNamed( + NameYourWalletView.routeName, + arguments: Tuple2( + AddWalletType.New, + coin, + ), + ); + }, + child: Text( + "Create new wallet", + style: isDesktop + ? STextStyles.desktopButtonEnabled(context) + : STextStyles.button(context), + ), ), ), - ), - SizedBox( - height: isDesktop ? 16 : 12, - ), + if (Platform.isAndroid || coin != Coin.wownero) + SizedBox( + height: isDesktop ? 16 : 12, + ), ConstrainedBox( constraints: BoxConstraints( minHeight: isDesktop ? 70 : 0, diff --git a/lib/pages/exchange_view/exchange_form.dart b/lib/pages/exchange_view/exchange_form.dart index e65242fbd..a15e13843 100644 --- a/lib/pages/exchange_view/exchange_form.dart +++ b/lib/pages/exchange_view/exchange_form.dart @@ -35,6 +35,7 @@ import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_exchange.dart'; import 'package:stackwallet/services/exchange/trocador/trocador_exchange.dart'; import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/amount/amount_unit.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -160,26 +161,15 @@ class _ExchangeFormState extends ConsumerState { if (value == null) { return null; } - try { - // wtf Dart????? - // This turns "99999999999999999999" into 100000000000000000000.0 - // final numFromLocalised = NumberFormat.decimalPattern( - // ref.read(localeServiceChangeNotifierProvider).locale) - // .parse(value); - // return Decimal.tryParse(numFromLocalised.toString()); - try { - return Decimal.parse(value); - } catch (_) { - try { - return Decimal.parse(value.replaceAll(",", ".")); - } catch (_) { - rethrow; - } - } - } catch (_) { - return null; - } + return AmountUnit.normal + .tryParse( + value, + locale: ref.read(localeServiceChangeNotifierProvider).locale, + coin: Coin.bitcoin, // dummy value (not used due to override) + overrideWithDecimalPlacesFromString: true, + ) + ?.decimal; } Future _getAggregateCurrency(Currency currency) async { @@ -809,6 +799,14 @@ class _ExchangeFormState extends ConsumerState { // if (_swapLock) { _sendController.text = ref.read(efSendAmountStringProvider); // } + + if (_sendFocusNode.hasFocus) { + _sendController.selection = TextSelection.fromPosition( + TextPosition( + offset: _sendController.text.length, + ), + ); + } } }); ref.listen(efSendAmountStringProvider, (previous, String next) { @@ -820,11 +818,19 @@ class _ExchangeFormState extends ConsumerState { ? "-" : ref.read(efReceiveAmountStringProvider); // } + + if (_receiveFocusNode.hasFocus) { + _receiveController.selection = TextSelection.fromPosition( + TextPosition( + offset: _receiveController.text.length, + ), + ); + } } }); ref.listen(efEstimateProvider.notifier, (previous, next) { - final estimate = (next as StateController).state; + final estimate = (next).state; if (ref.read(efReversedProvider)) { updateSend(estimate); } else { diff --git a/lib/pages/send_view/confirm_transaction_view.dart b/lib/pages/send_view/confirm_transaction_view.dart index 3193e7bb7..a1fc66f21 100644 --- a/lib/pages/send_view/confirm_transaction_view.dart +++ b/lib/pages/send_view/confirm_transaction_view.dart @@ -493,51 +493,54 @@ class _ConfirmTransactionViewState ], ), ), - if (coin == Coin.epicCash) + if (coin == Coin.epicCash && + (transactionInfo["onChainNote"] as String).isNotEmpty) const SizedBox( height: 12, ), - if (coin == Coin.epicCash) + if (coin == Coin.epicCash && + (transactionInfo["onChainNote"] as String).isNotEmpty) RoundedWhiteContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - "On chain note", - style: STextStyles.smallMed12(context), - ), - const SizedBox( - height: 4, - ), - Text( - transactionInfo["onChainNote"] as String, - style: STextStyles.itemSubtitle12(context), - ), - ], + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "On chain note", + style: STextStyles.smallMed12(context), + ), + const SizedBox( + height: 4, + ), + Text( + transactionInfo["onChainNote"] as String, + style: STextStyles.itemSubtitle12(context), + ), + ], + ), ), - ), - const SizedBox( - height: 12, - ), - RoundedWhiteContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - (coin == Coin.epicCash) ? "Local Note" : - "Note", - style: STextStyles.smallMed12(context), - ), - const SizedBox( - height: 4, - ), - Text( - transactionInfo["note"] as String, - style: STextStyles.itemSubtitle12(context), - ), - ], + if ((transactionInfo["note"] as String).isNotEmpty) + const SizedBox( + height: 12, + ), + if ((transactionInfo["note"] as String).isNotEmpty) + RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + (coin == Coin.epicCash) ? "Local Note" : "Note", + style: STextStyles.smallMed12(context), + ), + const SizedBox( + height: 4, + ), + Text( + transactionInfo["note"] as String, + style: STextStyles.itemSubtitle12(context), + ), + ], + ), ), - ), ], ), if (isDesktop) diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart index 235bb5e95..812fcc94b 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/install_theme_from_file_dialog.dart @@ -164,7 +164,7 @@ class _InstallThemeFromFileDialogState ); if (mounted) { Navigator.of(context).pop(); - if (!result) { + if (!result!) { unawaited( showDialog( context: context, diff --git a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart index 0c45a68f7..003063bde 100644 --- a/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart +++ b/lib/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart @@ -72,11 +72,11 @@ class _StackThemeCardState extends ConsumerState { } Future _downloadPressed() async { - final result = await showLoading( + final result = (await showLoading( whileFuture: _downloadAndInstall(), context: context, message: "Downloading and installing theme...", - ); + ))!; if (mounted) { final message = result 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 59f443db9..fcacc60d4 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 @@ -20,11 +20,12 @@ import 'package:stackwallet/pages/home_view/home_view.dart'; import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/advanced_views/debug_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart'; -import 'package:stackwallet/pages/settings_views/global_settings_view/xpub_view.dart'; import 'package:stackwallet/pages/settings_views/sub_widgets/settings_list_button.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/providers/ui/transaction_filter_provider.dart'; import 'package:stackwallet/route_generator.dart'; @@ -231,7 +232,7 @@ class _WalletSettingsViewState extends ConsumerState { .mnemonic; if (mounted) { - Navigator.push( + await Navigator.push( context, RouteGenerator.getRoute( shouldUseMaterialRoute: @@ -305,6 +306,25 @@ class _WalletSettingsViewState extends ConsumerState { ); }, ), + if (coin == Coin.nano || coin == Coin.banano) + const SizedBox( + height: 8, + ), + if (coin == Coin.nano || coin == Coin.banano) + Consumer( + builder: (_, ref, __) { + return SettingsListButton( + iconAssetName: Assets.svg.eye, + title: "Change representative", + onPressed: () { + Navigator.of(context).pushNamed( + ChangeRepresentativeView.routeName, + arguments: widget.walletId, + ); + }, + ); + }, + ), const SizedBox( height: 8, ), @@ -434,18 +454,20 @@ class _EpiBoxInfoFormState extends ConsumerState { TextButton( onPressed: () async { try { - wallet.updateEpicboxConfig( + await wallet.updateEpicboxConfig( hostController.text, int.parse(portController.text), ); - showFloatingFlushBar( - context: context, - message: "Epicbox info saved!", - type: FlushBarType.success, - ); - wallet.refresh(); + if (mounted) { + await showFloatingFlushBar( + context: context, + message: "Epicbox info saved!", + type: FlushBarType.success, + ); + } + unawaited(wallet.refresh()); } catch (e) { - showFloatingFlushBar( + await showFloatingFlushBar( context: context, message: "Failed to save epicbox info: $e", type: FlushBarType.warning, diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart new file mode 100644 index 000000000..a91b0cb4e --- /dev/null +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart @@ -0,0 +1,402 @@ +/* + * This file is part of Stack Wallet. + * + * Copyright (c) 2023 Cypher Stack + * All Rights Reserved. + * The code is distributed under GPLv3 license, see LICENSE file for details. + * Generated by Cypher Stack on 2023-05-26 + * + */ + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; +import 'package:stackwallet/services/coins/banano/banano_wallet.dart'; +import 'package:stackwallet/services/coins/nano/nano_wallet.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/clipboard_interface.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/show_loading.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/conditional_parent.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; +import 'package:stackwallet/widgets/loading_indicator.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; +import 'package:stackwallet/widgets/stack_text_field.dart'; +import 'package:stackwallet/widgets/textfield_icon_button.dart'; + +class ChangeRepresentativeView extends ConsumerStatefulWidget { + const ChangeRepresentativeView({ + Key? key, + required this.walletId, + this.clipboardInterface = const ClipboardWrapper(), + }) : super(key: key); + + final String walletId; + final ClipboardInterface clipboardInterface; + + static const String routeName = "/changeRepresentative"; + + @override + ConsumerState createState() => _XPubViewState(); +} + +class _XPubViewState extends ConsumerState { + final _textController = TextEditingController(); + final _textFocusNode = FocusNode(); + final bool isDesktop = Util.isDesktop; + + late ClipboardInterface _clipboardInterface; + + String? representative; + + Future loadRepresentative() async { + final manager = + ref.read(walletsChangeNotifierProvider).getManager(widget.walletId); + + if (manager.coin == Coin.nano) { + return (manager.wallet as NanoWallet).getCurrentRepresentative(); + } else if (manager.coin == Coin.banano) { + return (manager.wallet as BananoWallet).getCurrentRepresentative(); + } + throw Exception("Unsupported wallet attempted to show representative!"); + } + + Future _save() async { + final manager = + ref.read(walletsChangeNotifierProvider).getManager(widget.walletId); + + final changeFuture = manager.coin == Coin.nano + ? (manager.wallet as NanoWallet).changeRepresentative + : (manager.wallet as BananoWallet).changeRepresentative; + + final result = await showLoading( + whileFuture: changeFuture(_textController.text), + context: context, + message: "Updating representative...", + isDesktop: Util.isDesktop, + onException: (ex) { + String msg = ex.toString(); + while (msg.isNotEmpty && msg.startsWith("Exception:")) { + msg = msg.substring(10).trim(); + } + showFloatingFlushBar( + type: FlushBarType.warning, + message: msg, + context: context, + ); + }); + + if (mounted) { + if (result != null && result) { + setState(() { + representative = _textController.text; + _textController.text = ""; + }); + await showFloatingFlushBar( + type: FlushBarType.success, + message: "Representative changed", + context: context, + ); + } + } + } + + @override + void initState() { + _clipboardInterface = widget.clipboardInterface; + + super.initState(); + } + + @override + void dispose() { + _textController.dispose(); + _textFocusNode.dispose(); + super.dispose(); + } + + Future _copy() async { + await _clipboardInterface + .setData(ClipboardData(text: representative ?? "")); + if (mounted) { + unawaited(showFloatingFlushBar( + type: FlushBarType.info, + message: "Copied to clipboard", + iconAsset: Assets.svg.copy, + context: context, + )); + } + } + + @override + Widget build(BuildContext context) { + return ConditionalParent( + condition: !isDesktop, + builder: (child) => Background( + child: SafeArea( + child: Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () async { + Navigator.of(context).pop(); + }, + ), + title: Text( + "Wallet representative", + style: STextStyles.navBarTitle(context), + ), + actions: [ + Padding( + padding: const EdgeInsets.all(10), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + color: Theme.of(context) + .extension()! + .background, + shadows: const [], + icon: SvgPicture.asset( + Assets.svg.copy, + width: 24, + height: 24, + color: Theme.of(context) + .extension()! + .topNavIconPrimary, + ), + onPressed: () { + if (representative != null) { + _copy(); + } + }, + ), + ), + ), + ], + ), + body: Padding( + padding: const EdgeInsets.only( + top: 12, + left: 16, + right: 16, + ), + child: child, + ), + ), + ), + ), + child: ConditionalParent( + condition: isDesktop, + builder: (child) => DesktopDialog( + maxWidth: 600, + maxHeight: double.infinity, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 32, + ), + child: Text( + "Change representative", + style: STextStyles.desktopH2(context), + ), + ), + DesktopDialogCloseButton( + onPressedOverride: Navigator.of( + context, + rootNavigator: true, + ).pop, + ), + ], + ), + AnimatedSize( + duration: const Duration( + milliseconds: 150, + ), + child: Padding( + padding: const EdgeInsets.fromLTRB(32, 0, 32, 32), + child: child, + ), + ), + ], + ), + ), + child: Column( + children: [ + if (isDesktop) const SizedBox(height: 24), + ConditionalParent( + condition: !isDesktop, + builder: (child) => Expanded( + child: child, + ), + child: FutureBuilder( + future: loadRepresentative(), + builder: (context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData) { + representative = snapshot.data!; + } + + const height = 600.0; + Widget child; + if (representative == null) { + child = const SizedBox( + key: Key("loadingRepresentative"), + height: height, + child: Center( + child: LoadingIndicator( + width: 100, + ), + ), + ); + } else { + child = Column( + children: [ + ConditionalParent( + condition: !isDesktop, + builder: (child) => RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + child, + ], + ), + ), + child: ConditionalParent( + condition: isDesktop, + builder: (child) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Current representative", + style: STextStyles.desktopTextExtraExtraSmall( + context), + ), + const SizedBox( + height: 4, + ), + Row( + children: [ + child, + ], + ), + ], + ), + child: SelectableText( + representative!, + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ) + : STextStyles.itemSubtitle12(context), + ), + ), + ), + const SizedBox( + height: 24, + ), + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autocorrect: Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop ? false : true, + controller: _textController, + style: isDesktop + ? STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textFieldActiveText, + height: 1.8, + ) + : STextStyles.field(context), + focusNode: _textFocusNode, + decoration: standardInputDecoration( + "Enter new representative", + _textFocusNode, + context, + desktopMed: isDesktop, + ).copyWith( + contentPadding: isDesktop + ? const EdgeInsets.only( + left: 16, + top: 11, + bottom: 12, + right: 5, + ) + : null, + suffixIcon: _textController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + _textController.text = ""; + }); + }, + ), + ], + ), + ), + ) + : null, + ), + ), + ), + if (isDesktop) const SizedBox(height: 60), + if (!isDesktop) const Spacer(), + PrimaryButton( + label: "Save", + onPressed: _save, + ), + if (!isDesktop) + const SizedBox( + height: 16, + ), + ], + ); + } + + return AnimatedSwitcher( + duration: const Duration( + milliseconds: 200, + ), + child: child, + ); + }, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/settings_views/global_settings_view/xpub_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart similarity index 100% rename from lib/pages/settings_views/global_settings_view/xpub_view.dart rename to lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart diff --git a/lib/pages/token_view/sub_widgets/my_token_select_item.dart b/lib/pages/token_view/sub_widgets/my_token_select_item.dart index 31dc2b085..323ddcf8b 100644 --- a/lib/pages/token_view/sub_widgets/my_token_select_item.dart +++ b/lib/pages/token_view/sub_widgets/my_token_select_item.dart @@ -100,7 +100,7 @@ class _MyTokenSelectItemState extends ConsumerState { message: "Loading ${widget.token.name}", ); - if (!success) { + if (!success!) { return; } diff --git a/lib/pages/wallet_view/sub_widgets/wallet_summary.dart b/lib/pages/wallet_view/sub_widgets/wallet_summary.dart index a82145817..08f5f22c8 100644 --- a/lib/pages/wallet_view/sub_widgets/wallet_summary.dart +++ b/lib/pages/wallet_view/sub_widgets/wallet_summary.dart @@ -52,6 +52,7 @@ class WalletSummary extends StatelessWidget { walletId: walletId, width: constraints.maxWidth, height: constraints.maxHeight, + isFavorite: false, ), Positioned.fill( child: Padding( diff --git a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart index e8ccf98de..2b1ba62c4 100644 --- a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart +++ b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart @@ -358,7 +358,6 @@ class _TransactionDetailsViewState final currentHeight = ref.watch(walletsChangeNotifierProvider .select((value) => value.getManager(walletId).currentHeight)); - print("THIS TRANSACTION IS $_transaction"); return ConditionalParent( @@ -474,7 +473,9 @@ class _TransactionDetailsViewState ), SelectableText( _transaction.isCancelled - ? "Cancelled" + ? coin == Coin.ethereum + ? "Failed" + : "Cancelled" : whatIsIt( _transaction, currentHeight, @@ -585,7 +586,9 @@ class _TransactionDetailsViewState // child: SelectableText( _transaction.isCancelled - ? "Cancelled" + ? coin == Coin.ethereum + ? "Failed" + : "Cancelled" : whatIsIt( _transaction, currentHeight, @@ -781,8 +784,8 @@ class _TransactionDetailsViewState isDesktop ? const _Divider() : const SizedBox( - height: 12, - ), + height: 12, + ), if (coin == Coin.epicCash) RoundedWhiteContainer( padding: isDesktop @@ -790,22 +793,22 @@ class _TransactionDetailsViewState : const EdgeInsets.all(12), child: Row( mainAxisAlignment: - MainAxisAlignment.spaceBetween, + MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: - CrossAxisAlignment.start, + CrossAxisAlignment.start, children: [ Text( "On chain note", style: isDesktop ? STextStyles - .desktopTextExtraExtraSmall( - context) + .desktopTextExtraExtraSmall( + context) : STextStyles.itemSubtitle( - context), + context), ), const SizedBox( height: 8, @@ -814,18 +817,16 @@ class _TransactionDetailsViewState _transaction.otherData ?? "", style: isDesktop ? STextStyles - .desktopTextExtraExtraSmall( - context) - .copyWith( - color: Theme.of( - context) - .extension< - StackColors>()! - .textDark, - ) - : STextStyles - .itemSubtitle12( - context), + .desktopTextExtraExtraSmall( + context) + .copyWith( + color: Theme.of(context) + .extension< + StackColors>()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context), ), ], ), @@ -854,7 +855,9 @@ class _TransactionDetailsViewState MainAxisAlignment.spaceBetween, children: [ Text( - (coin == Coin.epicCash) ? "Local Note" : "Note ", + (coin == Coin.epicCash) + ? "Local Note" + : "Note ", style: isDesktop ? STextStyles .desktopTextExtraExtraSmall( @@ -923,7 +926,9 @@ class _TransactionDetailsViewState notesServiceChangeNotifierProvider( walletId) .select((value) => value.getNoteFor( - txid: (coin == Coin.epicCash)? _transaction.slateId! : _transaction.txid ))), + txid: (coin == Coin.epicCash) + ? _transaction.slateId! + : _transaction.txid))), builder: (builderContext, AsyncSnapshot snapshot) { if (snapshot.connectionState == diff --git a/lib/pages/wallets_view/sub_widgets/favorite_card.dart b/lib/pages/wallets_view/sub_widgets/favorite_card.dart index b3f5e97e5..107b323e0 100644 --- a/lib/pages/wallets_view/sub_widgets/favorite_card.dart +++ b/lib/pages/wallets_view/sub_widgets/favorite_card.dart @@ -149,6 +149,7 @@ class _FavoriteCardState extends ConsumerState { walletId: widget.walletId, width: widget.width, height: widget.height, + isFavorite: true, ), child: Padding( padding: const EdgeInsets.all(12.0), 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 68fe09aa8..61c61a807 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 @@ -13,7 +13,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:stackwallet/pages/settings_views/global_settings_view/xpub_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart'; import 'package:stackwallet/pages_desktop_specific/addresses/desktop_wallet_addresses_view.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart'; import 'package:stackwallet/providers/providers.dart'; @@ -21,11 +22,13 @@ import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/text_styles.dart'; enum _WalletOptions { addressList, deleteWallet, + changeRepresentative, showXpub; String get prettyName { @@ -34,6 +37,8 @@ enum _WalletOptions { return "Address list"; case _WalletOptions.deleteWallet: return "Delete wallet"; + case _WalletOptions.changeRepresentative: + return "Change representative"; case _WalletOptions.showXpub: return "Show xPub"; } @@ -70,6 +75,9 @@ class WalletOptionsButton extends StatelessWidget { onAddressListPressed: () async { Navigator.of(context).pop(_WalletOptions.addressList); }, + onChangeRepPressed: () async { + Navigator.of(context).pop(_WalletOptions.changeRepresentative); + }, onShowXpubPressed: () async { Navigator.of(context).pop(_WalletOptions.showXpub); }, @@ -134,6 +142,32 @@ class WalletOptionsButton extends StatelessWidget { ), ); + if (result == true) { + if (context.mounted) { + Navigator.of(context).pop(); + } + } + break; + case _WalletOptions.changeRepresentative: + final result = await showDialog( + context: context, + barrierDismissible: false, + builder: (context) => Navigator( + initialRoute: ChangeRepresentativeView.routeName, + onGenerateRoute: RouteGenerator.generateRoute, + onGenerateInitialRoutes: (_, __) { + return [ + RouteGenerator.generateRoute( + RouteSettings( + name: ChangeRepresentativeView.routeName, + arguments: walletId, + ), + ), + ]; + }, + ), + ); + if (result == true) { if (context.mounted) { Navigator.of(context).pop(); @@ -171,18 +205,24 @@ class WalletOptionsPopupMenu extends ConsumerWidget { required this.onDeletePressed, required this.onAddressListPressed, required this.onShowXpubPressed, + required this.onChangeRepPressed, required this.walletId, }) : super(key: key); final VoidCallback onDeletePressed; final VoidCallback onAddressListPressed; final VoidCallback onShowXpubPressed; + final VoidCallback onChangeRepPressed; final String walletId; @override Widget build(BuildContext context, WidgetRef ref) { - final bool xpubEnabled = ref.watch(walletsChangeNotifierProvider - .select((value) => value.getManager(walletId).hasXPub)); + final manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(walletId))); + final bool xpubEnabled = manager.hasXPub; + + final bool canChangeRep = + manager.coin == Coin.nano || manager.coin == Coin.banano; return Stack( children: [ @@ -237,6 +277,43 @@ class WalletOptionsPopupMenu extends ConsumerWidget { ), ), ), + if (canChangeRep) + const SizedBox( + height: 8, + ), + if (canChangeRep) + TransparentButton( + onPressed: onChangeRepPressed, + child: Padding( + padding: const EdgeInsets.all(8), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SvgPicture.asset( + Assets.svg.eye, + width: 20, + height: 20, + color: Theme.of(context) + .extension()! + .textFieldActiveSearchIconLeft, + ), + const SizedBox(width: 14), + Expanded( + child: Text( + _WalletOptions.changeRepresentative.prettyName, + style: STextStyles.desktopTextExtraExtraSmall( + context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ), + ), + ), + ], + ), + ), + ), if (xpubEnabled) const SizedBox( height: 8, diff --git a/lib/pages_desktop_specific/password/create_password_view.dart b/lib/pages_desktop_specific/password/create_password_view.dart index 68205d077..2986cd0da 100644 --- a/lib/pages_desktop_specific/password/create_password_view.dart +++ b/lib/pages_desktop_specific/password/create_password_view.dart @@ -125,7 +125,7 @@ class _CreatePasswordViewState extends ConsumerState { } } - if (!widget.restoreFromSWB) { + if (!widget.restoreFromSWB && mounted) { unawaited(showFloatingFlushBar( type: FlushBarType.success, message: "Your password is set up", diff --git a/lib/pages_desktop_specific/password/desktop_login_view.dart b/lib/pages_desktop_specific/password/desktop_login_view.dart index 10e1477b4..471da2763 100644 --- a/lib/pages_desktop_specific/password/desktop_login_view.dart +++ b/lib/pages_desktop_specific/password/desktop_login_view.dart @@ -84,10 +84,10 @@ class _DesktopLoginViewState extends ConsumerState { unawaited( showDialog( context: context, - builder: (context) => Column( + builder: (context) => const Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, - children: const [ + children: [ LoadingIndicator( width: 200, height: 200, diff --git a/lib/providers/exchange/exchange_form_state_provider.dart b/lib/providers/exchange/exchange_form_state_provider.dart index 07447f169..b4a923b78 100644 --- a/lib/providers/exchange/exchange_form_state_provider.dart +++ b/lib/providers/exchange/exchange_form_state_provider.dart @@ -13,8 +13,12 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:stackwallet/models/exchange/active_pair.dart'; import 'package:stackwallet/models/exchange/response_objects/estimate.dart'; import 'package:stackwallet/models/exchange/response_objects/range.dart'; +import 'package:stackwallet/providers/global/locale_provider.dart'; import 'package:stackwallet/services/exchange/exchange.dart'; import 'package:stackwallet/services/exchange/exchange_response.dart'; +import 'package:stackwallet/utilities/amount/amount.dart'; +import 'package:stackwallet/utilities/amount/amount_unit.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/exchange_rate_type_enum.dart'; import 'package:tuple/tuple.dart'; @@ -44,7 +48,22 @@ final efSendAmountStringProvider = StateProvider((ref) { if (refreshing && reversed) { return "-"; } else { - return ref.watch(efSendAmountProvider)?.toString() ?? ""; + final decimal = ref.watch(efSendAmountProvider); + String string = ""; + if (decimal != null) { + final amount = Amount.fromDecimal(decimal, fractionDigits: decimal.scale); + final locale = ref.watch(localeServiceChangeNotifierProvider).locale; + string = AmountUnit.normal.displayAmount( + amount: amount, + locale: locale, + coin: Coin + .nano, // use nano just to ensure decimal.scale < Coin.value.decimals + withUnitName: false, + maxDecimalPlaces: decimal.scale, + ); + } + + return string; } }); final efReceiveAmountStringProvider = StateProvider((ref) { @@ -54,7 +73,22 @@ final efReceiveAmountStringProvider = StateProvider((ref) { if (refreshing && reversed == false) { return "-"; } else { - return ref.watch(efReceiveAmountProvider)?.toString() ?? ""; + final decimal = ref.watch(efReceiveAmountProvider); + String string = ""; + if (decimal != null) { + final amount = Amount.fromDecimal(decimal, fractionDigits: decimal.scale); + final locale = ref.watch(localeServiceChangeNotifierProvider).locale; + string = AmountUnit.normal.displayAmount( + amount: amount, + locale: locale, + coin: Coin + .nano, // use nano just to ensure decimal.scale < Coin.value.decimals + withUnitName: false, + maxDecimalPlaces: decimal.scale, + ); + } + + return string; } }); diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 7ccc378c9..66239aaf7 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -103,14 +103,15 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/support_vi import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart'; -import 'package:stackwallet/pages/settings_views/global_settings_view/xpub_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_recovery_phrase_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/delete_wallet_warning_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/rename_wallet_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart'; import 'package:stackwallet/pages/stack_privacy_calls.dart'; import 'package:stackwallet/pages/token_view/my_tokens_view.dart'; import 'package:stackwallet/pages/token_view/token_contract_details_view.dart'; @@ -564,6 +565,20 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case ChangeRepresentativeView.routeName: + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => ChangeRepresentativeView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + case AppearanceSettingsView.routeName: return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, diff --git a/lib/services/coins/banano/banano_wallet.dart b/lib/services/coins/banano/banano_wallet.dart index 8511f5599..a050b4027 100644 --- a/lib/services/coins/banano/banano_wallet.dart +++ b/lib/services/coins/banano/banano_wallet.dart @@ -14,9 +14,9 @@ import 'package:stackwallet/services/event_bus/events/global/node_connection_sta import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; -import 'package:stackwallet/services/mixins/coin_control_interface.dart'; import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart'; +import 'package:stackwallet/services/nano_api.dart'; import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/transaction_notification_tracker.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; @@ -32,8 +32,7 @@ const int MINIMUM_CONFIRMATIONS = 1; const String DEFAULT_REPRESENTATIVE = "ban_1ka1ium4pfue3uxtntqsrib8mumxgazsjf58gidh1xeo5te3whsq8z476goo"; -class BananoWallet extends CoinServiceAPI - with WalletCache, WalletDB, CoinControlInterface { +class BananoWallet extends CoinServiceAPI with WalletCache, WalletDB { BananoWallet({ required String walletId, required String walletName, @@ -925,4 +924,51 @@ class BananoWallet extends CoinServiceAPI ); await updateCachedChainHeight(height ?? 0); } + + Future getCurrentRepresentative() async { + final serverURI = Uri.parse(getCurrentNode().host); + final address = await currentReceivingAddress; + + final response = await NanoAPI.getAccountInfo( + server: serverURI, + representative: true, + account: address, + ); + + return response.accountInfo?.representative ?? DEFAULT_REPRESENTATIVE; + } + + Future changeRepresentative(String newRepresentative) async { + try { + final serverURI = Uri.parse(getCurrentNode().host); + final balance = this.balance.spendable.raw.toString(); + final String privateKey = await getPrivateKeyFromMnemonic(); + final address = await currentReceivingAddress; + + final response = await NanoAPI.getAccountInfo( + server: serverURI, + representative: true, + account: address, + ); + + if (response.accountInfo == null) { + throw response.exception ?? Exception("Failed to get account info"); + } + + final work = await requestWork(response.accountInfo!.frontier); + + return await NanoAPI.changeRepresentative( + server: serverURI, + accountType: NanoAccountType.BANANO, + account: address, + newRepresentative: newRepresentative, + previousBlock: response.accountInfo!.frontier, + balance: balance, + privateKey: privateKey, + work: work!, + ); + } catch (_) { + rethrow; + } + } } diff --git a/lib/services/coins/ethereum/ethereum_wallet.dart b/lib/services/coins/ethereum/ethereum_wallet.dart index 9990406d9..62f3020e0 100644 --- a/lib/services/coins/ethereum/ethereum_wallet.dart +++ b/lib/services/coins/ethereum/ethereum_wallet.dart @@ -1023,6 +1023,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB { final response = await EthereumAPI.getEthTransactions( address: thisAddress, firstBlock: isRescan ? 0 : firstBlock, + includeTokens: true, ); if (response.value == null) { @@ -1057,8 +1058,10 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB { txFailed = true; } isIncoming = false; - } else { + } else if (checksumEthereumAddress(element.to) == thisAddress) { isIncoming = true; + } else { + continue; } //Calculate fees (GasLimit * gasPrice) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index e62b7d069..079b96ff4 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -26,7 +26,6 @@ import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart'; import 'package:stackwallet/electrumx_rpc/electrumx.dart'; import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models; -import 'package:stackwallet/models/lelantus_coin.dart'; import 'package:stackwallet/models/lelantus_fee_data.dart'; import 'package:stackwallet/models/paymint/fee_object_model.dart'; import 'package:stackwallet/models/signing_data.dart'; @@ -36,7 +35,6 @@ import 'package:stackwallet/services/event_bus/events/global/refresh_percent_cha import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; -import 'package:stackwallet/services/mixins/firo_hive.dart'; import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart'; import 'package:stackwallet/services/mixins/xpubable.dart'; @@ -60,8 +58,8 @@ import 'package:uuid/uuid.dart'; const DUST_LIMIT = 1000; const MINIMUM_CONFIRMATIONS = 1; -const MINT_LIMIT = 100100000000; -const int LELANTUS_VALUE_SPEND_LIMIT_PER_TRANSACTION = 5001 * 100000000; +const MINT_LIMIT = 5001 * 100000000; +const MINT_LIMIT_TESTNET = 1001 * 100000000; const JMINT_INDEX = 5; const MINT_INDEX = 2; @@ -161,6 +159,7 @@ Future executeNative(Map arguments) async { final mnemonicPassphrase = arguments['mnemonicPassphrase'] as String; final coin = arguments['coin'] as Coin; final network = arguments['network'] as NetworkType; + final walletId = arguments['walletId'] as String; final restoreData = await isolateRestore( mnemonic, @@ -170,6 +169,7 @@ Future executeNative(Map arguments) async { setDataMap, usedSerialNumbers, network, + walletId, ); sendPort.send(restoreData); return; @@ -206,13 +206,14 @@ Future> isolateRestore( Map _setDataMap, List _usedSerialNumbers, NetworkType network, + String walletId, ) async { List jindexes = []; - List> lelantusCoins = []; + List lelantusCoins = []; final List spendTxIds = []; - var lastFoundIndex = 0; - var currentIndex = 0; + int lastFoundIndex = 0; + int currentIndex = 0; try { Set usedSerialNumbersSet = _usedSerialNumbers.toSet(); @@ -239,7 +240,7 @@ Future> isolateRestore( isTestnet: coin == Coin.firoTestNet, ); - for (var setId = 1; setId <= _latestSetId; setId++) { + for (int setId = 1; setId <= _latestSetId; setId++) { final setData = _setDataMap[setId] as Map; final foundCoin = (setData["coins"] as List).firstWhere( (e) => e[1] == mintTag, @@ -264,32 +265,25 @@ Future> isolateRestore( isTestnet: coin == Coin.firoTestNet, ); final bool isUsed = usedSerialNumbersSet.contains(serialNumber); - final duplicateCoin = lelantusCoins.firstWhere( - (element) { - final coin = element.values.first; - return coin.txId == txId && - coin.index == currentIndex && - coin.anonymitySetId != setId; - }, - orElse: () => {}, + + lelantusCoins.removeWhere((e) => + e.txid == txId && + e.mintIndex == currentIndex && + e.anonymitySetId != setId); + + lelantusCoins.add( + isar_models.LelantusCoin( + walletId: walletId, + mintIndex: currentIndex, + value: amount.toString(), + txid: txId, + anonymitySetId: setId, + isUsed: isUsed, + isJMint: false, + otherData: + publicCoin, // not really needed but saved just in case + ), ); - if (duplicateCoin.isNotEmpty) { - Logging.instance.log( - "Firo isolateRestore removing duplicate coin: $duplicateCoin", - level: LogLevel.Info, - ); - lelantusCoins.remove(duplicateCoin); - } - lelantusCoins.add({ - txId: LelantusCoin( - currentIndex, - amount, - publicCoin, - txId, - setId, - isUsed, - ) - }); Logging.instance.log( "amount $amount used $isUsed", level: LogLevel.Info, @@ -322,32 +316,24 @@ Future> isolateRestore( isTestnet: coin == Coin.firoTestNet, ); bool isUsed = usedSerialNumbersSet.contains(serialNumber); - final duplicateCoin = lelantusCoins.firstWhere( - (element) { - final coin = element.values.first; - return coin.txId == txId && - coin.index == currentIndex && - coin.anonymitySetId != setId; - }, - orElse: () => {}, + lelantusCoins.removeWhere((e) => + e.txid == txId && + e.mintIndex == currentIndex && + e.anonymitySetId != setId); + + lelantusCoins.add( + isar_models.LelantusCoin( + walletId: walletId, + mintIndex: currentIndex, + value: amount.toString(), + txid: txId, + anonymitySetId: setId, + isUsed: isUsed, + isJMint: true, + otherData: + publicCoin, // not really needed but saved just in case + ), ); - if (duplicateCoin.isNotEmpty) { - Logging.instance.log( - "Firo isolateRestore removing duplicate coin: $duplicateCoin", - level: LogLevel.Info, - ); - lelantusCoins.remove(duplicateCoin); - } - lelantusCoins.add({ - txId: LelantusCoin( - currentIndex, - amount, - publicCoin, - txId, - setId, - isUsed, - ) - }); jindexes.add(currentIndex); spendTxIds.add(txId); @@ -363,11 +349,6 @@ Future> isolateRestore( level: LogLevel.Warning, ); } - } else { - Logging.instance.log( - "Coin not found in data with the mint tag: $mintTag", - level: LogLevel.Warning, - ); } } @@ -384,8 +365,6 @@ Future> isolateRestore( // Logging.instance.log("jmints $spendTxIds", addToDebugMessagesDB: false); result['_lelantus_coins'] = lelantusCoins; - result['mintIndex'] = lastFoundIndex + 1; - result['jindex'] = jindexes; result['spendTxIds'] = spendTxIds; return result; @@ -396,73 +375,74 @@ Future> staticProcessRestore( Map result, int currentHeight, ) async { - List? _l = result['_lelantus_coins'] as List?; - final List> lelantusCoins = []; - for (var el in _l ?? []) { - lelantusCoins.add({el.keys.first: el.values.first as LelantusCoin}); - } + List lelantusCoins = + result['_lelantus_coins'] as List; // Edit the receive transactions with the mint fees. - Map editedTransactions = - {}; - for (var item in lelantusCoins) { - item.forEach((key, value) { - String txid = value.txId; - isar_models.Transaction? tx; + List editedTransactions = []; + + for (final coin in lelantusCoins) { + String txid = coin.txid; + isar_models.Transaction? tx; + try { + tx = txns.firstWhere((e) => e.txid == txid); + } catch (_) { + tx = null; + } + + if (tx == null || tx.subType == isar_models.TransactionSubType.join) { + // This is a jmint. + continue; + } + + List inputTxns = []; + for (final input in tx.inputs) { + isar_models.Transaction? inputTx; try { - tx = txns.firstWhere((e) => e.txid == txid); + inputTx = txns.firstWhere((e) => e.txid == input.txid); } catch (_) { - tx = null; + inputTx = null; } + if (inputTx != null) { + inputTxns.add(inputTx); + } + } + if (inputTxns.isEmpty) { + //some error. + Logging.instance.log( + "cryptic \"//some error\" occurred in staticProcessRestore on lelantus coin: $coin", + level: LogLevel.Error, + ); + continue; + } - if (tx == null || tx.subType == isar_models.TransactionSubType.join) { - // This is a jmint. - return; - } - List inputs = []; - for (var element in tx.inputs) { - isar_models.Transaction? input; - try { - input = txns.firstWhere((e) => e.txid == element.txid); - } catch (_) { - input = null; - } - if (input != null) { - inputs.add(input); - } - } - if (inputs.isEmpty) { - //some error. - return; - } - - int mintFee = tx.fee; - int sharedFee = mintFee ~/ inputs.length; - for (var element in inputs) { - editedTransactions[element.txid] = isar_models.Transaction( - walletId: element.walletId, - txid: element.txid, - timestamp: element.timestamp, - type: element.type, - subType: isar_models.TransactionSubType.mint, - amount: element.amount, - amountString: Amount( - rawValue: BigInt.from(element.amount), - fractionDigits: Coin.firo.decimals, - ).toJsonString(), - fee: sharedFee, - height: element.height, - isCancelled: false, - isLelantus: true, - slateId: null, - otherData: txid, - nonce: null, - inputs: element.inputs, - outputs: element.outputs, - numberOfMessages: null, - )..address.value = element.address.value; - } - }); + int mintFee = tx.fee; + int sharedFee = mintFee ~/ inputTxns.length; + for (final inputTx in inputTxns) { + final edited = isar_models.Transaction( + walletId: inputTx.walletId, + txid: inputTx.txid, + timestamp: inputTx.timestamp, + type: inputTx.type, + subType: isar_models.TransactionSubType.mint, + amount: inputTx.amount, + amountString: Amount( + rawValue: BigInt.from(inputTx.amount), + fractionDigits: Coin.firo.decimals, + ).toJsonString(), + fee: sharedFee, + height: inputTx.height, + isCancelled: false, + isLelantus: true, + slateId: null, + otherData: txid, + nonce: null, + inputs: inputTx.inputs, + outputs: inputTx.outputs, + numberOfMessages: null, + )..address.value = inputTx.address.value; + editedTransactions.add(edited); + } } // Logging.instance.log(editedTransactions, addToDebugMessagesDB: false); @@ -472,12 +452,13 @@ Future> staticProcessRestore( } // Logging.instance.log(transactionMap, addToDebugMessagesDB: false); - editedTransactions.forEach((key, value) { - transactionMap.update(key, (_value) => value); - }); + // update with edited transactions + for (final tx in editedTransactions) { + transactionMap[tx.txid] = tx; + } transactionMap.removeWhere((key, value) => - lelantusCoins.any((element) => element.containsKey(key)) || + lelantusCoins.any((element) => element.txid == key) || ((value.height == -1 || value.height == null) && !value.isConfirmed(currentHeight, MINIMUM_CONFIRMATIONS))); @@ -694,7 +675,6 @@ Future isolateCreateJoinSplitTransaction( "fee": fee, "vSize": extTx.virtualSize(), "jmintValue": changeToMint, - "publicCoin": "jmintData.publicCoin", "spendCoinIndexes": spendCoinIndexes, "height": locktime, "txType": "Sent", @@ -763,7 +743,7 @@ Future _setTestnetWrapper(bool isTestnet) async { /// Handles a single instance of a firo wallet class FiroWallet extends CoinServiceAPI - with WalletCache, WalletDB, FiroHive + with WalletCache, WalletDB implements XPubAble { // Constructor FiroWallet({ @@ -784,7 +764,6 @@ class FiroWallet extends CoinServiceAPI _cachedElectrumXClient = cachedClient; _secureStore = secureStore; initCache(walletId, coin); - initFiroHive(walletId); initWalletDB(mockableOverride: mockableOverride); Logging.instance.log("$walletName isolates length: ${isolates.length}", @@ -1151,7 +1130,8 @@ class FiroWallet extends CoinServiceAPI } } } catch (e, s) { - Logging.instance.log("Exception rethrown from prepareSend(): $e\n$s", + Logging.instance.log( + "Exception rethrown from prepareSendPublic(): $e\n$s", level: LogLevel.Error); rethrow; } @@ -1159,7 +1139,8 @@ class FiroWallet extends CoinServiceAPI throw ArgumentError("Invalid fee rate argument provided!"); } } catch (e, s) { - Logging.instance.log("Exception rethrown from prepareSend(): $e\n$s", + Logging.instance.log( + "Exception rethrown from prepareSendPublic(): $e\n$s", level: LogLevel.Error); rethrow; } @@ -1237,18 +1218,6 @@ class FiroWallet extends CoinServiceAPI try { final txid = txData["txid"] as String; - // temporarily update apdate available balance until a full refresh is done - - // TODO: something here causes an exception to be thrown giving user false info that the tx failed - // Decimal sendTotal = - // Format.satoshisToAmount(txData["value"] as int, coin: coin); - // sendTotal += Decimal.parse(txData["fees"].toString()); - - // TODO: is this needed? - // final bals = await balances; - // bals[0] -= sendTotal; - // _balances = Future(() => bals); - return txid; } catch (e, s) { //todo: come back to this @@ -1264,53 +1233,6 @@ class FiroWallet extends CoinServiceAPI } } - // /// returns txid on successful send - // /// - // /// can throw - // @override - // Future send({ - // required String toAddress, - // required int amount, - // Map args = const {}, - // }) async { - // try { - // dynamic txHexOrError = - // await _createJoinSplitTransaction(amount, toAddress, false); - // Logging.instance.log("txHexOrError $txHexOrError", level: LogLevel.Error); - // if (txHexOrError is int) { - // // Here, we assume that transaction crafting returned an error - // switch (txHexOrError) { - // case 1: - // throw Exception("Insufficient balance!"); - // default: - // throw Exception("Error Creating Transaction!"); - // } - // } else { - // if (await _submitLelantusToNetwork( - // txHexOrError as Map)) { - // final txid = txHexOrError["txid"] as String; - // - // // temporarily update apdate available balance until a full refresh is done - // Decimal sendTotal = - // Format.satoshisToAmount(txHexOrError["value"] as int, coin: coin); - // sendTotal += Decimal.parse(txHexOrError["fees"].toString()); - // final bals = await balances; - // bals[0] -= sendTotal; - // _balances = Future(() => bals); - // - // return txid; - // } else { - // //TODO provide more info - // throw Exception("Transaction failed."); - // } - // } - // } catch (e, s) { - // Logging.instance.log("Exception rethrown in firo send(): $e\n$s", - // level: LogLevel.Error); - // rethrow; - // } - // } - Future> _getMnemonicList() async { final _mnemonicString = await mnemonicString; if (_mnemonicString == null) { @@ -1442,16 +1364,32 @@ class FiroWallet extends CoinServiceAPI feeRatePerKB: selectedTxFeeRate, ); - if (feeForOneOutput < vSizeForOneOutput + 1) { - feeForOneOutput = vSizeForOneOutput + 1; - } - - final int amount = satoshiAmountToSend - feeForOneOutput; + int amount = satoshiAmountToSend - feeForOneOutput; dynamic txn = await buildTransaction( utxoSigningData: utxoSigningData, recipients: recipientsArray, satoshiAmounts: [amount], ); + + int count = 0; + int fee = feeForOneOutput; + int vsize = txn["vSize"] as int; + + while (fee < vsize && count < 10) { + // 10 being some reasonable max + count++; + fee += count; + amount = satoshiAmountToSend - fee; + + txn = await buildTransaction( + utxoSigningData: utxoSigningData, + recipients: recipientsArray, + satoshiAmounts: [amount], + ); + + vsize = txn["vSize"] as int; + } + Map transactionObject = { "hex": txn["hex"], "recipient": recipientsArray[0], @@ -2192,7 +2130,6 @@ class FiroWallet extends CoinServiceAPI value: "", ); - await firoUpdateJIndex([]); // Generate and add addresses to relevant arrays final initialReceivingAddress = await _generateAddressForChain(0, 0); final initialChangeAddress = await _generateAddressForChain(1, 0); @@ -2248,9 +2185,9 @@ class FiroWallet extends CoinServiceAPI _feeObject = Future(() => feeObj); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.60, walletId)); - final lelantusCoins = getLelantusCoinMap(); - Logging.instance.log("_lelantus_coins at refresh: $lelantusCoins", - level: LogLevel.Warning, printFullLength: true); + // final lelantusCoins = getLelantusCoinMap(); + // Logging.instance.log("_lelantus_coins at refresh: $lelantusCoins", + // level: LogLevel.Warning, printFullLength: true); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.70, walletId)); await _refreshLelantusData(); @@ -2327,7 +2264,8 @@ class FiroWallet extends CoinServiceAPI level: LogLevel.Error); } - final List lelantusCoins = await _getUnspentCoins(); + final List lelantusCoins = + await _getUnspentCoins(); final root = await Bip32Utils.getBip32Root( _mnemonic!, @@ -2339,7 +2277,7 @@ class FiroWallet extends CoinServiceAPI final derivePath = constructDerivePath( networkWIF: _network.wif, chain: MINT_INDEX, - index: coin.index, + index: coin.mintIndex, ); final keyPair = await Bip32Utils.getBip32NodeFromRoot(root, derivePath); @@ -2349,76 +2287,34 @@ class FiroWallet extends CoinServiceAPI } final String privateKey = Format.uint8listToString(keyPair.privateKey!); return DartLelantusEntry(coin.isUsed ? 1 : 0, 0, coin.anonymitySetId, - coin.value, coin.index, privateKey); + int.parse(coin.value), coin.mintIndex, privateKey); }).toList(); final lelantusEntries = await Future.wait(waitLelantusEntries); if (lelantusEntries.isNotEmpty) { + // should be redundant as _getUnspentCoins() should + // already remove all where value=0 lelantusEntries.removeWhere((element) => element.amount == 0); } return lelantusEntries; } - List> getLelantusCoinMap() { - final _l = firoGetLelantusCoins(); - final List> lelantusCoins = []; - for (var el in _l ?? []) { - lelantusCoins.add({el.keys.first: el.values.first as LelantusCoin}); - } - return lelantusCoins; - } + Future> _getUnspentCoins() async { + final lelantusCoinsList = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .isUsedEqualTo(false) + .not() + .group((q) => q + .valueEqualTo("0") + .or() + .anonymitySetIdEqualTo(ANONYMITY_SET_EMPTY_ID)) + .findAll(); - Future> _getUnspentCoins() async { - final List> lelantusCoins = getLelantusCoinMap(); - if (lelantusCoins.isNotEmpty) { - lelantusCoins.removeWhere((element) => - element.values.any((elementCoin) => elementCoin.value == 0)); - } - final jindexes = firoGetJIndex(); - - List coins = []; - - List lelantusCoinsList = - lelantusCoins.fold([], (previousValue, element) { - previousValue.add(element.values.first); - return previousValue; - }); - - final currentChainHeight = await chainHeight; - - for (int i = 0; i < lelantusCoinsList.length; i++) { - // Logging.instance.log("lelantusCoinsList[$i]: ${lelantusCoinsList[i]}"); - final txid = lelantusCoinsList[i].txId; - final txn = await cachedElectrumXClient.getTransaction( - txHash: txid, - verbose: true, - coin: coin, - ); - final confirmations = txn["confirmations"]; - bool isUnconfirmed = confirmations is int && confirmations < 1; - - final tx = await db.getTransaction(walletId, txid); - - if (!jindexes!.contains(lelantusCoinsList[i].index) && - tx != null && - tx.isLelantus == true && - !(tx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS))) { - isUnconfirmed = true; - } - - if (tx != null && - !tx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - continue; - } - if (!lelantusCoinsList[i].isUsed && - lelantusCoinsList[i].anonymitySetId != ANONYMITY_SET_EMPTY_ID && - !isUnconfirmed) { - coins.add(lelantusCoinsList[i]); - } - } - return coins; + return lelantusCoinsList; } // index 0 and 1 for the funds available to spend. @@ -2427,62 +2323,48 @@ class FiroWallet extends CoinServiceAPI Future _refreshBalance() async { try { final utxosUpdateFuture = _refreshUTXOs(); - final List> lelantusCoins = - getLelantusCoinMap(); - if (lelantusCoins.isNotEmpty) { - lelantusCoins.removeWhere((element) => - element.values.any((elementCoin) => elementCoin.value == 0)); - } + final lelantusCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .isUsedEqualTo(false) + .not() + .valueEqualTo(0.toString()) + .findAll(); final currentChainHeight = await chainHeight; - final jindexes = firoGetJIndex(); int intLelantusBalance = 0; int unconfirmedLelantusBalance = 0; - for (final element in lelantusCoins) { - element.forEach((key, lelantusCoin) { - isar_models.Transaction? txn = db.isar.transactions - .where() - .txidWalletIdEqualTo( - lelantusCoin.txId, - walletId, - ) - .findFirstSync(); + for (final lelantusCoin in lelantusCoins) { + isar_models.Transaction? txn = db.isar.transactions + .where() + .txidWalletIdEqualTo( + lelantusCoin.txid, + walletId, + ) + .findFirstSync(); - if (txn == null) { - // TODO: ?????????????????????????????????????? - } else { - bool isLelantus = txn.isLelantus == true; - if (!jindexes!.contains(lelantusCoin.index) && isLelantus) { - if (!lelantusCoin.isUsed && - txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - // mint tx, add value to balance - intLelantusBalance += lelantusCoin.value; - } /* else { - // This coin is not confirmed and may be replaced - }*/ - } else if (jindexes.contains(lelantusCoin.index) && - isLelantus && - !lelantusCoin.isUsed && - !txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - unconfirmedLelantusBalance += lelantusCoin.value; - } else if (jindexes.contains(lelantusCoin.index) && - !lelantusCoin.isUsed) { - intLelantusBalance += lelantusCoin.value; - } else if (!lelantusCoin.isUsed && - (txn.isLelantus == true - ? true - : txn.isConfirmed( - currentChainHeight, MINIMUM_CONFIRMATIONS) != - false)) { - intLelantusBalance += lelantusCoin.value; - } else if (!isLelantus && - txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) == - false) { - unconfirmedLelantusBalance += lelantusCoin.value; - } + if (txn == null) { + Logging.instance.log( + "Transaction not found in DB for lelantus coin: $lelantusCoin", + level: LogLevel.Fatal, + ); + } else { + if (txn.isLelantus != true) { + Logging.instance.log( + "Bad database state found in $walletName $walletId for _refreshBalance lelantus", + level: LogLevel.Fatal, + ); } - }); + + if (txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { + // mint tx, add value to balance + intLelantusBalance += int.parse(lelantusCoin.value); + } else { + unconfirmedLelantusBalance += int.parse(lelantusCoin.value); + } + } } _balancePrivate = Balance( @@ -2551,17 +2433,19 @@ class FiroWallet extends CoinServiceAPI } } - final List> lelantusCoins = getLelantusCoinMap(); - if (lelantusCoins.isNotEmpty) { - lelantusCoins.removeWhere((element) => - element.values.any((elementCoin) => elementCoin.value == 0)); - } + final lelantusCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .not() + .valueEqualTo(0.toString()) + .findAll(); + final data = await _txnData; for (final value in data) { if (value.inputs.isNotEmpty) { for (var element in value.inputs) { - if (lelantusCoins - .any((element) => element.keys.contains(value.txid)) && + if (lelantusCoins.any((e) => e.txid == value.txid) && spendableOutputs.firstWhere( (output) => output?.txid == element.txid, orElse: () => null) != @@ -2626,24 +2510,88 @@ class FiroWallet extends CoinServiceAPI } Future>> createMintsFromAmount(int total) async { - var tmpTotal = total; - var index = 1; - var mints = >[]; - final nextFreeMintIndex = firoGetMintIndex(); + int tmpTotal = total; + int counter = 0; + final lastUsedIndex = await db.getHighestUsedMintIndex(walletId: walletId); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; + + final root = await Bip32Utils.getBip32Root( + (await mnemonic).join(" "), + (await mnemonicPassphrase)!, + _network, + ); + + final mints = >[]; while (tmpTotal > 0) { - final mintValue = min(tmpTotal, MINT_LIMIT); - final mint = await _getMintHex( - mintValue, - nextFreeMintIndex + index, + final index = nextFreeMintIndex + counter; + + final bip32.BIP32 mintKeyPair = await Bip32Utils.getBip32NodeFromRoot( + root, + constructDerivePath( + networkWIF: _network.wif, + chain: MINT_INDEX, + index: index, + ), ); - mints.add({ - "value": mintValue, - "script": mint, - "index": nextFreeMintIndex + index, - "publicCoin": "", - }); - tmpTotal = tmpTotal - MINT_LIMIT; - index++; + + final String mintTag = CreateTag( + Format.uint8listToString(mintKeyPair.privateKey!), + index, + Format.uint8listToString(mintKeyPair.identifier), + isTestnet: coin == Coin.firoTestNet, + ); + final List> anonymitySets; + try { + anonymitySets = await fetchAnonymitySets(); + } catch (e, s) { + Logging.instance.log( + "Firo needs better internet to create mints: $e\n$s", + level: LogLevel.Fatal, + ); + rethrow; + } + + bool isUsedMintTag = false; + + // stupid dynamic maps + for (final set in anonymitySets) { + final setCoins = set["coins"] as List; + for (final coin in setCoins) { + if (coin[1] == mintTag) { + isUsedMintTag = true; + break; + } + } + if (isUsedMintTag) { + break; + } + } + + if (isUsedMintTag) { + Logging.instance.log( + "Found used index when minting", + level: LogLevel.Warning, + ); + } + + if (!isUsedMintTag) { + final mintValue = min(tmpTotal, + (coin == Coin.firoTestNet ? MINT_LIMIT_TESTNET : MINT_LIMIT)); + final mint = await _getMintHex( + mintValue, + index, + ); + + mints.add({ + "value": mintValue, + "script": mint, + "index": index, + }); + tmpTotal = tmpTotal - + (coin == Coin.firoTestNet ? MINT_LIMIT_TESTNET : MINT_LIMIT); + } + + counter++; } return mints; } @@ -2669,8 +2617,6 @@ class FiroWallet extends CoinServiceAPI int satoshisPerRecipient, List> mintsMap, ) async { - //todo: check if print needed - // debugPrint(utxosToUse.toString()); List addressStringsToGet = []; // Populating the addresses to derive @@ -2794,9 +2740,6 @@ class FiroWallet extends CoinServiceAPI amount += utxosToUse[i].value; } - final index = firoGetMintIndex(); - Logging.instance.log("index of mint $index", level: LogLevel.Info); - for (var mintsElement in mintsMap) { Logging.instance.log("using $mintsElement", level: LogLevel.Info); Uint8List mintu8 = @@ -2829,7 +2772,6 @@ class FiroWallet extends CoinServiceAPI rawValue: BigInt.from(fee), fractionDigits: coin.decimals, ).decimal.toDouble(), - "publicCoin": "", "height": height, "txType": "Sent", "confirmed_status": false, @@ -2843,146 +2785,75 @@ class FiroWallet extends CoinServiceAPI }; } + // TODO: verify this function does what we think it does Future _refreshLelantusData() async { - final List> lelantusCoins = getLelantusCoinMap(); - final jindexes = firoGetJIndex(); - - // Get all joinsplit transaction ids - - final listLelantusTxData = await db - .getTransactions(walletId) + final lelantusCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) .filter() - .isLelantusEqualTo(true) + .isUsedEqualTo(false) + .not() + .valueEqualTo(0.toString()) .findAll(); - List joinsplits = []; - for (final tx in listLelantusTxData) { - if (tx.subType == isar_models.TransactionSubType.join) { - joinsplits.add(tx.txid); - } - } - for (final coin - in lelantusCoins.fold([], (previousValue, element) { - (previousValue as List).add(element.values.first); - return previousValue; - })) { - if (jindexes != null) { - if (jindexes.contains(coin.index) && !joinsplits.contains(coin.txId)) { - joinsplits.add(coin.txId); - } - } - } - Map> data = - {}; - for (final entry in listLelantusTxData) { - data[entry.txid] = Tuple2(entry.address.value, entry); - } + final List updatedCoins = []; - // Grab the most recent information on all the joinsplits + final usedSerialNumbersSet = (await getUsedCoinSerials()).toSet(); - final updatedJSplit = await getJMintTransactions( - cachedElectrumXClient, - joinsplits, - coin, + final root = await Bip32Utils.getBip32Root( + (await mnemonic).join(" "), + (await mnemonicPassphrase)!, + _network, ); - final currentChainHeight = await chainHeight; + for (final coin in lelantusCoins) { + final _derivePath = constructDerivePath( + networkWIF: _network.wif, + chain: MINT_INDEX, + index: coin.mintIndex, + ); + final bip32.BIP32 mintKeyPair = await Bip32Utils.getBip32NodeFromRoot( + root, + _derivePath, + ); - // update all of joinsplits that are now confirmed. - for (final tx in updatedJSplit.entries) { - isar_models.Transaction? currentTx; + final String serialNumber = GetSerialNumber( + int.parse(coin.value), + Format.uint8listToString(mintKeyPair.privateKey!), + coin.mintIndex, + isTestnet: this.coin == Coin.firoTestNet, + ); + final bool isUsed = usedSerialNumbersSet.contains(serialNumber); + if (isUsed) { + updatedCoins.add(coin.copyWith(isUsed: isUsed)); + } + + final tx = await db.getTransaction(walletId, coin.txid); + if (tx == null) { + print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + } + } + + if (updatedCoins.isNotEmpty) { try { - currentTx = - listLelantusTxData.firstWhere((e) => e.txid == tx.value.txid); - } catch (_) { - currentTx = null; - } - - if (currentTx == null) { - // this send was accidentally not included in the list - tx.value.isLelantus = true; - data[tx.value.txid] = - Tuple2(tx.value.address.value ?? tx.key, tx.value); - - continue; - } - if (currentTx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) != - tx.value.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - tx.value.isLelantus = true; - data[tx.value.txid] = - Tuple2(tx.value.address.value ?? tx.key, tx.value); + await db.isar.writeTxn(() async { + for (final c in updatedCoins) { + await db.isar.lelantusCoins.deleteByMintIndexWalletId( + c.mintIndex, + c.walletId, + ); + } + await db.isar.lelantusCoins.putAll(updatedCoins); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + rethrow; } } - - // Logging.instance.log(txData.txChunks); - final listTxData = await _txnData; - for (final value in listTxData) { - // ignore change addresses - // bool hasAtLeastOneReceive = false; - // int howManyReceiveInputs = 0; - // for (var element in value.inputs) { - // if (listLelantusTxData.containsKey(element.txid) && - // listLelantusTxData[element.txid]!.txType == "Received" - // // && - // // listLelantusTxData[element.txid].subType != "mint" - // ) { - // // hasAtLeastOneReceive = true; - // // howManyReceiveInputs++; - // } - // } - - if (value.type == isar_models.TransactionType.incoming && - value.subType != isar_models.TransactionSubType.mint) { - // Every receive other than a mint should be shown. Mints will be collected and shown from the send side - value.isLelantus = true; - data[value.txid] = Tuple2(value.address.value, value); - } else if (value.type == isar_models.TransactionType.outgoing) { - // all sends should be shown, mints will be displayed correctly in the ui - value.isLelantus = true; - data[value.txid] = Tuple2(value.address.value, value); - } - } - - // TODO: optimize this whole lelantus process - - final List> txnsData = - []; - - for (final value in data.values) { - // allow possible null address on mints as we don't display address - // this should normally never be null anyways but old (dbVersion up to 4) - // migrated transactions may not have had an address (full rescan should - // fix this) - isar_models.Address? transactionAddress; - try { - transactionAddress = - value.item2.subType == isar_models.TransactionSubType.mint - ? value.item1 - : value.item1!; - } catch (_) { - Logging.instance - .log("_refreshLelantusData value: $value", level: LogLevel.Fatal); - } - final outs = - value.item2.outputs.where((_) => true).toList(growable: false); - final ins = value.item2.inputs.where((_) => true).toList(growable: false); - - txnsData.add(Tuple2( - value.item2.copyWith(inputs: ins, outputs: outs).item1, - transactionAddress)); - } - - await db.addNewTransactionData(txnsData, walletId); - - // // update the _lelantusTransactionData - // final models.TransactionData newTxData = - // models.TransactionData.fromMap(listLelantusTxData); - // // Logging.instance.log(newTxData.txChunks); - // _lelantusTransactionData = Future(() => newTxData); - // await DB.instance.put( - // boxName: walletId, key: 'latest_lelantus_tx_model', value: newTxData); - // return newTxData; } Future _getMintHex(int amount, int index) async { @@ -3030,51 +2901,63 @@ class FiroWallet extends CoinServiceAPI Logging.instance.log( "_submitLelantusToNetwork txid: ${transactionInfo['txid']}", level: LogLevel.Info); + if (txid == transactionInfo['txid']) { - final index = firoGetMintIndex(); - final List> lelantusCoins = - getLelantusCoinMap(); - List> coins; - if (lelantusCoins.isEmpty) { - coins = []; - } else { - coins = [...lelantusCoins]; - } + final lastUsedIndex = + await db.getHighestUsedMintIndex(walletId: walletId); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; if (transactionInfo['spendCoinIndexes'] != null) { // This is a joinsplit + final spentCoinIndexes = + transactionInfo['spendCoinIndexes'] as List; + final List updatedCoins = []; + // Update all of the coins that have been spent. - for (final lCoinMap in coins) { - final lCoin = lCoinMap.values.first; - if ((transactionInfo['spendCoinIndexes'] as List) - .contains(lCoin.index)) { - lCoinMap[lCoinMap.keys.first] = LelantusCoin( - lCoin.index, - lCoin.value, - lCoin.publicCoin, - lCoin.txId, - lCoin.anonymitySetId, - true); + + for (final index in spentCoinIndexes) { + final possibleCoin = await db.isar.lelantusCoins + .where() + .mintIndexWalletIdEqualTo(index, walletId) + .findFirst(); + + if (possibleCoin != null) { + updatedCoins.add(possibleCoin.copyWith(isUsed: true)); } } // if a jmint was made add it to the unspent coin index - LelantusCoin jmint = LelantusCoin( - index, - transactionInfo['jmintValue'] as int? ?? 0, - transactionInfo['publicCoin'] as String, - transactionInfo['txid'] as String, - latestSetId, - false); - if (jmint.value > 0) { - coins.add({jmint.txId: jmint}); - final jindexes = firoGetJIndex()!; - jindexes.add(index); - await firoUpdateJIndex(jindexes); - await firoUpdateMintIndex(index + 1); + final jmint = isar_models.LelantusCoin( + walletId: walletId, + mintIndex: nextFreeMintIndex, + value: (transactionInfo['jmintValue'] as int? ?? 0).toString(), + txid: transactionInfo['txid'] as String, + anonymitySetId: latestSetId, + isUsed: false, + isJMint: true, + otherData: null, + ); + + try { + await db.isar.writeTxn(() async { + for (final c in updatedCoins) { + await db.isar.lelantusCoins.deleteByMintIndexWalletId( + c.mintIndex, + c.walletId, + ); + } + await db.isar.lelantusCoins.putAll(updatedCoins); + + await db.isar.lelantusCoins.put(jmint); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + rethrow; } - await firoUpdateLelantusCoins(coins); final amount = Amount.fromDecimal( Decimal.parse(transactionInfo["amount"].toString()), @@ -3087,14 +2970,8 @@ class FiroWallet extends CoinServiceAPI txid: transactionInfo['txid'] as String, timestamp: transactionInfo['timestamp'] as int? ?? (DateTime.now().millisecondsSinceEpoch ~/ 1000), - type: transactionInfo['txType'] == "Received" - ? isar_models.TransactionType.incoming - : isar_models.TransactionType.outgoing, - subType: transactionInfo["subType"] == "mint" - ? isar_models.TransactionSubType.mint - : transactionInfo["subType"] == "join" - ? isar_models.TransactionSubType.join - : isar_models.TransactionSubType.none, + type: isar_models.TransactionType.outgoing, + subType: isar_models.TransactionSubType.join, amount: amount.raw.toInt(), amountString: amount.toJsonString(), fee: Amount.fromDecimal( @@ -3133,40 +3010,40 @@ class FiroWallet extends CoinServiceAPI txnsData.add(Tuple2(transaction, transactionAddress)); await db.addNewTransactionData(txnsData, walletId); - - // final models.TransactionData newTxData = - // models.TransactionData.fromMap(transactions); - // await DB.instance.put( - // boxName: walletId, - // key: 'latest_lelantus_tx_model', - // value: newTxData); - // final ldata = DB.instance.get( - // boxName: walletId, - // key: 'latest_lelantus_tx_model') as models.TransactionData; - // _lelantusTransactionData = Future(() => ldata); } else { // This is a mint Logging.instance.log("this is a mint", level: LogLevel.Info); - // TODO: transactionInfo['mintsMap'] + final List updatedCoins = []; + for (final mintMap in transactionInfo['mintsMap'] as List>) { - final index = mintMap['index'] as int?; - LelantusCoin mint = LelantusCoin( - index!, - mintMap['value'] as int, - mintMap['publicCoin'] as String, - transactionInfo['txid'] as String, - latestSetId, - false, + final index = mintMap['index'] as int; + final mint = isar_models.LelantusCoin( + walletId: walletId, + mintIndex: index, + value: (mintMap['value'] as int).toString(), + txid: transactionInfo['txid'] as String, + anonymitySetId: latestSetId, + isUsed: false, + isJMint: false, + otherData: null, ); - if (mint.value > 0) { - coins.add({mint.txId: mint}); - await firoUpdateMintIndex(index + 1); - } + + updatedCoins.add(mint); } // Logging.instance.log(coins); - await firoUpdateLelantusCoins(coins); + try { + await db.isar.writeTxn(() async { + await db.isar.lelantusCoins.putAll(updatedCoins); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + rethrow; + } } return true; } else { @@ -3204,20 +3081,6 @@ class FiroWallet extends CoinServiceAPI Logging.instance.log("fetched fees: $feeObject", level: LogLevel.Info); return feeObject; - - // final result = await electrumXClient.getFeeRate(); - // - // final locale = await Devicelocale.currentLocale; - // final String fee = - // Format.satoshiAmountToPrettyString(result["rate"] as int, locale!); - // - // final fees = { - // "fast": fee, - // "average": fee, - // "slow": fee, - // }; - // final FeeObject feeObject = FeeObject.fromJson(fees); - // return feeObject; } catch (e) { Logging.instance .log("Exception rethrown from _getFees(): $e", level: LogLevel.Error); @@ -3666,11 +3529,21 @@ class FiroWallet extends CoinServiceAPI ), ); } + final txid = txObject["txid"] as String; const subType = isar_models.TransactionSubType.join; + final type = nonWalletAddressFoundInOutputs ? isar_models.TransactionType.outgoing - : isar_models.TransactionType.incoming; + : (await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .txidEqualTo(txid) + .findFirst()) == + null + ? isar_models.TransactionType.incoming + : isar_models.TransactionType.sentToSelf; final amount = nonWalletAddressFoundInOutputs ? totalOutputValue @@ -3697,7 +3570,7 @@ class FiroWallet extends CoinServiceAPI final tx = isar_models.Transaction( walletId: walletId, - txid: txObject["txid"] as String, + txid: txid, timestamp: txObject["blocktime"] as int? ?? (DateTime.now().millisecondsSinceEpoch ~/ 1000), type: type, @@ -3947,7 +3820,6 @@ class FiroWallet extends CoinServiceAPI coin: coin, ); - // todo check here if we should mark as blocked final utxo = isar_models.UTXO( walletId: walletId, txid: txn["txid"] as String, @@ -3985,7 +3857,6 @@ class FiroWallet extends CoinServiceAPI Logging.instance .log('Outputs fetched: $outputArray', level: LogLevel.Info); - // TODO move this out of here and into IDB await db.isar.writeTxn(() async { await db.isar.utxos.where().walletIdEqualTo(walletId).deleteAll(); await db.isar.utxos.putAll(outputArray); @@ -4072,45 +3943,6 @@ class FiroWallet extends CoinServiceAPI ); } - // /// Takes in a list of isar_models.UTXOs and adds a name (dependent on object index within list) - // /// and checks for the txid associated with the utxo being blocked and marks it accordingly. - // /// Now also checks for output labeling. - // Future _sortOutputs(List utxos) async { - // final blockedHashArray = - // DB.instance.get(boxName: walletId, key: 'blocked_tx_hashes') - // as List?; - // final List lst = []; - // if (blockedHashArray != null) { - // for (var hash in blockedHashArray) { - // lst.add(hash as String); - // } - // } - // final labels = - // DB.instance.get(boxName: walletId, key: 'labels') as Map? ?? - // {}; - // - // _outputsList = []; - // - // for (var i = 0; i < utxos.length; i++) { - // if (labels[utxos[i].txid] != null) { - // utxos[i].txName = labels[utxos[i].txid] as String? ?? ""; - // } else { - // utxos[i].txName = 'Output #$i'; - // } - // - // if (utxos[i].status.confirmed == false) { - // _outputsList.add(utxos[i]); - // } else { - // if (lst.contains(utxos[i].txid)) { - // utxos[i].blocked = true; - // _outputsList.add(utxos[i]); - // } else if (!lst.contains(utxos[i].txid)) { - // _outputsList.add(utxos[i]); - // } - // } - // } - // } - @override Future fullRescan( int maxUnusedAddressGap, @@ -4620,6 +4452,7 @@ class FiroWallet extends CoinServiceAPI "setDataMap": setDataMap, "usedSerialNumbers": usedSerialNumbers, "network": _network, + "walletId": walletId, }); await Future.wait([dataFuture]); @@ -4638,11 +4471,20 @@ class FiroWallet extends CoinServiceAPI await chainHeight, ); - await Future.wait([ - firoUpdateMintIndex(message['mintIndex'] as int), - firoUpdateLelantusCoins(message['_lelantus_coins'] as List), - firoUpdateJIndex(message['jindex'] as List), - ]); + final coins = message['_lelantus_coins'] as List; + + try { + await db.isar.writeTxn(() async { + await db.isar.lelantusCoins.putAll(coins); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + // don't just rethrow since isar likes to strip stack traces for some reason + throw Exception("e=$e & s=$s"); + } final transactionMap = message["newTxMap"] as Map; @@ -4726,7 +4568,8 @@ class FiroWallet extends CoinServiceAPI int spendAmount, String address, bool subtractFeeFromAmount) async { final _mnemonic = await mnemonicString; final _mnemonicPassphrase = await mnemonicPassphrase; - final index = firoGetMintIndex(); + final lastUsedIndex = await db.getHighestUsedMintIndex(walletId: walletId); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; final lelantusEntry = await _getLelantusEntry(); final anonymitySets = await fetchAnonymitySets(); final locktime = await getBlockHead(electrumXClient); @@ -4740,7 +4583,7 @@ class FiroWallet extends CoinServiceAPI "subtractFeeFromAmount": subtractFeeFromAmount, "mnemonic": _mnemonic, "mnemonicPassphrase": _mnemonicPassphrase, - "index": index, + "index": nextFreeMintIndex, // "price": price, "lelantusEntries": lelantusEntry, "locktime": locktime, @@ -4828,75 +4671,6 @@ class FiroWallet extends CoinServiceAPI this.isActive = isActive; }; - Future getCoinsToJoinSplit( - int required, - ) async { - List coins = await _getLelantusEntry(); - if (required > LELANTUS_VALUE_SPEND_LIMIT_PER_TRANSACTION) { - return false; - } - - int availableBalance = coins.fold( - 0, (previousValue, element) => previousValue + element.amount); - - if (required > availableBalance) { - return false; - } - - // sort by biggest amount. if it is same amount we will prefer the older block - coins.sort((a, b) => - (a.amount != b.amount ? a.amount > b.amount : a.height < b.height) - ? -1 - : 1); - int spendVal = 0; - - List coinsToSpend = []; - - while (spendVal < required) { - if (coins.isEmpty) { - break; - } - - DartLelantusEntry? chosen; - int need = required - spendVal; - - var itr = coins.first; - if (need >= itr.amount) { - chosen = itr; - coins.remove(itr); - } else { - for (int index = coins.length - 1; index != 0; index--) { - var coinIt = coins[index]; - var nextItr = coins[index - 1]; - - if (coinIt.amount >= need && - (index - 1 == 0 || nextItr.amount != coinIt.amount)) { - chosen = coinIt; - coins.remove(chosen); - break; - } - } - } - - // TODO: investigate the bug here where chosen is null, conditions, given one mint - spendVal += chosen!.amount; - coinsToSpend.insert(coinsToSpend.length, chosen); - } - - // sort by group id ay ascending order. it is mandatory for creating proper joinsplit - coinsToSpend.sort((a, b) => a.anonymitySetId < b.anonymitySetId ? 1 : -1); - - int changeToMint = spendVal - required; - List indices = []; - for (var l in coinsToSpend) { - indices.add(l.index); - } - List coinsToBeSpentOut = []; - coinsToBeSpentOut.addAll(coinsToSpend); - - return {"changeToMint": changeToMint, "coinsToSpend": coinsToBeSpentOut}; - } - Future estimateJoinSplitFee( int spendAmount, ) async { @@ -4927,36 +4701,6 @@ class FiroWallet extends CoinServiceAPI Logging.instance.log('Closing estimateJoinSplit!', level: LogLevel.Info); return (message as LelantusFeeData).fee; } - // int fee; - // int size; - // - // for (fee = 0;;) { - // int currentRequired = spendAmount; - // - // TODO: investigate the bug here - // var map = await getCoinsToJoinSplit(currentRequired); - // if (map is bool && !map) { - // return 0; - // } - // - // List coinsToBeSpent = - // map['coinsToSpend'] as List; - // - // // 1054 is constant part, mainly Schnorr and Range proofs, 2560 is for each sigma/aux data - // // 179 other parts of tx, assuming 1 utxo and 1 jmint - // size = 1054 + 2560 * coinsToBeSpent.length + 180; - // // uint64_t feeNeeded = GetMinimumFee(size, DEFAULT_TX_CONFIRM_TARGET); - // int feeNeeded = - // size; //TODO(Levon) temporary, use real estimation methods here - // - // if (fee >= feeNeeded) { - // break; - // } - // - // fee = feeNeeded; - // } - // - // return fee; @override Future estimateFeeFor(Amount amount, int feeRate) async { @@ -5019,7 +4763,6 @@ class FiroWallet extends CoinServiceAPI } } - // TODO: correct formula for firo? Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { return Amount( rawValue: BigInt.from(((181 * inputCount) + (34 * outputCount) + 10) * diff --git a/lib/services/coins/nano/nano_wallet.dart b/lib/services/coins/nano/nano_wallet.dart index 1e783bf5f..391303675 100644 --- a/lib/services/coins/nano/nano_wallet.dart +++ b/lib/services/coins/nano/nano_wallet.dart @@ -24,9 +24,9 @@ import 'package:stackwallet/services/event_bus/events/global/node_connection_sta import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; -import 'package:stackwallet/services/mixins/coin_control_interface.dart'; import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart'; +import 'package:stackwallet/services/nano_api.dart'; import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/transaction_notification_tracker.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; @@ -42,8 +42,7 @@ const int MINIMUM_CONFIRMATIONS = 1; const String DEFAULT_REPRESENTATIVE = "nano_38713x95zyjsqzx6nm1dsom1jmm668owkeb9913ax6nfgj15az3nu8xkx579"; -class NanoWallet extends CoinServiceAPI - with WalletCache, WalletDB, CoinControlInterface { +class NanoWallet extends CoinServiceAPI with WalletCache, WalletDB { NanoWallet({ required String walletId, required String walletName, @@ -937,4 +936,51 @@ class NanoWallet extends CoinServiceAPI ); await updateCachedChainHeight(height ?? 0); } + + Future getCurrentRepresentative() async { + final serverURI = Uri.parse(getCurrentNode().host); + final address = await currentReceivingAddress; + + final response = await NanoAPI.getAccountInfo( + server: serverURI, + representative: true, + account: address, + ); + + return response.accountInfo?.representative ?? DEFAULT_REPRESENTATIVE; + } + + Future changeRepresentative(String newRepresentative) async { + try { + final serverURI = Uri.parse(getCurrentNode().host); + final balance = this.balance.spendable.raw.toString(); + final String privateKey = await getPrivateKeyFromMnemonic(); + final address = await currentReceivingAddress; + + final response = await NanoAPI.getAccountInfo( + server: serverURI, + representative: true, + account: address, + ); + + if (response.accountInfo == null) { + throw response.exception ?? Exception("Failed to get account info"); + } + + final work = await requestWork(response.accountInfo!.frontier); + + return await NanoAPI.changeRepresentative( + server: serverURI, + accountType: NanoAccountType.NANO, + account: address, + newRepresentative: newRepresentative, + previousBlock: response.accountInfo!.frontier, + balance: balance, + privateKey: privateKey, + work: work!, + ); + } catch (_) { + rethrow; + } + } } diff --git a/lib/services/ethereum/ethereum_api.dart b/lib/services/ethereum/ethereum_api.dart index d30b9ea27..5e95e3aa1 100644 --- a/lib/services/ethereum/ethereum_api.dart +++ b/lib/services/ethereum/ethereum_api.dart @@ -10,7 +10,6 @@ import 'dart:convert'; -import 'package:decimal/decimal.dart'; import 'package:http/http.dart'; import 'package:stackwallet/dto/ethereum/eth_token_tx_dto.dart'; import 'package:stackwallet/dto/ethereum/eth_token_tx_extra_dto.dart'; @@ -50,6 +49,7 @@ abstract class EthereumAPI { static Future>> getEthTransactions({ required String address, int firstBlock = 0, + bool includeTokens = false, }) async { try { final response = await get( @@ -67,7 +67,7 @@ abstract class EthereumAPI { for (final map in list!) { final txn = EthTxDTO.fromMap(Map.from(map as Map)); - if (txn.hasToken == 0) { + if (txn.hasToken == 0 || includeTokens) { txns.add(txn); } } @@ -76,9 +76,11 @@ abstract class EthereumAPI { null, ); } else { - throw EthApiException( - "getEthTransactions($address) response is empty but status code is " - "${response.statusCode}", + // nice that the api returns an empty body instead of being + // consistent and returning a json object with no transactions + return EthereumResponse( + [], + null, ); } } else { @@ -196,9 +198,11 @@ abstract class EthereumAPI { null, ); } else { - throw EthApiException( - "getEthTransactionNonces($txns) response is empty but status code is " - "${response.statusCode}", + // nice that the api returns an empty body instead of being + // consistent and returning a json object with no transactions + return EthereumResponse( + [], + null, ); } } else { @@ -252,13 +256,13 @@ abstract class EthereumAPI { ); } else { throw EthApiException( - "getEthTransaction($txids) response is empty but status code is " + "getEthTokenTransactionsByTxids($txids) response is empty but status code is " "${response.statusCode}", ); } } else { throw EthApiException( - "getEthTransaction($txids) failed with status code: " + "getEthTokenTransactionsByTxids($txids) failed with status code: " "${response.statusCode}", ); } @@ -269,7 +273,7 @@ abstract class EthereumAPI { ); } catch (e, s) { Logging.instance.log( - "getEthTransaction($txids): $e\n$s", + "getEthTokenTransactionsByTxids($txids): $e\n$s", level: LogLevel.Error, ); return EthereumResponse( @@ -307,9 +311,11 @@ abstract class EthereumAPI { null, ); } else { - throw EthApiException( - "getTokenTransactions($address, $tokenContractAddress) response is empty but status code is " - "${response.statusCode}", + // nice that the api returns an empty body instead of being + // consistent and returning a json object with no transactions + return EthereumResponse( + [], + null, ); } } else { @@ -424,10 +430,10 @@ abstract class EthereumAPI { final map = json["data"].first as Map; final balance = - Decimal.tryParse(map["balance"].toString()) ?? Decimal.zero; + BigInt.tryParse(map["units"].toString()) ?? BigInt.zero; return EthereumResponse( - Amount.fromDecimal(balance, fractionDigits: map["decimals"] as int), + Amount(rawValue: balance, fractionDigits: map["decimals"] as int), null, ); } else { diff --git a/lib/services/exchange/exchange_data_loading_service.dart b/lib/services/exchange/exchange_data_loading_service.dart index 6fefba289..ee0ed50a0 100644 --- a/lib/services/exchange/exchange_data_loading_service.dart +++ b/lib/services/exchange/exchange_data_loading_service.dart @@ -135,6 +135,9 @@ class ExchangeDataLoadingService { Future loadAll() async { if (!_locked) { _locked = true; + if (_isar == null) { + await initDB(); + } Logging.instance.log( "ExchangeDataLoadingService.loadAll starting...", level: LogLevel.Info, diff --git a/lib/services/locale_service.dart b/lib/services/locale_service.dart index 7f5cde716..8cfb6f765 100644 --- a/lib/services/locale_service.dart +++ b/lib/services/locale_service.dart @@ -21,7 +21,7 @@ class LocaleService extends ChangeNotifier { Future loadLocale({bool notify = true}) async { _locale = Platform.isWindows ? "en_US" - : await Devicelocale.currentLocale ?? "en_US"; + : (await Devicelocale.currentAsLocale)?.toString() ?? "en_US"; if (notify) { notifyListeners(); } diff --git a/lib/services/mixins/firo_hive.dart b/lib/services/mixins/firo_hive.dart deleted file mode 100644 index be9845453..000000000 --- a/lib/services/mixins/firo_hive.dart +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of Stack Wallet. - * - * Copyright (c) 2023 Cypher Stack - * All Rights Reserved. - * The code is distributed under GPLv3 license, see LICENSE file for details. - * Generated by Cypher Stack on 2023-05-26 - * - */ - -import 'package:stackwallet/db/hive/db.dart'; - -mixin FiroHive { - late final String _walletId; - - void initFiroHive(String walletId) { - _walletId = walletId; - } - - // jindex - List? firoGetJIndex() { - return DB.instance.get(boxName: _walletId, key: "jindex") as List?; - } - - Future firoUpdateJIndex(List jIndex) async { - await DB.instance.put( - boxName: _walletId, - key: "jindex", - value: jIndex, - ); - } - - // _lelantus_coins - List? firoGetLelantusCoins() { - return DB.instance.get(boxName: _walletId, key: "_lelantus_coins") - as List?; - } - - Future firoUpdateLelantusCoins(List lelantusCoins) async { - await DB.instance.put( - boxName: _walletId, - key: "_lelantus_coins", - value: lelantusCoins, - ); - } - - // mintIndex - int firoGetMintIndex() { - return DB.instance.get(boxName: _walletId, key: "mintIndex") - as int? ?? - 0; - } - - Future firoUpdateMintIndex(int mintIndex) async { - await DB.instance.put( - boxName: _walletId, - key: "mintIndex", - value: mintIndex, - ); - } -} diff --git a/lib/services/nano_api.dart b/lib/services/nano_api.dart new file mode 100644 index 000000000..3173bfbe6 --- /dev/null +++ b/lib/services/nano_api.dart @@ -0,0 +1,130 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:nanodart/nanodart.dart'; + +class NanoAPI { + static Future< + ({ + NAccountInfo? accountInfo, + Exception? exception, + })> getAccountInfo({ + required Uri server, + required bool representative, + required String account, + }) async { + NAccountInfo? accountInfo; + Exception? exception; + + try { + final response = await http.post( + server, + headers: { + "Content-Type": "application/json", + }, + body: jsonEncode({ + "action": "account_info", + "representative": "true", + "account": account, + }), + ); + + final map = jsonDecode(response.body); + + if (map is Map && map["error"] != null) { + throw Exception(map["error"].toString()); + } + + accountInfo = NAccountInfo( + frontier: map["frontier"] as String, + representative: map["representative"] as String, + ); + } on Exception catch (e) { + exception = e; + } catch (e) { + exception = Exception(e.toString()); + } + + return (accountInfo: accountInfo, exception: exception); + } + + static Future changeRepresentative({ + required Uri server, + required int accountType, + required String account, + required String newRepresentative, + required String previousBlock, + required String balance, + required String privateKey, + required String work, + }) async { + Map block = { + "type": "state", + "account": account, + "previous": previousBlock, + "representative": newRepresentative, + "balance": balance, + "link": + "0000000000000000000000000000000000000000000000000000000000000000", + "work": work, + }; + + final String hash; + + try { + hash = NanoBlocks.computeStateHash( + accountType, + account, + previousBlock, + newRepresentative, + BigInt.parse(balance), + block["link"] as String, + ); + } catch (e) { + if (e is RangeError) { + throw Exception("Invalid representative format"); + } + rethrow; + } + + final signature = NanoSignatures.signBlock(hash, privateKey); + + block["signature"] = signature; + + final map = await postBlock(server: server, block: block); + + if (map is Map && map["error"] != null) { + throw Exception(map["error"].toString()); + } + + return map["error"] == null; + } + + // TODO: GET RID OF DYNAMIC AND USED TYPED DATA + static Future postBlock({ + required Uri server, + required Map block, + }) async { + final response = await http.post( + server, + headers: { + "Content-Type": "application/json", + }, + body: jsonEncode({ + "action": "process", + "json_block": "true", + "subtype": "change", + "block": block, + }), + ); + + return jsonDecode(response.body); + } +} + +class NAccountInfo { + final String frontier; + final String representative; + + NAccountInfo({required this.frontier, required this.representative}); +} diff --git a/lib/services/notifications_api.dart b/lib/services/notifications_api.dart index e1c42fabe..3ef97462a 100644 --- a/lib/services/notifications_api.dart +++ b/lib/services/notifications_api.dart @@ -26,6 +26,7 @@ class NotificationApi { priority: Priority.high, ticker: 'ticker'), iOS: IOSNotificationDetails(), + macOS: MacOSNotificationDetails(), ); } @@ -34,8 +35,13 @@ class NotificationApi { const iOS = IOSInitializationSettings(); const linux = LinuxInitializationSettings( defaultActionName: "temporary_stack_wallet"); - const settings = - InitializationSettings(android: android, iOS: iOS, linux: linux); + const macOS = MacOSInitializationSettings(); + const settings = InitializationSettings( + android: android, + iOS: iOS, + linux: linux, + macOS: macOS, + ); await _notifications.initialize( settings, onSelectNotification: (payload) async { @@ -71,8 +77,10 @@ class NotificationApi { final id = prefs.currentNotificationId; String confirms = ""; - if (txid != null) { - confirms = " (${confirmations!}/${requiredConfirmations!})"; + if (txid != null && + confirmations != null && + requiredConfirmations != null) { + confirms = " ($confirmations/$requiredConfirmations)"; } final NotificationModel model = NotificationModel( diff --git a/lib/themes/coin_card_provider.dart b/lib/themes/coin_card_provider.dart index fc84faebd..b34e9e6f1 100644 --- a/lib/themes/coin_card_provider.dart +++ b/lib/themes/coin_card_provider.dart @@ -22,3 +22,14 @@ final coinCardProvider = Provider.family((ref, coin) { return null; } }); + +final coinCardFavoritesProvider = Provider.family((ref, coin) { + final assets = ref.watch(themeAssetsProvider); + + if (assets is ThemeAssetsV3) { + return assets.coinCardFavoritesImages?[coin.mainNetVersion] ?? + assets.coinCardImages?[coin.mainNetVersion]; + } else { + return null; + } +}); diff --git a/lib/utilities/address_utils.dart b/lib/utilities/address_utils.dart index 454391f56..0093e1d00 100644 --- a/lib/utilities/address_utils.dart +++ b/lib/utilities/address_utils.dart @@ -10,11 +10,11 @@ import 'dart:convert'; +import 'package:bitbox/bitbox.dart' as bitbox; import 'package:bitcoindart/bitcoindart.dart'; import 'package:crypto/crypto.dart'; import 'package:flutter_libepiccash/epic_cash.dart'; import 'package:nanodart/nanodart.dart'; -import 'package:stackwallet/services/coins/bitcoincash/bitcoincash_wallet.dart'; import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart'; import 'package:stackwallet/services/coins/ecash/ecash_wallet.dart'; import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; @@ -64,7 +64,27 @@ class AddressUtils { case Coin.litecoin: return Address.validateAddress(address, litecoin); case Coin.bitcoincash: - return Address.validateAddress(address, bitcoincash); + try { + // 0 for bitcoincash: address scheme, 1 for legacy address + final format = bitbox.Address.detectFormat(address); + + if (coin == Coin.bitcoincashTestnet) { + return true; + } + + if (format == bitbox.Address.formatCashAddr) { + String addr = address; + if (addr.contains(":")) { + addr = addr.split(":").last; + } + + return addr.startsWith("q"); + } else { + return address.startsWith("1"); + } + } catch (e) { + return false; + } case Coin.dogecoin: return Address.validateAddress(address, dogecoin); case Coin.epicCash: @@ -94,7 +114,27 @@ class AddressUtils { case Coin.litecoinTestNet: return Address.validateAddress(address, litecointestnet); case Coin.bitcoincashTestnet: - return Address.validateAddress(address, bitcoincashtestnet); + try { + // 0 for bitcoincash: address scheme, 1 for legacy address + final format = bitbox.Address.detectFormat(address); + + if (coin == Coin.bitcoincashTestnet) { + return true; + } + + if (format == bitbox.Address.formatCashAddr) { + String addr = address; + if (addr.contains(":")) { + addr = addr.split(":").last; + } + + return addr.startsWith("q"); + } else { + return address.startsWith("1"); + } + } catch (e) { + return false; + } case Coin.firoTestNet: return Address.validateAddress(address, firoTestNetwork); case Coin.dogecoinTestNet: diff --git a/lib/utilities/amount/amount.dart b/lib/utilities/amount/amount.dart index 0e8790064..0014a4eab 100644 --- a/lib/utilities/amount/amount.dart +++ b/lib/utilities/amount/amount.dart @@ -11,8 +11,7 @@ import 'dart:convert'; import 'package:decimal/decimal.dart'; -import 'package:intl/number_symbols.dart'; -import 'package:intl/number_symbols_data.dart'; +import 'package:stackwallet/utilities/util.dart'; class Amount { Amount({ @@ -52,8 +51,7 @@ class Amount { } // get number symbols for decimal place and group separator - final numberSymbols = numberFormatSymbols[locale] as NumberSymbols? ?? - numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?; + final numberSymbols = Util.getSymbolsFor(locale: locale); final groupSeparator = numberSymbols?.GROUP_SEP ?? ","; final decimalSeparator = numberSymbols?.DECIMAL_SEP ?? "."; @@ -101,8 +99,7 @@ class Amount { final wholeNumber = decimal.truncate(); // get number symbols for decimal place and group separator - final numberSymbols = numberFormatSymbols[locale] as NumberSymbols? ?? - numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?; + final numberSymbols = Util.getSymbolsFor(locale: locale); final String separator = numberSymbols?.DECIMAL_SEP ?? "."; diff --git a/lib/utilities/amount/amount_formatter.dart b/lib/utilities/amount/amount_formatter.dart index 7c5a8f29b..1ae577507 100644 --- a/lib/utilities/amount/amount_formatter.dart +++ b/lib/utilities/amount/amount_formatter.dart @@ -22,11 +22,13 @@ final pMaxDecimals = Provider.family( ); final pAmountFormatter = Provider.family((ref, coin) { + final locale = ref.watch( + localeServiceChangeNotifierProvider.select((value) => value.locale), + ); + return AmountFormatter( unit: ref.watch(pAmountUnit(coin)), - locale: ref.watch( - localeServiceChangeNotifierProvider.select((value) => value.locale), - ), + locale: locale, coin: coin, maxDecimals: ref.watch(pMaxDecimals(coin)), ); @@ -68,6 +70,11 @@ class AmountFormatter { String string, { EthContract? ethContract, }) { - return unit.tryParse(string, locale: locale, coin: coin); + return unit.tryParse( + string, + locale: locale, + coin: coin, + tokenContract: ethContract, + ); } } diff --git a/lib/utilities/amount/amount_input_formatter.dart b/lib/utilities/amount/amount_input_formatter.dart index dfc2b5732..2ac5d7871 100644 --- a/lib/utilities/amount/amount_input_formatter.dart +++ b/lib/utilities/amount/amount_input_formatter.dart @@ -1,9 +1,8 @@ import 'dart:math'; import 'package:flutter/services.dart'; -import 'package:intl/number_symbols.dart'; -import 'package:intl/number_symbols_data.dart'; import 'package:stackwallet/utilities/amount/amount_unit.dart'; +import 'package:stackwallet/utilities/util.dart'; class AmountInputFormatter extends TextInputFormatter { final int decimals; @@ -20,8 +19,7 @@ class AmountInputFormatter extends TextInputFormatter { TextEditingValue formatEditUpdate( TextEditingValue oldValue, TextEditingValue newValue) { // get number symbols for decimal place and group separator - final numberSymbols = numberFormatSymbols[locale] as NumberSymbols? ?? - numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?; + final numberSymbols = Util.getSymbolsFor(locale: locale); final decimalSeparator = numberSymbols?.DECIMAL_SEP ?? "."; final groupSeparator = numberSymbols?.GROUP_SEP ?? ","; diff --git a/lib/utilities/amount/amount_unit.dart b/lib/utilities/amount/amount_unit.dart index 79b36130f..91fef8bc7 100644 --- a/lib/utilities/amount/amount_unit.dart +++ b/lib/utilities/amount/amount_unit.dart @@ -11,11 +11,10 @@ import 'dart:math' as math; import 'package:decimal/decimal.dart'; -import 'package:intl/number_symbols.dart'; -import 'package:intl/number_symbols_data.dart'; import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/util.dart'; // preserve index order as index is used to store value in preferences enum AmountUnit { @@ -169,6 +168,7 @@ extension AmountUnitExt on AmountUnit { required String locale, required Coin coin, EthContract? tokenContract, + bool overrideWithDecimalPlacesFromString = false, }) { final precisionLost = value.startsWith("~"); @@ -188,8 +188,7 @@ extension AmountUnitExt on AmountUnit { } // get number symbols for decimal place and group separator - final numberSymbols = numberFormatSymbols[locale] as NumberSymbols? ?? - numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?; + final numberSymbols = Util.getSymbolsFor(locale: locale); final groupSeparator = numberSymbols?.GROUP_SEP ?? ","; final decimalSeparator = numberSymbols?.DECIMAL_SEP ?? "."; @@ -203,7 +202,9 @@ extension AmountUnitExt on AmountUnit { return null; } - final decimalPlaces = tokenContract?.decimals ?? coin.decimals; + final decimalPlaces = overrideWithDecimalPlacesFromString + ? decimal.scale + : tokenContract?.decimals ?? coin.decimals; final realShift = math.min(shift, decimalPlaces); return decimal.shift(0 - realShift).toAmount(fractionDigits: decimalPlaces); @@ -237,8 +238,7 @@ extension AmountUnitExt on AmountUnit { String returnValue = wholeNumber.toString(); // get number symbols for decimal place and group separator - final numberSymbols = numberFormatSymbols[locale] as NumberSymbols? ?? - numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?; + final numberSymbols = Util.getSymbolsFor(locale: locale); // insert group separator final regex = RegExp(r'\B(?=(\d{3})+(?!\d))'); diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index 2dd7be287..ad9d68997 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -58,7 +58,7 @@ abstract class Constants { // Enable Logger.print statements static const bool disableLogger = false; - static const int currentDataVersion = 10; + static const int currentDataVersion = 11; static const int rescanV1 = 1; diff --git a/lib/utilities/db_version_migration.dart b/lib/utilities/db_version_migration.dart index 293b5d48c..f54c2b85a 100644 --- a/lib/utilities/db_version_migration.dart +++ b/lib/utilities/db_version_migration.dart @@ -180,7 +180,6 @@ class DbVersionMigrator with WalletDB { // clear possible broken firo cache await DB.instance.clearSharedTransactionCache(coin: Coin.firo); - // update version await DB.instance.put( boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 4); @@ -343,12 +342,85 @@ class DbVersionMigrator with WalletDB { // try to continue migrating return await migrate(10, secureStore: secureStore); + case 10: + // migrate + await _v10(secureStore); + + // update version + await DB.instance.put( + boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 11); + + // try to continue migrating + return await migrate(11, secureStore: secureStore); + default: // finally return return; } } + Future _v10(SecureStorageInterface secureStore) async { + await Hive.openBox(DB.boxNameAllWalletsData); + await Hive.openBox(DB.boxNamePrefs); + final walletsService = WalletsService(secureStorageInterface: secureStore); + final prefs = Prefs.instance; + final walletInfoList = await walletsService.walletNames; + await prefs.init(); + await MainDB.instance.initMainDB(); + + for (final walletId in walletInfoList.keys) { + final info = walletInfoList[walletId]!; + assert(info.walletId == walletId); + + if (info.coin == Coin.firo && + MainDB.instance.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .countSync() == + 0) { + final walletBox = await Hive.openBox(walletId); + + final hiveLCoins = DB.instance.get( + boxName: walletId, + key: "_lelantus_coins", + ) as List? ?? + []; + + final jindexes = (DB.instance + .get(boxName: walletId, key: "jindex") as List? ?? + []) + .cast(); + + final List coins = []; + for (final e in hiveLCoins) { + final map = e as Map; + final lcoin = map.values.first as LelantusCoin; + + final isJMint = jindexes.contains(lcoin.index); + + final coin = isar_models.LelantusCoin( + walletId: walletId, + txid: lcoin.txId, + value: lcoin.value.toString(), + mintIndex: lcoin.index, + anonymitySetId: lcoin.anonymitySetId, + isUsed: lcoin.isUsed, + isJMint: isJMint, + otherData: null, + ); + + coins.add(coin); + } + + if (coins.isNotEmpty) { + await MainDB.instance.isar.writeTxn(() async { + await MainDB.instance.isar.lelantusCoins.putAll(coins); + }); + } + } + } + } + Future _v4(SecureStorageInterface secureStore) async { await Hive.openBox(DB.boxNameAllWalletsData); await Hive.openBox(DB.boxNamePrefs); diff --git a/lib/utilities/show_loading.dart b/lib/utilities/show_loading.dart index db4437d6f..759eaa05c 100644 --- a/lib/utilities/show_loading.dart +++ b/lib/utilities/show_loading.dart @@ -12,15 +12,17 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/widgets/custom_loading_overlay.dart'; -Future showLoading({ +Future showLoading({ required Future whileFuture, required BuildContext context, required String message, String? subMessage, bool isDesktop = false, bool opaqueBG = false, + void Function(Exception)? onException, }) async { unawaited( showDialog( @@ -43,10 +45,24 @@ Future showLoading({ ), ); - final result = await whileFuture; + Exception? ex; + T? result; + + try { + result = await whileFuture; + } catch (e, s) { + Logging.instance.log( + "showLoading caught: $e\n$s", + level: LogLevel.Warning, + ); + ex = e is Exception ? e : Exception(e.toString()); + } if (context.mounted) { Navigator.of(context, rootNavigator: isDesktop).pop(); + if (ex != null) { + onException?.call(ex); + } } return result; diff --git a/lib/utilities/stack_file_system.dart b/lib/utilities/stack_file_system.dart index 843bb1b71..f36fadf01 100644 --- a/lib/utilities/stack_file_system.dart +++ b/lib/utilities/stack_file_system.dart @@ -27,8 +27,8 @@ abstract class StackFileSystem { } else if (Platform.isWindows) { appDirectory = await getApplicationSupportDirectory(); } else if (Platform.isMacOS) { - // currently run in ipad mode?? - throw Exception("Unsupported platform"); + appDirectory = await getLibraryDirectory(); + appDirectory = Directory("${appDirectory.path}/stackwallet"); } else if (Platform.isIOS) { // todo: check if we need different behaviour here if (Util.isDesktop) { diff --git a/lib/utilities/util.dart b/lib/utilities/util.dart index 265cd2e06..c73f697cc 100644 --- a/lib/utilities/util.dart +++ b/lib/utilities/util.dart @@ -14,11 +14,21 @@ import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/material.dart'; +import 'package:intl/number_symbols.dart'; +import 'package:intl/number_symbols_data.dart'; abstract class Util { static Directory? libraryPath; static double? screenWidth; + static NumberSymbols? getSymbolsFor({required String locale}) { + return numberFormatSymbols[locale] as NumberSymbols? ?? + numberFormatSymbols[locale.replaceAll("-", "_")] as NumberSymbols? ?? + numberFormatSymbols[locale.substring(3).toLowerCase()] + as NumberSymbols? ?? + numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?; + } + static bool get isDesktop { // special check for running on linux based phones if (Platform.isLinux && screenWidth != null && screenWidth! < 800) { diff --git a/lib/widgets/coin_card.dart b/lib/widgets/coin_card.dart index 0bebc5fd8..ebc67bb4e 100644 --- a/lib/widgets/coin_card.dart +++ b/lib/widgets/coin_card.dart @@ -25,11 +25,13 @@ class CoinCard extends ConsumerWidget { required this.walletId, required this.width, required this.height, + required this.isFavorite, }); final String walletId; final double width; final double height; + final bool isFavorite; @override Widget build(BuildContext context, WidgetRef ref) { @@ -38,7 +40,7 @@ class CoinCard extends ConsumerWidget { .select((value) => value.getManager(walletId).coin), ); - final bool hasCardImageBg = ref.watch(coinCardProvider(coin)) != null; + final bool hasCardImageBg = (isFavorite) ? ref.watch(coinCardFavoritesProvider(coin)) != null : ref.watch(coinCardProvider(coin)) != null; return Stack( children: [ @@ -54,7 +56,9 @@ class CoinCard extends ConsumerWidget { fit: BoxFit.cover, image: FileImage( File( - ref.watch(coinCardProvider(coin))!, + (isFavorite) + ? ref.watch(coinCardFavoritesProvider(coin))! + : ref.watch(coinCardProvider(coin))!, ), ), ), diff --git a/lib/widgets/crypto_notifications.dart b/lib/widgets/crypto_notifications.dart index f94c74ed9..b570ae4f9 100644 --- a/lib/widgets/crypto_notifications.dart +++ b/lib/widgets/crypto_notifications.dart @@ -13,6 +13,7 @@ import 'dart:async'; import 'package:event_bus/event_bus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/services/notifications_api.dart'; import 'package:stackwallet/themes/coin_icon_provider.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -84,6 +85,8 @@ class _CryptoNotificationsState extends ConsumerState { @override void initState() { + NotificationApi.prefs = ref.read(prefsChangeNotifierProvider); + NotificationApi.notificationsService = ref.read(notificationsProvider); _streamSubscription = CryptoNotificationsEventBus.instance .on() .listen( diff --git a/lib/widgets/transaction_card.dart b/lib/widgets/transaction_card.dart index 7791ff3a0..41b81e2da 100644 --- a/lib/widgets/transaction_card.dart +++ b/lib/widgets/transaction_card.dart @@ -230,7 +230,9 @@ class _TransactionCardState extends ConsumerState { fit: BoxFit.scaleDown, child: Text( _transaction.isCancelled - ? "Cancelled" + ? coin == Coin.ethereum + ? "Failed" + : "Cancelled" : whatIsIt( _transaction.type, coin, diff --git a/lib/widgets/wallet_card.dart b/lib/widgets/wallet_card.dart index 63b3a4a0e..09dcb0e2e 100644 --- a/lib/widgets/wallet_card.dart +++ b/lib/widgets/wallet_card.dart @@ -140,7 +140,7 @@ class SimpleWalletCard extends ConsumerWidget { isDesktop: Util.isDesktop, ); - if (!success) { + if (!success!) { // TODO: show error dialog here? Logging.instance.log( "Failed to load token wallet for $contract", diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index b741c3c0a..b044b4b00 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,6 +6,9 @@ import FlutterMacOS import Foundation import connectivity_plus +import cw_monero +import cw_shared_external +import cw_wownero import desktop_drop import device_info_plus import devicelocale @@ -13,10 +16,10 @@ import flutter_libepiccash import flutter_local_notifications import flutter_secure_storage_macos import isar_flutter_libs +import lelantus import package_info_plus import path_provider_foundation import share_plus -import shared_preferences_foundation import stack_wallet_backup import url_launcher_macos import wakelock_macos @@ -24,6 +27,9 @@ import window_size func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + CwMoneroPlugin.register(with: registry.registrar(forPlugin: "CwMoneroPlugin")) + CwSharedExternalPlugin.register(with: registry.registrar(forPlugin: "CwSharedExternalPlugin")) + CwWowneroPlugin.register(with: registry.registrar(forPlugin: "CwWowneroPlugin")) DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) @@ -31,10 +37,10 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin")) + LelantusPlugin.register(with: registry.registrar(forPlugin: "LelantusPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) - SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) StackWalletBackupPlugin.register(with: registry.registrar(forPlugin: "StackWalletBackupPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin")) diff --git a/macos/Podfile b/macos/Podfile index dade8dfad..c795730db 100644 --- a/macos/Podfile +++ b/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.11' +platform :osx, '10.14' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -31,6 +31,9 @@ target 'Runner' do use_modular_headers! flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end end post_install do |installer| diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 82579cd8e..69c91af48 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,24 +1,76 @@ PODS: - - connectivity_plus_macos (0.0.1): + - connectivity_plus (0.0.1): - FlutterMacOS - ReachabilitySwift + - cw_monero (0.0.1): + - cw_monero/Boost (= 0.0.1) + - cw_monero/Monero (= 0.0.1) + - cw_monero/OpenSSL (= 0.0.1) + - cw_monero/Sodium (= 0.0.1) + - cw_monero/Unbound (= 0.0.1) + - FlutterMacOS + - cw_monero/Boost (0.0.1): + - FlutterMacOS + - cw_monero/Monero (0.0.1): + - FlutterMacOS + - cw_monero/OpenSSL (0.0.1): + - FlutterMacOS + - cw_monero/Sodium (0.0.1): + - FlutterMacOS + - cw_monero/Unbound (0.0.1): + - FlutterMacOS + - cw_shared_external (0.0.1): + - cw_shared_external/Boost (= 0.0.1) + - cw_shared_external/OpenSSL (= 0.0.1) + - cw_shared_external/Sodium (= 0.0.1) + - FlutterMacOS + - cw_shared_external/Boost (0.0.1): + - FlutterMacOS + - cw_shared_external/OpenSSL (0.0.1): + - FlutterMacOS + - cw_shared_external/Sodium (0.0.1): + - FlutterMacOS + - cw_wownero (0.0.1): + - cw_wownero/Boost (= 0.0.1) + - cw_wownero/OpenSSL (= 0.0.1) + - cw_wownero/Sodium (= 0.0.1) + - cw_wownero/Unbound (= 0.0.1) + - cw_wownero/Wownero (= 0.0.1) + - FlutterMacOS + - cw_wownero/Boost (0.0.1): + - FlutterMacOS + - cw_wownero/OpenSSL (0.0.1): + - FlutterMacOS + - cw_wownero/Sodium (0.0.1): + - FlutterMacOS + - cw_wownero/Unbound (0.0.1): + - FlutterMacOS + - cw_wownero/Wownero (0.0.1): + - FlutterMacOS + - desktop_drop (0.0.1): + - FlutterMacOS + - device_info_plus (0.0.1): + - FlutterMacOS + - devicelocale (0.0.1): + - FlutterMacOS - flutter_libepiccash (0.0.1): - FlutterMacOS - flutter_local_notifications (0.0.1): - FlutterMacOS - - flutter_secure_storage_macos (3.3.1): + - flutter_secure_storage_macos (6.1.1): - FlutterMacOS - FlutterMacOS (1.0.0) - isar_flutter_libs (1.0.0): - FlutterMacOS - - package_info_plus_macos (0.0.1): + - lelantus (0.0.1): - FlutterMacOS - - path_provider_macos (0.0.1): + - package_info_plus (0.0.1): + - FlutterMacOS + - path_provider_foundation (0.0.1): + - Flutter - FlutterMacOS - ReachabilitySwift (5.0.0) - - share_plus_macos (0.0.1): - - FlutterMacOS - - shared_preferences_macos (0.0.1): + - share_plus (0.0.1): - FlutterMacOS - stack_wallet_backup (0.0.1): - FlutterMacOS @@ -30,16 +82,22 @@ PODS: - FlutterMacOS DEPENDENCIES: - - connectivity_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus_macos/macos`) + - connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`) + - cw_monero (from `Flutter/ephemeral/.symlinks/plugins/cw_monero/macos`) + - cw_shared_external (from `Flutter/ephemeral/.symlinks/plugins/cw_shared_external/macos`) + - cw_wownero (from `Flutter/ephemeral/.symlinks/plugins/cw_wownero/macos`) + - desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`) + - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) + - devicelocale (from `Flutter/ephemeral/.symlinks/plugins/devicelocale/macos`) - flutter_libepiccash (from `Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos`) - flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`) - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - isar_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos`) - - package_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos`) - - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`) - - share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`) - - shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`) + - lelantus (from `Flutter/ephemeral/.symlinks/plugins/lelantus/macos`) + - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) + - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - stack_wallet_backup (from `Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`) @@ -50,8 +108,20 @@ SPEC REPOS: - ReachabilitySwift EXTERNAL SOURCES: - connectivity_plus_macos: - :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus_macos/macos + connectivity_plus: + :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos + cw_monero: + :path: Flutter/ephemeral/.symlinks/plugins/cw_monero/macos + cw_shared_external: + :path: Flutter/ephemeral/.symlinks/plugins/cw_shared_external/macos + cw_wownero: + :path: Flutter/ephemeral/.symlinks/plugins/cw_wownero/macos + desktop_drop: + :path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos + device_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos + devicelocale: + :path: Flutter/ephemeral/.symlinks/plugins/devicelocale/macos flutter_libepiccash: :path: Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos flutter_local_notifications: @@ -62,14 +132,14 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral isar_flutter_libs: :path: Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos - package_info_plus_macos: - :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos - path_provider_macos: - :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos - share_plus_macos: - :path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos - shared_preferences_macos: - :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos + lelantus: + :path: Flutter/ephemeral/.symlinks/plugins/lelantus/macos + package_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin + share_plus: + :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos stack_wallet_backup: :path: Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos url_launcher_macos: @@ -80,22 +150,28 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/window_size/macos SPEC CHECKSUMS: - connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308 - flutter_libepiccash: b33f7396504712b513b8ff019a3f6f3bdae54cfb + connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 + cw_monero: a3442556ad3c06365c912735e4a23942a28692b1 + cw_shared_external: 1f631d1132521baac5f4caed43176fa10d4e0d8b + cw_wownero: b4adb1e701fc363de27fa222fcaf4eff6f5fa63a + desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 + device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f + devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225 + flutter_libepiccash: 9113ac75dd325f8bcf00bc3ab583c7fc2780cf3c flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4 - flutter_secure_storage_macos: 6ceee8fbc7f484553ad17f79361b556259df89aa - FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811 - isar_flutter_libs: 1948109973b6c2e46d6196b1537688a36a6edeac - package_info_plus_macos: f010621b07802a241d96d01876d6705f15e77c1c - path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19 + flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + isar_flutter_libs: 43385c99864c168fadba7c9adeddc5d38838ca6a + lelantus: 3dfbf92b1e66b3573494dfe3d6a21c4988b5361b + package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce + path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 - share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4 - shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727 + share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 stack_wallet_backup: 6ebc60b1bdcf11cf1f1cbad9aa78332e1e15778c - url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3 + url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451 wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 window_size: 339dafa0b27a95a62a843042038fa6c3c48de195 -PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c +PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 COCOAPODS: 1.11.3 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 6506a5075..c2c2e62ad 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -21,15 +21,27 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 36299DF6FDF6725B2B9C51D5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6BB87EF657A3ADFB1CE3E959 /* Pods_Runner.framework */; }; + B98151812A674022009D013C /* mobileliblelantus.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B98151802A674022009D013C /* mobileliblelantus.framework */; }; + B98151822A67402A009D013C /* mobileliblelantus.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = B98151802A674022009D013C /* mobileliblelantus.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + B98151842A674143009D013C /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B98151832A674143009D013C /* libsqlite3.0.tbd */; }; + BFD0376C00E1FFD46376BB9D /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9206484E84CB0AD93E3E68CA /* Pods_RunnerTests.framework */; }; + F653CA022D33E8B60E11A9F3 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 33CC10E52044A3C60003C045 /* Project object */; @@ -46,6 +58,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + B98151822A67402A009D013C /* mobileliblelantus.framework in Bundle Framework */, ); name = "Bundle Framework"; runOnlyForDeploymentPostprocessing = 0; @@ -53,9 +66,14 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0FA914E59929120BA65E8403 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 174539D042E7AC2AB25A83EB /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 27CB73AACA5743180CC6CD50 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* Stack Wallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Stack Wallet.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* Stack Wallet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Stack Wallet.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -67,26 +85,47 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 6BB87EF657A3ADFB1CE3E959 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 41EE721BF40B8DE895352A2C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 937DF254AD7EDA15AFE96BD9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 9206484E84CB0AD93E3E68CA /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - BC4589C48A71C3A1A477DD76 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - EA2D897BC13EBFB1DE697D5C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + ACB8E553D75AA4AC9A7656CE /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + B98151802A674022009D013C /* mobileliblelantus.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = mobileliblelantus.framework; path = ../crypto_plugins/flutter_liblelantus/scripts/macos/mobileliblelantus/mobileliblelantus.framework; sourceTree = ""; }; + B98151832A674143009D013C /* libsqlite3.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; }; + BF5E76865ACB46314AC27D8F /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BFD0376C00E1FFD46376BB9D /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 33CC10EA2044A3C60003C045 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 36299DF6FDF6725B2B9C51D5 /* Pods_Runner.framework in Frameworks */, + B98151842A674143009D013C /* libsqlite3.0.tbd in Frameworks */, + B98151812A674022009D013C /* mobileliblelantus.framework in Frameworks */, + F653CA022D33E8B60E11A9F3 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; 33BA886A226E78AF003329D5 /* Configs */ = { isa = PBXGroup; children = ( @@ -103,9 +142,10 @@ children = ( 33FAB671232836740065AC1E /* Runner */, 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, - 9000119722579F22067B9BC0 /* Pods */, + F0D4A0626F78BE1EF2A1E0D6 /* Pods */, ); sourceTree = ""; }; @@ -113,6 +153,7 @@ isa = PBXGroup; children = ( 33CC10ED2044A3C60003C045 /* Stack Wallet.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -152,39 +193,63 @@ path = Runner; sourceTree = ""; }; - 9000119722579F22067B9BC0 /* Pods */ = { - isa = PBXGroup; - children = ( - EA2D897BC13EBFB1DE697D5C /* Pods-Runner.debug.xcconfig */, - 937DF254AD7EDA15AFE96BD9 /* Pods-Runner.release.xcconfig */, - BC4589C48A71C3A1A477DD76 /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( - 6BB87EF657A3ADFB1CE3E959 /* Pods_Runner.framework */, + B98151832A674143009D013C /* libsqlite3.0.tbd */, + B98151802A674022009D013C /* mobileliblelantus.framework */, + E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */, + 9206484E84CB0AD93E3E68CA /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; }; + F0D4A0626F78BE1EF2A1E0D6 /* Pods */ = { + isa = PBXGroup; + children = ( + 41EE721BF40B8DE895352A2C /* Pods-Runner.debug.xcconfig */, + 0FA914E59929120BA65E8403 /* Pods-Runner.release.xcconfig */, + ACB8E553D75AA4AC9A7656CE /* Pods-Runner.profile.xcconfig */, + BF5E76865ACB46314AC27D8F /* Pods-RunnerTests.debug.xcconfig */, + 174539D042E7AC2AB25A83EB /* Pods-RunnerTests.release.xcconfig */, + 27CB73AACA5743180CC6CD50 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + FF8CC09C83E12FC1C6EE6A8F /* [CP] Check Pods Manifest.lock */, + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 33CC10EC2044A3C60003C045 /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - DF80A3E256A63BF2D2008937 /* [CP] Check Pods Manifest.lock */, + AAFD86C9DC38B4393BC9D8E0 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 8D7CC24E5AE846869656D4D1 /* [CP] Embed Pods Frameworks */, + 529691D83C3BADE14E2EAC03 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -206,6 +271,10 @@ LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; 33CC10EC2044A3C60003C045 = { CreatedOnToolsVersion = 9.2; LastSwiftMigration = 1100; @@ -236,12 +305,20 @@ projectRoot = ""; targets = ( 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, 33CC111A2044C6BA0003C045 /* Flutter Assemble */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 33CC10EB2044A3C60003C045 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -256,6 +333,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -291,7 +369,7 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 8D7CC24E5AE846869656D4D1 /* [CP] Embed Pods Frameworks */ = { + 529691D83C3BADE14E2EAC03 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -308,7 +386,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - DF80A3E256A63BF2D2008937 /* [CP] Check Pods Manifest.lock */ = { + AAFD86C9DC38B4393BC9D8E0 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -330,9 +408,39 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + FF8CC09C83E12FC1C6EE6A8F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 33CC10E92044A3C60003C045 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -346,6 +454,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; @@ -366,6 +479,51 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BF5E76865ACB46314AC27D8F /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackWallet.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Stack Wallet.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stack_wallet"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 174539D042E7AC2AB25A83EB /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackWallet.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Stack Wallet.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stack_wallet"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 27CB73AACA5743180CC6CD50 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackWallet.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Stack Wallet.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/stack_wallet"; + }; + name = Profile; + }; 338D0CE9231458BD00FA5F75 /* Profile */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; @@ -404,9 +562,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; + ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + STRIP_INSTALLED_PRODUCT = NO; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; @@ -421,6 +581,30 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_monero\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_shared_external\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_wownero\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/desktop_drop\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/device_info_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_local_notifications\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_secure_storage_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/isar_flutter_libs\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/lelantus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/package_info_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/path_provider_foundation\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/share_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/window_size\"", + "\"${PROJECT_DIR}/../crypto_plugins/flutter_liblelantus/scripts/macos/mobileliblelantus\"", + ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -483,10 +667,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + STRIP_INSTALLED_PRODUCT = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -530,9 +715,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; + ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + STRIP_INSTALLED_PRODUCT = NO; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; @@ -547,6 +734,30 @@ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_monero\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_shared_external\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_wownero\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/desktop_drop\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/device_info_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_local_notifications\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_secure_storage_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/isar_flutter_libs\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/lelantus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/package_info_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/path_provider_foundation\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/share_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/window_size\"", + "\"${PROJECT_DIR}/../crypto_plugins/flutter_liblelantus/scripts/macos/mobileliblelantus\"", + ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -567,6 +778,30 @@ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_CONFIGURATION_BUILD_DIR}/ReachabilitySwift\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_monero\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_shared_external\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/cw_wownero\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/desktop_drop\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/device_info_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_local_notifications\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_secure_storage_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/isar_flutter_libs\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/lelantus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/package_info_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/path_provider_foundation\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/share_plus\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock_macos\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/window_size\"", + "\"${PROJECT_DIR}/../crypto_plugins/flutter_liblelantus/scripts/macos/mobileliblelantus\"", + ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -596,6 +831,16 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c9216c9be..a9d38bc3b 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -37,6 +37,17 @@ + + + + com.apple.security.network.server + com.apple.security.network.client + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift index 2722837ec..3cc05eb23 100644 --- a/macos/Runner/MainFlutterWindow.swift +++ b/macos/Runner/MainFlutterWindow.swift @@ -3,7 +3,7 @@ import FlutterMacOS class MainFlutterWindow: NSWindow { override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() + let flutterViewController = FlutterViewController() let windowFrame = self.frame self.contentViewController = flutterViewController self.setFrame(windowFrame, display: true) diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index 852fa1a47..48271acc9 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -4,5 +4,7 @@ com.apple.security.app-sandbox + com.apple.security.network.client + diff --git a/macos/RunnerTests/RunnerTests.swift b/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000..5418c9f53 --- /dev/null +++ b/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import FlutterMacOS +import Cocoa +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/pubspec.lock b/pubspec.lock index 2dcd3c598..e260cb994 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -226,10 +226,10 @@ packages: dependency: transitive description: name: cli_util - sha256: "66f86e916d285c1a93d3b79587d94bd71984a66aac4ff74e524cfa7877f1395c" + sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 url: "https://pub.dev" source: hosted - version: "0.3.5" + version: "0.4.0" clock: dependency: transitive description: @@ -584,10 +584,10 @@ packages: dependency: "direct dev" description: name: flutter_launcher_icons - sha256: ce0e501cfc258907842238e4ca605e74b7fd1cdf04b3b43e86c43f3e40a1592c + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" url: "https://pub.dev" source: hosted - version: "0.11.0" + version: "0.13.1" flutter_libepiccash: dependency: "direct main" description: @@ -646,10 +646,10 @@ packages: dependency: "direct main" description: name: flutter_native_splash - sha256: "6777a3abb974021a39b5fdd2d46a03ca390e03903b6351f21d10e7ecc969f12d" + sha256: ba45d8cfbd778478a74696b012f33ffb6b1760c9bc531b21e2964444a4870dae url: "https://pub.dev" source: hosted - version: "2.2.16" + version: "2.3.1" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -853,10 +853,10 @@ packages: dependency: transitive description: name: image - sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" + sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "4.0.17" import_sorter: dependency: "direct dev" description: @@ -948,7 +948,7 @@ packages: path: "crypto_plugins/flutter_liblelantus" relative: true source: path - version: "0.0.1" + version: "0.0.2" lints: dependency: transitive description: @@ -1349,62 +1349,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" - shared_preferences: - dependency: transitive - description: - name: shared_preferences - sha256: "16d3fb6b3692ad244a695c0183fca18cf81fd4b821664394a781de42386bf022" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - sha256: "6478c6bbbecfe9aced34c483171e90d7c078f5883558b30ec3163cf18402c749" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - shared_preferences_foundation: - dependency: transitive - description: - name: shared_preferences_foundation - sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb - url: "https://pub.dev" - source: hosted - version: "2.2.2" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d - url: "https://pub.dev" - source: hosted - version: "2.2.0" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173" - url: "https://pub.dev" - source: hosted - version: "2.2.0" shelf: dependency: transitive description: @@ -1631,10 +1575,10 @@ packages: dependency: transitive description: name: universal_io - sha256: "06866290206d196064fd61df4c7aea1ffe9a4e7c4ccaa8fcded42dd41948005d" + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.2" url_launcher: dependency: "direct main" description: @@ -1887,4 +1831,4 @@ packages: version: "1.0.0" sdks: dart: ">=3.0.2 <4.0.0" - flutter: ">=3.10.0" + flutter: ">=3.10.3" diff --git a/pubspec.yaml b/pubspec.yaml index fd5eac1dc..f5b4cf27b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Stack Wallet # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.7.15+181 +version: 1.7.16+182 environment: sdk: ">=3.0.2 <4.0.0" @@ -145,7 +145,7 @@ dev_dependencies: integration_test: sdk: flutter build_runner: ^2.1.7 - flutter_launcher_icons: ^0.11.0 + flutter_launcher_icons: ^0.13.1 hive_generator: ^2.0.0 dependency_validator: ^3.1.2 hive_test: ^1.0.1 @@ -157,7 +157,7 @@ dev_dependencies: flutter_lints: ^2.0.1 isar_generator: 3.0.5 -flutter_icons: +flutter_launcher_icons: android: true ios: true image_path: assets/icon/icon.png @@ -170,7 +170,7 @@ flutter_icons: icon_size: 48 # min:48, max:256, default: 48 macos: generate: true - image_path: assets/icon/icon.png + image_path: assets/icon/macos-icon.png flutter_native_splash: image: assets/images/splash.png diff --git a/scripts/macos/build_all.sh b/scripts/macos/build_all.sh new file mode 100755 index 000000000..646274593 --- /dev/null +++ b/scripts/macos/build_all.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +(cd ../../crypto_plugins/flutter_liblelantus/scripts/macos && ./build_all.sh ) & +(cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) & +(cd ../../crypto_plugins/flutter_libmonero/scripts/macos/ && ./build_all.sh ) & + +wait +echo "Done building" \ No newline at end of file diff --git a/test/services/coins/firo/firo_wallet_test.dart b/test/services/coins/firo/firo_wallet_test.dart index 68db15437..05b4aeeff 100644 --- a/test/services/coins/firo/firo_wallet_test.dart +++ b/test/services/coins/firo/firo_wallet_test.dart @@ -11,9 +11,7 @@ import 'package:mockito/mockito.dart'; import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart'; import 'package:stackwallet/electrumx_rpc/electrumx.dart'; -import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'; -import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; -import 'package:stackwallet/models/lelantus_coin.dart'; +import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/lelantus_fee_data.dart'; import 'package:stackwallet/models/paymint/transactions_model.dart' as old; import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; @@ -72,6 +70,7 @@ void main() { setData, List.from(usedSerials), firoNetwork, + "walletId", ); const currentHeight = 100000000000; @@ -113,10 +112,7 @@ void main() { final result = await staticProcessRestore(txData, message, currentHeight); expect(result, isA>()); - expect(result["mintIndex"], 8); - expect(result["jindex"], [2, 4, 6]); - expect( - result["_lelantus_coins"], isA>>()); + expect(result["_lelantus_coins"], isA>()); expect(result["newTxMap"], isA>()); }); @@ -133,6 +129,7 @@ void main() { setData, List.from(usedSerials), firoNetwork, + "walletId", ), throwsA(isA())); }); @@ -530,18 +527,10 @@ void main() { group("FiroWallet service class functions that depend on shared storage", () { const testWalletId = "testWalletID"; const testWalletName = "Test Wallet"; - bool hiveAdaptersRegistered = false; setUp(() async { await setUpTestHive(); - if (!hiveAdaptersRegistered) { - hiveAdaptersRegistered = true; - - // Registering Lelantus Model Adapters - Hive.registerAdapter(LelantusCoinAdapter()); - } - final wallets = await Hive.openBox('wallets'); await wallets.put('currentWalletName', testWalletName); }); @@ -1202,13 +1191,33 @@ void main() { txHash: BuildMintTxTestParams.utxoInfo["txid"] as String, coin: Coin.firo, )).thenAnswer((_) async => BuildMintTxTestParams.cachedClientResponse); + when(cachedClient.getAnonymitySet( + groupId: "1", + coin: Coin.firo, + )).thenAnswer( + (_) async => GetAnonymitySetSampleData.data, + ); + when(cachedClient.getAnonymitySet( + groupId: "2", + coin: Coin.firo, + )).thenAnswer( + (_) async => GetAnonymitySetSampleData.data, + ); when(client.getBlockHeadTip()).thenAnswer( (_) async => {"height": 455873, "hex": "this value not used here"}); + when(client.getLatestCoinId()).thenAnswer((_) async => 2); when(mainDB.getAddress("${testWalletId}buildMintTransaction", any)) .thenAnswer((realInvocation) async => null); + when(mainDB.getHighestUsedMintIndex( + walletId: "${testWalletId}submitHexToNetwork")) + .thenAnswer((_) async => null); + when(mainDB.getHighestUsedMintIndex( + walletId: "testWalletIDbuildMintTransaction")) + .thenAnswer((_) async => null); + final firo = FiroWallet( walletName: testWalletName, walletId: "${testWalletId}buildMintTransaction", diff --git a/test/services/coins/firo/firo_wallet_test.mocks.dart b/test/services/coins/firo/firo_wallet_test.mocks.dart index 95d1750e9..2365f83b1 100644 --- a/test/services/coins/firo/firo_wallet_test.mocks.dart +++ b/test/services/coins/firo/firo_wallet_test.mocks.dart @@ -1151,4 +1151,14 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override + _i5.Future getHighestUsedMintIndex({required String? walletId}) => + (super.noSuchMethod( + Invocation.method( + #getHighestUsedMintIndex, + [], + {#walletId: walletId}, + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); } diff --git a/test/services/coins/manager_test.mocks.dart b/test/services/coins/manager_test.mocks.dart index 8b48a38a9..5656feb88 100644 --- a/test/services/coins/manager_test.mocks.dart +++ b/test/services/coins/manager_test.mocks.dart @@ -14,7 +14,6 @@ import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart' as _i5; import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i4; import 'package:stackwallet/models/balance.dart' as _i6; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i13; -import 'package:stackwallet/models/lelantus_coin.dart' as _i15; import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i3; import 'package:stackwallet/models/signing_data.dart' as _i14; import 'package:stackwallet/services/coins/firo/firo_wallet.dart' as _i10; @@ -589,15 +588,6 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); @override - List> getLelantusCoinMap() => - (super.noSuchMethod( - Invocation.method( - #getLelantusCoinMap, - [], - ), - returnValue: >[], - ) as List>); - @override _i11.Future anonymizeAllPublicFunds() => (super.noSuchMethod( Invocation.method( #anonymizeAllPublicFunds, @@ -755,15 +745,6 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); @override - _i11.Future getCoinsToJoinSplit(int? required) => - (super.noSuchMethod( - Invocation.method( - #getCoinsToJoinSplit, - [required], - ), - returnValue: _i11.Future.value(), - ) as _i11.Future); - @override _i11.Future estimateJoinSplitFee(int? spendAmount) => (super.noSuchMethod( Invocation.method( @@ -1061,51 +1042,6 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { ), returnValueForMissingStub: null, ); - @override - void initFiroHive(String? walletId) => super.noSuchMethod( - Invocation.method( - #initFiroHive, - [walletId], - ), - returnValueForMissingStub: null, - ); - @override - _i11.Future firoUpdateJIndex(List? jIndex) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateJIndex, - [jIndex], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); - @override - _i11.Future firoUpdateLelantusCoins(List? lelantusCoins) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateLelantusCoins, - [lelantusCoins], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); - @override - int firoGetMintIndex() => (super.noSuchMethod( - Invocation.method( - #firoGetMintIndex, - [], - ), - returnValue: 0, - ) as int); - @override - _i11.Future firoUpdateMintIndex(int? mintIndex) => (super.noSuchMethod( - Invocation.method( - #firoUpdateMintIndex, - [mintIndex], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); } /// A class which mocks [ElectrumX]. diff --git a/test/widget_tests/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index 4fc6f8832..35e8a2560 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -1602,15 +1602,6 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet { returnValueForMissingStub: _i19.Future.value(), ) as _i19.Future); @override - List> getLelantusCoinMap() => - (super.noSuchMethod( - Invocation.method( - #getLelantusCoinMap, - [], - ), - returnValue: >[], - ) as List>); - @override _i19.Future anonymizeAllPublicFunds() => (super.noSuchMethod( Invocation.method( #anonymizeAllPublicFunds, @@ -1768,15 +1759,6 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet { returnValueForMissingStub: _i19.Future.value(), ) as _i19.Future); @override - _i19.Future getCoinsToJoinSplit(int? required) => - (super.noSuchMethod( - Invocation.method( - #getCoinsToJoinSplit, - [required], - ), - returnValue: _i19.Future.value(), - ) as _i19.Future); - @override _i19.Future estimateJoinSplitFee(int? spendAmount) => (super.noSuchMethod( Invocation.method( @@ -2075,51 +2057,6 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet { ), returnValueForMissingStub: null, ); - @override - void initFiroHive(String? walletId) => super.noSuchMethod( - Invocation.method( - #initFiroHive, - [walletId], - ), - returnValueForMissingStub: null, - ); - @override - _i19.Future firoUpdateJIndex(List? jIndex) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateJIndex, - [jIndex], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future firoUpdateLelantusCoins(List? lelantusCoins) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateLelantusCoins, - [lelantusCoins], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - int firoGetMintIndex() => (super.noSuchMethod( - Invocation.method( - #firoGetMintIndex, - [], - ), - returnValue: 0, - ) as int); - @override - _i19.Future firoUpdateMintIndex(int? mintIndex) => (super.noSuchMethod( - Invocation.method( - #firoUpdateMintIndex, - [mintIndex], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); } /// A class which mocks [LocaleService]. @@ -3559,4 +3496,14 @@ class MockMainDB extends _i1.Mock implements _i14.MainDB { returnValue: _i19.Future.value(), returnValueForMissingStub: _i19.Future.value(), ) as _i19.Future); + @override + _i19.Future getHighestUsedMintIndex({required String? walletId}) => + (super.noSuchMethod( + Invocation.method( + #getHighestUsedMintIndex, + [], + {#walletId: walletId}, + ), + returnValue: _i19.Future.value(), + ) as _i19.Future); }