From 141a7ebfcafa2550ae867616db8c7b8b0f7b0b75 Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Tue, 28 Jan 2025 23:53:43 +0200 Subject: [PATCH] v4.23.0 release candidate (#1974) * v4.23.0 release candidate * - Fix restoring zano from QR - Fix Zano confirmations count - Fix birdpay - Fix balance display * Fix Zano assets showing amount before they are added * - handle fetching token data while the API is busy - potential fix for duplicate transactions * fix receive confirmations, maybe * revert onChangeWallet cleanup * Fix confirmations not updating * improve zano wallet opening, fix CI commands and messages on slack (#1979) Co-authored-by: Omar Hatem * Cache wallet when creating/restoring as well * - hardcode Trocador Maximum limit for Zano temporarily - Configure Cake Zano node to use SSL * reformatting [skip ci] * revert to non-ssl * update build numbers [skip ci] * disable zano for desktop [skip ci] --------- Co-authored-by: cyan --- .github/workflows/pr_test_build_android.yml | 13 +- .github/workflows/pr_test_build_linux.yml | 19 +- assets/text/Monerocom_Release_Notes.txt | 2 + assets/text/Release_Notes.txt | 5 +- assets/zano_node_list.yml | 3 + cw_core/lib/node.dart | 57 ++++-- cw_monero/pubspec.lock | 4 +- cw_wownero/pubspec.lock | 4 +- cw_zano/lib/api/model/transfer.dart | 105 ++++++----- .../lib/model/pending_zano_transaction.dart | 4 +- cw_zano/lib/model/zano_transaction_info.dart | 21 ++- cw_zano/lib/zano_formatter.dart | 6 +- cw_zano/lib/zano_wallet.dart | 169 ++++++++++-------- cw_zano/lib/zano_wallet_addresses.dart | 3 +- cw_zano/lib/zano_wallet_api.dart | 116 ++++++------ cw_zano/lib/zano_wallet_service.dart | 3 +- lib/core/address_validator.dart | 4 +- lib/di.dart | 2 +- lib/entities/default_settings_migration.dart | 2 +- lib/entities/parse_address_from_domain.dart | 12 +- lib/entities/zano_alias.dart | 2 +- .../provider/trocador_exchange_provider.dart | 5 +- .../pages/balance/crypto_balance_widget.dart | 2 +- .../dashboard/pages/transactions_page.dart | 1 - .../dashboard/widgets/transaction_raw.dart | 2 - .../screens/wallet_keys/wallet_keys_page.dart | 6 +- lib/store/app_store.dart | 2 - .../dashboard/balance_view_model.dart | 19 +- .../dashboard/dashboard_view_model.dart | 120 ++++++++++++- .../dashboard/transaction_list_item.dart | 5 + .../restore/wallet_restore_from_qr_code.dart | 11 +- lib/view_model/wallet_keys_view_model.dart | 2 - lib/view_model/wallet_new_vm.dart | 7 +- lib/view_model/wallet_restore_view_model.dart | 2 - lib/zano/cw_zano.dart | 4 +- scripts/android/app_env.sh | 8 +- scripts/ios/app_env.sh | 8 +- scripts/linux/app_env.sh | 4 +- scripts/macos/app_config.sh | 2 +- scripts/macos/app_env.sh | 8 +- scripts/windows/build_exe_installer.iss | 2 +- tool/configure.dart | 2 +- 42 files changed, 472 insertions(+), 306 deletions(-) diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml index 32daf8abf..99dce1125 100644 --- a/.github/workflows/pr_test_build_android.yml +++ b/.github/workflows/pr_test_build_android.yml @@ -34,10 +34,19 @@ jobs: - name: Fix github actions messing up $HOME... run: 'echo HOME=/root | sudo tee -a $GITHUB_ENV' - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} - name: configure git run: | + git config --global --add safe.directory '*' git config --global user.email "ci@cakewallet.com" git config --global user.name "CakeWallet CI" + - name: Get the full commit message + run: | + FULL_MESSAGE="$(git log -1 --pretty=%B)" + echo "message<> $GITHUB_ENV + echo "$FULL_MESSAGE" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV - name: Add secrets run: | touch lib/.secrets.g.dart @@ -274,14 +283,14 @@ jobs: echo "APK_FILE=$apk_file" >> $GITHUB_ENV - name: Upload artifact to slack - if: ${{ !contains(github.event.head_commit.message, 'skip slack') }} + if: ${{ !contains(env.message, 'skip slack') }} continue-on-error: true uses: adrey/slack-file-upload-action@1.0.5 with: token: ${{ secrets.SLACK_APP_TOKEN }} path: ${{ env.APK_FILE }} channel: ${{ secrets.SLACK_APK_CHANNEL }} - initial_comment: ${{ github.event.head_commit.message }} + initial_comment: ${{ env.message }} - name: cleanup run: rm -rf build/app/outputs/flutter-apk/test-apk/ diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml index fc345e240..d11625e0f 100644 --- a/.github/workflows/pr_test_build_linux.yml +++ b/.github/workflows/pr_test_build_linux.yml @@ -30,10 +30,19 @@ jobs: - name: Fix github actions messing up $HOME... run: 'echo HOME=/root | sudo tee -a $GITHUB_ENV' - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} - name: configure git run: | + git config --global --add safe.directory '*' git config --global user.email "ci@cakewallet.com" git config --global user.name "CakeWallet CI" + - name: Get the full commit message + run: | + FULL_MESSAGE="$(git log -1 --pretty=%B)" + echo "message<> $GITHUB_ENV + echo "$FULL_MESSAGE" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV - name: Add secrets run: | touch lib/.secrets.g.dart @@ -231,7 +240,7 @@ jobs: name: cakewallet_linux - name: Prepare virtual desktop - if: ${{ contains(github.event.head_commit.message, 'run tests') }} + if: ${{ contains(env.message, 'run tests') }} run: | nohup Xvfb :99 -screen 0 720x1280x16 & echo DISPLAY=:99 | sudo tee -a $GITHUB_ENV @@ -247,28 +256,28 @@ jobs: # isn't much in those wallets anyway, we still wouldn't like to leak it to anyone who is able to access github. - name: Test [confirm_seeds_flow_test] - if: ${{ contains(github.event.head_commit.message, 'run tests') }} + if: ${{ contains(env.message, 'run tests') }} timeout-minutes: 20 run: | xmessage -timeout 30 "confirm_seeds_flow_test" & rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet exec timeout --signal=SIGKILL 900 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites/confirm_seeds_flow_test.dart - name: Test [create_wallet_flow_test] - if: ${{ contains(github.event.head_commit.message, 'run tests') }} + if: ${{ contains(env.message, 'run tests') }} timeout-minutes: 20 run: | xmessage -timeout 30 "create_wallet_flow_test" & rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet exec timeout --signal=SIGKILL 900 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites/create_wallet_flow_test.dart - name: Test [exchange_flow_test] - if: ${{ contains(github.event.head_commit.message, 'run tests') }} + if: ${{ contains(env.message, 'run tests') }} timeout-minutes: 20 run: | xmessage -timeout 30 "exchange_flow_test" & rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet exec timeout --signal=SIGKILL 900 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites/exchange_flow_test.dart - name: Test [restore_wallet_through_seeds_flow_test] - if: ${{ contains(github.event.head_commit.message, 'run tests') }} + if: ${{ contains(env.message, 'run tests') }} timeout-minutes: 20 run: | xmessage -timeout 30 "restore_wallet_through_seeds_flow_test" & diff --git a/assets/text/Monerocom_Release_Notes.txt b/assets/text/Monerocom_Release_Notes.txt index 09092a8df..1d8d05469 100644 --- a/assets/text/Monerocom_Release_Notes.txt +++ b/assets/text/Monerocom_Release_Notes.txt @@ -1,2 +1,4 @@ +Monero restore with passphrase support +Add Chainflip Exchange provider UI enhancements Bug fixes \ No newline at end of file diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt index 8abed72a2..e85cecc77 100644 --- a/assets/text/Release_Notes.txt +++ b/assets/text/Release_Notes.txt @@ -1,4 +1,5 @@ -Bitcoin and Litecoin enhancements -Solana and Nano fixes/improvements +Add Zano Wallet +Monero/Wownero/Zano restore with passphrase support +Add Chainflip Exchange provider UI enhancements Bug fixes \ No newline at end of file diff --git a/assets/zano_node_list.yml b/assets/zano_node_list.yml index 9ee602d82..f7b874fcb 100644 --- a/assets/zano_node_list.yml +++ b/assets/zano_node_list.yml @@ -1,4 +1,7 @@ - uri: 37.27.100.59:10500 + useSSL: false +- + uri: zano.cakewallet.com:11211 is_default: true useSSL: false \ No newline at end of file diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart index 780e7143f..c984cd03b 100644 --- a/cw_core/lib/node.dart +++ b/cw_core/lib/node.dart @@ -102,10 +102,9 @@ class Node extends HiveObject with Keyable { case WalletType.polygon: case WalletType.solana: case WalletType.tron: - return Uri.parse( - "http${isSSL ? "s" : ""}://$uriRaw${path!.startsWith("/") ? path : "/$path"}"); case WalletType.zano: - return Uri.https(uriRaw, ''); + return Uri.parse( + "http${isSSL ? "s" : ""}://$uriRaw${path!.startsWith("/") || path!.isEmpty ? path : "/$path"}"); case WalletType.none: throw Exception('Unexpected type ${type.toString()} for Node uri'); } @@ -177,7 +176,33 @@ class Node extends HiveObject with Keyable { } Future requestZanoNode() async { - return requestMoneroNode(methodName: "getinfo"); + final path = '/json_rpc'; + final rpcUri = isSSL ? Uri.https(uri.authority, path) : Uri.http(uri.authority, path); + final body = {'jsonrpc': '2.0', 'id': '0', 'method': "getinfo"}; + + try { + final authenticatingClient = HttpClient(); + authenticatingClient.badCertificateCallback = + ((X509Certificate cert, String host, int port) => true); + + final http.Client client = ioc.IOClient(authenticatingClient); + + final jsonBody = json.encode(body); + + final response = await client.post( + rpcUri, + headers: {'Content-Type': 'application/json'}, + body: jsonBody, + ); + + printV("node check response: ${response.body}"); + + final resBody = json.decode(response.body) as Map; + return resBody['result']['height'] != null; + } catch (e) { + printV("error: $e"); + return false; + } } Future requestMoneroNode({String methodName = 'get_info'}) async { @@ -185,7 +210,6 @@ class Node extends HiveObject with Keyable { return await requestNodeWithProxy(); } - final path = '/json_rpc'; final rpcUri = isSSL ? Uri.https(uri.authority, path) : Uri.http(uri.authority, path); final body = {'jsonrpc': '2.0', 'id': '0', 'method': methodName}; @@ -195,7 +219,6 @@ class Node extends HiveObject with Keyable { authenticatingClient.badCertificateCallback = ((X509Certificate cert, String host, int port) => true); - final http.Client client = ioc.IOClient(authenticatingClient); final jsonBody = json.encode(body); @@ -209,13 +232,13 @@ class Node extends HiveObject with Keyable { if (response.statusCode == 401) { final daemonRpc = DaemonRpc( rpcUri.toString(), - username: login??'', - password: password??'', + username: login ?? '', + password: password ?? '', ); final response = await daemonRpc.call('get_info', {}); return !(response['offline'] as bool); } - + printV("node check response: ${response.body}"); if ((response.body.contains("400 Bad Request") // Some other generic error @@ -298,10 +321,7 @@ class Node extends HiveObject with Keyable { try { final response = await http.post( uri, - headers: { - "Content-Type": "application/json", - "nano-app": "cake-wallet" - }, + headers: {"Content-Type": "application/json", "nano-app": "cake-wallet"}, body: jsonEncode( { "action": "account_balance", @@ -407,8 +427,7 @@ class DigestAuth { } /// Helper to format the nonce count. - String _formatNonceCount(int count) => - count.toRadixString(16).padLeft(8, '0'); + String _formatNonceCount(int count) => count.toRadixString(16).padLeft(8, '0'); /// Compute the MD5 hash of a string. String md5Hash(String input) { @@ -424,8 +443,7 @@ class DaemonRpc { DaemonRpc(this.rpcUrl, {required this.username, required this.password}); /// Perform a JSON-RPC call with Digest Authentication. - Future> call( - String method, Map params) async { + Future> call(String method, Map params) async { final http.Client client = http.Client(); final DigestAuth digestAuth = DigestAuth(username, password); @@ -475,11 +493,12 @@ class DaemonRpc { throw Exception('RPC call failed: ${authenticatedResponse.body}'); } - final Map result = jsonDecode(authenticatedResponse.body) as Map; + final Map result = + jsonDecode(authenticatedResponse.body) as Map; if (result['error'] != null) { throw Exception('RPC Error: ${result['error']}'); } return result['result'] as Map; } -} \ No newline at end of file +} diff --git a/cw_monero/pubspec.lock b/cw_monero/pubspec.lock index a44f8264b..a6008cbea 100644 --- a/cw_monero/pubspec.lock +++ b/cw_monero/pubspec.lock @@ -511,8 +511,8 @@ packages: dependency: "direct main" description: path: "impls/monero.dart" - ref: 9526921acb774b523a2e1d9ba9a7b389acfc6b70 - resolved-ref: 9526921acb774b523a2e1d9ba9a7b389acfc6b70 + ref: "9526921acb774b523a2e1d9ba9a7b389acfc6b70" + resolved-ref: "9526921acb774b523a2e1d9ba9a7b389acfc6b70" url: "https://github.com/mrcyjanek/monero_c" source: git version: "0.0.0" diff --git a/cw_wownero/pubspec.lock b/cw_wownero/pubspec.lock index 530a20b01..b836e83ee 100644 --- a/cw_wownero/pubspec.lock +++ b/cw_wownero/pubspec.lock @@ -471,8 +471,8 @@ packages: dependency: "direct main" description: path: "impls/monero.dart" - ref: 9526921acb774b523a2e1d9ba9a7b389acfc6b70 - resolved-ref: 9526921acb774b523a2e1d9ba9a7b389acfc6b70 + ref: "9526921acb774b523a2e1d9ba9a7b389acfc6b70" + resolved-ref: "9526921acb774b523a2e1d9ba9a7b389acfc6b70" url: "https://github.com/mrcyjanek/monero_c" source: git version: "0.0.0" diff --git a/cw_zano/lib/api/model/transfer.dart b/cw_zano/lib/api/model/transfer.dart index 1d46ca8b3..594490a4f 100644 --- a/cw_zano/lib/api/model/transfer.dart +++ b/cw_zano/lib/api/model/transfer.dart @@ -1,12 +1,10 @@ +import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/zano_asset.dart'; import 'package:cw_zano/api/model/employed_entries.dart'; import 'package:cw_zano/api/model/subtransfer.dart'; import 'package:collection/collection.dart'; -import 'package:cw_zano/model/zano_asset.dart'; import 'package:cw_zano/model/zano_transaction_info.dart'; -import 'package:cw_zano/zano_formatter.dart'; import 'package:cw_zano/zano_wallet.dart'; -import 'package:cw_zano/zano_wallet_api.dart'; class Transfer { final String comment; @@ -49,78 +47,87 @@ class Transfer { required this.unlockTime, }); - factory Transfer.fromJson(Map json) => Transfer( + factory Transfer.fromJson(Map json) => + Transfer( comment: json['comment'] as String? ?? '', - employedEntries: EmployedEntries.fromJson(json['employed_entries'] as Map? ?? {}), + employedEntries: EmployedEntries.fromJson( + json['employed_entries'] as Map? ?? {}), fee: json['fee'] as int? ?? 0, height: json['height'] as int? ?? 0, isMining: json['is_mining'] as bool? ?? false, isMixing: json['is_mixing'] as bool? ?? false, isService: json['is_service'] as bool? ?? false, paymentId: json['payment_id'] as String? ?? '', - remoteAddresses: json['remote_addresses'] == null ? [] : (json['remote_addresses'] as List).cast(), - remoteAliases: json['remote_aliases'] == null ? [] : (json['remote_aliases'] as List).cast(), + remoteAddresses: json['remote_addresses'] == null ? [] : (json['remote_addresses'] as List< + dynamic>).cast(), + remoteAliases: json['remote_aliases'] == null ? [] : (json['remote_aliases'] as List< + dynamic>).cast(), showSender: json['show_sender'] as bool? ?? false, - subtransfers: (json['subtransfers'] as List? ?? []).map((e) => Subtransfer.fromJson(e as Map)).toList(), + subtransfers: (json['subtransfers'] as List? ?? []).map((e) => + Subtransfer.fromJson(e as Map)).toList(), timestamp: json['timestamp'] as int? ?? 0, transferInternalIndex: json['transfer_internal_index'] == null ? 0 : json['transfer_internal_index'] is double - ? (json['transfer_internal_index'] as double).toInt() - : json['transfer_internal_index'] as int, + ? (json['transfer_internal_index'] as double).toInt() + : json['transfer_internal_index'] as int, txBlobSize: json['tx_blob_size'] as int? ?? 0, txHash: json['tx_hash'] as String? ?? '', txType: json['tx_type'] as int? ?? 0, unlockTime: json['unlock_time'] as int? ?? 0, ); - static Map makeMap(List transfers, Map zanoAssets, int currentDaemonHeight) { + static Map makeMap(List transfers, + Map zanoAssets, int currentDaemonHeight) { return Map.fromIterable( - transfers, - key: (item) => (item as Transfer).txHash, - value: (transfer) { - transfer as Transfer; - // Simple (only one subtransfer OR two subtransfers and the second is Zano, outgoing and amount equals to fee) or complex? - Subtransfer? single = transfer.subtransfers.singleOrNull; - if (transfer.subtransfers.length == 2) { - final zano = transfer.subtransfers.firstWhereOrNull((element) => element.assetId == ZanoWalletBase.zanoAssetId); - if (zano != null && !zano.isIncome && zano.amount == BigInt.from(transfer.fee)) { - single = transfer.subtransfers.firstWhere((element) => element.assetId != ZanoWalletBase.zanoAssetId); - } + transfers, + key: (item) => (item as Transfer).txHash, + value: (transfer) { + transfer as Transfer; + // Simple (only one subtransfer OR two subtransfers and the second is Zano, outgoing and amount equals to fee) or complex? + Subtransfer? single = transfer.subtransfers.singleOrNull; + if (transfer.subtransfers.length == 2) { + final zano = transfer.subtransfers.firstWhereOrNull((element) => + element.assetId == ZanoWalletBase.zanoAssetId); + if (zano != null && !zano.isIncome && zano.amount == BigInt.from(transfer.fee)) { + single = transfer.subtransfers.firstWhere((element) => element.assetId != + ZanoWalletBase.zanoAssetId); } - bool isSimple = single != null; - // TODO: for complex transactions we show zano or any other transaction, will fix it later - if (!isSimple) { - single = - transfer.subtransfers.firstWhereOrNull((element) => element.assetId == ZanoWalletBase.zanoAssetId) ?? transfer.subtransfers.first; + } + bool isSimple = single != null; + // TODO: for complex transactions we show zano or any other transaction, will fix it later + if (!isSimple) { + single = + transfer.subtransfers.firstWhereOrNull((element) => + element.assetId == ZanoWalletBase.zanoAssetId) ?? transfer.subtransfers.first; + } + if (single.assetId != ZanoWalletBase.zanoAssetId) { + final asset = zanoAssets[single.assetId]; + if (asset == null) { + printV('unknown asset ${single.assetId}'); } - if (single.assetId != ZanoWalletBase.zanoAssetId) { - final asset = zanoAssets[single.assetId]; - if (asset == null) { - ZanoWalletApi.error('unknown asset ${single.assetId}'); - } - final ticker = asset == null ? '***' : asset.ticker; - final decimalPoint = asset == null ? ZanoFormatter.defaultDecimalPoint : asset.decimalPoint; - return ZanoTransactionInfo.fromTransfer( - transfer, - confirmations: currentDaemonHeight - transfer.height, - isIncome: single.isIncome, - assetId: single.assetId, - amount: single.amount, - tokenSymbol: isSimple ? ticker : '*${ticker}', - decimalPoint: decimalPoint, - ); - } - final amount = single.isIncome ? single.amount : single.amount - BigInt.from(transfer.fee); + final ticker = asset == null ? '***' : asset.ticker; + final decimalPoint = asset == null ? 0 : asset.decimalPoint; return ZanoTransactionInfo.fromTransfer( transfer, confirmations: currentDaemonHeight - transfer.height, isIncome: single.isIncome, assetId: single.assetId, - amount: amount, - tokenSymbol: isSimple ? 'ZANO' : '*ZANO', + amount: single.amount, + tokenSymbol: isSimple ? ticker : '*${ticker}', + decimalPoint: decimalPoint, ); - }, - ); + } + final amount = single.isIncome ? single.amount : single.amount - BigInt.from(transfer.fee); + return ZanoTransactionInfo.fromTransfer( + transfer, + confirmations: currentDaemonHeight - transfer.height, + isIncome: single.isIncome, + assetId: single.assetId, + amount: amount, + tokenSymbol: isSimple ? 'ZANO' : '*ZANO', + ); + }, + ); } } diff --git a/cw_zano/lib/model/pending_zano_transaction.dart b/cw_zano/lib/model/pending_zano_transaction.dart index 001ec2cd1..719c370a1 100644 --- a/cw_zano/lib/model/pending_zano_transaction.dart +++ b/cw_zano/lib/model/pending_zano_transaction.dart @@ -32,10 +32,10 @@ class PendingZanoTransaction with PendingTransaction { String get hex => ''; @override - String get amountFormatted => '${ZanoFormatter.bigIntAmountToString(amount, decimalPoint)} $ticker'; + String get amountFormatted => ZanoFormatter.bigIntAmountToString(amount, decimalPoint); @override - String get feeFormatted => '${ZanoFormatter.bigIntAmountToString(fee)} ZANO'; + String get feeFormatted => ZanoFormatter.bigIntAmountToString(fee); TransferResult? transferResult; diff --git a/cw_zano/lib/model/zano_transaction_info.dart b/cw_zano/lib/model/zano_transaction_info.dart index 1e44da674..d643e9207 100644 --- a/cw_zano/lib/model/zano_transaction_info.dart +++ b/cw_zano/lib/model/zano_transaction_info.dart @@ -23,11 +23,11 @@ class ZanoTransactionInfo extends TransactionInfo { ZanoTransactionInfo.fromTransfer(Transfer transfer, {required int confirmations, - required bool isIncome, - required String assetId, - required BigInt amount, - this.tokenSymbol = 'ZANO', - this.decimalPoint = ZanoFormatter.defaultDecimalPoint}) + required bool isIncome, + required String assetId, + required BigInt amount, + this.tokenSymbol = 'ZANO', + this.decimalPoint = ZanoFormatter.defaultDecimalPoint}) : id = transfer.txHash, height = transfer.height, direction = isIncome ? TransactionDirection.incoming : TransactionDirection.outgoing, @@ -36,14 +36,18 @@ class ZanoTransactionInfo extends TransactionInfo { amount = amount.isValidInt ? amount.toInt() : 0, fee = transfer.fee, confirmations = confirmations, - isPending = false, - recipientAddress = transfer.remoteAddresses.isNotEmpty ? transfer.remoteAddresses.first : '' { + isPending = confirmations < 10, + recipientAddress = transfer.remoteAddresses.isNotEmpty + ? transfer.remoteAddresses.first + : '' { additionalInfo = { 'comment': transfer.comment, 'assetId': assetId, }; } + String get assetId => additionalInfo["assetId"] as String; + set assetId(String newId) => additionalInfo["assetId"] = newId; final String id; final int height; @@ -61,7 +65,8 @@ class ZanoTransactionInfo extends TransactionInfo { String? key; @override - String amountFormatted() => '${formatAmount(ZanoFormatter.bigIntAmountToString(zanoAmount, decimalPoint))} $tokenSymbol'; + String amountFormatted() => + '${formatAmount(ZanoFormatter.bigIntAmountToString(zanoAmount, decimalPoint))} $tokenSymbol'; @override String fiatAmount() => _fiatAmount ?? ''; diff --git a/cw_zano/lib/zano_formatter.dart b/cw_zano/lib/zano_formatter.dart index ffc5d20f3..e20e3a5f7 100644 --- a/cw_zano/lib/zano_formatter.dart +++ b/cw_zano/lib/zano_formatter.dart @@ -1,5 +1,6 @@ import 'dart:math'; +import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_zano/zano_wallet_api.dart'; import 'package:decimal/decimal.dart'; import 'package:decimal/intl.dart'; @@ -32,6 +33,9 @@ class ZanoFormatter { } static String bigIntAmountToString(BigInt amount, [int decimalPoint = defaultDecimalPoint]) { + if (decimalPoint == 0) { + return '0'; + } final numberFormat = NumberFormat()..maximumFractionDigits = decimalPoint ..minimumFractionDigits = 1; return numberFormat.format( @@ -66,7 +70,7 @@ class ZanoFormatter { } else if (d == null) { return BigInt.zero; } else { - ZanoWalletApi.error('cannot cast value of type ${d.runtimeType} to BigInt'); + printV(('cannot cast value of type ${d.runtimeType} to BigInt')); throw 'cannot cast value of type ${d.runtimeType} to BigInt'; //return BigInt.zero; } diff --git a/cw_zano/lib/zano_wallet.dart b/cw_zano/lib/zano_wallet.dart index bc9eb6188..b2283b082 100644 --- a/cw_zano/lib/zano_wallet.dart +++ b/cw_zano/lib/zano_wallet.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:core'; import 'dart:io'; import 'dart:math'; @@ -43,7 +44,7 @@ abstract class ZanoWalletBase extends WalletBase with Store, ZanoWalletApi { static const int _autoSaveIntervalSeconds = 30; - static const int _pollIntervalMilliseconds = 2000; + static const int _pollIntervalMilliseconds = 5000; static const int _maxLoadAssetsRetries = 5; @override @@ -152,19 +153,27 @@ abstract class ZanoWalletBase static Future open( {required String name, required String password, required WalletInfo walletInfo}) async { final path = await pathForWallet(name: name, type: walletInfo.type); - final wallet = ZanoWallet(walletInfo, password); - await wallet.initWallet(); - final createWalletResult = await wallet.loadWallet(path, password); - await wallet.initWallet(); - await wallet.parseCreateWalletResult(createWalletResult); - await wallet.init(createWalletResult.wi.address); - return wallet; + if (ZanoWalletApi.openWalletCache[path] != null) { + final wallet = ZanoWallet(walletInfo, password); + await wallet.parseCreateWalletResult(ZanoWalletApi.openWalletCache[path]!).then((_) { + unawaited(wallet.init(ZanoWalletApi.openWalletCache[path]!.wi.address)); + }); + return wallet; + } else { + final wallet = ZanoWallet(walletInfo, password); + await wallet.initWallet(); + final createWalletResult = await wallet.loadWallet(path, password); + await wallet.parseCreateWalletResult(createWalletResult).then((_) { + unawaited(wallet.init(createWalletResult.wi.address)); + }); + return wallet; + } } Future parseCreateWalletResult(CreateWalletResult result) async { hWallet = result.walletId; seed = result.seed; - ZanoWalletApi.info('setting hWallet = ${result.walletId}'); + printV('setting hWallet = ${result.walletId}'); walletAddresses.address = result.wi.address; await loadAssets(result.wi.balances, maxRetries: _maxLoadAssetsRetries); for (final item in result.wi.balances) { @@ -185,7 +194,7 @@ abstract class ZanoWalletBase @override Future close({bool shouldCleanup = true}) async { - closeWallet(); + closeWallet(null); _updateSyncInfoTimer?.cancel(); _autoSaveTimer?.cancel(); } @@ -285,7 +294,7 @@ abstract class ZanoWalletBase } while (result.lastItemIndex + 1 < result.totalTransfers); return Transfer.makeMap(transfers, zanoAssets, currentDaemonHeight); } catch (e) { - ZanoWalletApi.error(e.toString()); + printV((e.toString())); return {}; } } @@ -332,7 +341,7 @@ abstract class ZanoWalletBase await store(); await walletAddresses.updateAddressesInBox(); } catch (e) { - ZanoWalletApi.error('Error while saving Zano wallet file ${e.toString()}'); + printV(('Error while saving Zano wallet file ${e.toString()}')); } } @@ -349,7 +358,7 @@ abstract class ZanoWalletBase retryCount++; await Future.delayed(Duration(seconds: 1)); } else { - ZanoWalletApi.error('failed to load assets after $retryCount retries'); + printV(('failed to load assets after $retryCount retries')); break; } } @@ -371,65 +380,10 @@ abstract class ZanoWalletBase _lastKnownBlockHeight = 0; _initialSyncHeight = 0; _updateSyncInfoTimer ??= - Timer.periodic(Duration(milliseconds: _pollIntervalMilliseconds), (_) async { - GetWalletStatusResult walletStatus; - // ignoring get wallet status exception (in case of wrong wallet id) - try { - walletStatus = await getWalletStatus(); - } on ZanoWalletException { - return; - } - currentDaemonHeight = walletStatus.currentDaemonHeight; - _updateSyncProgress(walletStatus); - - // we can call getWalletInfo ONLY if getWalletStatus returns NOT is in long refresh and wallet state is 2 (ready) - if (!walletStatus.isInLongRefresh && walletStatus.walletState == 2) { - final walletInfo = await getWalletInfo(); - seed = walletInfo.wiExtended.seed; - keys = ZanoWalletKeys( - privateSpendKey: walletInfo.wiExtended.spendPrivateKey, - privateViewKey: walletInfo.wiExtended.viewPrivateKey, - publicSpendKey: walletInfo.wiExtended.spendPublicKey, - publicViewKey: walletInfo.wiExtended.viewPublicKey, - ); - loadAssets(walletInfo.wi.balances); - // matching balances and whitelists - // 1. show only balances available in whitelists - // 2. set whitelists available in balances as 'enabled' ('disabled' by default) - for (final b in walletInfo.wi.balances) { - if (b.assetId == zanoAssetId) { - balance[CryptoCurrency.zano] = ZanoBalance(total: b.total, unlocked: b.unlocked); - } else { - final asset = zanoAssets[b.assetId]; - if (asset == null) { - ZanoWalletApi.error('balance for an unknown asset ${b.assetInfo.assetId}'); - continue; - } - if (balance.keys.any( - (element) => element is ZanoAsset && element.assetId == b.assetInfo.assetId)) { - balance[balance.keys.firstWhere((element) => - element is ZanoAsset && element.assetId == b.assetInfo.assetId)] = - ZanoBalance( - total: b.total, unlocked: b.unlocked, decimalPoint: asset.decimalPoint); - } else { - balance[asset] = ZanoBalance( - total: b.total, unlocked: b.unlocked, decimalPoint: asset.decimalPoint); - } - } - } - await updateTransactions(); - // removing balances for assets missing in wallet info balances - balance.removeWhere( - (key, _) => - key != CryptoCurrency.zano && - !walletInfo.wi.balances - .any((element) => element.assetId == (key as ZanoAsset).assetId), - ); - } - }); + Timer.periodic(Duration(milliseconds: _pollIntervalMilliseconds), (_) => _updateSyncInfo()); } catch (e) { syncStatus = FailedSyncStatus(); - ZanoWalletApi.error(e.toString()); + printV((e.toString())); } } @@ -443,17 +397,13 @@ abstract class ZanoWalletBase } _isTransactionUpdating = true; final transactions = await fetchTransactions(); - if (transactions.length == transactionHistory.transactions.length) { - _isTransactionUpdating = false; - return; - } transactionHistory.clear(); transactionHistory.addMany(transactions); await transactionHistory.save(); _isTransactionUpdating = false; } catch (e) { printV("e: $e"); - ZanoWalletApi.error(e.toString()); + printV((e.toString())); _isTransactionUpdating = false; } } @@ -480,12 +430,12 @@ abstract class ZanoWalletBase if (asset.enabled) { final assetDescriptor = await addAssetsWhitelist(asset.assetId); if (assetDescriptor == null) { - ZanoWalletApi.error('Error adding zano asset'); + printV(('Error adding zano asset')); } } else { final result = await removeAssetsWhitelist(asset.assetId); if (result == false) { - ZanoWalletApi.error('Error removing zano asset'); + printV(('Error removing zano asset')); } } } @@ -495,7 +445,11 @@ abstract class ZanoWalletBase } Future getZanoAsset(String assetId) async { - return await getAssetInfo(assetId); + // wallet api is not available while the wallet is syncing so only call it if it's synced + if (syncStatus is SyncedSyncStatus) { + return await getAssetInfo(assetId); + } + return null; } Future _askForUpdateTransactionHistory() async => await updateTransactions(); @@ -514,7 +468,7 @@ abstract class ZanoWalletBase syncStatus = SyncingSyncStatus(blocksLeft, ptc); } } catch (e) { - ZanoWalletApi.error(e.toString()); + printV((e.toString())); } } @@ -542,4 +496,61 @@ abstract class ZanoWalletBase // 1. Actual new height; 2. Blocks left to finish; 3. Progress in percents; _onNewBlock.call(syncHeight, left, ptc); } + + void _updateSyncInfo() async { + GetWalletStatusResult walletStatus; + // ignoring get wallet status exception (in case of wrong wallet id) + try { + walletStatus = await getWalletStatus(); + } on ZanoWalletException { + return; + } + currentDaemonHeight = walletStatus.currentDaemonHeight; + _updateSyncProgress(walletStatus); + + // we can call getWalletInfo ONLY if getWalletStatus returns NOT is in long refresh and wallet state is 2 (ready) + if (!walletStatus.isInLongRefresh && walletStatus.walletState == 2) { + final walletInfo = await getWalletInfo(); + seed = walletInfo.wiExtended.seed; + keys = ZanoWalletKeys( + privateSpendKey: walletInfo.wiExtended.spendPrivateKey, + privateViewKey: walletInfo.wiExtended.viewPrivateKey, + publicSpendKey: walletInfo.wiExtended.spendPublicKey, + publicViewKey: walletInfo.wiExtended.viewPublicKey, + ); + loadAssets(walletInfo.wi.balances); + // matching balances and whitelists + // 1. show only balances available in whitelists + // 2. set whitelists available in balances as 'enabled' ('disabled' by default) + for (final b in walletInfo.wi.balances) { + if (b.assetId == zanoAssetId) { + balance[CryptoCurrency.zano] = ZanoBalance(total: b.total, unlocked: b.unlocked); + } else { + final asset = zanoAssets[b.assetId]; + if (asset == null) { + printV('balance for an unknown asset ${b.assetInfo.assetId}'); + continue; + } + if (balance.keys.any( + (element) => element is ZanoAsset && element.assetId == b.assetInfo.assetId)) { + balance[balance.keys.firstWhere((element) => + element is ZanoAsset && element.assetId == b.assetInfo.assetId)] = + ZanoBalance( + total: b.total, unlocked: b.unlocked, decimalPoint: asset.decimalPoint); + } else { + balance[asset] = ZanoBalance( + total: b.total, unlocked: b.unlocked, decimalPoint: asset.decimalPoint); + } + } + } + await updateTransactions(); + // removing balances for assets missing in wallet info balances + balance.removeWhere( + (key, _) => + key != CryptoCurrency.zano && + !walletInfo.wi.balances + .any((element) => element.assetId == (key as ZanoAsset).assetId), + ); + } + } } diff --git a/cw_zano/lib/zano_wallet_addresses.dart b/cw_zano/lib/zano_wallet_addresses.dart index e485e8525..39e61be7f 100644 --- a/cw_zano/lib/zano_wallet_addresses.dart +++ b/cw_zano/lib/zano_wallet_addresses.dart @@ -1,3 +1,4 @@ +import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/wallet_addresses.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_zano/zano_wallet_api.dart'; @@ -34,7 +35,7 @@ abstract class ZanoWalletAddressesBase extends WalletAddresses with Store { addressesMap[address] = ''; await saveAddressesInBox(); } catch (e) { - ZanoWalletApi.error(e.toString()); + printV(e.toString()); } } } diff --git a/cw_zano/lib/zano_wallet_api.dart b/cw_zano/lib/zano_wallet_api.dart index c8cfde551..f7052bd87 100644 --- a/cw_zano/lib/zano_wallet_api.dart +++ b/cw_zano/lib/zano_wallet_api.dart @@ -1,5 +1,6 @@ import 'dart:convert' as convert; import 'dart:ffi'; +import 'dart:io'; import 'dart:isolate'; import 'package:cw_core/transaction_priority.dart'; @@ -22,7 +23,6 @@ import 'package:cw_zano/api/model/transfer_params.dart'; import 'package:cw_zano/api/model/transfer_result.dart'; import 'package:cw_zano/zano_wallet_exceptions.dart'; import 'package:ffi/ffi.dart'; -import 'package:flutter/foundation.dart'; import 'package:json_bigint/json_bigint.dart'; import 'package:monero/zano.dart' as zano; import 'package:monero/src/generated_bindings_zano.g.dart' as zanoapi; @@ -30,8 +30,6 @@ import 'package:monero/src/generated_bindings_zano.g.dart' as zanoapi; mixin ZanoWalletApi { static const _maxReopenAttempts = 5; static const _logInfo = false; - static const _logError = true; - static const _logJson = false; static const int _zanoMixinValue = 10; int _hWallet = 0; @@ -46,16 +44,22 @@ mixin ZanoWalletApi { void setPassword(String password) => zano.PlainWallet_resetWalletPassword(hWallet, password); - void closeWallet([int? walletToClose]) async { - info('close_wallet ${walletToClose ?? hWallet}'); - final result = await _closeWallet(walletToClose ?? hWallet); - info('close_wallet result $result'); + void closeWallet(int? walletToClose, {bool force = false}) async { + printV('close_wallet ${walletToClose ?? hWallet}'); + if (Platform.isWindows || force) { + final result = await _closeWallet(walletToClose ?? hWallet); + printV('close_wallet result $result'); + openWalletCache.removeWhere((_, cwr) => cwr.walletId == (walletToClose ?? hWallet)); + } } + bool isInit = false; + Future initWallet() async { // pathForWallet(name: , type: type) + if (isInit) return true; final result = zano.PlainWallet_init("", "", 0); - printV(result); + isInit = true; return result == "OK"; } @@ -67,21 +71,19 @@ mixin ZanoWalletApi { Future getWalletInfo() async { final json = await _getWalletInfo(hWallet); final result = GetWalletInfoResult.fromJson(jsonDecode(json)); - _json('get_wallet_info', json); - info('get_wallet_info got ${result.wi.balances.length} balances: ${result.wi.balances} seed: ${_shorten(result.wiExtended.seed)}'); + printV('get_wallet_info got ${result.wi.balances.length} balances: ${result.wi.balances}'); return result; } Future getWalletStatus() async { final json = await _getWalletStatus(hWallet); if (json == Consts.errorWalletWrongId) { - error('wrong wallet id'); + printV('wrong wallet id'); throw ZanoWalletException('Wrong wallet id'); } final status = GetWalletStatusResult.fromJson(jsonDecode(json)); - _json('get_wallet_status', json); if (_logInfo) - info( + printV( 'get_wallet_status connected: ${status.isDaemonConnected} in refresh: ${status.isInLongRefresh} progress: ${status.progress} wallet state: ${status.walletState} sync: ${status.currentWalletHeight}/${status.currentDaemonHeight} ${(status.currentWalletHeight/status.currentDaemonHeight*100).toStringAsFixed(2)}%'); return status; } @@ -96,7 +98,7 @@ mixin ZanoWalletApi { jsonDecode(invokeResult); } catch (e) { if (invokeResult.contains(Consts.errorWalletWrongId)) throw ZanoWalletException('Wrong wallet id'); - error('exception in parsing json in invokeMethod: $invokeResult'); + printV('exception in parsing json in invokeMethod: $invokeResult'); rethrow; } return invokeResult; @@ -105,7 +107,6 @@ mixin ZanoWalletApi { Future> getAssetsWhitelist() async { try { final json = await invokeMethod('assets_whitelist_get', '{}'); - _json('assets_whitelist_get', json); final map = jsonDecode(json) as Map?; _checkForErrors(map); List assets(String type, bool isGlobalWhitelist) => @@ -117,12 +118,12 @@ mixin ZanoWalletApi { final globalWhitelist = assets('global_whitelist', true); final ownAssets = assets('own_assets', false); if (_logInfo) - info('assets_whitelist_get got local whitelist: ${localWhitelist.length} ($localWhitelist); ' + printV('assets_whitelist_get got local whitelist: ${localWhitelist.length} ($localWhitelist); ' 'global whitelist: ${globalWhitelist.length} ($globalWhitelist); ' 'own assets: ${ownAssets.length} ($ownAssets)'); return [...globalWhitelist, ...localWhitelist, ...ownAssets]; } catch (e) { - error('assets_whitelist_get $e'); + printV('assets_whitelist_get $e'); return []; // rethrow; } @@ -131,19 +132,18 @@ mixin ZanoWalletApi { Future addAssetsWhitelist(String assetId) async { try { final json = await invokeMethod('assets_whitelist_add', AssetIdParams(assetId: assetId)); - _json('assets_whitelist_add $assetId', json); final map = jsonDecode(json) as Map?; _checkForErrors(map); if (map!['result']!['status']! == 'OK') { final assetDescriptor = ZanoAsset.fromJson(map['result']!['asset_descriptor']! as Map); - info('assets_whitelist_add added ${assetDescriptor.fullName} ${assetDescriptor.ticker}'); + printV('assets_whitelist_add added ${assetDescriptor.fullName} ${assetDescriptor.ticker}'); return assetDescriptor; } else { - info('assets_whitelist_add status ${map['result']!['status']!}'); + printV('assets_whitelist_add status ${map['result']!['status']!}'); return null; } } catch (e) { - error('assets_whitelist_add $e'); + printV('assets_whitelist_add $e'); return null; } } @@ -151,13 +151,12 @@ mixin ZanoWalletApi { Future removeAssetsWhitelist(String assetId) async { try { final json = await invokeMethod('assets_whitelist_remove', AssetIdParams(assetId: assetId)); - _json('assets_whitelist_remove $assetId', json); final map = jsonDecode(json) as Map?; _checkForErrors(map); - info('assets_whitelist_remove status ${map!['result']!['status']!}'); + printV('assets_whitelist_remove status ${map!['result']!['status']!}'); return (map['result']!['status']! == 'OK'); } catch (e) { - error('assets_whitelist_remove $e'); + printV('assets_whitelist_remove $e'); return false; } } @@ -173,21 +172,20 @@ mixin ZanoWalletApi { final methodName = 'get_asset_info'; final params = AssetIdParams(assetId: assetId); final result = await _proxyToDaemon('/json_rpc', '{"method": "$methodName","params": ${jsonEncode(params)}}'); - _json('$methodName $assetId', result?.body ?? ''); if (result == null) { - error('get_asset_info empty result'); + printV('get_asset_info empty result'); return null; } final map = jsonDecode(result.body) as Map?; if (map!['error'] != null) { - info('get_asset_info $assetId error ${map['error']!['code']} ${map['error']!['message']}'); + printV('get_asset_info $assetId error ${map['error']!['code']} ${map['error']!['message']}'); return null; } else if (map['result']!['status']! == 'OK') { final assetDescriptor = ZanoAsset.fromJson(map['result']!['asset_descriptor']! as Map); - info('get_asset_info $assetId ${assetDescriptor.fullName} ${assetDescriptor.ticker}'); + printV('get_asset_info $assetId ${assetDescriptor.fullName} ${assetDescriptor.ticker}'); return assetDescriptor; } else { - info('get_asset_info $assetId status ${map['result']!['status']!}'); + printV('get_asset_info $assetId status ${map['result']!['status']!}'); return null; } } @@ -199,33 +197,32 @@ mixin ZanoWalletApi { _checkForErrors(map); return StoreResult.fromJson(map!['result'] as Map); } catch (e) { - error('store $e'); + printV('store $e'); return null; } } Future getRecentTxsAndInfo({required int offset, required int count}) async { - info('get_recent_txs_and_info $offset $count'); + printV('get_recent_txs_and_info $offset $count'); try { final json = await invokeMethod('get_recent_txs_and_info', GetRecentTxsAndInfoParams(offset: offset, count: count)); - _json('get_recent_txs_and_info', json); final map = jsonDecode(json) as Map?; _checkForErrors(map); final lastItemIndex = map?['result']?['last_item_index'] as int?; final totalTransfers = map?['result']?['total_transfers'] as int?; final transfers = map?['result']?['transfers'] as List?; if (transfers == null || lastItemIndex == null || totalTransfers == null) { - error('get_recent_txs_and_info empty transfers'); + printV('get_recent_txs_and_info empty transfers'); return GetRecentTxsAndInfoResult.empty(); } - info('get_recent_txs_and_info transfers.length: ${transfers.length}'); + printV('get_recent_txs_and_info transfers.length: ${transfers.length}'); return GetRecentTxsAndInfoResult( transfers: transfers.map((e) => Transfer.fromJson(e as Map)).toList(), lastItemIndex: lastItemIndex, totalTransfers: totalTransfers, ); } catch (e) { - error('get_recent_txs_and_info $e'); + printV('get_recent_txs_and_info $e'); return GetRecentTxsAndInfoResult.empty(); } } @@ -237,9 +234,8 @@ mixin ZanoWalletApi { String _shorten(String s) => s.length > 10 ? '${s.substring(0, 4)}...${s.substring(s.length - 4)}' : s; Future createWallet(String path, String password) async { - info('create_wallet path $path password ${_shorten(password)}'); + printV('create_wallet path $path password ${_shorten(password)}'); final json = zano.PlainWallet_generate(path, password); - _json('create_wallet', json); final map = jsonDecode(json) as Map?; if (map?['error'] != null) { final code = map!['error']?['code'] ?? ''; @@ -250,14 +246,14 @@ mixin ZanoWalletApi { throw ZanoWalletException('Error creating wallet file, empty response'); } final result = CreateWalletResult.fromJson(map!['result'] as Map); - info('create_wallet ${result.name} ${result.seed}'); + openWalletCache[path] = result; + printV('create_wallet ${result.name} ${result.seed}'); return result; } Future restoreWalletFromSeed(String path, String password, String seed, String? passphrase) async { - info('restore_wallet path $path password ${_shorten(password)} seed ${_shorten(seed)}'); + printV('restore_wallet path $path password ${_shorten(password)} seed ${_shorten(seed)}'); final json = zano.PlainWallet_restore(seed, path, password, passphrase??''); - _json('restore_wallet', json); final map = jsonDecode(json) as Map?; if (map?['error'] != null) { final code = map!['error']!['code'] ?? ''; @@ -273,28 +269,29 @@ mixin ZanoWalletApi { throw RestoreFromSeedsException('Error restoring wallet, empty response'); } final result = CreateWalletResult.fromJson(map!['result'] as Map); - info('restore_wallet ${result.name} ${result.wi.address}'); + openWalletCache[path] = result; + printV('restore_wallet ${result.name} ${result.wi.address}'); return result; } FutureloadWallet(String path, String password, [int attempt = 0]) async { - info('load_wallet1 path $path password ${_shorten(password)}'); + printV('load_wallet1 path $path password ${_shorten(password)}'); final String json; try { json = zano.PlainWallet_open(path, password); } catch (e) { - error('error in loadingWallet $e'); + printV('error in loadingWallet $e'); rethrow; } - info('load_wallet2: $json'); + // printV('load_wallet2: $json'); final map = jsonDecode(json) as Map?; if (map?['error'] != null) { final code = map?['error']!['code'] ?? ''; final message = map?['error']!['message'] ?? ''; if (code == Consts.errorAlreadyExists && attempt <= _maxReopenAttempts) { // already connected to this wallet. closing and trying to reopen - info('already connected. closing and reopen wallet (attempt $attempt)'); - closeWallet(attempt); + printV('already connected. closing and reopen wallet (attempt $attempt)'); + closeWallet(attempt, force: true); await Future.delayed(const Duration(milliseconds: 500)); return await loadWallet(path, password, attempt + 1); } @@ -304,10 +301,13 @@ mixin ZanoWalletApi { throw ZanoWalletException('Error loading wallet, empty response'); } final result = CreateWalletResult.fromJson(map!['result'] as Map); - info('load_wallet3 ${result.name} ${result.wi.address}'); + printV('load_wallet3 ${result.name} ${result.wi.address}'); + openWalletCache[path] = result; return result; } + static Map openWalletCache = {}; + Future transfer(List destinations, BigInt fee, String comment) async { final params = TransferParams( destinations: destinations, @@ -319,24 +319,23 @@ mixin ZanoWalletApi { hideReceiver: true, ); final json = await invokeMethod('transfer', params); - _json('transfer', json); final map = jsonDecode(json); final resultMap = map as Map?; if (resultMap != null) { final transferResultMap = resultMap['result'] as Map?; if (transferResultMap != null) { final transferResult = TransferResult.fromJson(transferResultMap); - info('transfer success hash ${transferResult.txHash}'); + printV('transfer success hash ${transferResult.txHash}'); return transferResult; } else { final errorCode = resultMap['error']?['code']; final code = errorCode is int ? errorCode.toString() : errorCode as String? ?? ''; final message = resultMap['error']?['message'] as String? ?? ''; - error('transfer error $code $message'); + printV('transfer error $code $message'); throw TransferException('Transfer error, $message ($code)'); } } - error('transfer error empty result'); + printV('transfer error empty result'); throw TransferException('Transfer error, empty result'); } @@ -358,21 +357,6 @@ mixin ZanoWalletApi { } } - /*Future _writeLog(String method, String logMessage) async { - final dir = await getDownloadsDirectory(); - final logFile = File('${dir!.path}/$method.txt'); - final date = DateTime.now(); - String twoDigits(int value) => value.toString().padLeft(2, '0'); - String removeCRandLF(String input) => input.replaceAll(RegExp('\r|\n'), ''); - await logFile.writeAsString('${twoDigits(date.hour)}:${twoDigits(date.minute)}:${twoDigits(date.second)} ${removeCRandLF(logMessage)}\n', - mode: FileMode.append); - }*/ - - static void info(String s) => _logInfo ? debugPrint('[info] $s') : null; - static void error(String s) => _logError ? debugPrint('[error] $s') : null; - static void printWrapped(String text) => RegExp('.{1,800}').allMatches(text).map((m) => m.group(0)).forEach(print); - static void _json(String methodName, String json) => _logJson ? printWrapped('$methodName $json') : null; - } Future callSyncMethod(String methodName, int hWallet, String params) async { diff --git a/cw_zano/lib/zano_wallet_service.dart b/cw_zano/lib/zano_wallet_service.dart index d20154204..5bb7ed266 100644 --- a/cw_zano/lib/zano_wallet_service.dart +++ b/cw_zano/lib/zano_wallet_service.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:cw_core/pathForWallet.dart'; +import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; @@ -55,7 +56,7 @@ class ZanoWalletService extends WalletService create(WalletCredentials credentials, {bool? isTestnet}) async { - ZanoWalletApi.info('zanowallet service create isTestnet $isTestnet'); + printV('zanowallet service create isTestnet $isTestnet'); return await ZanoWalletBase.create(credentials: credentials); } diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart index 68039bdfb..65c9faff9 100644 --- a/lib/core/address_validator.dart +++ b/lib/core/address_validator.dart @@ -135,7 +135,7 @@ class AddressValidator extends TextValidator { case CryptoCurrency.btcln: pattern = '(lnbc|LNBC)([0-9]{1,}[a-zA-Z0-9]+)'; case CryptoCurrency.zano: - pattern = r'([1-9A-HJ-NP-Za-km-z]{90,200})|(@[\w\d-.]+)'; + pattern = r'([1-9A-HJ-NP-Za-km-z]{90,200})|(@[\w\d.-]+)'; default: return ''; } @@ -316,7 +316,7 @@ class AddressValidator extends TextValidator { case CryptoCurrency.trx: pattern = '(T|t)[1-9A-HJ-NP-Za-km-z]{33}'; case CryptoCurrency.zano: - pattern = '([1-9A-HJ-NP-Za-km-z]{90,200})|(@[\w\d-.]+)'; + pattern = '([1-9A-HJ-NP-Za-km-z]{90,200})|(@[\w\d.-]+)'; default: if (type.tag == CryptoCurrency.eth.title) { pattern = '0x[0-9a-zA-Z]{42}'; diff --git a/lib/di.dart b/lib/di.dart index 33e8c33ff..9f2256304 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -495,7 +495,7 @@ Future setup({ settingsStore: getIt.get(), fiatConvertationStore: getIt.get())); - getIt.registerLazySingleton(() => DashboardViewModel( + getIt.registerFactory(() => DashboardViewModel( balanceViewModel: getIt.get(), appStore: getIt.get(), tradesStore: getIt.get(), diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 8d89e65c4..8f5b4c64a 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -44,7 +44,7 @@ const solanaDefaultNodeUri = 'solana-mainnet.core.chainstack.com'; const tronDefaultNodeUri = 'api.trongrid.io'; const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002'; const wowneroDefaultNodeUri = 'node3.monerodevs.org:34568'; -const zanoDefaultNodeUri = '37.27.100.59:10500'; +const zanoDefaultNodeUri = 'zano.cakewallet.com:11211'; const moneroWorldNodeUri = '.moneroworld.com'; Future defaultSettingsMigration( diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart index da8bcf074..28b9609f6 100644 --- a/lib/entities/parse_address_from_domain.dart +++ b/lib/entities/parse_address_from_domain.dart @@ -141,7 +141,7 @@ class AddressResolver { if (currency == CryptoCurrency.zano && settingsStore.lookupsZanoAlias) { final formattedName = text.substring(1); final zanoAddress = await ZanoAlias.fetchZanoAliasAddress(formattedName); - if (zanoAddress != null) { + if (zanoAddress != null && zanoAddress.isNotEmpty) { return ParsedAddress.zanoAddress( address: zanoAddress, name: text, @@ -154,7 +154,7 @@ class AddressResolver { final addressFromBio = extractAddressByType( raw: twitterUser.description, type: CryptoCurrency.fromString(ticker, walletCurrency: wallet.currency)); - if (addressFromBio != null) { + if (addressFromBio != null && addressFromBio.isNotEmpty) { return ParsedAddress.fetchTwitterAddress( address: addressFromBio, name: text, @@ -192,7 +192,7 @@ class AddressResolver { if (mastodonUser != null) { String? addressFromBio = extractAddressByType(raw: mastodonUser.note, type: currency); - if (addressFromBio != null) { + if (addressFromBio != null && addressFromBio.isNotEmpty) { return ParsedAddress.fetchMastodonAddress( address: addressFromBio, name: text, @@ -207,7 +207,7 @@ class AddressResolver { String? addressFromPinnedPost = extractAddressByType(raw: userPinnedPostsText, type: currency); - if (addressFromPinnedPost != null) { + if (addressFromPinnedPost != null && addressFromPinnedPost.isNotEmpty) { return ParsedAddress.fetchMastodonAddress( address: addressFromPinnedPost, name: text, @@ -248,7 +248,7 @@ class AddressResolver { } final thorChainAddress = await ThorChainExchangeProvider.lookupAddressByName(text); - if (thorChainAddress != null) { + if (thorChainAddress != null && thorChainAddress.isNotEmpty) { String? address = thorChainAddress[ticker] ?? (ticker == 'RUNE' ? thorChainAddress['THOR'] : null); if (address != null) { @@ -301,7 +301,7 @@ class AddressResolver { if (nostrUserData != null) { String? addressFromBio = extractAddressByType(raw: nostrUserData.about, type: currency); - if (addressFromBio != null) { + if (addressFromBio != null && addressFromBio.isNotEmpty) { return ParsedAddress.nostrAddress( address: addressFromBio, name: text, diff --git a/lib/entities/zano_alias.dart b/lib/entities/zano_alias.dart index 399055de0..1ddf95178 100644 --- a/lib/entities/zano_alias.dart +++ b/lib/entities/zano_alias.dart @@ -6,7 +6,7 @@ import 'package:http/http.dart' as http; class ZanoAlias { static Future fetchZanoAliasAddress(String alias) async { try { - final uri = Uri.parse("http://37.27.100.59:10500/json_rpc"); + final uri = Uri.parse("http://zano.cakewallet.com:11211/json_rpc"); final response = await http.post( uri, body: json.encode({ diff --git a/lib/exchange/provider/trocador_exchange_provider.dart b/lib/exchange/provider/trocador_exchange_provider.dart index f49fc6e05..b9aea3caf 100644 --- a/lib/exchange/provider/trocador_exchange_provider.dart +++ b/lib/exchange/provider/trocador_exchange_provider.dart @@ -107,8 +107,9 @@ class TrocadorExchangeProvider extends ExchangeProvider { final coinJson = responseJSON.first as Map; return Limits( - min: coinJson['minimum'] as double, - max: coinJson['maximum'] as double, + min: coinJson['minimum'] as double?, + // TODO: remove hardcoded value and call `api/new_rate` when Trocador adds min and max to it + max: from == CryptoCurrency.zano ? 2600 : coinJson['maximum'] as double?, ); } diff --git a/lib/src/screens/dashboard/pages/balance/crypto_balance_widget.dart b/lib/src/screens/dashboard/pages/balance/crypto_balance_widget.dart index 0bdf388d3..6b3256dad 100644 --- a/lib/src/screens/dashboard/pages/balance/crypto_balance_widget.dart +++ b/lib/src/screens/dashboard/pages/balance/crypto_balance_widget.dart @@ -173,7 +173,7 @@ class CryptoBalanceWidget extends StatelessWidget { frozenFiatBalance: balance.fiatFrozenBalance, currency: balance.asset, hasAdditionalBalance: - dashboardViewModel.balanceViewModel.hasAdditionalBalance, + dashboardViewModel.balanceViewModel.hasAdditionalBalance(balance.asset), hasSecondAdditionalBalance: dashboardViewModel.balanceViewModel.hasSecondAdditionalBalance, hasSecondAvailableBalance: diff --git a/lib/src/screens/dashboard/pages/transactions_page.dart b/lib/src/screens/dashboard/pages/transactions_page.dart index 79a037627..ccf08737b 100644 --- a/lib/src/screens/dashboard/pages/transactions_page.dart +++ b/lib/src/screens/dashboard/pages/transactions_page.dart @@ -118,7 +118,6 @@ class TransactionsPage extends StatelessWidget { dashboardViewModel.balanceViewModel.isFiatDisabled ? '' : item.formattedFiatAmount, - isPending: transaction.isPending, title: item.formattedTitle + item.formattedStatus + transactionType, tags: tags, diff --git a/lib/src/screens/dashboard/widgets/transaction_raw.dart b/lib/src/screens/dashboard/widgets/transaction_raw.dart index 2d7cbb809..d604863c8 100644 --- a/lib/src/screens/dashboard/widgets/transaction_raw.dart +++ b/lib/src/screens/dashboard/widgets/transaction_raw.dart @@ -10,7 +10,6 @@ class TransactionRow extends StatelessWidget { required this.formattedDate, required this.formattedAmount, required this.formattedFiatAmount, - required this.isPending, required this.tags, required this.title, required this.onTap, @@ -22,7 +21,6 @@ class TransactionRow extends StatelessWidget { final String formattedDate; final String formattedAmount; final String formattedFiatAmount; - final bool isPending; final String title; final List tags; diff --git a/lib/src/screens/wallet_keys/wallet_keys_page.dart b/lib/src/screens/wallet_keys/wallet_keys_page.dart index e8a57332e..844678b90 100644 --- a/lib/src/screens/wallet_keys/wallet_keys_page.dart +++ b/lib/src/screens/wallet_keys/wallet_keys_page.dart @@ -175,7 +175,7 @@ class _WalletKeysPageBodyState extends State dataToCopy: isLegacySeed ? widget.walletKeysViewModel.legacySeed : widget.walletKeysViewModel.seed, - onShowQR: () async => _showQR(context, false), + onShowQR: () async => _showQR(context), ), ], ); @@ -315,8 +315,8 @@ class _WalletKeysPageBodyState extends State showBar(context, S.of(context).copied_key_to_clipboard(title)); } - Future _showQR(BuildContext context, bool isLegacySeed) async { - final url = await widget.walletKeysViewModel.getUrl(isLegacySeed); + Future _showQR(BuildContext context) async { + final url = await widget.walletKeysViewModel.getUrl(false); BrightnessUtil.changeBrightnessForFunction(() async { await Navigator.pushNamed( diff --git a/lib/store/app_store.dart b/lib/store/app_store.dart index a24e2cb77..ff3ba0535 100644 --- a/lib/store/app_store.dart +++ b/lib/store/app_store.dart @@ -3,7 +3,6 @@ import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/utils/exception_handler.dart'; -import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; @@ -55,6 +54,5 @@ abstract class AppStoreBase with Store { getIt .get() .setInt(PreferencesKey.currentWalletType, serializeToInt(wallet.type)); - getIt.get().onWalletChange(wallet); } } diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index 66d74a966..730f07a93 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -197,15 +197,14 @@ abstract class BalanceViewModelBase with Store { } } - @computed - String get additionalBalance { - final walletBalance = _walletBalance; + String additionalBalance(CryptoCurrency cryptoCurrency) { + final balance = _currencyBalance(cryptoCurrency); if (displayMode == BalanceDisplayMode.hiddenBalance) { return '0.0'; } - return walletBalance.formattedAdditionalBalance; + return balance.formattedAdditionalBalance; } @computed @@ -217,7 +216,7 @@ abstract class BalanceViewModelBase with Store { key, BalanceRecord( availableBalance: '●●●●●●', - additionalBalance: additionalBalance, + additionalBalance: additionalBalance(key), frozenBalance: '', secondAvailableBalance: '●●●●●●', secondAdditionalBalance: '●●●●●●', @@ -287,10 +286,9 @@ abstract class BalanceViewModelBase with Store { @observable bool mwebEnabled = false; - @computed - bool get hasAdditionalBalance { + bool hasAdditionalBalance(CryptoCurrency currency) { bool isWalletTypeActivated = _hasAdditionalBalanceForWalletType(wallet.type); - bool isNotZeroAmount = additionalBalance != "0.0"; + bool isNotZeroAmount = additionalBalance(currency) != "0.0"; return isWalletTypeActivated && isNotZeroAmount; } @@ -384,9 +382,8 @@ abstract class BalanceViewModelBase with Store { return balance; } - @computed - Balance get _walletBalance { - final balance = wallet.balance[wallet.currency]; + Balance _currencyBalance(CryptoCurrency cryptoCurrency) { + final balance = wallet.balance[cryptoCurrency]; if (balance == null) { throw Exception('No balance for ${wallet.currency}'); diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index dff315b1f..80984294c 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -157,18 +157,128 @@ abstract class DashboardViewModelBase with Store { type = appStore.wallet!.type, transactions = ObservableList(), wallet = appStore.wallet! { + name = wallet.name; + type = wallet.type; isShowFirstYatIntroduction = false; isShowSecondYatIntroduction = false; isShowThirdYatIntroduction = false; updateActions(); - onWalletChange(wallet); + final _wallet = wallet; + + if (_wallet.type == WalletType.monero) { + subname = monero!.getCurrentAccount(_wallet).label; + + _onMoneroAccountChangeReaction = reaction( + (_) => monero!.getMoneroWalletDetails(wallet).account, + (Account account) => _onMoneroAccountChange(_wallet)); + + _onMoneroBalanceChangeReaction = reaction( + (_) => monero!.getMoneroWalletDetails(wallet).balance, + (MoneroBalance balance) => _onMoneroTransactionsUpdate(_wallet)); + + final _accountTransactions = _wallet.transactionHistory.transactions.values + .where((tx) => + monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id) + .toList(); + + final sortedTransactions = [..._accountTransactions]; + sortedTransactions.sort((a, b) => a.date.compareTo(b.date)); + + transactions = ObservableList.of( + sortedTransactions.map( + (transaction) => TransactionListItem( + transaction: transaction, + balanceViewModel: balanceViewModel, + settingsStore: appStore.settingsStore, + key: ValueKey('monero_transaction_history_item_${transaction.id}_key'), + ), + ), + ); + } else if (_wallet.type == WalletType.wownero) { + subname = wow.wownero!.getCurrentAccount(_wallet).label; + + _onMoneroAccountChangeReaction = reaction( + (_) => wow.wownero!.getWowneroWalletDetails(wallet).account, + (wow.Account account) => _onMoneroAccountChange(_wallet)); + + _onMoneroBalanceChangeReaction = reaction( + (_) => wow.wownero!.getWowneroWalletDetails(wallet).balance, + (wow.WowneroBalance balance) => _onMoneroTransactionsUpdate(_wallet)); + + final _accountTransactions = _wallet.transactionHistory.transactions.values + .where((tx) => + wow.wownero!.getTransactionInfoAccountId(tx) == + wow.wownero!.getCurrentAccount(wallet).id) + .toList(); + + final sortedTransactions = [..._accountTransactions]; + sortedTransactions.sort((a, b) => a.date.compareTo(b.date)); + + transactions = ObservableList.of( + sortedTransactions.map( + (transaction) => TransactionListItem( + transaction: transaction, + balanceViewModel: balanceViewModel, + settingsStore: appStore.settingsStore, + key: ValueKey('wownero_transaction_history_item_${transaction.id}_key'), + ), + ), + ); + } else { + final sortedTransactions = [...wallet.transactionHistory.transactions.values]; + sortedTransactions.sort((a, b) => a.date.compareTo(b.date)); + + transactions = ObservableList.of( + sortedTransactions.map( + (transaction) => TransactionListItem( + transaction: transaction, + balanceViewModel: balanceViewModel, + settingsStore: appStore.settingsStore, + key: ValueKey('${_wallet.type.name}_transaction_history_item_${transaction.id}_key'), + ), + ), + ); + } // TODO: nano sub-account generation is disabled: // if (_wallet.type == WalletType.nano || _wallet.type == WalletType.banano) { // subname = nano!.getCurrentAccount(_wallet).label; // } + reaction((_) => appStore.wallet, (wallet) { + _onWalletChange(wallet); + _checkMweb(); + }); + + connectMapToListWithTransform( + appStore.wallet!.transactionHistory.transactions, + transactions, + (TransactionInfo? transaction) => TransactionListItem( + transaction: transaction!, + balanceViewModel: balanceViewModel, + settingsStore: appStore.settingsStore, + key: ValueKey( + '${_wallet.type.name}_transaction_history_item_${transaction.id}_key', + ), + ), filter: (TransactionInfo? transaction) { + if (transaction == null) { + return false; + } + + final wallet = _wallet; + if (wallet.type == WalletType.monero) { + return monero!.getTransactionInfoAccountId(transaction) == + monero!.getCurrentAccount(wallet).id; + } + if (wallet.type == WalletType.wownero) { + return wow.wownero!.getTransactionInfoAccountId(transaction) == + wow.wownero!.getCurrentAccount(wallet).id; + } + + return true; + }); + if (hasSilentPayments) { silentPaymentsScanningActive = bitcoin!.getScanningActive(wallet); @@ -482,11 +592,11 @@ abstract class DashboardViewModelBase with Store { } @action - void onWalletChange( + void _onWalletChange( WalletBase, TransactionInfo>? wallet) { - if (wallet == null) return; - - _checkMweb(); + if (wallet == null) { + return; + } this.wallet = wallet; type = wallet.type; diff --git a/lib/view_model/dashboard/transaction_list_item.dart b/lib/view_model/dashboard/transaction_list_item.dart index ff678423d..864448293 100644 --- a/lib/view_model/dashboard/transaction_list_item.dart +++ b/lib/view_model/dashboard/transaction_list_item.dart @@ -105,6 +105,7 @@ class TransactionListItem extends ActionListItem with Keyable { WalletType.haven, WalletType.wownero, WalletType.litecoin, + WalletType.zano, ].contains(balanceViewModel.wallet.type)) { return formattedPendingStatus; } @@ -214,6 +215,10 @@ class TransactionListItem extends ActionListItem with Keyable { break; case WalletType.zano: final asset = zano!.assetOfTransaction(balanceViewModel.wallet, transaction); + if (asset == null) { + amount = "0.00"; + break; + } final price = balanceViewModel.fiatConvertationStore.prices[asset]; amount = calculateFiatAmountRaw( cryptoAmount: zano!.formatterIntAmountToDouble(amount: transaction.amount, currency: asset, forFee: false), diff --git a/lib/view_model/restore/wallet_restore_from_qr_code.dart b/lib/view_model/restore/wallet_restore_from_qr_code.dart index 9fde8386b..0e4664f48 100644 --- a/lib/view_model/restore/wallet_restore_from_qr_code.dart +++ b/lib/view_model/restore/wallet_restore_from_qr_code.dart @@ -76,9 +76,7 @@ class WalletRestoreFromQRCode { RegExp _getPattern(int wordCount) => RegExp(r'(?<=\W|^)((?:\w+\s+){' + (wordCount - 1).toString() + r'}\w+)(?=\W|$)'); - List patternCounts = walletType == WalletType.monero || walletType == WalletType.wownero - ? [25, 16, 14, 13] - : [24, 18, 12]; + final List patternCounts = [12, 13, 14, 16, 18, 24, 25, 26]; for (final count in patternCounts) { final pattern = _getPattern(count); @@ -122,7 +120,9 @@ class WalletRestoreFromQRCode { queryParameters['seed'] = _extractSeedPhraseFromUrl(code, walletType!); } if (queryParameters['address'] == null) { - queryParameters['address'] = _extractAddressFromUrl(code, walletType!); + try { + queryParameters['address'] = _extractAddressFromUrl(code, walletType!); + } catch (_) {} } Map credentials = {'type': walletType, ...queryParameters, 'raw_qr': code}; @@ -223,7 +223,8 @@ class WalletRestoreFromQRCode { if (type == WalletType.monero) { final codeParsed = json.decode(credentials['raw_qr'].toString()); - if (codeParsed["version"] != 0) throw UnimplementedError("Found view-only restore with unsupported version"); + if (codeParsed["version"] != 0) + throw UnimplementedError("Found view-only restore with unsupported version"); if (codeParsed["primaryAddress"] == null || codeParsed["privateViewKey"] == null || codeParsed["restoreHeight"] == null) { diff --git a/lib/view_model/wallet_keys_view_model.dart b/lib/view_model/wallet_keys_view_model.dart index fa58873c6..f93a95d2f 100644 --- a/lib/view_model/wallet_keys_view_model.dart +++ b/lib/view_model/wallet_keys_view_model.dart @@ -3,10 +3,8 @@ import 'package:cake_wallet/haven/haven.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; -import 'package:cake_wallet/src/widgets/text_info_box.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/wownero/wownero.dart'; -import 'package:cake_wallet/zano/zano.dart'; import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/wallet_base.dart'; diff --git a/lib/view_model/wallet_new_vm.dart b/lib/view_model/wallet_new_vm.dart index 9b29f55ff..e79e73684 100644 --- a/lib/view_model/wallet_new_vm.dart +++ b/lib/view_model/wallet_new_vm.dart @@ -73,11 +73,16 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store { ? advancedPrivacySettingsViewModel.seedPhraseLength.value : 24; case WalletType.nano: + case WalletType.banano: return seedSettingsViewModel.nanoSeedType == NanoSeedType.bip39 ? advancedPrivacySettingsViewModel.seedPhraseLength.value : 24; - default: + case WalletType.none: return 24; + case WalletType.haven: + return 25; + case WalletType.zano: + return 26; } } diff --git a/lib/view_model/wallet_restore_view_model.dart b/lib/view_model/wallet_restore_view_model.dart index ccc6dab26..8a497a605 100644 --- a/lib/view_model/wallet_restore_view_model.dart +++ b/lib/view_model/wallet_restore_view_model.dart @@ -72,8 +72,6 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { } static const moneroSeedMnemonicLength = 25; - static const electrumSeedMnemonicLength = 24; - static const electrumShortSeedMnemonicLength = 12; late List availableModes; final bool hasSeedLanguageSelector; diff --git a/lib/zano/cw_zano.dart b/lib/zano/cw_zano.dart index cf91f085a..f7f10a91e 100644 --- a/lib/zano/cw_zano.dart +++ b/lib/zano/cw_zano.dart @@ -114,14 +114,14 @@ class CWZano extends Zano { } @override - CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) { + CryptoCurrency? assetOfTransaction(WalletBase wallet, TransactionInfo transaction) { transaction as ZanoTransactionInfo; if (transaction.tokenSymbol == CryptoCurrency.zano.title) { return CryptoCurrency.zano; } wallet as ZanoWallet; final asset = wallet.zanoAssets.values.firstWhereOrNull((element) => element?.ticker == transaction.tokenSymbol); - return asset ?? CryptoCurrency.zano; + return asset; } String getZanoAssetAddress(CryptoCurrency asset) => (asset as ZanoAsset).assetId; diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index 45e28379d..5bc1ee952 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.19.1" -MONERO_COM_BUILD_NUMBER=110 +MONERO_COM_VERSION="1.20.0" +MONERO_COM_BUILD_NUMBER=111 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" MONERO_COM_SCHEME="monero.com" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.22.1" -CAKEWALLET_BUILD_NUMBER=242 +CAKEWALLET_VERSION="4.23.0" +CAKEWALLET_BUILD_NUMBER=244 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_SCHEME="cakewallet" diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index 079b12391..c599c8a1b 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.19.1" -MONERO_COM_BUILD_NUMBER=107 +MONERO_COM_VERSION="1.20.0" +MONERO_COM_BUILD_NUMBER=108 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.22.1" -CAKEWALLET_BUILD_NUMBER=289 +CAKEWALLET_VERSION="4.23.0" +CAKEWALLET_BUILD_NUMBER=291 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/scripts/linux/app_env.sh b/scripts/linux/app_env.sh index 5103eeccc..06c6b040e 100755 --- a/scripts/linux/app_env.sh +++ b/scripts/linux/app_env.sh @@ -14,8 +14,8 @@ if [ -n "$1" ]; then fi CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="1.12.1" -CAKEWALLET_BUILD_NUMBER=43 +CAKEWALLET_VERSION="1.13.0" +CAKEWALLET_BUILD_NUMBER=44 if ! [[ " ${TYPES[*]} " =~ " ${APP_LINUX_TYPE} " ]]; then echo "Wrong app type." diff --git a/scripts/macos/app_config.sh b/scripts/macos/app_config.sh index e909bb9a2..bb4750803 100755 --- a/scripts/macos/app_config.sh +++ b/scripts/macos/app_config.sh @@ -36,7 +36,7 @@ case $APP_MACOS_TYPE in $MONERO_COM) CONFIG_ARGS="--monero";; $CAKEWALLET) - CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero --zano";; #--haven + CONFIG_ARGS="--monero --bitcoin --ethereum --polygon --nano --bitcoinCash --solana --tron --wownero";; #--haven esac cp -rf pubspec_description.yaml pubspec.yaml diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh index af2b6ab1e..36898948e 100755 --- a/scripts/macos/app_env.sh +++ b/scripts/macos/app_env.sh @@ -16,13 +16,13 @@ if [ -n "$1" ]; then fi MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.9.1" -MONERO_COM_BUILD_NUMBER=40 +MONERO_COM_VERSION="1.10.0" +MONERO_COM_BUILD_NUMBER=41 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="1.15.1" -CAKEWALLET_BUILD_NUMBER=101 +CAKEWALLET_VERSION="1.16.0" +CAKEWALLET_BUILD_NUMBER=102 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then diff --git a/scripts/windows/build_exe_installer.iss b/scripts/windows/build_exe_installer.iss index 950800896..40c2a8af5 100644 --- a/scripts/windows/build_exe_installer.iss +++ b/scripts/windows/build_exe_installer.iss @@ -1,5 +1,5 @@ #define MyAppName "Cake Wallet" -#define MyAppVersion "0.3.1" +#define MyAppVersion "0.4.0" #define MyAppPublisher "Cake Labs LLC" #define MyAppURL "https://cakewallet.com/" #define MyAppExeName "CakeWallet.exe" diff --git a/tool/configure.dart b/tool/configure.dart index 0cf1a5da2..f363dba15 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -1447,7 +1447,7 @@ abstract class Zano { double formatterIntAmountToDouble({required int amount, required CryptoCurrency currency, required bool forFee}); int formatterParseAmount({required String amount, required CryptoCurrency currency}); WalletService createZanoWalletService(Box walletInfoSource); - CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo tx); + CryptoCurrency? assetOfTransaction(WalletBase wallet, TransactionInfo tx); List getZanoAssets(WalletBase wallet); String getZanoAssetAddress(CryptoCurrency asset); Future changeZanoAssetAvailability(WalletBase wallet, CryptoCurrency token);