From 8855e5a605166dc12ef7a639477a45fddbe6e26a Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 2 Oct 2025 11:07:40 -0600 Subject: [PATCH 01/30] rename pubspec template file --- scripts/app_config/templates/configure_template_files.sh | 2 +- .../templates/{pubspec.template => pubspec.template.yaml} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename scripts/app_config/templates/{pubspec.template => pubspec.template.yaml} (100%) diff --git a/scripts/app_config/templates/configure_template_files.sh b/scripts/app_config/templates/configure_template_files.sh index 35cb26219a..b4731683da 100755 --- a/scripts/app_config/templates/configure_template_files.sh +++ b/scripts/app_config/templates/configure_template_files.sh @@ -4,7 +4,7 @@ set -x -e export TEMPLATES_DIR="${APP_PROJECT_ROOT_DIR}/scripts/app_config/templates" -export T_PUBSPEC="${TEMPLATES_DIR}/pubspec.template" +export T_PUBSPEC="${TEMPLATES_DIR}/pubspec.template.yaml" export ACTUAL_PUBSPEC="${APP_PROJECT_ROOT_DIR}/pubspec.yaml" export ANDROID_TF_0="android/app/build.gradle" diff --git a/scripts/app_config/templates/pubspec.template b/scripts/app_config/templates/pubspec.template.yaml similarity index 100% rename from scripts/app_config/templates/pubspec.template rename to scripts/app_config/templates/pubspec.template.yaml From 93ff3407a6ab757a2363a1172cf0e5265d45a07a Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 2 Oct 2025 15:02:54 -0600 Subject: [PATCH 02/30] WIP --- .gitignore | 3 + analysis_options.yaml | 2 + .../wallet/impl/mimblewimblecoin_wallet.dart | 159 ++++---- lib/wl_gen/interfaces/libmwc_interface.dart | 198 ++++++++++ scripts/app_config/configure_stack_wallet.sh | 25 ++ .../templates/pubspec.template.yaml | 6 +- tool/gen_interfaces.dart | 83 +++++ tool/process_pubspec_deps.dart | 96 +++++ .../MWC_libmwc_interface_impl.template.dart | 348 ++++++++++++++++++ 9 files changed, 829 insertions(+), 91 deletions(-) create mode 100644 lib/wl_gen/interfaces/libmwc_interface.dart create mode 100644 tool/gen_interfaces.dart create mode 100644 tool/process_pubspec_deps.dart create mode 100644 tool/wl_templates/MWC_libmwc_interface_impl.template.dart diff --git a/.gitignore b/.gitignore index 30fb5a6e17..e360da5157 100644 --- a/.gitignore +++ b/.gitignore @@ -112,3 +112,6 @@ crypto_plugins/cs_monero/built_outputs crypto_plugins/cs_monero/build crypto_plugins/*.diff /devtools_options.yaml + +# generated interfaces +lib/wl_gen/generated/ \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml index db030aa148..1252b9a85b 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -18,6 +18,8 @@ analyzer: - "crypto_plugins/**" - "bin/cache/**" - "lib/generated_plugin_registrant.dart" + - "lib/wl_gen/generated/**" + - '**/*.template.dart' # For more information see: # https://dart.dev/guides/language/analysis-options#enabling-additional-type-checks diff --git a/lib/wallets/wallet/impl/mimblewimblecoin_wallet.dart b/lib/wallets/wallet/impl/mimblewimblecoin_wallet.dart index 8eec728dbe..3850cb7500 100644 --- a/lib/wallets/wallet/impl/mimblewimblecoin_wallet.dart +++ b/lib/wallets/wallet/impl/mimblewimblecoin_wallet.dart @@ -4,9 +4,6 @@ import 'dart:io'; import 'package:decimal/decimal.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter_libmwc/lib.dart' as mimblewimblecoin; -import 'package:flutter_libmwc/models/transaction.dart' - as mimblewimblecoin_models; import 'package:isar_community/isar.dart'; import 'package:mutex/mutex.dart'; import 'package:stack_wallet_backup/generate_password.dart'; @@ -33,6 +30,7 @@ import '../../../utilities/flutter_secure_storage_interface.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/stack_file_system.dart'; import '../../../utilities/test_mwcmqs_connection.dart'; +import '../../../wl_gen/interfaces/libmwc_interface.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/bip39_wallet.dart'; @@ -109,10 +107,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { if (password == null) { throw Exception('Wallet password not found'); } - final opened = await mimblewimblecoin.Libmwc.openWallet( - config: config, - password: password, - ); + final opened = await libMwc.openWallet(config: config, password: password); await secureStorageInterface.write( key: '${walletId}_wallet', value: opened, @@ -123,10 +118,11 @@ class MimblewimblecoinWallet extends Bip39Wallet { /// Returns an empty String on success, error message on failure. Future cancelPendingTransactionAndPost(String txSlateId) async { try { - final String wallet = - (await secureStorageInterface.read(key: '${walletId}_wallet'))!; + final String wallet = (await secureStorageInterface.read( + key: '${walletId}_wallet', + ))!; - final result = await mimblewimblecoin.Libmwc.cancelTransaction( + final result = await libMwc.cancelTransaction( wallet: wallet, transactionId: txSlateId, ); @@ -177,7 +173,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { final handle = await _ensureWalletOpen(); // Generate S1 slate JSON. - final s1Json = await mimblewimblecoin.Libmwc.txInit( + final s1Json = await libMwc.txInit( wallet: handle, amount: amount.raw.toInt(), minimumConfirmations: @@ -187,7 +183,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { ); // Encode to slatepack. - final encoded = await mimblewimblecoin.Libmwc.encodeSlatepack( + final encoded = await libMwc.encodeSlatepack( slateJson: s1Json, recipientAddress: recipientAddress, encrypt: encrypt, @@ -260,15 +256,12 @@ class MimblewimblecoinWallet extends Bip39Wallet { final handle = await secureStorageInterface.read( key: '${walletId}_wallet', ); - final result = - handle != null - ? await mimblewimblecoin.Libmwc.decodeSlatepackWithWallet( - wallet: handle, - slatepack: slatepack, - ) - : await mimblewimblecoin.Libmwc.decodeSlatepack( - slatepack: slatepack, - ); + final result = handle != null + ? await libMwc.decodeSlatepackWithWallet( + wallet: handle, + slatepack: slatepack, + ) + : await libMwc.decodeSlatepack(slatepack: slatepack); return SlatepackDecodeResult( success: true, @@ -289,19 +282,19 @@ class MimblewimblecoinWallet extends Bip39Wallet { final handle = await _ensureWalletOpen(); // Decode to get slate JSON and sender address. - final decoded = await mimblewimblecoin.Libmwc.decodeSlatepackWithWallet( + final decoded = await libMwc.decodeSlatepackWithWallet( wallet: handle, slatepack: slatepack, ); // Receive and get updated slate JSON. - final received = await mimblewimblecoin.Libmwc.txReceiveDetailed( + final received = await libMwc.txReceiveDetailed( wallet: handle, slateJson: decoded.slateJson, ); // Encode response back to sender if address available. - final encoded = await mimblewimblecoin.Libmwc.encodeSlatepack( + final encoded = await libMwc.encodeSlatepack( slateJson: received.slateJson, recipientAddress: decoded.senderAddress, encrypt: decoded.senderAddress != null, @@ -328,13 +321,13 @@ class MimblewimblecoinWallet extends Bip39Wallet { final handle = await _ensureWalletOpen(); // Decode to get slate JSON. - final decoded = await mimblewimblecoin.Libmwc.decodeSlatepackWithWallet( + final decoded = await libMwc.decodeSlatepackWithWallet( wallet: handle, slatepack: slatepack, ); // Finalize transaction. - final finalized = await mimblewimblecoin.Libmwc.txFinalize( + final finalized = await libMwc.txFinalize( wallet: handle, slateJson: decoded.slateJson, ); @@ -358,7 +351,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { final wallet = await secureStorageInterface.read( key: '${walletId}_wallet', ); - mimblewimblecoin.Libmwc.startMwcMqsListener( + libMwc.startMwcMqsListener( wallet: wallet!, mwcmqsConfig: mwcmqsConfig.toString(), ); @@ -371,7 +364,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { /// Stop MWCMQS listener. Future stopSlatepackListener() async { try { - mimblewimblecoin.Libmwc.stopMwcMqsListener(); + libMwc.stopMwcMqsListener(); } catch (e, s) { Logging.instance.e('Failed to stop slatepack listener: $e\n$s'); } @@ -379,7 +372,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { /// Validate MWC address. bool validateMwcAddress(String address) { - return mimblewimblecoin.Libmwc.validateSendAddress(address: address); + return libMwc.validateSendAddress(address: address); } /// Detect if an address is a slatepack. @@ -422,15 +415,12 @@ class MimblewimblecoinWallet extends Bip39Wallet { ); // Decode the slatepack - final decoded = - wallet != null - ? await mimblewimblecoin.Libmwc.decodeSlatepackWithWallet( - wallet: wallet, - slatepack: slatepack, - ) - : await mimblewimblecoin.Libmwc.decodeSlatepack( - slatepack: slatepack, - ); + final decoded = wallet != null + ? await libMwc.decodeSlatepackWithWallet( + wallet: wallet, + slatepack: slatepack, + ) + : await libMwc.decodeSlatepack(slatepack: slatepack); // Parse the slate JSON to extract metadata final slateData = jsonDecode(decoded.slateJson); @@ -610,7 +600,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); try { final available = info.cachedBalance.spendable.raw.toInt(); - final transactionFees = await mimblewimblecoin.Libmwc.getTransactionFees( + final transactionFees = await libMwc.getTransactionFees( wallet: wallet!, amount: satoshiAmount, minimumConfirmations: cryptoCurrency.minConfirms, @@ -619,8 +609,9 @@ class MimblewimblecoinWallet extends Bip39Wallet { int realFee = 0; try { - realFee = - (Decimal.parse(transactionFees.fee.toString())).toBigInt().toInt(); + realFee = (Decimal.parse( + transactionFees.fee.toString(), + )).toBigInt().toInt(); } catch (e, s) { //todo: come back to this debugPrint("$e $s"); @@ -639,7 +630,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { if (!syncMutex.isLocked) { await syncMutex.protect(() async { // How does getWalletBalances start syncing???? - await mimblewimblecoin.Libmwc.getWalletBalances( + await libMwc.getWalletBalances( wallet: wallet!, refreshFromNode: refreshFromNode, minimumConfirmations: 10, @@ -661,7 +652,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { _allWalletBalances() async { final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); const refreshFromNode = 0; - return await mimblewimblecoin.Libmwc.getWalletBalances( + return await libMwc.getWalletBalances( wallet: wallet!, refreshFromNode: refreshFromNode, minimumConfirmations: cryptoCurrency.minConfirms, @@ -729,7 +720,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { ) async { final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); - final walletAddress = await mimblewimblecoin.Libmwc.getAddressInfo( + final walletAddress = await libMwc.getAddressInfo( wallet: wallet!, index: index, ); @@ -752,7 +743,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { Future _startScans() async { try { //First stop the current listener - mimblewimblecoin.Libmwc.stopMwcMqsListener(); + libMwc.stopMwcMqsListener(); final wallet = await secureStorageInterface.read( key: '${walletId}_wallet', ); @@ -774,7 +765,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { "chainHeight: $chainHeight, lastScannedBlock: $lastScannedBlock", ); - final int nextScannedBlock = await mimblewimblecoin.Libmwc.scanOutputs( + final int nextScannedBlock = await libMwc.scanOutputs( wallet: wallet!, startHeight: lastScannedBlock, numberOfBlocks: scanChunkSize, @@ -809,7 +800,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { Logging.instance.i("STARTING WALLET LISTENER ...."); final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); final MwcMqsConfigModel mwcmqsConfig = await getMwcMqsConfig(); - mimblewimblecoin.Libmwc.startMwcMqsListener( + libMwc.startMwcMqsListener( wallet: wallet!, mwcmqsConfig: mwcmqsConfig.toString(), ); @@ -877,7 +868,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { final String stringConfig = await _getConfig(); final MwcMqsConfigModel mwcmqsConfig = await getMwcMqsConfig(); //if (!_logsInitialized) { - // await mimblewimblecoin.Libmwc.initLogs(config: stringConfig); + // await libMwc.initLogs(config: stringConfig); // _logsInitialized = true; // Set flag to true after initializing // } await secureStorageInterface.write( @@ -895,7 +886,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { final String name = walletId; - await mimblewimblecoin.Libmwc.initializeNewWallet( + await libMwc.initializeNewWallet( config: stringConfig, mnemonic: mnemonicString, password: password, @@ -903,7 +894,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { ); //Open wallet - encodedWallet = await mimblewimblecoin.Libmwc.openWallet( + encodedWallet = await libMwc.openWallet( config: stringConfig, password: password, ); @@ -937,14 +928,14 @@ class MimblewimblecoinWallet extends Bip39Wallet { try { final config = await _getRealConfig(); //if (!_logsInitialized) { - // await mimblewimblecoin.Libmwc.initLogs(config: config); + // await libMwc.initLogs(config: config); // _logsInitialized = true; // Set flag to true after initializing //} final password = await secureStorageInterface.read( key: '${walletId}_password', ); - final walletOpen = await mimblewimblecoin.Libmwc.openWallet( + final walletOpen = await libMwc.openWallet( config: config, password: password!, ); @@ -991,7 +982,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { if (receiverAddress.startsWith("http://") || receiverAddress.startsWith("https://")) { - transaction = await mimblewimblecoin.Libmwc.txHttpSend( + transaction = await libMwc.txHttpSend( wallet: wallet!, selectionStrategyIsAll: 0, minimumConfirmations: cryptoCurrency.minConfirms, @@ -1000,7 +991,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { address: txData.recipients!.first.address, ); } else if (receiverAddress.startsWith("mwcmqs://")) { - transaction = await mimblewimblecoin.Libmwc.createTransaction( + transaction = await libMwc.createTransaction( wallet: wallet!, amount: txData.recipients!.first.amount.raw.toInt(), address: txData.recipients!.first.address, @@ -1129,7 +1120,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { value: mwcmqsConfig.toString(), ); - await mimblewimblecoin.Libmwc.recoverWallet( + await libMwc.recoverWallet( config: stringConfig, password: password, mnemonic: await getMnemonic(), @@ -1153,7 +1144,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { ); //Open Wallet - final walletOpen = await mimblewimblecoin.Libmwc.openWallet( + final walletOpen = await libMwc.openWallet( config: stringConfig, password: password, ); @@ -1331,20 +1322,19 @@ class MimblewimblecoinWallet extends Bip39Wallet { ); const refreshFromNode = 1; - final myAddresses = - await mainDB - .getAddresses(walletId) - .filter() - .typeEqualTo(AddressType.mimbleWimble) - .and() - .subTypeEqualTo(AddressSubType.receiving) - .and() - .valueIsNotEmpty() - .valueProperty() - .findAll(); + final myAddresses = await mainDB + .getAddresses(walletId) + .filter() + .typeEqualTo(AddressType.mimbleWimble) + .and() + .subTypeEqualTo(AddressSubType.receiving) + .and() + .valueIsNotEmpty() + .valueProperty() + .findAll(); final myAddressesSet = myAddresses.toSet(); - final transactions = await mimblewimblecoin.Libmwc.getTransactions( + final transactions = await libMwc.getTransactions( wallet: wallet!, refreshFromNode: refreshFromNode, ); @@ -1357,13 +1347,12 @@ class MimblewimblecoinWallet extends Bip39Wallet { Logging.instance.i("tx: $tx"); final isIncoming = - tx.txType == mimblewimblecoin_models.TransactionType.TxReceived || - tx.txType == - mimblewimblecoin_models.TransactionType.TxReceivedCancelled; + libMwc.txTypeIsReceived(tx.txType) || + libMwc.txTypeIsReceiveCancelled(tx.txType); final slateId = tx.txSlateId; final commitId = slatesToCommits[slateId]?['commitId'] as String?; - final numberOfMessages = tx.messages?.messages.length; - final onChainNote = tx.messages?.messages[0].message; + final numberOfMessages = tx.messages?.length; + final onChainNote = tx.messages?.first.message; final addressFrom = slatesToCommits[slateId]?["from"] as String?; final addressTo = slatesToCommits[slateId]?["to"] as String?; @@ -1446,15 +1435,12 @@ class MimblewimblecoinWallet extends Bip39Wallet { "slateId": slateId, "onChainNote": onChainNote, "isCancelled": - tx.txType == - mimblewimblecoin_models.TransactionType.TxSentCancelled || - tx.txType == - mimblewimblecoin_models.TransactionType.TxReceivedCancelled, - "overrideFee": - Amount( - rawValue: BigInt.from(fee), - fractionDigits: cryptoCurrency.fractionDigits, - ).toJsonString(), + libMwc.txTypeIsSentCancelled(tx.txType) || + libMwc.txTypeIsReceiveCancelled(tx.txType), + "overrideFee": Amount( + rawValue: BigInt.from(fee), + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), }; final txn = TransactionV2( @@ -1531,9 +1517,7 @@ class MimblewimblecoinWallet extends Bip39Wallet { @override Future updateChainHeight() async { final config = await _getRealConfig(); - final latestHeight = await mimblewimblecoin.Libmwc.getChainHeight( - config: config, - ); + final latestHeight = await libMwc.getChainHeight(config: config); await info.updateCachedChainHeight( newHeight: latestHeight, isar: mainDB.isar, @@ -1605,10 +1589,7 @@ Future deleteMimblewimblecoinWallet({ return "Tried to delete non existent mimblewimblecoin wallet file with walletId=$walletId"; } else { try { - return mimblewimblecoin.Libmwc.deleteWallet( - wallet: wallet, - config: config!, - ); + return libMwc.deleteWallet(wallet: wallet, config: config!); } catch (e, s) { Logging.instance.e("$e\n$s"); return "deleteMimblewimblecoinWallet($walletId) failed..."; diff --git a/lib/wl_gen/interfaces/libmwc_interface.dart b/lib/wl_gen/interfaces/libmwc_interface.dart new file mode 100644 index 0000000000..60536cfb96 --- /dev/null +++ b/lib/wl_gen/interfaces/libmwc_interface.dart @@ -0,0 +1,198 @@ +export '../generated/libmwc_interface_impl.dart'; + +abstract class LibMwcInterface { + const LibMwcInterface(); + + bool txTypeIsReceived(Enum value); + bool txTypeIsReceiveCancelled(Enum value); + bool txTypeIsSentCancelled(Enum value); + + Future initializeNewWallet({ + required String config, + required String mnemonic, + required String password, + required String name, + }); + + Future openWallet({required String config, required String password}); + + Future recoverWallet({ + required String config, + required String password, + required String mnemonic, + required String name, + }); + + Future<({String commitId, String slateId})> txHttpSend({ + required String wallet, + required int selectionStrategyIsAll, + required int minimumConfirmations, + required String message, + required int amount, + required String address, + }); + + Future<({String commitId, String slateId})> createTransaction({ + required String wallet, + required int amount, + required String address, + required int secretKeyIndex, + required String mwcmqsConfig, + required int minimumConfirmations, + required String note, + }); + + Future cancelTransaction({ + required String wallet, + required String transactionId, + }); + + Future txInit({ + required String wallet, + required int amount, + int minimumConfirmations = 1, + bool selectionStrategyIsAll = false, + String message = '', + }); + + Future> getTransactions({ + required String wallet, + required int refreshFromNode, + }); + + Future<({String? recipientAddress, String slatepack, bool wasEncrypted})> + encodeSlatepack({ + required String slateJson, + String? recipientAddress, + bool encrypt = false, + String? wallet, + }); + Future< + ({ + String? recipientAddress, + String? senderAddress, + String slateJson, + bool wasEncrypted, + }) + > + decodeSlatepackWithWallet({ + required String wallet, + required String slatepack, + }); + + Future< + ({ + String? recipientAddress, + String? senderAddress, + String slateJson, + bool wasEncrypted, + }) + > + decodeSlatepack({required String slatepack}); + + Future<({String commitId, String slateId, String slateJson})> + txReceiveDetailed({required String wallet, required String slateJson}); + + Future<({String commitId, String slateId})> txFinalize({ + required String wallet, + required String slateJson, + }); + + void startMwcMqsListener({ + required String wallet, + required String mwcmqsConfig, + }); + + void stopMwcMqsListener(); + + bool validateSendAddress({required String address}); + + Future<({int fee, bool strategyUseAll, int total})> getTransactionFees({ + required String wallet, + required int amount, + required int minimumConfirmations, + required int available, + }); + + Future< + ({ + double awaitingFinalization, + double pending, + double spendable, + double total, + }) + > + getWalletBalances({ + required String wallet, + required int refreshFromNode, + required int minimumConfirmations, + }); + + Future getAddressInfo({required String wallet, required int index}); + + Future scanOutputs({ + required String wallet, + required int startHeight, + required int numberOfBlocks, + }); + + Future getChainHeight({required String config}); + + Future deleteWallet({required String wallet, required String config}); +} + +class MwcTransaction { + final String parentKeyId; + final int id; + final String? txSlateId; + final Enum txType; + final String creationTs; + final String confirmationTs; + final bool confirmed; + final int numInputs; + final int numOutputs; + final String amountCredited; + final String amountDebited; + final String? fee; + final String? ttlCutoffHeight; + final List? messages; + final String? storedTx; + final String? kernelExcess; + final int? kernelLookupMinHeight; + final String? paymentProof; + + MwcTransaction({ + required this.parentKeyId, + required this.id, + this.txSlateId, + required this.txType, + required this.creationTs, + required this.confirmationTs, + required this.confirmed, + required this.numInputs, + required this.numOutputs, + required this.amountCredited, + required this.amountDebited, + this.fee, + this.ttlCutoffHeight, + this.messages, + this.storedTx, + this.kernelExcess, + this.kernelLookupMinHeight, + this.paymentProof, + }); +} + +class MwcMessage { + final String id; + final String publicKey; + final String? message; + final String? messageSig; + + MwcMessage({ + required this.id, + required this.publicKey, + this.message, + this.messageSig, + }); +} diff --git a/scripts/app_config/configure_stack_wallet.sh b/scripts/app_config/configure_stack_wallet.sh index b407b7d473..b4f503d346 100755 --- a/scripts/app_config/configure_stack_wallet.sh +++ b/scripts/app_config/configure_stack_wallet.sh @@ -23,6 +23,31 @@ else sed -i "s/description: PLACEHOLDER/description: ${NEW_NAME}/g" "${PUBSPEC_FILE}" fi +dart "${APP_PROJECT_ROOT_DIR}/tool/process_pubspec_deps.dart" \ + "${PUBSPEC_FILE}" \ + MWC \ + MWEBD \ + XMR \ + SAL \ + TOR \ + EPIC \ + FIRO \ + XEL \ + FROST + +dart "${APP_PROJECT_ROOT_DIR}/tool/gen_interfaces.dart" \ + "${APP_PROJECT_ROOT_DIR}/tool/wl_templates" \ + "${APP_PROJECT_ROOT_DIR}/lib/wl_gen/generated" \ + MWC \ + MWEBD \ + XMR \ + SAL \ + TOR \ + EPIC \ + FIRO \ + XEL \ + FROST + pushd "${APP_PROJECT_ROOT_DIR}" BUILT_COMMIT_HASH=$(git log -1 --pretty=format:"%H") popd diff --git a/scripts/app_config/templates/pubspec.template.yaml b/scripts/app_config/templates/pubspec.template.yaml index 78f73adf66..98a63dbb86 100644 --- a/scripts/app_config/templates/pubspec.template.yaml +++ b/scripts/app_config/templates/pubspec.template.yaml @@ -47,8 +47,10 @@ dependencies: flutter_libepiccash: path: ./crypto_plugins/flutter_libepiccash - flutter_libmwc: - path: ./crypto_plugins/flutter_libmwc +# %%ENABLE_MWC%% +# flutter_libmwc: +# path: ./crypto_plugins/flutter_libmwc +# %%END_ENABLE_MWC%% bitcoindart: git: diff --git a/tool/gen_interfaces.dart b/tool/gen_interfaces.dart new file mode 100644 index 0000000000..fc48494555 --- /dev/null +++ b/tool/gen_interfaces.dart @@ -0,0 +1,83 @@ +import 'dart:io'; + +void main(List args) { + if (args.length < 2) { + print( + 'Usage: dart process_pubspec_deps.dart [marker2 ...]', + ); + exit(1); + } + + final generatedDir = Directory(args[1]); + if (!generatedDir.existsSync()) generatedDir.createSync(); + + final templatesDir = args[0]; + + final List onFilePrefixes = args.length > 2 ? args.sublist(2) : []; + + final templates = Directory(templatesDir).listSync().whereType(); + + print(templates.map((e) => e.uri.pathSegments.last)); + + for (final template in templates) { + final templateName = template.uri.pathSegments.last; + + final prefix = templateName.split("_").first; + + final outfileUri = generatedDir.uri.resolve( + templateName + .replaceFirst("${prefix}_", "") + .replaceFirst(".template.", "."), + ); + + _genFile( + outfileUri.toFilePath(), + template, + onFilePrefixes.contains(prefix), + ); + } +} + +void _genFile(String outputFilePath, File template, bool on) { + final lines = template.readAsLinesSync(); + + // add empty line and gen warning + + final List updatedLines = [ + "// GENERATED CODE - DO NOT MODIFY BY HAND", + "", + ]; + + final keepMarker = on ? "ON" : "OFF"; + final dropMarker = on ? "OFF" : "ON"; + + bool? keepLine; + + for (final line in lines) { + if (line.trim() == "//$keepMarker") { + keepLine = true; + continue; + } + + if (line.trim() == "//$dropMarker") { + keepLine = false; + continue; + } + + if (line.trim().startsWith("//END_")) { + keepLine = null; + continue; + } + + if (keepLine != false) { + updatedLines.add(line); + } + } + + // add empty line + if (updatedLines.last.isNotEmpty) updatedLines.add(""); + + File(outputFilePath).writeAsStringSync(updatedLines.join('\n')); + + print("Done gen of $template => $outputFilePath"); +} diff --git a/tool/process_pubspec_deps.dart b/tool/process_pubspec_deps.dart new file mode 100644 index 0000000000..279dfe26af --- /dev/null +++ b/tool/process_pubspec_deps.dart @@ -0,0 +1,96 @@ +import 'dart:io'; + +void main(List args) { + if (args.isEmpty) { + print( + 'Usage: dart process_pubspec_deps.dart [marker2 ...]', + ); + exit(1); + } + + if (args.length == 1) { + print('No markers specified. Nothing to do.'); + exit(0); + } + + _process(args[0], args.sublist(1)); +} + +void _process(final String filePath, final List enableCoinMarkers) { + final lines = File(filePath).readAsLinesSync(); + + String? activeMarker; + final List updatedLines = []; + + for (final line in lines) { + bool markerSet = false; + + // Check if line starts any marker block + for (final marker in enableCoinMarkers) { + if (line.contains('# %%ENABLE_$marker%%')) { + activeMarker = marker; + markerSet = true; + updatedLines.add(line); + break; + } + } + + if (markerSet) continue; + + // Check if line ends the active marker block + if (activeMarker != null && + line.contains('# %%END_ENABLE_$activeMarker%%')) { + activeMarker = null; + updatedLines.add(line); + continue; + } + + // If inside an active marker block, uncomment line + if (activeMarker != null) { + updatedLines.add(line.replaceFirst("#", "")); + } else { + updatedLines.add(line); + } + } + + File(filePath).writeAsStringSync(updatedLines.join('\n')); + print( + 'Updated pubspec.yaml with enabled blocks: ${enableCoinMarkers.join(", ")}', + ); +} + +void _genFile(String outputFilePath, String template, bool on) { + final lines = File(template).readAsLinesSync(); + + // add empty line and gen warning + + final List updatedLines = [ + "// GENERATED CODE - DO NOT MODIFY BY HAND", + "", + ]; + + final keepMarker = on ? "ON" : "OFF"; + + bool? keepLine; + + for (final line in lines) { + if (line.trim() == "//$keepMarker") { + keepLine = true; + continue; + } + + if (line.trim() == "//END_$keepMarker") { + keepLine = false; + continue; + } + + if (keepLine != false) { + updatedLines.add(line); + } + } + + // add empty line + if (updatedLines.last.isNotEmpty) updatedLines.add(""); + + File(outputFilePath).writeAsStringSync(updatedLines.join('\n')); +} diff --git a/tool/wl_templates/MWC_libmwc_interface_impl.template.dart b/tool/wl_templates/MWC_libmwc_interface_impl.template.dart new file mode 100644 index 0000000000..356c071ec9 --- /dev/null +++ b/tool/wl_templates/MWC_libmwc_interface_impl.template.dart @@ -0,0 +1,348 @@ +//ON +import 'package:flutter_libmwc/lib.dart' as mimblewimblecoin; +import 'package:flutter_libmwc/models/transaction.dart' + as mimblewimblecoin_models; + +//END_ON +import '../interfaces/libmwc_interface.dart'; + +LibMwcInterface get libMwc => _getLibMwc(); + +//OFF +LibMwcInterface _getLibMwc() => throw Exception("MWC not enabled!"); + +//END_OFF + +//ON +LibMwcInterface _getLibMwc() => const _LibMwcInterfaceImpl(); + +final class _LibMwcInterfaceImpl extends LibMwcInterface { + const _LibMwcInterfaceImpl(); + + @override + Future cancelTransaction({ + required String wallet, + required String transactionId, + }) { + return mimblewimblecoin.Libmwc.cancelTransaction( + wallet: wallet, + transactionId: transactionId, + ); + } + + @override + Future<({String commitId, String slateId})> createTransaction({ + required String wallet, + required int amount, + required String address, + required int secretKeyIndex, + required String mwcmqsConfig, + required int minimumConfirmations, + required String note, + }) { + return mimblewimblecoin.Libmwc.createTransaction( + wallet: wallet, + amount: amount, + address: address, + secretKeyIndex: secretKeyIndex, + mwcmqsConfig: mwcmqsConfig, + minimumConfirmations: minimumConfirmations, + note: note, + ); + } + + @override + Future< + ({ + String? recipientAddress, + String? senderAddress, + String slateJson, + bool wasEncrypted, + }) + > + decodeSlatepack({required String slatepack}) { + return mimblewimblecoin.Libmwc.decodeSlatepack(slatepack: slatepack); + } + + @override + Future< + ({ + String? recipientAddress, + String? senderAddress, + String slateJson, + bool wasEncrypted, + }) + > + decodeSlatepackWithWallet({ + required String wallet, + required String slatepack, + }) { + return mimblewimblecoin.Libmwc.decodeSlatepackWithWallet( + wallet: wallet, + slatepack: slatepack, + ); + } + + @override + Future deleteWallet({ + required String wallet, + required String config, + }) { + return mimblewimblecoin.Libmwc.deleteWallet(wallet: wallet, config: config); + } + + @override + Future<({String? recipientAddress, String slatepack, bool wasEncrypted})> + encodeSlatepack({ + required String slateJson, + String? recipientAddress, + bool encrypt = false, + String? wallet, + }) { + return mimblewimblecoin.Libmwc.encodeSlatepack( + slateJson: slateJson, + recipientAddress: recipientAddress, + encrypt: encrypt, + wallet: wallet, + ); + } + + @override + Future getAddressInfo({required String wallet, required int index}) { + return mimblewimblecoin.Libmwc.getAddressInfo(wallet: wallet, index: index); + } + + @override + Future getChainHeight({required String config}) { + return mimblewimblecoin.Libmwc.getChainHeight(config: config); + } + + @override + Future<({int fee, bool strategyUseAll, int total})> getTransactionFees({ + required String wallet, + required int amount, + required int minimumConfirmations, + required int available, + }) { + return mimblewimblecoin.Libmwc.getTransactionFees( + wallet: wallet, + amount: amount, + minimumConfirmations: minimumConfirmations, + available: available, + ); + } + + @override + Future> getTransactions({ + required String wallet, + required int refreshFromNode, + }) async { + final transactions = await mimblewimblecoin.Libmwc.getTransactions( + wallet: wallet, + refreshFromNode: refreshFromNode, + ); + + return transactions + .map( + (e) => MwcTransaction( + parentKeyId: e.parentKeyId, + id: e.id, + txType: e.txType, + creationTs: e.creationTs, + confirmationTs: e.confirmationTs, + confirmed: e.confirmed, + numInputs: e.numInputs, + numOutputs: e.numOutputs, + amountCredited: e.amountCredited, + amountDebited: e.amountDebited, + txSlateId: e.txSlateId, + fee: e.fee, + ttlCutoffHeight: e.ttlCutoffHeight, + messages: e.messages?.messages + .map( + (f) => MwcMessage( + id: f.id, + publicKey: f.publicKey, + message: f.message, + messageSig: f.messageSig, + ), + ) + .toList(), + storedTx: e.storedTx, + kernelExcess: e.kernelExcess, + kernelLookupMinHeight: e.kernelLookupMinHeight, + paymentProof: e.paymentProof, + ), + ) + .toList(); + } + + @override + Future< + ({ + double awaitingFinalization, + double pending, + double spendable, + double total, + }) + > + getWalletBalances({ + required String wallet, + required int refreshFromNode, + required int minimumConfirmations, + }) { + return mimblewimblecoin.Libmwc.getWalletBalances( + wallet: wallet, + refreshFromNode: refreshFromNode, + minimumConfirmations: minimumConfirmations, + ); + } + + @override + Future initializeNewWallet({ + required String config, + required String mnemonic, + required String password, + required String name, + }) { + return mimblewimblecoin.Libmwc.initializeNewWallet( + config: config, + mnemonic: mnemonic, + password: password, + name: name, + ); + } + + @override + Future openWallet({ + required String config, + required String password, + }) { + return mimblewimblecoin.Libmwc.openWallet( + config: config, + password: password, + ); + } + + @override + Future recoverWallet({ + required String config, + required String password, + required String mnemonic, + required String name, + }) { + return mimblewimblecoin.Libmwc.recoverWallet( + config: config, + password: password, + mnemonic: mnemonic, + name: name, + ); + } + + @override + Future scanOutputs({ + required String wallet, + required int startHeight, + required int numberOfBlocks, + }) { + return mimblewimblecoin.Libmwc.scanOutputs( + wallet: wallet, + startHeight: startHeight, + numberOfBlocks: numberOfBlocks, + ); + } + + @override + void startMwcMqsListener({ + required String wallet, + required String mwcmqsConfig, + }) { + return mimblewimblecoin.Libmwc.startMwcMqsListener( + wallet: wallet, + mwcmqsConfig: mwcmqsConfig, + ); + } + + @override + void stopMwcMqsListener() { + return mimblewimblecoin.Libmwc.stopMwcMqsListener(); + } + + @override + Future<({String commitId, String slateId})> txFinalize({ + required String wallet, + required String slateJson, + }) { + return mimblewimblecoin.Libmwc.txFinalize( + wallet: wallet, + slateJson: slateJson, + ); + } + + @override + Future<({String commitId, String slateId})> txHttpSend({ + required String wallet, + required int selectionStrategyIsAll, + required int minimumConfirmations, + required String message, + required int amount, + required String address, + }) { + return mimblewimblecoin.Libmwc.txHttpSend( + wallet: wallet, + selectionStrategyIsAll: selectionStrategyIsAll, + minimumConfirmations: minimumConfirmations, + message: message, + amount: amount, + address: address, + ); + } + + @override + Future txInit({ + required String wallet, + required int amount, + int minimumConfirmations = 1, + bool selectionStrategyIsAll = false, + String message = '', + }) { + return mimblewimblecoin.Libmwc.txInit( + wallet: wallet, + amount: amount, + message: message, + minimumConfirmations: minimumConfirmations, + selectionStrategyIsAll: selectionStrategyIsAll, + ); + } + + @override + Future<({String commitId, String slateId, String slateJson})> + txReceiveDetailed({required String wallet, required String slateJson}) { + return mimblewimblecoin.Libmwc.txReceiveDetailed( + wallet: wallet, + slateJson: slateJson, + ); + } + + @override + bool txTypeIsReceiveCancelled(Enum value) { + return value == mimblewimblecoin_models.TransactionType.TxReceivedCancelled; + } + + @override + bool txTypeIsReceived(Enum value) { + return value == mimblewimblecoin_models.TransactionType.TxReceived; + } + + @override + bool txTypeIsSentCancelled(Enum value) { + return value == mimblewimblecoin_models.TransactionType.TxSentCancelled; + } + + @override + bool validateSendAddress({required String address}) { + return mimblewimblecoin.Libmwc.validateSendAddress(address: address); + } +} + +//END_ON From a7861184617e9f77bf21cdf06876feda2a02fc9c Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 2 Oct 2025 16:19:30 -0600 Subject: [PATCH 03/30] prep config scripts --- scripts/app_config/configure_campfire.sh | 11 +++ scripts/app_config/configure_stack_duo.sh | 13 +++ .../templates/pubspec.template.yaml | 80 +++++++++++-------- 3 files changed, 72 insertions(+), 32 deletions(-) diff --git a/scripts/app_config/configure_campfire.sh b/scripts/app_config/configure_campfire.sh index 258058aae5..8f25938488 100755 --- a/scripts/app_config/configure_campfire.sh +++ b/scripts/app_config/configure_campfire.sh @@ -29,6 +29,17 @@ else sed -i "s/description: PLACEHOLDER/description: ${NEW_NAME}/g" "${PUBSPEC_FILE}" fi +dart "${APP_PROJECT_ROOT_DIR}/tool/process_pubspec_deps.dart" \ + "${PUBSPEC_FILE}" \ + TOR \ + FIRO + +dart "${APP_PROJECT_ROOT_DIR}/tool/gen_interfaces.dart" \ + "${APP_PROJECT_ROOT_DIR}/tool/wl_templates" \ + "${APP_PROJECT_ROOT_DIR}/lib/wl_gen/generated" \ + TOR \ + FIRO + pushd "${APP_PROJECT_ROOT_DIR}" BUILT_COMMIT_HASH=$(git log -1 --pretty=format:"%H") popd diff --git a/scripts/app_config/configure_stack_duo.sh b/scripts/app_config/configure_stack_duo.sh index 4c871fff3c..17de39bd3a 100755 --- a/scripts/app_config/configure_stack_duo.sh +++ b/scripts/app_config/configure_stack_duo.sh @@ -23,6 +23,19 @@ else sed -i "s/description: PLACEHOLDER/description: ${NEW_NAME}/g" "${PUBSPEC_FILE}" fi +dart "${APP_PROJECT_ROOT_DIR}/tool/process_pubspec_deps.dart" \ + "${PUBSPEC_FILE}" \ + XMR \ + TOR \ + FROST + +dart "${APP_PROJECT_ROOT_DIR}/tool/gen_interfaces.dart" \ + "${APP_PROJECT_ROOT_DIR}/tool/wl_templates" \ + "${APP_PROJECT_ROOT_DIR}/lib/wl_gen/generated" \ + XMR \ + TOR \ + FROST + pushd "${APP_PROJECT_ROOT_DIR}" BUILT_COMMIT_HASH=$(git log -1 --pretty=format:"%H") popd diff --git a/scripts/app_config/templates/pubspec.template.yaml b/scripts/app_config/templates/pubspec.template.yaml index 98a63dbb86..39da588390 100644 --- a/scripts/app_config/templates/pubspec.template.yaml +++ b/scripts/app_config/templates/pubspec.template.yaml @@ -24,18 +24,56 @@ dependencies: mutex: ^3.0.0 web_socket_channel: ^2.4.0 - frostdart: - path: ./crypto_plugins/frostdart +# %%ENABLE_FROST%% +# frostdart: +# path: ./crypto_plugins/frostdart +# %%END_ENABLE_FROST%% + +# %%ENABLE_XEL%% +# xelis_flutter: +# git: +# url: https://github.com/cypherstack/xelis-flutter-ffi.git +# ref: 5dd5c50713160fa15fb06ff44886ae035eed62fd +# %%END_ENABLE_XEL%% + +# %%ENABLE_FIRO%% +# flutter_libsparkmobile: +# git: +# url: https://github.com/cypherstack/flutter_libsparkmobile.git +# ref: 84a139a25ab1691762002fafcae351e3d444a5c7 +# %%END_ENABLE_FIRO%% + +# %%ENABLE_EPIC%% +# flutter_libepiccash: +# path: ./crypto_plugins/flutter_libepiccash +# %%END_ENABLE_EPIC%% - xelis_flutter: - git: - url: https://github.com/cypherstack/xelis-flutter-ffi.git - ref: 5dd5c50713160fa15fb06ff44886ae035eed62fd +# %%ENABLE_MWC%% +# flutter_libmwc: +# path: ./crypto_plugins/flutter_libmwc +# %%END_ENABLE_MWC%% - flutter_libsparkmobile: - git: - url: https://github.com/cypherstack/flutter_libsparkmobile.git - ref: 84a139a25ab1691762002fafcae351e3d444a5c7 +# %%ENABLE_TOR%% +# tor_ffi_plugin: +# git: +# url: https://github.com/cypherstack/tor.git +# ref: 21077186e6bf773ec8a7cd57ef149b2cee5daa7b +# %%END_ENABLE_TOR%% + +# %%ENABLE_XMR%% +# cs_monero: 1.0.0-pre.3 +# cs_monero_flutter_libs: 1.0.0-pre.0 +# monero_rpc: ^2.0.0 +# %%END_ENABLE_XMR%% + +# %%ENABLE_SAL%% +# cs_salvium: ^1.2.1 +# cs_salvium_flutter_libs: ^1.0.4 +# %%END_ENABLE_SAL%% + +# %%ENABLE_MWEB%% +# flutter_mwebd: ^0.0.1-pre.8 +# %%END_ENABLE_MWEB%% # cs_monero compat (unpublished) compat: @@ -44,14 +82,6 @@ dependencies: path: compat ref: 44b8c0f8e1cc7ddbfa33c0b3f11279e64646138 - flutter_libepiccash: - path: ./crypto_plugins/flutter_libepiccash - -# %%ENABLE_MWC%% -# flutter_libmwc: -# path: ./crypto_plugins/flutter_libmwc -# %%END_ENABLE_MWC%% - bitcoindart: git: url: https://github.com/cypherstack/bitcoindart.git @@ -67,11 +97,6 @@ dependencies: url: https://github.com/cypherstack/bip47.git ref: a6e7941b98a43a613708b1a12564bc17e712cfc7 - tor_ffi_plugin: - git: - url: https://github.com/cypherstack/tor.git - ref: 21077186e6bf773ec8a7cd57ef149b2cee5daa7b - fusiondart: git: url: https://github.com/cypherstack/fusiondart.git @@ -192,7 +217,6 @@ dependencies: calendar_date_picker2: ^1.0.2 sqlite3: 2.9.0 sqlite3_flutter_libs: 0.5.25 -# camera_linux: ^0.0.8 camera_linux: git: url: https://github.com/cypherstack/camera-linux @@ -207,11 +231,7 @@ dependencies: blockchain_utils: ^3.3.0 on_chain: ^4.0.1 cbor: ^6.3.3 - cs_monero: 1.0.0-pre.3 - cs_monero_flutter_libs: 1.0.0-pre.0 - monero_rpc: ^2.0.0 digest_auth: ^1.0.1 -# logger: ^2.5.0 logger: git: url: https://github.com/cypherstack/logger @@ -223,9 +243,6 @@ dependencies: drift: ^2.28.2 drift_flutter: ^0.2.7 path: ^1.9.1 - cs_salvium: ^1.2.1 - cs_salvium_flutter_libs: ^1.0.4 - flutter_mwebd: ^0.0.1-pre.8 mweb_client: ^0.2.0 fixnum: ^1.1.1 @@ -241,7 +258,6 @@ dev_dependencies: hive_test: ^1.0.1 mockito: ^5.4.6 mockingjay: ^0.2.0 -# lint: ^1.10.0 analyzer: ^8.2.2 import_sorter: ^4.6.0 flutter_lints: ^3.0.1 From 6ad4811e188a61aa077724f95831823bdc3e9ad9 Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 2 Oct 2025 17:08:07 -0600 Subject: [PATCH 04/30] prep config scripts fixes --- .../templates/pubspec.template.yaml | 32 +++++++-------- tool/process_pubspec_deps.dart | 40 +------------------ 2 files changed, 18 insertions(+), 54 deletions(-) diff --git a/scripts/app_config/templates/pubspec.template.yaml b/scripts/app_config/templates/pubspec.template.yaml index 39da588390..71610671b9 100644 --- a/scripts/app_config/templates/pubspec.template.yaml +++ b/scripts/app_config/templates/pubspec.template.yaml @@ -24,56 +24,56 @@ dependencies: mutex: ^3.0.0 web_socket_channel: ^2.4.0 -# %%ENABLE_FROST%% +# %%ENABLE_FROST%% # frostdart: # path: ./crypto_plugins/frostdart -# %%END_ENABLE_FROST%% +# %%END_ENABLE_FROST%% -# %%ENABLE_XEL%% +# %%ENABLE_XEL%% # xelis_flutter: # git: # url: https://github.com/cypherstack/xelis-flutter-ffi.git # ref: 5dd5c50713160fa15fb06ff44886ae035eed62fd -# %%END_ENABLE_XEL%% +# %%END_ENABLE_XEL%% -# %%ENABLE_FIRO%% +# %%ENABLE_FIRO%% # flutter_libsparkmobile: # git: # url: https://github.com/cypherstack/flutter_libsparkmobile.git # ref: 84a139a25ab1691762002fafcae351e3d444a5c7 -# %%END_ENABLE_FIRO%% +# %%END_ENABLE_FIRO%% -# %%ENABLE_EPIC%% +# %%ENABLE_EPIC%% # flutter_libepiccash: # path: ./crypto_plugins/flutter_libepiccash -# %%END_ENABLE_EPIC%% +# %%END_ENABLE_EPIC%% # %%ENABLE_MWC%% # flutter_libmwc: # path: ./crypto_plugins/flutter_libmwc # %%END_ENABLE_MWC%% -# %%ENABLE_TOR%% +# %%ENABLE_TOR%% # tor_ffi_plugin: # git: # url: https://github.com/cypherstack/tor.git # ref: 21077186e6bf773ec8a7cd57ef149b2cee5daa7b -# %%END_ENABLE_TOR%% +# %%END_ENABLE_TOR%% -# %%ENABLE_XMR%% +# %%ENABLE_XMR%% # cs_monero: 1.0.0-pre.3 # cs_monero_flutter_libs: 1.0.0-pre.0 # monero_rpc: ^2.0.0 -# %%END_ENABLE_XMR%% +# %%END_ENABLE_XMR%% -# %%ENABLE_SAL%% +# %%ENABLE_SAL%% # cs_salvium: ^1.2.1 # cs_salvium_flutter_libs: ^1.0.4 -# %%END_ENABLE_SAL%% +# %%END_ENABLE_SAL%% -# %%ENABLE_MWEB%% +# %%ENABLE_MWEBD%% # flutter_mwebd: ^0.0.1-pre.8 -# %%END_ENABLE_MWEB%% +# %%END_ENABLE_MWEBD%% # cs_monero compat (unpublished) compat: diff --git a/tool/process_pubspec_deps.dart b/tool/process_pubspec_deps.dart index 279dfe26af..80110b748b 100644 --- a/tool/process_pubspec_deps.dart +++ b/tool/process_pubspec_deps.dart @@ -27,7 +27,7 @@ void _process(final String filePath, final List enableCoinMarkers) { // Check if line starts any marker block for (final marker in enableCoinMarkers) { - if (line.contains('# %%ENABLE_$marker%%')) { + if (line.trim() == "# %%ENABLE_$marker%%") { activeMarker = marker; markerSet = true; updatedLines.add(line); @@ -39,7 +39,7 @@ void _process(final String filePath, final List enableCoinMarkers) { // Check if line ends the active marker block if (activeMarker != null && - line.contains('# %%END_ENABLE_$activeMarker%%')) { + line.trim() == "# %%END_ENABLE_$activeMarker%%") { activeMarker = null; updatedLines.add(line); continue; @@ -58,39 +58,3 @@ void _process(final String filePath, final List enableCoinMarkers) { 'Updated pubspec.yaml with enabled blocks: ${enableCoinMarkers.join(", ")}', ); } - -void _genFile(String outputFilePath, String template, bool on) { - final lines = File(template).readAsLinesSync(); - - // add empty line and gen warning - - final List updatedLines = [ - "// GENERATED CODE - DO NOT MODIFY BY HAND", - "", - ]; - - final keepMarker = on ? "ON" : "OFF"; - - bool? keepLine; - - for (final line in lines) { - if (line.trim() == "//$keepMarker") { - keepLine = true; - continue; - } - - if (line.trim() == "//END_$keepMarker") { - keepLine = false; - continue; - } - - if (keepLine != false) { - updatedLines.add(line); - } - } - - // add empty line - if (updatedLines.last.isNotEmpty) updatedLines.add(""); - - File(outputFilePath).writeAsStringSync(updatedLines.join('\n')); -} From 2b7e404a098320495475daf5a80fdd21822e5db4 Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 3 Oct 2025 10:13:21 -0600 Subject: [PATCH 05/30] implement mwebd ffi package optional import --- lib/services/mwebd_service.dart | 60 +++++++--------- .../interfaces/mwebd_server_interface.dart | 62 +++++++++++++++++ ..._mwebd_server_interface_impl.template.dart | 69 +++++++++++++++++++ 3 files changed, 155 insertions(+), 36 deletions(-) create mode 100644 lib/wl_gen/interfaces/mwebd_server_interface.dart create mode 100644 tool/wl_templates/MWEBD_mwebd_server_interface_impl.template.dart diff --git a/lib/services/mwebd_service.dart b/lib/services/mwebd_service.dart index cc5e6bdc0f..3c60a9ac9d 100644 --- a/lib/services/mwebd_service.dart +++ b/lib/services/mwebd_service.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'dart:io'; import 'dart:math'; -import 'package:flutter_mwebd/flutter_mwebd.dart'; import 'package:mutex/mutex.dart'; import 'package:mweb_client/mweb_client.dart'; @@ -11,6 +10,7 @@ import '../utilities/logger.dart'; import '../utilities/prefs.dart'; import '../utilities/stack_file_system.dart'; import '../wallets/crypto_currency/crypto_currency.dart'; +import '../wl_gen/interfaces/mwebd_server_interface.dart'; import 'event_bus/events/global/tor_connection_status_changed_event.dart'; import 'event_bus/events/global/tor_status_changed_event.dart'; import 'event_bus/global_event_bus.dart'; @@ -24,8 +24,7 @@ final class MwebdService { CryptoCurrencyNetwork.test4 => throw UnimplementedError(), }; - final Map - _map = {}; + final Map _map = {}; late final StreamSubscription _torStatusListener; @@ -80,38 +79,30 @@ final class MwebdService { // update function called when Tor pref changed Future _update(({InternetAddress host, int port})? proxyInfo) async { await _updateLock.protect(() async { - final proxy = - proxyInfo == null - ? "" - : "socks5://${proxyInfo.host.address}:${proxyInfo.port}"; + final proxy = proxyInfo == null + ? "" + : "socks5://${proxyInfo.host.address}:${proxyInfo.port}"; final nets = _map.keys; for (final net in nets) { - final old = _map.remove(net)!; - - await old.client.cleanup(); - await old.server.stopServer(); + final oldClient = _map.remove(net)!; + await oldClient.cleanup(); + final oldServerInfo = await mwebdServerInterface.stopServer(net); final port = await _getRandomUnusedPort(); if (port == null) { throw Exception("Could not find an unused port for mwebd"); } - final newServer = MwebdServer( - chain: old.server.chain, - dataDir: old.server.dataDir, - peer: old.server.peer, + final serverPort = await mwebdServerInterface.createAndStartServer( + net, + chain: oldServerInfo.chain, + dataDir: oldServerInfo.dataDir, + peer: oldServerInfo.peer, proxy: proxy, serverPort: port, ); - await newServer.createServer(); - await newServer.startServer(); - - final newClient = MwebClient.fromHost( - "127.0.0.1", - newServer.serverPort, - ); - _map[net] = (server: newServer, client: newClient); + _map[net] = MwebClient.fromHost("127.0.0.1", serverPort); } }); } @@ -125,10 +116,10 @@ final class MwebdService { } if (_map.isNotEmpty) { - for (final old in _map.values) { + for (final net in _map.keys) { try { - await old.client.cleanup(); - await old.server.stopServer(); + await _map[net]?.cleanup(); + await mwebdServerInterface.stopServer(net); } catch (e, s) { Logging.instance.i( "Switching mwebd chain. Error likely expected here.", @@ -163,35 +154,32 @@ final class MwebdService { proxy = ""; } - final newServer = MwebdServer( + final serverPort = await mwebdServerInterface.createAndStartServer( + net, chain: chain, dataDir: dir.path, peer: defaultPeer(net), proxy: proxy, serverPort: port, ); - await newServer.createServer(); - await newServer.startServer(); - final newClient = MwebClient.fromHost("127.0.0.1", newServer.serverPort); - - _map[net] = (server: newServer, client: newClient); + _map[net] = MwebClient.fromHost("127.0.0.1", serverPort); Logging.instance.i("MwebdService init($net) completed!"); }); } /// Get server status. Returns null if no server was initialized. - Future getServerStatus(CryptoCurrencyNetwork net) async { - return await _updateLock.protect(() async { - return await _map[net]?.server.getStatus(); + Future getServerStatus(CryptoCurrencyNetwork net) { + return _updateLock.protect(() { + return mwebdServerInterface.getServerStatus(net); }); } /// Get client for network. Returns null if no server was initialized. Future getClient(CryptoCurrencyNetwork net) async { return await _updateLock.protect(() async { - return _map[net]?.client; + return _map[net]; }); } diff --git a/lib/wl_gen/interfaces/mwebd_server_interface.dart b/lib/wl_gen/interfaces/mwebd_server_interface.dart new file mode 100644 index 0000000000..08dc08f65d --- /dev/null +++ b/lib/wl_gen/interfaces/mwebd_server_interface.dart @@ -0,0 +1,62 @@ +import '../../wallets/crypto_currency/crypto_currency.dart'; + +export '../generated/mwebd_server_interface_impl.dart'; + +abstract class MwebdServerInterface { + Future createAndStartServer( + CryptoCurrencyNetwork net, { + required String chain, + required String dataDir, + required String peer, + String proxy = "", + required int serverPort, + }); + + Future<({String chain, String dataDir, String peer})> stopServer( + CryptoCurrencyNetwork net, + ); + + Future getServerStatus(CryptoCurrencyNetwork net); +} + +// local copy +class Status { + final int blockHeaderHeight; + final int mwebHeaderHeight; + final int mwebUtxosHeight; + final int blockTime; + + Status({ + required this.blockHeaderHeight, + required this.mwebHeaderHeight, + required this.mwebUtxosHeight, + required this.blockTime, + }); + + @override + String toString() { + return 'Status(' + 'blockHeaderHeight: $blockHeaderHeight, ' + 'mwebHeaderHeight: $mwebHeaderHeight, ' + 'mwebUtxosHeight: $mwebUtxosHeight, ' + 'blockTime: $blockTime' + ')'; + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Status && + blockHeaderHeight == other.blockHeaderHeight && + mwebHeaderHeight == other.mwebHeaderHeight && + mwebUtxosHeight == other.mwebUtxosHeight && + blockTime == other.blockTime; + + @override + int get hashCode => Object.hash( + blockHeaderHeight, + mwebHeaderHeight, + mwebUtxosHeight, + blockTime, + ); +} diff --git a/tool/wl_templates/MWEBD_mwebd_server_interface_impl.template.dart b/tool/wl_templates/MWEBD_mwebd_server_interface_impl.template.dart new file mode 100644 index 0000000000..bf3793e1b0 --- /dev/null +++ b/tool/wl_templates/MWEBD_mwebd_server_interface_impl.template.dart @@ -0,0 +1,69 @@ +//ON +import 'package:flutter_mwebd/flutter_mwebd.dart' hide Status; + +//END_ON +import '../../wallets/crypto_currency/crypto_currency.dart'; +import '../interfaces/mwebd_server_interface.dart'; + +MwebdServerInterface get mwebdServerInterface => _getInterface(); + +//OFF +MwebdServerInterface _getInterface() => throw Exception("MWEBD not enabled!"); + +//END_OFF +//ON +MwebdServerInterface _getInterface() => _MwebdServerInterfaceImpl(); + +class _MwebdServerInterfaceImpl extends MwebdServerInterface { + final Map _map = {}; + + @override + Future createAndStartServer( + CryptoCurrencyNetwork net, { + required String chain, + required String dataDir, + required String peer, + String proxy = "", + required int serverPort, + }) async { + if (_map[net] != null) { + throw Exception("Server for $net already exists"); + } + + final newServer = MwebdServer( + chain: chain, + dataDir: dataDir, + peer: peer, + proxy: proxy, + serverPort: serverPort, + ); + await newServer.createServer(); + await newServer.startServer(); + _map[net] = newServer; + return newServer.serverPort; + } + + @override + Future<({String chain, String dataDir, String peer})> stopServer( + CryptoCurrencyNetwork net, + ) async { + final server = _map.remove(net); + await server!.stopServer(); + return (chain: server.chain, dataDir: server.dataDir, peer: server.peer); + } + + @override + Future getServerStatus(CryptoCurrencyNetwork net) async { + final status = await _map[net]?.getStatus(); + if (status == null) return null; + + return Status( + blockHeaderHeight: status.blockHeaderHeight, + mwebHeaderHeight: status.mwebHeaderHeight, + mwebUtxosHeight: status.mwebUtxosHeight, + blockTime: status.blockTime, + ); + } +} + +//END_ON From 9c73ed9becec7931f0c58a039905eed9a1a75f1a Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 3 Oct 2025 16:30:35 -0600 Subject: [PATCH 06/30] implement xmr/wow ffi package optional import --- lib/main.dart | 200 ++- .../restore_wallet_view.dart | 349 +++-- lib/pages/send_view/send_view.dart | 1270 ++++++++--------- .../transaction_fee_selection_sheet.dart | 329 +++-- .../edit_refresh_height_view.dart | 99 +- .../sub_widgets/desktop_send_fee_form.dart | 338 +++-- lib/services/churning_service.dart | 50 +- lib/wallets/models/tx_data.dart | 22 +- lib/wallets/wallet/impl/monero_wallet.dart | 104 +- lib/wallets/wallet/impl/wownero_wallet.dart | 90 +- .../intermediate/lib_monero_wallet.dart | 404 +++--- lib/widgets/desktop/desktop_fee_dialog.dart | 110 +- .../interfaces/cs_monero_interface.dart | 380 +++++ ...XMR_cs_monero_interface_impl.template.dart | 541 +++++++ 14 files changed, 2538 insertions(+), 1748 deletions(-) create mode 100644 lib/wl_gen/interfaces/cs_monero_interface.dart create mode 100644 tool/wl_templates/XMR_cs_monero_interface_impl.template.dart diff --git a/lib/main.dart b/lib/main.dart index c750009507..c75941ecc2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,7 +15,6 @@ import 'dart:ui'; import 'package:coinlib_flutter/coinlib_flutter.dart'; import 'package:compat/compat.dart' as lib_monero_compat; -import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -79,6 +78,7 @@ import 'wallets/crypto_currency/crypto_currency.dart'; import 'wallets/isar/providers/all_wallets_info_provider.dart'; import 'wallets/wallet/wallet_mixin_interfaces/spark_interface.dart'; import 'widgets/crypto_notifications.dart'; +import 'wl_gen/interfaces/cs_monero_interface.dart'; final openedFromSWBFileStringStateProvider = StateProvider( (ref) => null, @@ -189,7 +189,7 @@ void main(List args) async { DB.instance.hive.registerAdapter(lib_monero_compat.WalletTypeAdapter()); - lib_monero.Logging.useLogger = kDebugMode; + csMonero.setUseCsMoneroLoggerInternal(kDebugMode); DB.instance.hive.init( (await StackFileSystem.applicationHiveDirectory()).path, @@ -372,8 +372,9 @@ class _MaterialAppWithThemeState extends ConsumerState Constants.exchangeForExperiencedUsers(familiarity); if (Util.isDesktop) { - _desktopHasPassword = - await ref.read(storageCryptoHandlerProvider).hasPassword(); + _desktopHasPassword = await ref + .read(storageCryptoHandlerProvider) + .hasPassword(); } } @@ -480,14 +481,14 @@ class _MaterialAppWithThemeState extends ConsumerState final brightness = WidgetsBinding.instance.window.platformBrightness; switch (brightness) { case Brightness.dark: - themeId = - ref.read(prefsChangeNotifierProvider).systemBrightnessDarkThemeId; + themeId = ref + .read(prefsChangeNotifierProvider) + .systemBrightnessDarkThemeId; break; case Brightness.light: - themeId = - ref - .read(prefsChangeNotifierProvider) - .systemBrightnessLightThemeId; + themeId = ref + .read(prefsChangeNotifierProvider) + .systemBrightnessLightThemeId; break; } } else { @@ -506,8 +507,9 @@ class _MaterialAppWithThemeState extends ConsumerState ref.read(applicationThemesDirectoryPathProvider.notifier).state = StackFileSystem.themesDir!.path; - ref.read(themeProvider.state).state = - ref.read(pThemeService).getTheme(themeId: themeId)!; + ref.read(themeProvider.state).state = ref + .read(pThemeService) + .getTheme(themeId: themeId)!; if (Platform.isAndroid) { // fetch open file if it exists @@ -531,21 +533,22 @@ class _MaterialAppWithThemeState extends ConsumerState String themeId; switch (WidgetsBinding.instance.window.platformBrightness) { case Brightness.dark: - themeId = - ref.read(prefsChangeNotifierProvider).systemBrightnessDarkThemeId; + themeId = ref + .read(prefsChangeNotifierProvider) + .systemBrightnessDarkThemeId; break; case Brightness.light: - themeId = - ref - .read(prefsChangeNotifierProvider) - .systemBrightnessLightThemeId; + themeId = ref + .read(prefsChangeNotifierProvider) + .systemBrightnessLightThemeId; break; } WidgetsBinding.instance.addPostFrameCallback((_) { if (ref.read(prefsChangeNotifierProvider).enableSystemBrightness) { - ref.read(themeProvider.state).state = - ref.read(pThemeService).getTheme(themeId: themeId)!; + ref.read(themeProvider.state).state = ref + .read(pThemeService) + .getTheme(themeId: themeId)!; } }); }; @@ -673,17 +676,16 @@ class _MaterialAppWithThemeState extends ConsumerState ref.read(pNavKey).currentContext!, RouteGenerator.getRoute( shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute, - builder: - (_) => LockscreenView( - showBackButton: true, - routeOnSuccess: RestoreFromEncryptedStringView.routeName, - routeOnSuccessArguments: encrypted, - biometricsCancelButtonString: "CANCEL", - biometricsLocalizedReason: - "Authenticate to restore ${AppConfig.appName} backup", - biometricsAuthenticationTitle: - "Restore ${AppConfig.prefix} backup", - ), + builder: (_) => LockscreenView( + showBackButton: true, + routeOnSuccess: RestoreFromEncryptedStringView.routeName, + routeOnSuccessArguments: encrypted, + biometricsCancelButtonString: "CANCEL", + biometricsLocalizedReason: + "Authenticate to restore ${AppConfig.appName} backup", + biometricsAuthenticationTitle: + "Restore ${AppConfig.prefix} backup", + ), settings: const RouteSettings(name: "/swbrestorelockscreen"), ), ), @@ -807,85 +809,79 @@ class _MaterialAppWithThemeState extends ConsumerState ), ), home: CryptoNotifications( - child: - Util.isDesktop - ? FutureBuilder( - future: loadShared(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (_desktopHasPassword) { - String? startupWalletId; - if (ref + child: Util.isDesktop + ? FutureBuilder( + future: loadShared(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (_desktopHasPassword) { + String? startupWalletId; + if (ref + .read(prefsChangeNotifierProvider) + .gotoWalletOnStartup) { + startupWalletId = ref .read(prefsChangeNotifierProvider) - .gotoWalletOnStartup) { - startupWalletId = - ref - .read(prefsChangeNotifierProvider) - .startupWalletId; - } - - return DesktopLoginView( - startupWalletId: startupWalletId, - load: () => load(true), - ); - } else { - return const IntroView(); + .startupWalletId; } + + return DesktopLoginView( + startupWalletId: startupWalletId, + load: () => load(true), + ); } else { - return const LoadingView(); + return const IntroView(); } - }, - ) - : FutureBuilder( - future: load(false), - builder: ( - BuildContext context, - AsyncSnapshot snapshot, - ) { - if (snapshot.connectionState == ConnectionState.done) { - // FlutterNativeSplash.remove(); - if (ref.read(pAllWalletsInfo).isNotEmpty || - ref.read(prefsChangeNotifierProvider).hasPin) { - // return HomeView(); - - String? startupWalletId; - if (ref + } else { + return const LoadingView(); + } + }, + ) + : FutureBuilder( + future: load(false), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + // FlutterNativeSplash.remove(); + if (ref.read(pAllWalletsInfo).isNotEmpty || + ref.read(prefsChangeNotifierProvider).hasPin) { + // return HomeView(); + + String? startupWalletId; + if (ref + .read(prefsChangeNotifierProvider) + .gotoWalletOnStartup) { + startupWalletId = ref .read(prefsChangeNotifierProvider) - .gotoWalletOnStartup) { - startupWalletId = - ref - .read(prefsChangeNotifierProvider) - .startupWalletId; - } - - return LockscreenView( - isInitialAppLogin: true, - routeOnSuccess: HomeView.routeName, - routeOnSuccessArguments: startupWalletId, - biometricsAuthenticationTitle: - "Unlock ${AppConfig.prefix}", - biometricsLocalizedReason: - "Unlock your ${AppConfig.appName} using biometrics", - biometricsCancelButtonString: "Cancel", - ); - } else { - if (AppConfig.appName == "Campfire" && - !CampfireMigration.didRun && - CampfireMigration.hasOldWallets) { - return const CampfireMigrateView(); - } else { - return const IntroView(); - } + .startupWalletId; } + + return LockscreenView( + isInitialAppLogin: true, + routeOnSuccess: HomeView.routeName, + routeOnSuccessArguments: startupWalletId, + biometricsAuthenticationTitle: + "Unlock ${AppConfig.prefix}", + biometricsLocalizedReason: + "Unlock your ${AppConfig.appName} using biometrics", + biometricsCancelButtonString: "Cancel", + ); } else { - // CURRENTLY DISABLED as cannot be animated - // technically not needed as FlutterNativeSplash will overlay - // anything returned here until the future completes but - // FutureBuilder requires you to return something - return const LoadingView(); + if (AppConfig.appName == "Campfire" && + !CampfireMigration.didRun && + CampfireMigration.hasOldWallets) { + return const CampfireMigrateView(); + } else { + return const IntroView(); + } } - }, - ), + } else { + // CURRENTLY DISABLED as cannot be animated + // technically not needed as FlutterNativeSplash will overlay + // anything returned here until the future completes but + // FutureBuilder requires you to return something + return const LoadingView(); + } + }, + ), ), ); } diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart index dceb76d80d..f1bf78b8ca 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart @@ -16,7 +16,6 @@ import 'dart:math'; import 'package:bip39/bip39.dart' as bip39; import 'package:bip39/src/wordlists/english.dart' as bip39wordlist; -import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -62,6 +61,7 @@ import '../../../widgets/icon_widgets/qrcode_icon.dart'; import '../../../widgets/table_view/table_view.dart'; import '../../../widgets/table_view/table_view_cell.dart'; import '../../../widgets/table_view/table_view_row.dart'; +import '../../../wl_gen/interfaces/cs_monero_interface.dart'; import '../../home_view/home_view.dart'; import '../add_token_view/edit_wallet_tokens_view.dart'; import '../select_wallet_for_token_view.dart'; @@ -153,10 +153,9 @@ class _RestoreWalletViewState extends ConsumerState { _seedWordCount = widget.seedWordsLength; isDesktop = Util.isDesktop; - textSelectionControls = - Platform.isIOS - ? CustomCupertinoTextSelectionControls(onPaste: onControlsPaste) - : CustomMaterialTextSelectionControls(onPaste: onControlsPaste); + textSelectionControls = Platform.isIOS + ? CustomCupertinoTextSelectionControls(onPaste: onControlsPaste) + : CustomMaterialTextSelectionControls(onPaste: onControlsPaste); for (int i = 0; i < _seedWordCount; i++) { _controllers.add(TextEditingController()); @@ -189,7 +188,7 @@ class _RestoreWalletViewState extends ConsumerState { // Salvium use's Monero's wordlists. switch (widget.seedWordsLength) { case 25: - return lib_monero.getMoneroWordList("English").contains(word); + return csMonero.getMoneroWordList("English").contains(word); case 16: return Monero.sixteenWordsWordList.contains(word); default: @@ -197,9 +196,9 @@ class _RestoreWalletViewState extends ConsumerState { } } if (widget.coin is Wownero) { - final wowneroWordList = lib_monero.getWowneroWordList( + final wowneroWordList = csMonero.getWowneroWordList( "English", - seedWordsLength: widget.seedWordsLength, + widget.seedWordsLength, ); return wowneroWordList.contains(word); } @@ -409,10 +408,8 @@ class _RestoreWalletViewState extends ConsumerState { ref .read(newEthWalletTriggerTempUntilHiveCompletelyDeleted.state) .state = !ref - .read( - newEthWalletTriggerTempUntilHiveCompletelyDeleted.state, - ) - .state; + .read(newEthWalletTriggerTempUntilHiveCompletelyDeleted.state) + .state; } if (mounted) { @@ -511,43 +508,42 @@ class _RestoreWalletViewState extends ConsumerState { case FormInputStatus.empty: color = Theme.of(context).extension()!.textFieldDefaultBG; prefixColor = Theme.of(context).extension()!.textSubtitle2; - borderColor = - Theme.of(context).extension()!.textFieldDefaultBG; + borderColor = Theme.of( + context, + ).extension()!.textFieldDefaultBG; break; case FormInputStatus.invalid: color = Theme.of(context).extension()!.textFieldErrorBG; - prefixColor = - Theme.of( - context, - ).extension()!.textFieldErrorSearchIconLeft; - borderColor = - Theme.of(context).extension()!.textFieldErrorBorder; + prefixColor = Theme.of( + context, + ).extension()!.textFieldErrorSearchIconLeft; + borderColor = Theme.of( + context, + ).extension()!.textFieldErrorBorder; suffixIcon = SvgPicture.asset( Assets.svg.alertCircle, width: 16, height: 16, - color: - Theme.of( - context, - ).extension()!.textFieldErrorSearchIconRight, + color: Theme.of( + context, + ).extension()!.textFieldErrorSearchIconRight, ); break; case FormInputStatus.valid: color = Theme.of(context).extension()!.textFieldSuccessBG; - prefixColor = - Theme.of( - context, - ).extension()!.textFieldSuccessSearchIconLeft; - borderColor = - Theme.of(context).extension()!.textFieldSuccessBorder; + prefixColor = Theme.of( + context, + ).extension()!.textFieldSuccessSearchIconLeft; + borderColor = Theme.of( + context, + ).extension()!.textFieldSuccessBorder; suffixIcon = SvgPicture.asset( Assets.svg.checkCircle, width: 16, height: 16, - color: - Theme.of( - context, - ).extension()!.textFieldSuccessSearchIconRight, + color: Theme.of( + context, + ).extension()!.textFieldSuccessSearchIconRight, ); break; } @@ -706,90 +702,85 @@ class _RestoreWalletViewState extends ConsumerState { final isDesktop = Util.isDesktop; return MasterScaffold( isDesktop: isDesktop, - appBar: - isDesktop - ? const DesktopAppBar( - isCompactHeight: false, - leading: AppBarBackButton(), - trailing: ExitToMyStackButton(), - ) - : AppBar( - leading: AppBarBackButton( - onPressed: () async { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed( - const Duration(milliseconds: 50), - ); - } - if (context.mounted) { - Navigator.of(context).pop(); - } - }, - ), - actions: [ - Padding( - padding: const EdgeInsets.only( - top: 10, - bottom: 10, - right: 10, - ), - child: AspectRatio( - aspectRatio: 1, - child: AppBarIconButton( - semanticsLabel: - "View QR Code Button. Opens Camera To Scan QR Code For Restoring Wallet.", - key: const Key("restoreWalletViewQrCodeButton"), - size: 36, - shadows: const [], - color: - Theme.of( - context, - ).extension()!.background, - icon: QrCodeIcon( - width: 20, - height: 20, - color: - Theme.of( - context, - ).extension()!.accentColorDark, - ), - onPressed: scanMnemonicQr, + appBar: isDesktop + ? const DesktopAppBar( + isCompactHeight: false, + leading: AppBarBackButton(), + trailing: ExitToMyStackButton(), + ) + : AppBar( + leading: AppBarBackButton( + onPressed: () async { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: 50), + ); + } + if (context.mounted) { + Navigator.of(context).pop(); + } + }, + ), + actions: [ + Padding( + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + right: 10, + ), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + semanticsLabel: + "View QR Code Button. Opens Camera To Scan QR Code For Restoring Wallet.", + key: const Key("restoreWalletViewQrCodeButton"), + size: 36, + shadows: const [], + color: Theme.of( + context, + ).extension()!.background, + icon: QrCodeIcon( + width: 20, + height: 20, + color: Theme.of( + context, + ).extension()!.accentColorDark, ), + onPressed: scanMnemonicQr, ), ), - Padding( - padding: const EdgeInsets.only( - top: 10, - bottom: 10, - right: 10, - ), - child: AspectRatio( - aspectRatio: 1, - child: AppBarIconButton( - semanticsLabel: - "Paste Button. Pastes From Clipboard For Restoring Wallet.", - key: const Key("restoreWalletPasteButton"), - size: 36, - shadows: const [], - color: - Theme.of( - context, - ).extension()!.background, - icon: ClipboardIcon( - width: 20, - height: 20, - color: - Theme.of( - context, - ).extension()!.accentColorDark, - ), - onPressed: pasteMnemonic, + ), + Padding( + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + right: 10, + ), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + semanticsLabel: + "Paste Button. Pastes From Clipboard For Restoring Wallet.", + key: const Key("restoreWalletPasteButton"), + size: 36, + shadows: const [], + color: Theme.of( + context, + ).extension()!.background, + icon: ClipboardIcon( + width: 20, + height: 20, + color: Theme.of( + context, + ).extension()!.accentColorDark, ), + onPressed: pasteMnemonic, ), ), - ], - ), + ), + ], + ), body: Container( color: Theme.of(context).extension()!.background, child: Padding( @@ -810,18 +801,16 @@ class _RestoreWalletViewState extends ConsumerState { SizedBox(height: isDesktop ? 0 : 4), Text( "Recovery phrase", - style: - isDesktop - ? STextStyles.desktopH2(context) - : STextStyles.pageTitleH1(context), + style: isDesktop + ? STextStyles.desktopH2(context) + : STextStyles.pageTitleH1(context), ), SizedBox(height: isDesktop ? 16 : 8), Text( "Enter your $_seedWordCount-word recovery phrase.", - style: - isDesktop - ? STextStyles.desktopSubtitleH2(context) - : STextStyles.subtitle(context), + style: isDesktop + ? STextStyles.desktopSubtitleH2(context) + : STextStyles.subtitle(context), ), SizedBox(height: isDesktop ? 16 : 10), if (isDesktop) @@ -841,10 +830,9 @@ class _RestoreWalletViewState extends ConsumerState { Assets.svg.clipboard, width: 22, height: 22, - color: - Theme.of(context) - .extension()! - .buttonTextSecondary, + color: Theme.of( + context, + ).extension()!.buttonTextSecondary, ), const SizedBox(width: 8), Text( @@ -910,8 +898,8 @@ class _RestoreWalletViewState extends ConsumerState { .onUserInteraction, selectionControls: i * 4 + j - 1 == 1 - ? textSelectionControls - : null, + ? textSelectionControls + : null, // focusNode: // _focusNodes[i * 4 + j - 1], onChanged: (value) { @@ -957,18 +945,19 @@ class _RestoreWalletViewState extends ConsumerState { _controllers[i * 4 + j - 1], - style: STextStyles.field( - context, - ).copyWith( - color: - Theme.of(context) + style: + STextStyles.field( + context, + ).copyWith( + color: Theme.of(context) .extension< StackColors >()! .textRestore, - fontSize: - isDesktop ? 16 : 14, - ), + fontSize: isDesktop + ? 16 + : 14, + ), ), if (_inputStatuses[i * 4 + j - @@ -987,16 +976,19 @@ class _RestoreWalletViewState extends ConsumerState { "Please check spelling", textAlign: TextAlign.left, - style: STextStyles.label( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textError, - ), + style: + STextStyles.label( + context, + ).copyWith( + color: + Theme.of( + context, + ) + .extension< + StackColors + >()! + .textError, + ), ), ), ), @@ -1048,10 +1040,9 @@ class _RestoreWalletViewState extends ConsumerState { autovalidateMode: AutovalidateMode .onUserInteraction, - selectionControls: - i == 1 - ? textSelectionControls - : null, + selectionControls: i == 1 + ? textSelectionControls + : null, onChanged: (value) { final FormInputStatus formInputStatus; @@ -1078,18 +1069,19 @@ class _RestoreWalletViewState extends ConsumerState { }); }, controller: _controllers[i], - style: STextStyles.field( - context, - ).copyWith( - color: - Theme.of(context) + style: + STextStyles.field( + context, + ).copyWith( + color: Theme.of(context) .extension< StackColors >()! .overlay, - fontSize: - isDesktop ? 16 : 14, - ), + fontSize: isDesktop + ? 16 + : 14, + ), ), if (_inputStatuses[i] == FormInputStatus.invalid) @@ -1106,16 +1098,19 @@ class _RestoreWalletViewState extends ConsumerState { "Please check spelling", textAlign: TextAlign.left, - style: STextStyles.label( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textError, - ), + style: + STextStyles.label( + context, + ).copyWith( + color: + Theme.of( + context, + ) + .extension< + StackColors + >()! + .textError, + ), ), ), ), @@ -1181,8 +1176,9 @@ class _RestoreWalletViewState extends ConsumerState { ), autovalidateMode: AutovalidateMode.onUserInteraction, - selectionControls: - i == 1 ? textSelectionControls : null, + selectionControls: i == 1 + ? textSelectionControls + : null, // focusNode: _focusNodes[i - 1], onChanged: (value) { final FormInputStatus formInputStatus; @@ -1212,10 +1208,9 @@ class _RestoreWalletViewState extends ConsumerState { }, controller: _controllers[i - 1], style: STextStyles.field(context).copyWith( - color: - Theme.of(context) - .extension()! - .textRestore, + color: Theme.of( + context, + ).extension()!.textRestore, fontSize: isDesktop ? 16 : 14, ), ), @@ -1232,14 +1227,12 @@ class _RestoreWalletViewState extends ConsumerState { child: Text( "Please check spelling", textAlign: TextAlign.left, - style: STextStyles.label( - context, - ).copyWith( - color: - Theme.of(context) + style: STextStyles.label(context) + .copyWith( + color: Theme.of(context) .extension()! .textError, - ), + ), ), ), ), diff --git a/lib/pages/send_view/send_view.dart b/lib/pages/send_view/send_view.dart index 1577e568e8..855d1bd889 100644 --- a/lib/pages/send_view/send_view.dart +++ b/lib/pages/send_view/send_view.dart @@ -11,7 +11,6 @@ import 'dart:async'; import 'dart:io'; -import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -76,6 +75,7 @@ import '../../widgets/rounded_white_container.dart'; import '../../widgets/stack_dialog.dart'; import '../../widgets/stack_text_field.dart'; import '../../widgets/textfield_icon_button.dart'; +import '../../wl_gen/interfaces/cs_monero_interface.dart'; import '../address_book_views/address_book_view.dart'; import '../coin_control/coin_control_view.dart'; import 'confirm_transaction_view.dart'; @@ -345,18 +345,19 @@ class _SendViewState extends ConsumerState { ); final Amount? amount; if (baseAmount != null) { - final _price = - ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin)?.value; + final _price = ref + .read(priceAnd24hChangeNotifierProvider) + .getPrice(coin) + ?.value; if (_price == null || _price == Decimal.zero) { amount = 0.toAmountAsRaw(fractionDigits: coin.fractionDigits); } else { - amount = - baseAmount <= Amount.zero - ? 0.toAmountAsRaw(fractionDigits: coin.fractionDigits) - : (baseAmount.decimal / _price) - .toDecimal(scaleOnInfinitePrecision: coin.fractionDigits) - .toAmount(fractionDigits: coin.fractionDigits); + amount = baseAmount <= Amount.zero + ? 0.toAmountAsRaw(fractionDigits: coin.fractionDigits) + : (baseAmount.decimal / _price) + .toDecimal(scaleOnInfinitePrecision: coin.fractionDigits) + .toAmount(fractionDigits: coin.fractionDigits); } if (_cachedAmountToSend != null && _cachedAmountToSend == amount) { return; @@ -397,8 +398,10 @@ class _SendViewState extends ConsumerState { } _cachedAmountToSend = amount; - final price = - ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin)?.value; + final price = ref + .read(priceAnd24hChangeNotifierProvider) + .getPrice(coin) + ?.value; if (price != null && price > Decimal.zero) { baseAmountController.text = (amount.decimal * price) @@ -463,12 +466,11 @@ class _SendViewState extends ConsumerState { fee = fee.split(" ").first; } - final value = - fee.contains(",") - ? Decimal.parse( - fee.replaceFirst(",", "."), - ).toAmount(fractionDigits: coin.fractionDigits) - : Decimal.parse(fee).toAmount(fractionDigits: coin.fractionDigits); + final value = fee.contains(",") + ? Decimal.parse( + fee.replaceFirst(",", "."), + ).toAmount(fractionDigits: coin.fractionDigits) + : Decimal.parse(fee).toAmount(fractionDigits: coin.fractionDigits); if (shouldSetState) { setState(() => _currentFee = value); @@ -559,25 +561,22 @@ class _SendViewState extends ConsumerState { Amount fee; if (coin is Monero) { - lib_monero.TransactionPriority specialMoneroId; + final int specialMoneroId; switch (ref.read(feeRateTypeMobileStateProvider.state).state) { case FeeRateType.fast: - specialMoneroId = lib_monero.TransactionPriority.high; + specialMoneroId = csMonero.getTxPriorityHigh(); break; case FeeRateType.average: - specialMoneroId = lib_monero.TransactionPriority.medium; + specialMoneroId = csMonero.getTxPriorityMedium(); break; case FeeRateType.slow: - specialMoneroId = lib_monero.TransactionPriority.normal; + specialMoneroId = csMonero.getTxPriorityNormal(); break; default: throw ArgumentError("custom fee not available for monero"); } - fee = await wallet.estimateFeeFor( - amount, - BigInt.from(specialMoneroId.value), - ); + fee = await wallet.estimateFeeFor(amount, BigInt.from(specialMoneroId)); cachedFees[amount] = ref .read(pAmountFormatter(coin)) .format(fee, withUnitName: true, indicatePrecisionLoss: false); @@ -629,10 +628,9 @@ class _SendViewState extends ConsumerState { amount: amount, recipientAddress: null, // No specific recipient for manual slatepack. - message: - onChainNoteController.text.isNotEmpty == true - ? onChainNoteController.text - : null, + message: onChainNoteController.text.isNotEmpty == true + ? onChainNoteController.text + : null, encrypt: false, // No encryption without recipient address. ); } @@ -672,10 +670,9 @@ class _SendViewState extends ConsumerState { await showDialog( context: context, barrierDismissible: false, - builder: - (context) => StackDialogBase( - child: MwcSlatepackDialog(slatepackResult: slatepackResult), - ), + builder: (context) => StackDialogBase( + child: MwcSlatepackDialog(slatepackResult: slatepackResult), + ), ); // Clear form after slatepack dialog is closed. @@ -692,11 +689,10 @@ class _SendViewState extends ConsumerState { if (mounted) { await showDialog( context: context, - builder: - (context) => StackOkDialog( - title: "Slatepack Creation Failed", - message: e.toString(), - ), + builder: (context) => StackOkDialog( + title: "Slatepack Creation Failed", + message: e.toString(), + ), ); } } @@ -716,18 +712,18 @@ class _SendViewState extends ConsumerState { availableBalance = wallet.info.cachedBalance.spendable; break; case BalanceType.private: - availableBalance = - isFiro - ? wallet.info.cachedBalanceTertiary.spendable - : wallet.info.cachedBalanceSecondary.spendable; + availableBalance = isFiro + ? wallet.info.cachedBalanceTertiary.spendable + : wallet.info.cachedBalanceSecondary.spendable; break; } } else { availableBalance = ref.read(pWalletBalance(walletId)).spendable; } - final coinControlEnabled = - ref.read(prefsChangeNotifierProvider).enableCoinControl; + final coinControlEnabled = ref + .read(prefsChangeNotifierProvider) + .enableCoinControl; if (coin is! Ethereum && !(wallet is CoinControlInterface && coinControlEnabled) || @@ -754,10 +750,9 @@ class _SendViewState extends ConsumerState { child: Text( "Cancel", style: STextStyles.button(context).copyWith( - color: - Theme.of( - context, - ).extension()!.accentColorDark, + color: Theme.of( + context, + ).extension()!.accentColorDark, ), ), onPressed: () { @@ -833,10 +828,10 @@ class _SendViewState extends ConsumerState { feeRateType: feeRate, utxos: (wallet is CoinControlInterface && - coinControlEnabled && - selectedUTXOs.isNotEmpty) - ? selectedUTXOs - : null, + coinControlEnabled && + selectedUTXOs.isNotEmpty) + ? selectedUTXOs + : null, ), ); } else if (wallet is FiroWallet) { @@ -855,10 +850,9 @@ class _SendViewState extends ConsumerState { ], feeRateType: ref.read(feeRateTypeMobileStateProvider), satsPerVByte: isCustomFee.value ? customFeeRate : null, - utxos: - (coinControlEnabled && selectedUTXOs.isNotEmpty) - ? selectedUTXOs - : null, + utxos: (coinControlEnabled && selectedUTXOs.isNotEmpty) + ? selectedUTXOs + : null, ), ); } else { @@ -869,16 +863,16 @@ class _SendViewState extends ConsumerState { address: _address!, amount: amount, isChange: false, - addressType: - wallet.cryptoCurrency.getAddressType(_address!)!, + addressType: wallet.cryptoCurrency.getAddressType( + _address!, + )!, ), ], feeRateType: ref.read(feeRateTypeMobileStateProvider), satsPerVByte: isCustomFee.value ? customFeeRate : null, - utxos: - (coinControlEnabled && selectedUTXOs.isNotEmpty) - ? selectedUTXOs - : null, + utxos: (coinControlEnabled && selectedUTXOs.isNotEmpty) + ? selectedUTXOs + : null, ), ); } @@ -887,31 +881,28 @@ class _SendViewState extends ConsumerState { case BalanceType.private: txDataFuture = wallet.prepareSendSpark( txData: TxData( - recipients: - ref.read(pValidSparkSendToAddress) - ? null - : [ - TxRecipient( - address: _address!, - amount: amount, - isChange: false, - addressType: - wallet.cryptoCurrency.getAddressType( - _address!, - )!, - ), - ], - sparkRecipients: - ref.read(pValidSparkSendToAddress) - ? [ - ( - address: _address!, - amount: amount, - memo: memoController.text, - isChange: false, - ), - ] - : null, + recipients: ref.read(pValidSparkSendToAddress) + ? null + : [ + TxRecipient( + address: _address!, + amount: amount, + isChange: false, + addressType: wallet.cryptoCurrency.getAddressType( + _address!, + )!, + ), + ], + sparkRecipients: ref.read(pValidSparkSendToAddress) + ? [ + ( + address: _address!, + amount: amount, + memo: memoController.text, + isChange: false, + ), + ] + : null, ), ); break; @@ -959,10 +950,10 @@ class _SendViewState extends ConsumerState { ethEIP1559Fee: ethFee, utxos: (wallet is CoinControlInterface && - coinControlEnabled && - selectedUTXOs.isNotEmpty) - ? selectedUTXOs - : null, + coinControlEnabled && + selectedUTXOs.isNotEmpty) + ? selectedUTXOs + : null, ), ); } @@ -975,10 +966,9 @@ class _SendViewState extends ConsumerState { if (isPaynymSend) { txData = txData.copyWith( paynymAccountLite: widget.accountLite!, - note: - noteController.text.isNotEmpty - ? noteController.text - : "PayNym send", + note: noteController.text.isNotEmpty + ? noteController.text + : "PayNym send", ); } else { txData = txData.copyWith(note: noteController.text); @@ -992,13 +982,12 @@ class _SendViewState extends ConsumerState { Navigator.of(context).push( RouteGenerator.getRoute( shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute, - builder: - (_) => ConfirmTransactionView( - txData: txData, - walletId: walletId, - isPaynymTransaction: isPaynymSend, - onSuccess: clearSendForm, - ), + builder: (_) => ConfirmTransactionView( + txData: txData, + walletId: walletId, + isPaynymTransaction: isPaynymSend, + onSuccess: clearSendForm, + ), settings: const RouteSettings( name: ConfirmTransactionView.routeName, ), @@ -1028,10 +1017,9 @@ class _SendViewState extends ConsumerState { child: Text( "Ok", style: STextStyles.button(context).copyWith( - color: - Theme.of( - context, - ).extension()!.accentColorDark, + color: Theme.of( + context, + ).extension()!.accentColorDark, ), ), onPressed: () { @@ -1089,10 +1077,9 @@ class _SendViewState extends ConsumerState { break; case BalanceType.private: - amount = - isFiro - ? ref.read(pWalletBalanceTertiary(walletId)).spendable - : ref.read(pWalletBalanceSecondary(walletId)).spendable; + amount = isFiro + ? ref.read(pWalletBalanceTertiary(walletId)).spendable + : ref.read(pWalletBalanceSecondary(walletId)).spendable; break; } } else { @@ -1153,32 +1140,32 @@ class _SendViewState extends ConsumerState { shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), - builder: - (_) => TransactionFeeSelectionSheet( - walletId: walletId, - amount: (Decimal.tryParse(cryptoAmountController.text) ?? + builder: (_) => TransactionFeeSelectionSheet( + walletId: walletId, + amount: + (Decimal.tryParse(cryptoAmountController.text) ?? ref.watch(pSendAmount)?.decimal ?? Decimal.zero) .toAmount(fractionDigits: coin.fractionDigits), - updateChosen: (String fee) { - if (fee == "custom") { - if (!isCustomFee.value) { - setState(() { - isCustomFee.value = true; - }); - } - return; - } - - _setCurrentFee(fee, true); + updateChosen: (String fee) { + if (fee == "custom") { + if (!isCustomFee.value) { setState(() { - _calculateFeesFuture = Future(() => fee); - if (isCustomFee.value) { - isCustomFee.value = false; - } + isCustomFee.value = true; }); - }, - ), + } + return; + } + + _setCurrentFee(fee, true); + setState(() { + _calculateFeesFuture = Future(() => fee); + if (isCustomFee.value) { + isCustomFee.value = false; + } + }); + }, + ), ); } @@ -1433,10 +1420,9 @@ class _SendViewState extends ConsumerState { children: [ Container( decoration: BoxDecoration( - color: - Theme.of( - context, - ).extension()!.popupBG, + color: Theme.of( + context, + ).extension()!.popupBG, borderRadius: BorderRadius.circular( Constants.size.circularBorderRadius, ), @@ -1489,38 +1475,31 @@ class _SendViewState extends ConsumerState { if (showPrivateBalance) { switch (balType) { case BalanceType.public: - amount = - ref - .read( - pWalletBalance( - walletId, - ), - ) - .spendable; + amount = ref + .read( + pWalletBalance(walletId), + ) + .spendable; break; case BalanceType.private: - amount = - ref - .read( - isMwebEnabled - ? pWalletBalanceSecondary( - walletId, - ) - : pWalletBalanceTertiary( - walletId, - ), - ) - .spendable; - break; - } - } else { - amount = - ref + amount = ref .read( - pWalletBalance(walletId), + isMwebEnabled + ? pWalletBalanceSecondary( + walletId, + ) + : pWalletBalanceTertiary( + walletId, + ), ) .spendable; + break; + } + } else { + amount = ref + .read(pWalletBalance(walletId)) + .spendable; } return GestureDetector( @@ -1672,95 +1651,103 @@ class _SendViewState extends ConsumerState { }, focusNode: _addressFocusNode, style: STextStyles.field(context), - decoration: standardInputDecoration( - isMwcSlatepack - ? "Enter ${coin.ticker} address (optional)" - : "Enter ${coin.ticker} address", - _addressFocusNode, - context, - ).copyWith( - contentPadding: const EdgeInsets.only( - left: 16, - top: 6, - bottom: 8, - right: 5, - ), - suffixIcon: Padding( - padding: - sendToController.text.isEmpty + decoration: + standardInputDecoration( + isMwcSlatepack + ? "Enter ${coin.ticker} address (optional)" + : "Enter ${coin.ticker} address", + _addressFocusNode, + context, + ).copyWith( + contentPadding: const EdgeInsets.only( + left: 16, + top: 6, + bottom: 8, + right: 5, + ), + suffixIcon: Padding( + padding: sendToController.text.isEmpty ? const EdgeInsets.only(right: 8) : const EdgeInsets.only(right: 0), - child: UnconstrainedBox( - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceAround, - children: [ - _addressToggleFlag - ? TextFieldIconButton( - semanticsLabel: - "Clear Button. Clears The Address Field Input.", - key: const Key( - "sendViewClearAddressFieldButtonKey", + child: UnconstrainedBox( + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + children: [ + _addressToggleFlag + ? TextFieldIconButton( + semanticsLabel: + "Clear Button. Clears The Address Field Input.", + key: const Key( + "sendViewClearAddressFieldButtonKey", + ), + onTap: () { + sendToController + .text = + ""; + _address = ""; + _setValidAddressProviders( + _address, + ); + setState(() { + _addressToggleFlag = + false; + }); + }, + child: const XIcon(), + ) + : TextFieldIconButton( + semanticsLabel: + "Paste Button. Pastes From Clipboard To Address Field Input.", + key: const Key( + "sendViewPasteAddressFieldButtonKey", + ), + onTap: _pasteAddress, + child: + sendToController + .text + .isEmpty + ? const ClipboardIcon() + : const XIcon(), + ), + if (sendToController + .text + .isEmpty) + TextFieldIconButton( + semanticsLabel: + "Address Book Button. Opens Address Book For Address Field.", + key: const Key( + "sendViewAddressBookButtonKey", + ), + onTap: () { + Navigator.of( + context, + ).pushNamed( + AddressBookView + .routeName, + arguments: widget.coin, + ); + }, + child: + const AddressBookIcon(), ), - onTap: () { - sendToController.text = ""; - _address = ""; - _setValidAddressProviders( - _address, - ); - setState(() { - _addressToggleFlag = - false; - }); - }, - child: const XIcon(), - ) - : TextFieldIconButton( - semanticsLabel: - "Paste Button. Pastes From Clipboard To Address Field Input.", - key: const Key( - "sendViewPasteAddressFieldButtonKey", + if (sendToController + .text + .isEmpty) + TextFieldIconButton( + semanticsLabel: + "Scan QR Button. Opens Camera For Scanning QR Code.", + key: const Key( + "sendViewScanQrButtonKey", + ), + onTap: _scanQr, + child: const QrCodeIcon(), ), - onTap: _pasteAddress, - child: - sendToController - .text - .isEmpty - ? const ClipboardIcon() - : const XIcon(), - ), - if (sendToController.text.isEmpty) - TextFieldIconButton( - semanticsLabel: - "Address Book Button. Opens Address Book For Address Field.", - key: const Key( - "sendViewAddressBookButtonKey", - ), - onTap: () { - Navigator.of( - context, - ).pushNamed( - AddressBookView.routeName, - arguments: widget.coin, - ); - }, - child: const AddressBookIcon(), - ), - if (sendToController.text.isEmpty) - TextFieldIconButton( - semanticsLabel: - "Scan QR Button. Opens Camera For Scanning QR Code.", - key: const Key( - "sendViewScanQrButtonKey", - ), - onTap: _scanQr, - child: const QrCodeIcon(), - ), - ], + ], + ), + ), ), ), - ), - ), ), ), const SizedBox(height: 10), @@ -1782,72 +1769,79 @@ class _SendViewState extends ConsumerState { onChanged: (_) { setState(() {}); }, - decoration: standardInputDecoration( - "Enter memo (optional)", - _memoFocus, - context, - ).copyWith( - counterText: '', - contentPadding: const EdgeInsets.only( - left: 16, - top: 6, - bottom: 8, - right: 5, - ), - suffixIcon: Padding( - padding: - memoController.text.isEmpty + decoration: + standardInputDecoration( + "Enter memo (optional)", + _memoFocus, + context, + ).copyWith( + counterText: '', + contentPadding: const EdgeInsets.only( + left: 16, + top: 6, + bottom: 8, + right: 5, + ), + suffixIcon: Padding( + padding: memoController.text.isEmpty ? const EdgeInsets.only(right: 8) : const EdgeInsets.only(right: 0), - child: UnconstrainedBox( - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceAround, - children: [ - memoController.text.isNotEmpty - ? TextFieldIconButton( - semanticsLabel: - "Clear Button. Clears The Memo Field Input.", - key: const Key( - "sendViewClearMemoFieldButtonKey", - ), - onTap: () { - memoController.text = ""; - setState(() {}); - }, - child: const XIcon(), - ) - : TextFieldIconButton( - semanticsLabel: - "Paste Button. Pastes From Clipboard To Memo Field Input.", - key: const Key( - "sendViewPasteMemoFieldButtonKey", - ), - onTap: () async { - final ClipboardData? data = - await clipboard.getData( - Clipboard.kTextPlain, - ); - if (data?.text != null && - data! - .text! - .isNotEmpty) { - final String content = - data.text!.trim(); - - memoController.text = - content.trim(); - - setState(() {}); - } - }, - child: const ClipboardIcon(), - ), - ], + child: UnconstrainedBox( + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + children: [ + memoController.text.isNotEmpty + ? TextFieldIconButton( + semanticsLabel: + "Clear Button. Clears The Memo Field Input.", + key: const Key( + "sendViewClearMemoFieldButtonKey", + ), + onTap: () { + memoController.text = + ""; + setState(() {}); + }, + child: const XIcon(), + ) + : TextFieldIconButton( + semanticsLabel: + "Paste Button. Pastes From Clipboard To Memo Field Input.", + key: const Key( + "sendViewPasteMemoFieldButtonKey", + ), + onTap: () async { + final ClipboardData? + data = await clipboard + .getData( + Clipboard + .kTextPlain, + ); + if (data?.text != + null && + data! + .text! + .isNotEmpty) { + final String + content = data.text! + .trim(); + + memoController + .text = content + .trim(); + + setState(() {}); + } + }, + child: + const ClipboardIcon(), + ), + ], + ), + ), ), ), - ), - ), ), ), Builder( @@ -1890,14 +1884,12 @@ class _SendViewState extends ConsumerState { child: Text( error, textAlign: TextAlign.left, - style: STextStyles.label( - context, - ).copyWith( - color: - Theme.of(context) + style: STextStyles.label(context) + .copyWith( + color: Theme.of(context) .extension()! .textError, - ), + ), ), ), ); @@ -1919,8 +1911,9 @@ class _SendViewState extends ConsumerState { children: [ TextField( autocorrect: Util.isDesktop ? false : true, - enableSuggestions: - Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop + ? false + : true, readOnly: true, textInputAction: TextInputAction.none, ), @@ -1929,10 +1922,9 @@ class _SendViewState extends ConsumerState { horizontal: 12, ), child: RawMaterialButton( - splashColor: - Theme.of( - context, - ).extension()!.highlight, + splashColor: Theme.of( + context, + ).extension()!.highlight, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( Constants.size.circularBorderRadius, @@ -1947,8 +1939,8 @@ class _SendViewState extends ConsumerState { top: Radius.circular(20), ), ), - builder: - (_) => DualBalanceSelectionSheet( + builder: (_) => + DualBalanceSelectionSheet( walletId: walletId, ), ); @@ -1977,28 +1969,26 @@ class _SendViewState extends ConsumerState { ) .state) { case BalanceType.public: - amount = - ref - .watch( - pWalletBalance( - walletId, - ), - ) - .spendable; + amount = ref + .watch( + pWalletBalance( + walletId, + ), + ) + .spendable; break; case BalanceType.private: - amount = - ref - .watch( - isFiro - ? pWalletBalanceTertiary( - walletId, - ) - : pWalletBalanceSecondary( - walletId, - ), - ) - .spendable; + amount = ref + .watch( + isFiro + ? pWalletBalanceTertiary( + walletId, + ) + : pWalletBalanceSecondary( + walletId, + ), + ) + .spendable; break; } @@ -2023,10 +2013,9 @@ class _SendViewState extends ConsumerState { Assets.svg.chevronDown, width: 8, height: 4, - color: - Theme.of(context) - .extension()! - .textSubtitle2, + color: Theme.of(context) + .extension()! + .textSubtitle2, ), ], ), @@ -2049,8 +2038,8 @@ class _SendViewState extends ConsumerState { showCoinControl, selectedUTXOs, ), - onTap: - () => _sendAllTapped(showCoinControl), + onTap: () => + _sendAllTapped(showCoinControl), ), ], ), @@ -2059,23 +2048,21 @@ class _SendViewState extends ConsumerState { autocorrect: Util.isDesktop ? false : true, enableSuggestions: Util.isDesktop ? false : true, style: STextStyles.smallMed14(context).copyWith( - color: - Theme.of( - context, - ).extension()!.textDark, + color: Theme.of( + context, + ).extension()!.textDark, ), key: const Key( "amountInputFieldCryptoTextFieldKey", ), controller: cryptoAmountController, focusNode: _cryptoFocus, - keyboardType: - Util.isDesktop - ? null - : const TextInputType.numberWithOptions( - signed: false, - decimal: true, - ), + keyboardType: Util.isDesktop + ? null + : const TextInputType.numberWithOptions( + signed: false, + decimal: true, + ), textAlign: TextAlign.right, inputFormatters: [ AmountInputFormatter( @@ -2111,14 +2098,12 @@ class _SendViewState extends ConsumerState { ref .watch(pAmountUnit(coin)) .unitForCoin(coin), - style: STextStyles.smallMed14( - context, - ).copyWith( - color: - Theme.of(context) + style: STextStyles.smallMed14(context) + .copyWith( + color: Theme.of(context) .extension()! .accentColorDark, - ), + ), ), ), ), @@ -2129,26 +2114,25 @@ class _SendViewState extends ConsumerState { if (Prefs.instance.externalCalls) TextField( autocorrect: Util.isDesktop ? false : true, - enableSuggestions: - Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop + ? false + : true, style: STextStyles.smallMed14(context).copyWith( - color: - Theme.of( - context, - ).extension()!.textDark, + color: Theme.of( + context, + ).extension()!.textDark, ), key: const Key( "amountInputFieldFiatTextFieldKey", ), controller: baseAmountController, focusNode: _baseFocus, - keyboardType: - Util.isDesktop - ? null - : const TextInputType.numberWithOptions( - signed: false, - decimal: true, - ), + keyboardType: Util.isDesktop + ? null + : const TextInputType.numberWithOptions( + signed: false, + decimal: true, + ), textAlign: TextAlign.right, inputFormatters: [ AmountInputFormatter( @@ -2184,14 +2168,12 @@ class _SendViewState extends ConsumerState { (value) => value.currency, ), ), - style: STextStyles.smallMed14( - context, - ).copyWith( - color: - Theme.of(context) + style: STextStyles.smallMed14(context) + .copyWith( + color: Theme.of(context) .extension()! .accentColorDark, - ), + ), ), ), ), @@ -2206,20 +2188,17 @@ class _SendViewState extends ConsumerState { children: [ Text( "Coin control", - style: STextStyles.w500_14( - context, - ).copyWith( - color: - Theme.of(context) + style: STextStyles.w500_14(context) + .copyWith( + color: Theme.of(context) .extension()! .textSubtitle1, - ), + ), ), CustomTextButton( - text: - selectedUTXOs.isEmpty - ? "Select coins" - : "Selected coins (${selectedUTXOs.length})", + text: selectedUTXOs.isEmpty + ? "Select coins" + : "Selected coins (${selectedUTXOs.length})", onTap: () async { if (FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); @@ -2229,12 +2208,9 @@ class _SendViewState extends ConsumerState { } if (context.mounted) { - final spendable = - ref - .read( - pWalletBalance(walletId), - ) - .spendable; + final spendable = ref + .read(pWalletBalance(walletId)) + .spendable; Amount? amount; if (ref.read(pSendAmount) != null) { @@ -2247,28 +2223,26 @@ class _SendViewState extends ConsumerState { } } - final result = await Navigator.of( - context, - ).pushNamed( - CoinControlView.routeName, - arguments: Tuple4( - walletId, - CoinControlViewType.use, - amount, - selectedUTXOs - .map((e) => e.utxo) - .toSet(), - ), - ); + final result = + await Navigator.of( + context, + ).pushNamed( + CoinControlView.routeName, + arguments: Tuple4( + walletId, + CoinControlViewType.use, + amount, + selectedUTXOs + .map((e) => e.utxo) + .toSet(), + ), + ); if (result is Set) { setState(() { - selectedUTXOs = - result - .map( - (e) => StandardInput(e), - ) - .toSet(); + selectedUTXOs = result + .map((e) => StandardInput(e)) + .toSet(); }); } } @@ -2292,42 +2266,47 @@ class _SendViewState extends ConsumerState { ), child: TextField( autocorrect: Util.isDesktop ? false : true, - enableSuggestions: - Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop + ? false + : true, maxLength: 256, controller: onChainNoteController, focusNode: _onChainNoteFocusNode, style: STextStyles.field(context), onChanged: (_) => setState(() {}), - decoration: standardInputDecoration( - "Type something...", - _onChainNoteFocusNode, - context, - ).copyWith( - suffixIcon: - onChainNoteController.text.isNotEmpty + decoration: + standardInputDecoration( + "Type something...", + _onChainNoteFocusNode, + context, + ).copyWith( + suffixIcon: + onChainNoteController + .text + .isNotEmpty ? Padding( - padding: const EdgeInsets.only( - right: 0, - ), - child: UnconstrainedBox( - child: Row( - children: [ - TextFieldIconButton( - child: const XIcon(), - onTap: () async { - setState(() { - onChainNoteController - .text = ""; - }); - }, - ), - ], + padding: const EdgeInsets.only( + right: 0, ), - ), - ) + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + onChainNoteController + .text = + ""; + }); + }, + ), + ], + ), + ), + ) : null, - ), + ), ), ), if (coin is Epiccash || coin is Mimblewimblecoin) @@ -2348,41 +2327,44 @@ class _SendViewState extends ConsumerState { ), child: TextField( autocorrect: Util.isDesktop ? false : true, - enableSuggestions: - Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop + ? false + : true, controller: noteController, focusNode: _noteFocusNode, style: STextStyles.field(context), onChanged: (_) => setState(() {}), - decoration: standardInputDecoration( - "Type something...", - _noteFocusNode, - context, - ).copyWith( - suffixIcon: - noteController.text.isNotEmpty + decoration: + standardInputDecoration( + "Type something...", + _noteFocusNode, + context, + ).copyWith( + suffixIcon: + noteController.text.isNotEmpty ? Padding( - padding: const EdgeInsets.only( - right: 0, - ), - child: UnconstrainedBox( - child: Row( - children: [ - TextFieldIconButton( - child: const XIcon(), - onTap: () async { - setState(() { - noteController.text = - ""; - }); - }, - ), - ], + padding: const EdgeInsets.only( + right: 0, ), - ), - ) + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + noteController + .text = + ""; + }); + }, + ), + ], + ), + ), + ) : null, - ), + ), ), ), if (coin is Epiccash || coin is Mimblewimblecoin) @@ -2401,41 +2383,42 @@ class _SendViewState extends ConsumerState { ), child: TextField( autocorrect: Util.isDesktop ? false : true, - enableSuggestions: - Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop + ? false + : true, controller: noteController, focusNode: _noteFocusNode, style: STextStyles.field(context), onChanged: (_) => setState(() {}), - decoration: standardInputDecoration( - "Type something...", - _noteFocusNode, - context, - ).copyWith( - suffixIcon: - noteController.text.isNotEmpty + decoration: + standardInputDecoration( + "Type something...", + _noteFocusNode, + context, + ).copyWith( + suffixIcon: noteController.text.isNotEmpty ? Padding( - padding: const EdgeInsets.only( - right: 0, - ), - child: UnconstrainedBox( - child: Row( - children: [ - TextFieldIconButton( - child: const XIcon(), - onTap: () async { - setState(() { - noteController.text = - ""; - }); - }, - ), - ], + padding: const EdgeInsets.only( + right: 0, ), - ), - ) + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + noteController.text = + ""; + }); + }, + ), + ], + ), + ), + ) : null, - ), + ), ), ), const SizedBox(height: 12), @@ -2461,8 +2444,9 @@ class _SendViewState extends ConsumerState { children: [ TextField( autocorrect: Util.isDesktop ? false : true, - enableSuggestions: - Util.isDesktop ? false : true, + enableSuggestions: Util.isDesktop + ? false + : true, controller: feeController, readOnly: true, textInputAction: TextInputAction.none, @@ -2476,10 +2460,9 @@ class _SendViewState extends ConsumerState { horizontal: 12, ), child: RawMaterialButton( - splashColor: - Theme.of(context) - .extension()! - .highlight, + splashColor: Theme.of( + context, + ).extension()!.highlight, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( Constants.size.circularBorderRadius, @@ -2487,138 +2470,128 @@ class _SendViewState extends ConsumerState { ), onPressed: isFiro && - ref - .watch( - publicPrivateBalanceStateProvider - .state, - ) - .state != - BalanceType.public - ? null - : _onFeeSelectPressed, + ref + .watch( + publicPrivateBalanceStateProvider + .state, + ) + .state != + BalanceType.public + ? null + : _onFeeSelectPressed, child: (isFiro && - ref - .watch( - publicPrivateBalanceStateProvider - .state, - ) - .state != - BalanceType.public) - ? Row( - children: [ - FutureBuilder( - future: - _calculateFeesFuture, - builder: ( - context, - snapshot, - ) { - if (snapshot.connectionState == - ConnectionState - .done && - snapshot.hasData) { - _setCurrentFee( - snapshot.data!, - false, - ); - return Text( - "~${snapshot.data!}", - style: - STextStyles.itemSubtitle( - context, - ), - ); - } else { - return AnimatedText( - stringsToLoopThrough: - stringsToLoopThrough, - style: - STextStyles.itemSubtitle( - context, - ), - ); - } - }, - ), - ], - ) - : Row( - mainAxisAlignment: - MainAxisAlignment - .spaceBetween, - children: [ - Row( - children: [ - Text( - ref - .watch( - feeRateTypeMobileStateProvider - .state, - ) - .state - .prettyName, + ref + .watch( + publicPrivateBalanceStateProvider + .state, + ) + .state != + BalanceType.public) + ? Row( + children: [ + FutureBuilder( + future: + _calculateFeesFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState + .done && + snapshot.hasData) { + _setCurrentFee( + snapshot.data!, + false, + ); + return Text( + "~${snapshot.data!}", style: - STextStyles.itemSubtitle12( + STextStyles.itemSubtitle( context, ), - ), - const SizedBox( - width: 10, - ), - FutureBuilder( - future: - _calculateFeesFuture, - builder: ( - context, - snapshot, - ) { - if (snapshot.connectionState == - ConnectionState - .done && - snapshot - .hasData) { - _setCurrentFee( - snapshot.data!, - false, - ); - return Text( - isCustomFee - .value - ? "" - : "~${snapshot.data!}", - style: - STextStyles.itemSubtitle( - context, - ), - ); - } else { - return AnimatedText( - stringsToLoopThrough: - stringsToLoopThrough, - style: - STextStyles.itemSubtitle( - context, - ), - ); - } - }, - ), - ], - ), - SvgPicture.asset( - Assets.svg.chevronDown, - width: 8, - height: 4, - color: - Theme.of(context) - .extension< - StackColors - >()! - .textSubtitle2, - ), - ], - ), + ); + } else { + return AnimatedText( + stringsToLoopThrough: + stringsToLoopThrough, + style: + STextStyles.itemSubtitle( + context, + ), + ); + } + }, + ), + ], + ) + : Row( + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Row( + children: [ + Text( + ref + .watch( + feeRateTypeMobileStateProvider + .state, + ) + .state + .prettyName, + style: + STextStyles.itemSubtitle12( + context, + ), + ), + const SizedBox(width: 10), + FutureBuilder( + future: + _calculateFeesFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState + .done && + snapshot + .hasData) { + _setCurrentFee( + snapshot.data!, + false, + ); + return Text( + isCustomFee.value + ? "" + : "~${snapshot.data!}", + style: + STextStyles.itemSubtitle( + context, + ), + ); + } else { + return AnimatedText( + stringsToLoopThrough: + stringsToLoopThrough, + style: + STextStyles.itemSubtitle( + context, + ), + ); + } + }, + ), + ], + ), + SvgPicture.asset( + Assets.svg.chevronDown, + width: 8, + height: 4, + color: Theme.of(context) + .extension< + StackColors + >()! + .textSubtitle2, + ), + ], + ), ), ), ), @@ -2649,23 +2622,18 @@ class _SendViewState extends ConsumerState { TextButton( onPressed: ref.watch(pPreviewTxButtonEnabled(coin)) - ? ref.watch( - pSelectedMwcTransactionMethod, - ) == - MwcTransactionMethod.slatepack - ? _createSlatepack - : _previewTransaction - : null, - style: - ref.watch(pPreviewTxButtonEnabled(coin)) - ? Theme.of(context) - .extension()! - .getPrimaryEnabledButtonStyle(context) - : Theme.of(context) - .extension()! - .getPrimaryDisabledButtonStyle( - context, - ), + ? ref.watch(pSelectedMwcTransactionMethod) == + MwcTransactionMethod.slatepack + ? _createSlatepack + : _previewTransaction + : null, + style: ref.watch(pPreviewTxButtonEnabled(coin)) + ? Theme.of(context) + .extension()! + .getPrimaryEnabledButtonStyle(context) + : Theme.of(context) + .extension()! + .getPrimaryDisabledButtonStyle(context), child: Text( ref.watch(pSelectedMwcTransactionMethod) == MwcTransactionMethod.slatepack diff --git a/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart b/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart index 3ec7048a32..8c05fa973d 100644 --- a/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart +++ b/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart @@ -8,7 +8,6 @@ * */ -import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -29,6 +28,7 @@ import '../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../wallets/wallet/impl/firo_wallet.dart'; import '../../../wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart'; import '../../../widgets/animated_text.dart'; +import '../../../wl_gen/interfaces/cs_monero_interface.dart'; final feeSheetSessionCacheProvider = ChangeNotifierProvider((ref) { @@ -91,7 +91,7 @@ class _TransactionFeeSelectionSheetState if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - BigInt.from(lib_monero.TransactionPriority.high.value), + BigInt.from(csMonero.getTxPriorityHigh()), ); ref.read(feeSheetSessionCacheProvider).fast[amount] = fee; } else if (coin is Firo) { @@ -128,7 +128,7 @@ class _TransactionFeeSelectionSheetState if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - BigInt.from(lib_monero.TransactionPriority.medium.value), + BigInt.from(csMonero.getTxPriorityMedium()), ); ref.read(feeSheetSessionCacheProvider).average[amount] = fee; } else if (coin is Firo) { @@ -164,7 +164,7 @@ class _TransactionFeeSelectionSheetState if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - BigInt.from(lib_monero.TransactionPriority.normal.value), + BigInt.from(csMonero.getTxPriorityNormal()), ); ref.read(feeSheetSessionCacheProvider).slow[amount] = fee; } else if (coin is Firo) { @@ -254,10 +254,9 @@ class _TransactionFeeSelectionSheetState Center( child: Container( decoration: BoxDecoration( - color: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + color: Theme.of( + context, + ).extension()!.textFieldDefaultBG, borderRadius: BorderRadius.circular( Constants.size.circularBorderRadius, ), @@ -268,10 +267,9 @@ class _TransactionFeeSelectionSheetState ), const SizedBox(height: 36), FutureBuilder( - future: - widget.isToken - ? ref.read(pCurrentTokenWallet)!.fees - : wallet.fees, + future: widget.isToken + ? ref.read(pCurrentTokenWallet)!.fees + : wallet.fees, builder: (context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) { @@ -289,10 +287,9 @@ class _TransactionFeeSelectionSheetState const SizedBox(height: 16), GestureDetector( onTap: () { - final state = - ref - .read(feeRateTypeMobileStateProvider.state) - .state; + final state = ref + .read(feeRateTypeMobileStateProvider.state) + .state; if (state != FeeRateType.fast) { ref.read(feeRateTypeMobileStateProvider.state).state = FeeRateType.fast; @@ -318,25 +315,23 @@ class _TransactionFeeSelectionSheetState width: 20, height: 20, child: Radio( - activeColor: - Theme.of(context) - .extension()! - .radioButtonIconEnabled, + activeColor: Theme.of(context) + .extension()! + .radioButtonIconEnabled, value: FeeRateType.fast, - groupValue: - ref - .watch( - feeRateTypeMobileStateProvider - .state, - ) - .state, + groupValue: ref + .watch( + feeRateTypeMobileStateProvider.state, + ) + .state, onChanged: (x) { ref - .read( - feeRateTypeMobileStateProvider - .state, - ) - .state = FeeRateType.fast; + .read( + feeRateTypeMobileStateProvider + .state, + ) + .state = + FeeRateType.fast; Navigator.of(context).pop(); }, @@ -373,30 +368,33 @@ class _TransactionFeeSelectionSheetState feeRate: feeObject!.fast, amount: amount, ), - builder: ( - _, - AsyncSnapshot snapshot, - ) { - if (snapshot.connectionState == - ConnectionState.done && - snapshot.hasData) { - return Text( - "(~${ref.watch(pAmountFormatter(coin)).format(snapshot.data!, indicatePrecisionLoss: false)})", - style: STextStyles.itemSubtitle( - context, - ), - textAlign: TextAlign.left, - ); - } else { - return AnimatedText( - stringsToLoopThrough: - stringsToLoopThrough, - style: STextStyles.itemSubtitle( - context, - ), - ); - } - }, + builder: + ( + _, + AsyncSnapshot snapshot, + ) { + if (snapshot.connectionState == + ConnectionState.done && + snapshot.hasData) { + return Text( + "(~${ref.watch(pAmountFormatter(coin)).format(snapshot.data!, indicatePrecisionLoss: false)})", + style: + STextStyles.itemSubtitle( + context, + ), + textAlign: TextAlign.left, + ); + } else { + return AnimatedText( + stringsToLoopThrough: + stringsToLoopThrough, + style: + STextStyles.itemSubtitle( + context, + ), + ); + } + }, ), ], ), @@ -426,10 +424,9 @@ class _TransactionFeeSelectionSheetState const SizedBox(height: 16), GestureDetector( onTap: () { - final state = - ref - .read(feeRateTypeMobileStateProvider.state) - .state; + final state = ref + .read(feeRateTypeMobileStateProvider.state) + .state; if (state != FeeRateType.average) { ref.read(feeRateTypeMobileStateProvider.state).state = FeeRateType.average; @@ -454,25 +451,23 @@ class _TransactionFeeSelectionSheetState width: 20, height: 20, child: Radio( - activeColor: - Theme.of(context) - .extension()! - .radioButtonIconEnabled, + activeColor: Theme.of(context) + .extension()! + .radioButtonIconEnabled, value: FeeRateType.average, - groupValue: - ref - .watch( - feeRateTypeMobileStateProvider - .state, - ) - .state, + groupValue: ref + .watch( + feeRateTypeMobileStateProvider.state, + ) + .state, onChanged: (x) { ref - .read( - feeRateTypeMobileStateProvider - .state, - ) - .state = FeeRateType.average; + .read( + feeRateTypeMobileStateProvider + .state, + ) + .state = + FeeRateType.average; Navigator.of(context).pop(); }, ), @@ -508,30 +503,33 @@ class _TransactionFeeSelectionSheetState feeRate: feeObject!.medium, amount: amount, ), - builder: ( - _, - AsyncSnapshot snapshot, - ) { - if (snapshot.connectionState == - ConnectionState.done && - snapshot.hasData) { - return Text( - "(~${ref.watch(pAmountFormatter(coin)).format(snapshot.data!, indicatePrecisionLoss: false)})", - style: STextStyles.itemSubtitle( - context, - ), - textAlign: TextAlign.left, - ); - } else { - return AnimatedText( - stringsToLoopThrough: - stringsToLoopThrough, - style: STextStyles.itemSubtitle( - context, - ), - ); - } - }, + builder: + ( + _, + AsyncSnapshot snapshot, + ) { + if (snapshot.connectionState == + ConnectionState.done && + snapshot.hasData) { + return Text( + "(~${ref.watch(pAmountFormatter(coin)).format(snapshot.data!, indicatePrecisionLoss: false)})", + style: + STextStyles.itemSubtitle( + context, + ), + textAlign: TextAlign.left, + ); + } else { + return AnimatedText( + stringsToLoopThrough: + stringsToLoopThrough, + style: + STextStyles.itemSubtitle( + context, + ), + ); + } + }, ), ], ), @@ -561,10 +559,9 @@ class _TransactionFeeSelectionSheetState const SizedBox(height: 16), GestureDetector( onTap: () { - final state = - ref - .read(feeRateTypeMobileStateProvider.state) - .state; + final state = ref + .read(feeRateTypeMobileStateProvider.state) + .state; if (state != FeeRateType.slow) { ref.read(feeRateTypeMobileStateProvider.state).state = FeeRateType.slow; @@ -586,25 +583,23 @@ class _TransactionFeeSelectionSheetState width: 20, height: 20, child: Radio( - activeColor: - Theme.of(context) - .extension()! - .radioButtonIconEnabled, + activeColor: Theme.of(context) + .extension()! + .radioButtonIconEnabled, value: FeeRateType.slow, - groupValue: - ref - .watch( - feeRateTypeMobileStateProvider - .state, - ) - .state, + groupValue: ref + .watch( + feeRateTypeMobileStateProvider.state, + ) + .state, onChanged: (x) { ref - .read( - feeRateTypeMobileStateProvider - .state, - ) - .state = FeeRateType.slow; + .read( + feeRateTypeMobileStateProvider + .state, + ) + .state = + FeeRateType.slow; Navigator.of(context).pop(); }, ), @@ -640,30 +635,33 @@ class _TransactionFeeSelectionSheetState feeRate: feeObject!.slow, amount: amount, ), - builder: ( - _, - AsyncSnapshot snapshot, - ) { - if (snapshot.connectionState == - ConnectionState.done && - snapshot.hasData) { - return Text( - "(~${ref.watch(pAmountFormatter(coin)).format(snapshot.data!, indicatePrecisionLoss: false)})", - style: STextStyles.itemSubtitle( - context, - ), - textAlign: TextAlign.left, - ); - } else { - return AnimatedText( - stringsToLoopThrough: - stringsToLoopThrough, - style: STextStyles.itemSubtitle( - context, - ), - ); - } - }, + builder: + ( + _, + AsyncSnapshot snapshot, + ) { + if (snapshot.connectionState == + ConnectionState.done && + snapshot.hasData) { + return Text( + "(~${ref.watch(pAmountFormatter(coin)).format(snapshot.data!, indicatePrecisionLoss: false)})", + style: + STextStyles.itemSubtitle( + context, + ), + textAlign: TextAlign.left, + ); + } else { + return AnimatedText( + stringsToLoopThrough: + stringsToLoopThrough, + style: + STextStyles.itemSubtitle( + context, + ), + ); + } + }, ), ], ), @@ -694,14 +692,14 @@ class _TransactionFeeSelectionSheetState if (wallet is ElectrumXInterface || coin is Ethereum) GestureDetector( onTap: () { - final state = - ref - .read(feeRateTypeMobileStateProvider.state) - .state; + final state = ref + .read(feeRateTypeMobileStateProvider.state) + .state; if (state != FeeRateType.custom) { ref - .read(feeRateTypeMobileStateProvider.state) - .state = FeeRateType.custom; + .read(feeRateTypeMobileStateProvider.state) + .state = + FeeRateType.custom; } widget.updateChosen("custom"); @@ -718,25 +716,24 @@ class _TransactionFeeSelectionSheetState width: 20, height: 20, child: Radio( - activeColor: - Theme.of(context) - .extension()! - .radioButtonIconEnabled, + activeColor: Theme.of(context) + .extension()! + .radioButtonIconEnabled, value: FeeRateType.custom, - groupValue: - ref - .watch( - feeRateTypeMobileStateProvider - .state, - ) - .state, + groupValue: ref + .watch( + feeRateTypeMobileStateProvider + .state, + ) + .state, onChanged: (x) { ref - .read( - feeRateTypeMobileStateProvider - .state, - ) - .state = FeeRateType.custom; + .read( + feeRateTypeMobileStateProvider + .state, + ) + .state = + FeeRateType.custom; Navigator.of(context).pop(); }, ), diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/edit_refresh_height_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/edit_refresh_height_view.dart index f1d0aa5f9a..47ce7d25c4 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/edit_refresh_height_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/edit_refresh_height_view.dart @@ -5,12 +5,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../notifications/show_flush_bar.dart'; import '../../../../providers/db/main_db_provider.dart'; -import '../../../../providers/global/wallets_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/constants.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; -import '../../../../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/conditional_parent.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -20,6 +19,7 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/icon_widgets/x_icon.dart'; import '../../../../widgets/stack_text_field.dart'; import '../../../../widgets/textfield_icon_button.dart'; +import '../../../../wl_gen/interfaces/cs_monero_interface.dart'; class EditRefreshHeightView extends ConsumerStatefulWidget { const EditRefreshHeightView({super.key, required this.walletId}); @@ -34,7 +34,6 @@ class EditRefreshHeightView extends ConsumerStatefulWidget { } class _EditRefreshHeightViewState extends ConsumerState { - late final LibMoneroWallet _wallet; late final TextEditingController _controller; final _focusNode = FocusNode(); @@ -48,11 +47,13 @@ class _EditRefreshHeightViewState extends ConsumerState { try { final newHeight = int.tryParse(_controller.text); if (newHeight != null && newHeight >= 0) { - await _wallet.info.updateRestoreHeight( - newRestoreHeight: newHeight, - isar: ref.read(mainDBProvider).isar, - ); - _wallet.libMoneroWallet!.setRefreshFromBlockHeight(newHeight); + await ref + .read(pWalletInfo(widget.walletId)) + .updateRestoreHeight( + newRestoreHeight: newHeight, + isar: ref.read(mainDBProvider).isar, + ); + csMonero.setRefreshFromBlockHeight(widget.walletId, newHeight); } else { errMessage = "Invalid height: ${_controller.text}"; } @@ -88,11 +89,8 @@ class _EditRefreshHeightViewState extends ConsumerState { @override void initState() { super.initState(); - _wallet = ref.read(pWallets).getWallet(widget.walletId) as LibMoneroWallet; - _controller = - TextEditingController() - ..text = - _wallet.libMoneroWallet!.getRefreshFromBlockHeight().toString(); + _controller = TextEditingController() + ..text = csMonero.getRefreshFromBlockHeight(widget.walletId).toString(); } @override @@ -118,8 +116,10 @@ class _EditRefreshHeightViewState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.end, children: [ DesktopDialogCloseButton( - onPressedOverride: - Navigator.of(context, rootNavigator: true).pop, + onPressedOverride: Navigator.of( + context, + rootNavigator: true, + ).pop, ), ], ), @@ -137,8 +137,9 @@ class _EditRefreshHeightViewState extends ConsumerState { builder: (child) { return Background( child: Scaffold( - backgroundColor: - Theme.of(context).extension()!.background, + backgroundColor: Theme.of( + context, + ).extension()!.background, appBar: AppBar( leading: AppBarBackButton( onPressed: () { @@ -167,51 +168,47 @@ class _EditRefreshHeightViewState extends ConsumerState { key: const Key("restoreHeightFieldKey"), controller: _controller, focusNode: _focusNode, - style: - Util.isDesktop - ? STextStyles.desktopTextMedium( - context, - ).copyWith(height: 2) - : STextStyles.field(context), + style: Util.isDesktop + ? STextStyles.desktopTextMedium(context).copyWith(height: 2) + : STextStyles.field(context), enableSuggestions: false, autocorrect: false, autofocus: true, onSubmitted: (_) => _save(), onChanged: (_) => setState(() {}), - decoration: standardInputDecoration( - "Restore height", - _focusNode, - context, - ).copyWith( - suffixIcon: - _controller.text.isNotEmpty + decoration: + standardInputDecoration( + "Restore height", + _focusNode, + context, + ).copyWith( + suffixIcon: _controller.text.isNotEmpty ? Padding( - padding: const EdgeInsets.only(right: 0), - child: UnconstrainedBox( - child: ConditionalParent( - condition: Util.isDesktop, - builder: - (child) => - SizedBox(height: 70, child: child), - child: Row( - children: [ - TextFieldIconButton( - child: const XIcon(), - onTap: () async { - setState(() { - _controller.text = ""; - }); - }, - ), - ], + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: ConditionalParent( + condition: Util.isDesktop, + builder: (child) => + SizedBox(height: 70, child: child), + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + _controller.text = ""; + }); + }, + ), + ], + ), ), ), - ), - ) + ) : Util.isDesktop ? const SizedBox(height: 70) : null, - ), + ), ), ), Util.isDesktop ? const SizedBox(height: 32) : const Spacer(), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send_fee_form.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send_fee_form.dart index 69757ac7c7..462262d9cd 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send_fee_form.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send_fee_form.dart @@ -1,4 +1,3 @@ -import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -22,6 +21,7 @@ import '../../../../widgets/custom_buttons/blue_text_button.dart'; import '../../../../widgets/desktop/desktop_fee_dialog.dart'; import '../../../../widgets/eth_fee_form.dart'; import '../../../../widgets/fee_slider.dart'; +import '../../../../wl_gen/interfaces/cs_monero_interface.dart'; class DesktopSendFeeForm extends ConsumerStatefulWidget { const DesktopSendFeeForm({ @@ -86,47 +86,44 @@ class _DesktopSendFeeFormState extends ConsumerState { children: [ ConditionalParent( condition: canEditFees, - builder: - (child) => Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - child, - CustomTextButton( - text: "Edit", - onTap: () async { - feeSelectionResult = - await showDialog<(FeeRateType, String?, String?)?>( - context: context, - builder: - (_) => DesktopFeeDialog( - walletId: widget.walletId, - isToken: widget.isToken, - ), - ); + builder: (child) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + child, + CustomTextButton( + text: "Edit", + onTap: () async { + feeSelectionResult = + await showDialog<(FeeRateType, String?, String?)?>( + context: context, + builder: (_) => DesktopFeeDialog( + walletId: widget.walletId, + isToken: widget.isToken, + ), + ); - if (feeSelectionResult != null) { - if (_isCustomFee && - feeSelectionResult!.$1 != FeeRateType.custom) { - _isCustomFee = false; - } else if (!_isCustomFee && - feeSelectionResult!.$1 == FeeRateType.custom) { - _isCustomFee = true; - } - } + if (feeSelectionResult != null) { + if (_isCustomFee && + feeSelectionResult!.$1 != FeeRateType.custom) { + _isCustomFee = false; + } else if (!_isCustomFee && + feeSelectionResult!.$1 == FeeRateType.custom) { + _isCustomFee = true; + } + } - setState(() {}); - }, - ), - ], + setState(() {}); + }, ), + ], + ), child: Text( "Transaction fee" "${_isCustomFee ? "" : " (${isEth ? "max" : "estimated"})"}", style: STextStyles.desktopTextExtraSmall(context).copyWith( - color: - Theme.of( - context, - ).extension()!.textFieldActiveSearchIconRight, + color: Theme.of( + context, + ).extension()!.textFieldActiveSearchIconRight, ), textAlign: TextAlign.left, ), @@ -135,162 +132,155 @@ class _DesktopSendFeeFormState extends ConsumerState { if (!_isCustomFee) Padding( padding: const EdgeInsets.all(10), - child: - (feeSelectionResult?.$2 == null) - ? FutureBuilder( - future: ref.watch( - pWallets.select( - (value) => value.getWallet(widget.walletId).fees, - ), + child: (feeSelectionResult?.$2 == null) + ? FutureBuilder( + future: ref.watch( + pWallets.select( + (value) => value.getWallet(widget.walletId).fees, ), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done && - snapshot.hasData) { - return DesktopFeeItem( - feeObject: snapshot.data, - feeRateType: FeeRateType.average, - walletId: widget.walletId, - isButton: false, - feeFor: ({ - required Amount amount, - required FeeRateType feeRateType, - required BigInt feeRate, - required CryptoCurrency coin, - }) async { - if (ref - .read( - widget.isToken - ? tokenFeeSessionCacheProvider - : feeSheetSessionCacheProvider, - ) - .average[amount] == - null) { - if (widget.isToken == false) { - final wallet = ref - .read(pWallets) - .getWallet(widget.walletId); + ), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData) { + return DesktopFeeItem( + feeObject: snapshot.data, + feeRateType: FeeRateType.average, + walletId: widget.walletId, + isButton: false, + feeFor: + ({ + required Amount amount, + required FeeRateType feeRateType, + required BigInt feeRate, + required CryptoCurrency coin, + }) async { + if (ref + .read( + widget.isToken + ? tokenFeeSessionCacheProvider + : feeSheetSessionCacheProvider, + ) + .average[amount] == + null) { + if (widget.isToken == false) { + final wallet = ref + .read(pWallets) + .getWallet(widget.walletId); - if (coin is Monero || coin is Wownero) { - final fee = await wallet.estimateFeeFor( - amount, - BigInt.from( - lib_monero - .TransactionPriority - .medium - .value, - ), - ); - ref - .read(feeSheetSessionCacheProvider) - .average[amount] = - fee; - } else if ((coin is Firo) && + if (coin is Monero || coin is Wownero) { + final fee = await wallet.estimateFeeFor( + amount, + BigInt.from( + csMonero.getTxPriorityMedium(), + ), + ); ref + .read( + feeSheetSessionCacheProvider, + ) + .average[amount] = + fee; + } else if ((coin is Firo) && + ref + .read( + publicPrivateBalanceStateProvider + .state, + ) + .state != + BalanceType.public) { + final firoWallet = wallet as FiroWallet; + + if (ref .read( publicPrivateBalanceStateProvider .state, ) - .state != - BalanceType.public) { - final firoWallet = wallet as FiroWallet; - - if (ref - .read( - publicPrivateBalanceStateProvider - .state, - ) - .state == - BalanceType.private) { + .state == + BalanceType.private) { + ref + .read(feeSheetSessionCacheProvider) + .average[amount] = await firoWallet + .estimateFeeForSpark(amount); + } + } else { ref .read(feeSheetSessionCacheProvider) - .average[amount] = await firoWallet - .estimateFeeForSpark(amount); + .average[amount] = await wallet + .estimateFeeFor(amount, feeRate); } } else { - ref - .read(feeSheetSessionCacheProvider) - .average[amount] = await wallet + final tokenWallet = ref.read( + pCurrentTokenWallet, + )!; + final fee = await tokenWallet .estimateFeeFor(amount, feeRate); + ref + .read(tokenFeeSessionCacheProvider) + .average[amount] = + fee; } - } else { - final tokenWallet = - ref.read(pCurrentTokenWallet)!; - final fee = await tokenWallet.estimateFeeFor( - amount, - feeRate, - ); - ref - .read(tokenFeeSessionCacheProvider) - .average[amount] = - fee; } - } - return ref - .read( - widget.isToken - ? tokenFeeSessionCacheProvider - : feeSheetSessionCacheProvider, - ) - .average[amount]!; - }, - isSelected: true, - ); - } else { - return Row( - children: [ - AnimatedText( - stringsToLoopThrough: stringsToLoopThrough, - style: STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension()! - .textFieldActiveText, - ), - ), - ], - ); - } - }, - ) - : Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - feeSelectionResult?.$2 ?? "", - style: STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of( - context, - ).extension()!.textFieldActiveText, - ), - textAlign: TextAlign.left, - ), - Text( - feeSelectionResult?.$3 ?? "", - style: STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension()! - .textFieldActiveSearchIconRight, - ), - ), - ], - ), + return ref + .read( + widget.isToken + ? tokenFeeSessionCacheProvider + : feeSheetSessionCacheProvider, + ) + .average[amount]!; + }, + isSelected: true, + ); + } else { + return Row( + children: [ + AnimatedText( + stringsToLoopThrough: stringsToLoopThrough, + style: + STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textFieldActiveText, + ), + ), + ], + ); + } + }, + ) + : Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + feeSelectionResult?.$2 ?? "", + style: STextStyles.desktopTextExtraExtraSmall(context) + .copyWith( + color: Theme.of( + context, + ).extension()!.textFieldActiveText, + ), + textAlign: TextAlign.left, + ), + Text( + feeSelectionResult?.$3 ?? "", + style: STextStyles.desktopTextExtraExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textFieldActiveSearchIconRight, + ), + ), + ], + ), ), if (_isCustomFee && isEth) EthFeeForm( - minGasLimit: - widget.isToken - ? kEthereumTokenMinGasLimit - : kEthereumMinGasLimit, - stateChanged: - (value) => widget.onCustomEip1559FeeOptionChanged?.call(value), + minGasLimit: widget.isToken + ? kEthereumTokenMinGasLimit + : kEthereumMinGasLimit, + stateChanged: (value) => + widget.onCustomEip1559FeeOptionChanged?.call(value), ), if (_isCustomFee && !isEth) Padding( diff --git a/lib/services/churning_service.dart b/lib/services/churning_service.dart index 0aecefe080..1e1885086f 100644 --- a/lib/services/churning_service.dart +++ b/lib/services/churning_service.dart @@ -1,18 +1,14 @@ import 'dart:async'; import 'dart:math'; -import 'package:cs_monero/cs_monero.dart'; import 'package:flutter/cupertino.dart'; import 'package:mutex/mutex.dart'; +import '../utilities/logger.dart'; import '../wallets/wallet/intermediate/lib_monero_wallet.dart'; +import '../wl_gen/interfaces/cs_monero_interface.dart'; -enum ChurnStatus { - waiting, - running, - failed, - success; -} +enum ChurnStatus { waiting, running, failed, success } class ChurningService extends ChangeNotifier { // stack only uses account 0 at this point in time @@ -21,7 +17,7 @@ class ChurningService extends ChangeNotifier { ChurningService({required this.wallet}); final LibMoneroWallet wallet; - Wallet get csWallet => wallet.libMoneroWallet!; + String get walletId => wallet.walletId; int rounds = 1; // default bool ignoreErrors = false; // default @@ -36,7 +32,9 @@ class ChurningService extends ChangeNotifier { Object? lastSeenError; bool _canChurn() { - if (csWallet.getUnlockedBalance(accountIndex: kAccount) > BigInt.zero) { + if (csMonero.walletInstanceExists(walletId) && + csMonero.getUnlockedBalance(walletId, accountIndex: kAccount)! > + BigInt.zero) { return true; } else { return false; @@ -50,7 +48,7 @@ class ChurningService extends ChangeNotifier { return; } - final outputs = await csWallet.getOutputs(refresh: true); + final outputs = await csMonero.getOutputs(walletId, refresh: true); final required = wallet.cryptoCurrency.minConfirms; int lowestNumberOfConfirms = required; @@ -127,14 +125,14 @@ class ChurningService extends ChangeNotifier { try { _stopConfirmsTimer(); - Logging.log?.i("Doing churn #${roundsCompleted + 1}"); + Logging.instance.i("Doing churn #${roundsCompleted + 1}"); await _churnTxSimple(); waitingForUnlockedBalance = ChurnStatus.success; makingChurnTransaction = ChurnStatus.success; roundsCompleted++; notifyListeners(); } catch (e, s) { - Logging.log?.e( + Logging.instance.e( "Churning round #${roundsCompleted + 1} failed", error: e, stackTrace: s, @@ -154,7 +152,7 @@ class ChurningService extends ChangeNotifier { } } } else { - Logging.log?.i("Can't churn yet, waiting..."); + Logging.instance.i("Can't churn yet, waiting..."); } if (!complete() && _running) { @@ -174,7 +172,7 @@ class ChurningService extends ChangeNotifier { done = true; _running = false; notifyListeners(); - Logging.log?.i("Churning complete"); + Logging.instance.i("Churning complete"); } void stopChurning() { @@ -184,24 +182,28 @@ class ChurningService extends ChangeNotifier { unpause(); } - Future _churnTxSimple({ - final TransactionPriority priority = TransactionPriority.normal, - }) async { - final address = csWallet.getAddress( + Future _churnTxSimple() async { + final address = csMonero.getAddress( + walletId, accountIndex: kAccount, addressIndex: 0, ); - final pending = await csWallet.createTx( - output: Recipient( - address: address.value, - amount: BigInt.zero, // Doesn't matter if `sweep` is true + final height = await wallet.chainHeight; + + final pending = await csMonero.createTx( + walletId, + output: CsRecipient( + address, + BigInt.zero, // Doesn't matter if `sweep` is true ), - priority: priority, + priority: csMonero.getTxPriorityNormal(), accountIndex: kAccount, sweep: true, + minConfirms: wallet.cryptoCurrency.minConfirms, + currentHeight: height, ); - await csWallet.commitTx(pending); + await csMonero.commitTx(walletId, pending); } } diff --git a/lib/wallets/models/tx_data.dart b/lib/wallets/models/tx_data.dart index 9a8dffd214..8ebed1e5e5 100644 --- a/lib/wallets/models/tx_data.dart +++ b/lib/wallets/models/tx_data.dart @@ -1,4 +1,3 @@ -import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:cs_salvium/cs_salvium.dart' as lib_salvium; import 'package:tezart/tezart.dart' as tezart; import 'package:web3dart/web3dart.dart' as web3dart; @@ -10,6 +9,8 @@ import '../../models/paynym/paynym_account_lite.dart'; import '../../utilities/amount/amount.dart'; import '../../utilities/enums/fee_rate_type_enum.dart'; import '../../widgets/eth_fee_form.dart'; +import '../../wl_gen/interfaces/cs_monero_interface.dart' + show CsPendingTransaction; import '../isar/models/spark_coin.dart'; import 'name_op_state.dart'; import 'tx_recipient.dart'; @@ -67,7 +68,7 @@ class TxData { final int? nonce; final BigInt? chainId; // wownero and monero specific - final lib_monero.PendingTransaction? pendingTransaction; + final CsPendingTransaction? pendingTransaction; // salvium final lib_salvium.PendingTransaction? pendingSalviumTransaction; @@ -169,10 +170,10 @@ class TxData { Amount? get amountSpark => sparkRecipients != null && sparkRecipients!.isNotEmpty - ? sparkRecipients! - .map((e) => e.amount) - .reduce((total, amount) => total += amount) - : null; + ? sparkRecipients! + .map((e) => e.amount) + .reduce((total, amount) => total += amount) + : null; Amount? get amountWithoutChange { if (recipients != null && recipients!.isNotEmpty) { @@ -230,10 +231,9 @@ class TxData { } } - int? get estimatedSatsPerVByte => - fee != null && vSize != null - ? (fee!.raw ~/ BigInt.from(vSize!)).toInt() - : null; + int? get estimatedSatsPerVByte => fee != null && vSize != null + ? (fee!.raw ~/ BigInt.from(vSize!)).toInt() + : null; TxData copyWith({ FeeRateType? feeRateType, @@ -259,7 +259,7 @@ class TxData { web3dart.Transaction? web3dartTransaction, int? nonce, BigInt? chainId, - lib_monero.PendingTransaction? pendingTransaction, + CsPendingTransaction? pendingTransaction, lib_salvium.PendingTransaction? pendingSalviumTransaction, int? jMintValue, List? spendCoinIndexes, diff --git a/lib/wallets/wallet/impl/monero_wallet.dart b/lib/wallets/wallet/impl/monero_wallet.dart index 03bbfd5c38..e250192d77 100644 --- a/lib/wallets/wallet/impl/monero_wallet.dart +++ b/lib/wallets/wallet/impl/monero_wallet.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'package:compat/compat.dart' as lib_monero_compat; -import 'package:cs_monero/cs_monero.dart' as lib_monero; import '../../../utilities/amount/amount.dart'; +import '../../../wl_gen/interfaces/cs_monero_interface.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../intermediate/lib_monero_wallet.dart'; @@ -13,36 +13,17 @@ class MoneroWallet extends LibMoneroWallet { @override Future estimateFeeFor(Amount amount, BigInt feeRate) async { - if (libMoneroWallet == null || + if (!csMonero.walletInstanceExists(walletId) || syncStatus is! lib_monero_compat.SyncedSyncStatus) { return Amount.zeroWith(fractionDigits: cryptoCurrency.fractionDigits); } - lib_monero.TransactionPriority priority; - switch (feeRate.toInt()) { - case 1: - priority = lib_monero.TransactionPriority.low; - break; - case 2: - priority = lib_monero.TransactionPriority.medium; - break; - case 3: - priority = lib_monero.TransactionPriority.high; - break; - case 4: - priority = lib_monero.TransactionPriority.last; - break; - case 0: - default: - priority = lib_monero.TransactionPriority.normal; - break; - } - int approximateFee = 0; await estimateFeeMutex.protect(() async { - approximateFee = await libMoneroWallet!.estimateFee( - priority, - amount.raw.toInt(), + approximateFee = await csMonero.estimateFee( + feeRate.toInt(), + amount.raw, + walletId: walletId, ); }); @@ -53,76 +34,67 @@ class MoneroWallet extends LibMoneroWallet { } @override - bool walletExists(String path) => lib_monero.MoneroWallet.isWalletExist(path); + bool walletExists(String path) => + csMonero.walletExists(path, csCoin: CsCoin.monero); @override - Future loadWallet({ - required String path, - required String password, - }) async { - libMoneroWallet = await lib_monero.MoneroWallet.loadWallet( - path: path, - password: password, - ); - } + Future loadWallet({required String path, required String password}) => + csMonero.loadWallet( + walletId, + path: path, + password: password, + csCoin: CsCoin.monero, + ); @override - Future getCreatedWallet({ + Future getCreatedWallet({ required String path, required String password, required int wordCount, required String seedOffset, - }) async { - final lib_monero.MoneroSeedType type; - switch (wordCount) { - case 16: - type = lib_monero.MoneroSeedType.sixteen; - break; - - case 25: - type = lib_monero.MoneroSeedType.twentyFive; - break; - - default: - throw Exception("Invalid mnemonic word count: $wordCount"); - } - - return await lib_monero.MoneroWallet.create( - path: path, - password: password, - seedType: type, - seedOffset: seedOffset, - ); - } + required final void Function(int refreshFromBlockHeight, String seed) + onCreated, + }) => csMonero.getCreatedWallet( + csCoin: CsCoin.monero, + path: path, + password: password, + wordCount: wordCount, + seedOffset: seedOffset, + onCreated: onCreated, + ); @override - Future getRestoredWallet({ + Future getRestoredWallet({ required String path, required String password, required String mnemonic, required String seedOffset, int height = 0, - }) async => await lib_monero.MoneroWallet.restoreWalletFromSeed( + }) => csMonero.getRestoredWallet( path: path, password: password, - seed: mnemonic, - restoreHeight: height, + mnemonic: mnemonic, + height: height, seedOffset: seedOffset, + csCoin: CsCoin.monero, + walletId: walletId, ); @override - Future getRestoredFromViewKeyWallet({ + Future getRestoredFromViewKeyWallet({ required String path, required String password, required String address, required String privateViewKey, int height = 0, - }) async => lib_monero.MoneroWallet.createViewOnlyWallet( + }) => csMonero.getRestoredFromViewKeyWallet( + walletId: walletId, + csCoin: CsCoin.monero, path: path, password: password, address: address, - viewKey: privateViewKey, - restoreHeight: height, + privateViewKey: privateViewKey, + height: height, ); @override diff --git a/lib/wallets/wallet/impl/wownero_wallet.dart b/lib/wallets/wallet/impl/wownero_wallet.dart index a8d5e1c655..560597141a 100644 --- a/lib/wallets/wallet/impl/wownero_wallet.dart +++ b/lib/wallets/wallet/impl/wownero_wallet.dart @@ -1,11 +1,11 @@ import 'dart:async'; import 'package:compat/compat.dart' as lib_monero_compat; -import 'package:cs_monero/cs_monero.dart' as lib_monero; import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/fee_rate_type_enum.dart'; +import '../../../wl_gen/interfaces/cs_monero_interface.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/lib_monero_wallet.dart'; @@ -16,33 +16,27 @@ class WowneroWallet extends LibMoneroWallet { @override Future estimateFeeFor(Amount amount, BigInt feeRate) async { - if (libMoneroWallet == null || + if (!csMonero.walletInstanceExists(walletId) || syncStatus is! lib_monero_compat.SyncedSyncStatus) { return Amount.zeroWith(fractionDigits: cryptoCurrency.fractionDigits); } - lib_monero.TransactionPriority priority; FeeRateType feeRateType = FeeRateType.slow; switch (feeRate.toInt()) { case 1: - priority = lib_monero.TransactionPriority.low; feeRateType = FeeRateType.average; break; case 2: - priority = lib_monero.TransactionPriority.medium; feeRateType = FeeRateType.average; break; case 3: - priority = lib_monero.TransactionPriority.high; feeRateType = FeeRateType.fast; break; case 4: - priority = lib_monero.TransactionPriority.last; feeRateType = FeeRateType.fast; break; case 0: default: - priority = lib_monero.TransactionPriority.normal; feeRateType = FeeRateType.slow; break; } @@ -71,9 +65,10 @@ class WowneroWallet extends LibMoneroWallet { // unsure why this delay? await Future.delayed(const Duration(milliseconds: 500)); } catch (e) { - approximateFee = libMoneroWallet!.estimateFee( - priority, - amount.raw.toInt(), + approximateFee = await csMonero.estimateFee( + feeRate.toInt(), + amount.raw, + walletId: walletId, ); } } @@ -91,77 +86,66 @@ class WowneroWallet extends LibMoneroWallet { @override bool walletExists(String path) => - lib_monero.WowneroWallet.isWalletExist(path); + csMonero.walletExists(path, csCoin: CsCoin.wownero); @override - Future loadWallet({ - required String path, - required String password, - }) async { - libMoneroWallet = await lib_monero.WowneroWallet.loadWallet( - path: path, - password: password, - ); - } + Future loadWallet({required String path, required String password}) => + csMonero.loadWallet( + walletId, + path: path, + password: password, + csCoin: CsCoin.wownero, + ); @override - Future getCreatedWallet({ + Future getCreatedWallet({ required String path, required String password, required int wordCount, required String seedOffset, - }) async { - final lib_monero.WowneroSeedType type; - switch (wordCount) { - case 16: - type = lib_monero.WowneroSeedType.sixteen; - break; - - case 25: - type = lib_monero.WowneroSeedType.twentyFive; - break; - - default: - throw Exception("Invalid mnemonic word count: $wordCount"); - } - - return await lib_monero.WowneroWallet.create( - path: path, - password: password, - seedType: type, - overrideDeprecated14WordSeedException: true, - seedOffset: seedOffset, - ); - } + required final void Function(int refreshFromBlockHeight, String seed) + onCreated, + }) => csMonero.getCreatedWallet( + csCoin: CsCoin.wownero, + path: path, + password: password, + wordCount: wordCount, + seedOffset: seedOffset, + onCreated: onCreated, + ); @override - Future getRestoredWallet({ + Future getRestoredWallet({ required String path, required String password, required String mnemonic, required String seedOffset, int height = 0, - }) async => await lib_monero.WowneroWallet.restoreWalletFromSeed( + }) => csMonero.getRestoredWallet( path: path, password: password, - seed: mnemonic, - restoreHeight: height, + mnemonic: mnemonic, + height: height, seedOffset: seedOffset, + csCoin: CsCoin.wownero, + walletId: walletId, ); @override - Future getRestoredFromViewKeyWallet({ + Future getRestoredFromViewKeyWallet({ required String path, required String password, required String address, required String privateViewKey, int height = 0, - }) async => lib_monero.WowneroWallet.createViewOnlyWallet( + }) => csMonero.getRestoredFromViewKeyWallet( + walletId: walletId, + csCoin: CsCoin.wownero, path: path, password: password, address: address, - viewKey: privateViewKey, - restoreHeight: height, + privateViewKey: privateViewKey, + height: height, ); @override diff --git a/lib/wallets/wallet/intermediate/lib_monero_wallet.dart b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart index a25b236a91..943de13d23 100644 --- a/lib/wallets/wallet/intermediate/lib_monero_wallet.dart +++ b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart @@ -4,7 +4,6 @@ import 'dart:io'; import 'dart:math'; import 'package:compat/compat.dart' as lib_monero_compat; -import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:isar_community/isar.dart'; import 'package:mutex/mutex.dart'; import 'package:stack_wallet_backup/generate_password.dart'; @@ -34,6 +33,7 @@ import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/fee_rate_type_enum.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/stack_file_system.dart'; +import '../../../wl_gen/interfaces/cs_monero_interface.dart'; import '../../crypto_currency/intermediate/cryptonote_currency.dart'; import '../../isar/models/wallet_info.dart'; import '../../models/tx_data.dart'; @@ -99,14 +99,13 @@ abstract class LibMoneroWallet await onUTXOsChanged(utxos); await updateBalance(shouldUpdateUtxos: false); } catch (e, s) { - lib_monero.Logging.log?.i("_startInit", error: e, stackTrace: s); + Logging.instance.e("_startInit", error: e, stackTrace: s); } }); }); } final lib_monero_compat.WalletType compatType; - lib_monero.Wallet? libMoneroWallet; lib_monero_compat.SyncStatus? get syncStatus => _syncStatus; lib_monero_compat.SyncStatus? _syncStatus; @@ -137,14 +136,16 @@ abstract class LibMoneroWallet Future loadWallet({required String path, required String password}); - Future getCreatedWallet({ + Future getCreatedWallet({ required String path, required String password, required int wordCount, required String seedOffset, + required final void Function(int refreshFromBlockHeight, String seed) + onCreated, }); - Future getRestoredWallet({ + Future getRestoredWallet({ required String path, required String password, required String mnemonic, @@ -152,7 +153,7 @@ abstract class LibMoneroWallet int height = 0, }); - Future getRestoredFromViewKeyWallet({ + Future getRestoredFromViewKeyWallet({ required String path, required String password, required String address, @@ -165,16 +166,18 @@ abstract class LibMoneroWallet bool walletExists(String path); String getTxKeyFor({required String txid}) { - if (libMoneroWallet == null) { + if (!csMonero.walletInstanceExists(walletId)) { throw Exception("Cannot get tx key in uninitialized libMoneroWallet"); } - return libMoneroWallet!.getTxKey(txid); + return csMonero.getTxKey(walletId, txid); } void _setListener() { - if (libMoneroWallet != null && libMoneroWallet!.getListeners().isEmpty) { - libMoneroWallet?.addListener( - lib_monero.WalletListener( + if (csMonero.walletInstanceExists(walletId) && + !csMonero.hasListeners(walletId)) { + csMonero.addListener( + walletId, + CsMoneroWalletListener( onSyncingUpdate: onSyncingUpdate, onNewBlock: onNewBlock, onBalancesChanged: onBalancesChanged, @@ -190,17 +193,16 @@ abstract class LibMoneroWallet Future open() async { bool wasNull = false; - if (libMoneroWallet == null) { + if (!csMonero.walletInstanceExists(walletId)) { wasNull = true; // libMoneroWalletT?.close(); final path = await pathForWallet(name: walletId, type: compatType); final String password; try { - password = - (await secureStorageInterface.read( - key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), - ))!; + password = (await secureStorageInterface.read( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + ))!; } catch (e, s) { throw Exception("Password not found $e, $s"); } @@ -227,15 +229,15 @@ abstract class LibMoneroWallet if (wasNull) { try { _setSyncStatus(lib_monero_compat.ConnectingSyncStatus()); - libMoneroWallet?.startSyncing(); + csMonero.startSyncing(walletId); } catch (_) { _setSyncStatus(lib_monero_compat.FailedSyncStatus()); // TODO log } } _setListener(); - libMoneroWallet?.startListeners(); - libMoneroWallet?.startAutoSaving(); + csMonero.startListeners(walletId); + csMonero.startAutoSaving(walletId); unawaited(refresh()); } @@ -260,16 +262,17 @@ abstract class LibMoneroWallet appRoot: appRoot, ); } - await libMoneroWallet!.save(); + await csMonero.save(walletId); } Address addressFor({required int index, int account = 0}) { - final address = libMoneroWallet!.getAddress( + final address = csMonero.getAddress( + walletId, accountIndex: account, addressIndex: index, ); - if (address.value.contains("111")) { + if (address.contains("111")) { throw Exception("111 address found!"); } @@ -277,7 +280,7 @@ abstract class LibMoneroWallet walletId: walletId, derivationIndex: index, derivationPath: null, - value: address.value, + value: address, publicKey: [], type: AddressType.cryptonote, subType: AddressSubType.receiving, @@ -287,19 +290,18 @@ abstract class LibMoneroWallet } Future getKeys() async { - final base = libMoneroWallet; - final oldInfo = getLibMoneroWalletInfo(walletId); - if (base == null || (oldInfo != null && oldInfo.name != walletId)) { + if (!csMonero.walletInstanceExists(walletId) || + (oldInfo != null && oldInfo.name != walletId)) { return null; } try { return CWKeyData( walletId: walletId, - publicViewKey: base.getPublicViewKey(), - privateViewKey: base.getPrivateViewKey(), - publicSpendKey: base.getPublicSpendKey(), - privateSpendKey: base.getPrivateSpendKey(), + publicViewKey: csMonero.getPublicViewKey(walletId), + privateViewKey: csMonero.getPrivateViewKey(walletId), + publicSpendKey: csMonero.getPublicSpendKey(walletId), + privateSpendKey: csMonero.getPrivateSpendKey(walletId), ); } catch (e, s) { Logging.instance.f("getKeys failed: ", error: e, stackTrace: s); @@ -318,16 +320,17 @@ abstract class LibMoneroWallet final path = await pathForWallet(name: walletId, type: compatType); final String password; try { - password = - (await secureStorageInterface.read( - key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), - ))!; + password = (await secureStorageInterface.read( + key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), + ))!; } catch (e, s) { throw Exception("Password not found $e, $s"); } await loadWallet(path: path, password: password); - final wallet = libMoneroWallet!; - return (wallet.getAddress().value, wallet.getPrivateViewKey()); + return ( + csMonero.getAddress(walletId), + csMonero.getPrivateViewKey(walletId), + ); } @override @@ -343,17 +346,23 @@ abstract class LibMoneroWallet key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), value: password, ); - final wallet = await getCreatedWallet( + + late final int refreshFromBlockHeight; + late final String seedPhrase; + + await getCreatedWallet( path: path, password: password, wordCount: wordCount, seedOffset: "", // default for non restored wallets for now + onCreated: (height, seed) { + refreshFromBlockHeight = height; + seedPhrase = seed; + }, ); - final height = wallet.getRefreshFromBlockHeight(); - await info.updateRestoreHeight( - newRestoreHeight: height, + newRestoreHeight: refreshFromBlockHeight, isar: mainDB.isar, ); @@ -361,7 +370,7 @@ abstract class LibMoneroWallet // before wallet.init() is called await secureStorageInterface.write( key: Wallet.mnemonicKey(walletId: walletId), - value: wallet.getSeed().trim(), + value: seedPhrase, ); await secureStorageInterface.write( key: Wallet.mnemonicPassphraseKey(walletId: walletId), @@ -384,8 +393,8 @@ abstract class LibMoneroWallet await mainDB.deleteWalletBlockchainData(walletId); highestPercentCached = 0; - unawaited(libMoneroWallet?.rescanBlockchain()); - libMoneroWallet?.startSyncing(); + unawaited(csMonero.rescanBlockchain(walletId)); + csMonero.startSyncing(walletId); // unawaited(save()); }); unawaited(refresh()); @@ -422,7 +431,12 @@ abstract class LibMoneroWallet key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), value: password, ); - final wallet = await getRestoredWallet( + + if (!csMonero.walletInstanceExists(walletId)) { + await exit(); + } + + await getRestoredWallet( path: path, password: password, mnemonic: mnemonic, @@ -430,12 +444,6 @@ abstract class LibMoneroWallet seedOffset: seedOffset, ); - if (libMoneroWallet != null) { - await exit(); - } - - libMoneroWallet = wallet; - _setListener(); final newReceivingAddress = @@ -444,7 +452,7 @@ abstract class LibMoneroWallet walletId: walletId, derivationIndex: 0, derivationPath: null, - value: wallet.getAddress().value, + value: csMonero.getAddress(walletId), publicKey: [], type: AddressType.cryptonote, subType: AddressSubType.receiving, @@ -463,12 +471,12 @@ abstract class LibMoneroWallet _setListener(); // libMoneroWallet?.setRecoveringFromSeed(isRecovery: true); - unawaited(libMoneroWallet?.rescanBlockchain()); - libMoneroWallet?.startSyncing(); + unawaited(csMonero.rescanBlockchain(walletId)); + csMonero.startSyncing(walletId); // await save(); - libMoneroWallet?.startListeners(); - libMoneroWallet?.startAutoSaving(); + csMonero.startListeners(walletId); + csMonero.startAutoSaving(walletId); } catch (e, s) { Logging.instance.e( "Exception rethrown from recoverFromMnemonic(): ", @@ -484,11 +492,11 @@ abstract class LibMoneroWallet bool _canPing = false; @override - Future pingCheck() async { + Future pingCheck() { if (_canPing) { - return (await libMoneroWallet?.isConnectedToDaemon()) ?? false; + return csMonero.isConnectedToDaemon(walletId); } else { - return false; + return Future.value(false); } } @@ -500,50 +508,50 @@ abstract class LibMoneroWallet throw Exception("TOR – clearnet mismatch"); } - final host = - node.host.endsWith(".onion") ? node.host : Uri.parse(node.host).host; + final host = node.host.endsWith(".onion") + ? node.host + : Uri.parse(node.host).host; ({InternetAddress host, int port})? proxy; - proxy = - prefs.useTor && !node.forceNoTor - ? TorService.sharedInstance.getProxyInfo() - : null; + proxy = prefs.useTor && !node.forceNoTor + ? TorService.sharedInstance.getProxyInfo() + : null; _setSyncStatus(lib_monero_compat.ConnectingSyncStatus()); try { if (_requireMutex) { await _torConnectingLock.protect(() async { - await libMoneroWallet?.connect( + await csMonero.connect( + walletId, daemonAddress: "$host:${node.port}", daemonUsername: node.loginName, daemonPassword: await node.getPassword(secureStorageInterface), trusted: node.trusted ?? false, useSSL: node.useSSL, - socksProxyAddress: - node.forceNoTor - ? null - : proxy == null - ? null - : "${proxy.host.address}:${proxy.port}", + socksProxyAddress: node.forceNoTor + ? null + : proxy == null + ? null + : "${proxy.host.address}:${proxy.port}", ); }); } else { - await libMoneroWallet?.connect( + await csMonero.connect( + walletId, daemonAddress: "$host:${node.port}", daemonUsername: node.loginName, daemonPassword: await node.getPassword(secureStorageInterface), trusted: node.trusted ?? false, useSSL: node.useSSL, - socksProxyAddress: - node.forceNoTor - ? null - : proxy == null - ? null - : "${proxy.host.address}:${proxy.port}", + socksProxyAddress: node.forceNoTor + ? null + : proxy == null + ? null + : "${proxy.host.address}:${proxy.port}", ); } - libMoneroWallet?.startSyncing(); - libMoneroWallet?.startListeners(); - libMoneroWallet?.startAutoSaving(); + csMonero.startSyncing(walletId); + csMonero.startListeners(walletId); + csMonero.startAutoSaving(walletId); _setSyncStatus(lib_monero_compat.ConnectedSyncStatus()); } catch (e, s) { @@ -560,22 +568,19 @@ abstract class LibMoneroWallet @override Future updateTransactions() async { - final base = libMoneroWallet; - - if (base == null) { + if (!csMonero.walletInstanceExists(walletId)) { return; } - final localTxids = - await mainDB.isar.transactionV2s - .where() - .walletIdEqualTo(walletId) - .filter() - .heightGreaterThan(0) - .txidProperty() - .findAll(); + final localTxids = await mainDB.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) + .filter() + .heightGreaterThan(0) + .txidProperty() + .findAll(); - final allTxids = await base.getAllTxids(refresh: true); + final allTxids = await csMonero.getAllTxids(walletId, refresh: true); final txidsToFetch = allTxids.toSet().difference(localTxids.toSet()); @@ -583,9 +588,17 @@ abstract class LibMoneroWallet return; } - final transactions = await base.getTxs(txids: txidsToFetch, refresh: false); + final transactions = await csMonero.getTxs( + walletId, + txids: txidsToFetch, + refresh: false, + ); - final allOutputs = await base.getOutputs(includeSpent: true, refresh: true); + final allOutputs = await csMonero.getOutputs( + walletId, + includeSpent: true, + refresh: true, + ); // final cachedTransactions = // DB.instance.get(boxName: walletId, key: 'latest_tx_model') @@ -671,16 +684,14 @@ abstract class LibMoneroWallet type: type, subType: TransactionSubType.none, otherData: jsonEncode({ - TxV2OdKeys.overrideFee: - Amount( - rawValue: tx.fee, - fractionDigits: cryptoCurrency.fractionDigits, - ).toJsonString(), - TxV2OdKeys.moneroAmount: - Amount( - rawValue: tx.amount, - fractionDigits: cryptoCurrency.fractionDigits, - ).toJsonString(), + TxV2OdKeys.overrideFee: Amount( + rawValue: tx.fee, + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), + TxV2OdKeys.moneroAmount: Amount( + rawValue: tx.amount, + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), TxV2OdKeys.moneroAccountIndex: tx.accountIndex, TxV2OdKeys.isMoneroTransaction: true, }), @@ -695,7 +706,7 @@ abstract class LibMoneroWallet Future get availableBalance async { try { return Amount( - rawValue: libMoneroWallet!.getUnlockedBalance(), + rawValue: csMonero.getUnlockedBalance(walletId)!, fractionDigits: cryptoCurrency.fractionDigits, ); } catch (_) { @@ -705,14 +716,14 @@ abstract class LibMoneroWallet Future get totalBalance async { try { - final full = libMoneroWallet?.getBalance(); + final full = csMonero.getBalance(walletId); if (full != null) { return Amount( rawValue: full, fractionDigits: cryptoCurrency.fractionDigits, ); } else { - final transactions = await libMoneroWallet!.getAllTxs(refresh: true); + final transactions = await csMonero.getAllTxs(walletId, refresh: true); BigInt transactionBalance = BigInt.zero; for (final tx in transactions) { if (!tx.isSpend) { @@ -735,10 +746,10 @@ abstract class LibMoneroWallet @override Future exit() async { Logging.instance.i("exit called on $walletId"); - libMoneroWallet?.stopAutoSaving(); - libMoneroWallet?.stopListeners(); - libMoneroWallet?.stopSyncing(); - await libMoneroWallet?.save(); + csMonero.stopAutoSaving(walletId); + csMonero.stopListeners(walletId); + csMonero.stopSyncing(walletId); + await csMonero.save(walletId); } Future pathForWalletDir({ @@ -820,7 +831,7 @@ abstract class LibMoneroWallet final _utxosUpdateLock = Mutex(); Future onUTXOsChanged(List utxos) async { await _utxosUpdateLock.protect(() async { - final cwUtxos = await libMoneroWallet?.getOutputs(refresh: true) ?? []; + final cwUtxos = await csMonero.getOutputs(walletId, refresh: true); // bool changed = false; @@ -837,12 +848,12 @@ abstract class LibMoneroWallet if (u.isBlocked) { if (!cw.isFrozen) { - await libMoneroWallet?.freezeOutput(cw.keyImage); + await csMonero.freezeOutput(walletId, cw.keyImage); // changed = true; } } else { if (cw.isFrozen) { - await libMoneroWallet?.thawOutput(cw.keyImage); + await csMonero.thawOutput(walletId, cw.keyImage); // changed = true; } } @@ -999,9 +1010,9 @@ abstract class LibMoneroWallet if (mismatch) { _canPing = false; - libMoneroWallet?.stopAutoSaving(); - libMoneroWallet?.stopListeners(); - libMoneroWallet?.stopSyncing(); + csMonero.stopAutoSaving(walletId); + csMonero.stopListeners(walletId); + csMonero.stopSyncing(walletId); _setSyncStatus(lib_monero_compat.FailedSyncStatus()); } @@ -1019,27 +1030,23 @@ abstract class LibMoneroWallet @override Future updateUTXOs() async { final List outputArray = []; - final utxos = - await libMoneroWallet?.getOutputs(refresh: true) ?? - []; + final utxos = await csMonero.getOutputs(walletId, refresh: true); for (final rawUTXO in utxos) { if (!rawUTXO.spent) { - final current = - await mainDB.isar.utxos - .where() - .walletIdEqualTo(walletId) - .filter() - .voutEqualTo(rawUTXO.vout) - .and() - .txidEqualTo(rawUTXO.hash) - .findFirst(); - final tx = - await mainDB.isar.transactionV2s - .where() - .walletIdEqualTo(walletId) - .filter() - .txidEqualTo(rawUTXO.hash) - .findFirst(); + final current = await mainDB.isar.utxos + .where() + .walletIdEqualTo(walletId) + .filter() + .voutEqualTo(rawUTXO.vout) + .and() + .txidEqualTo(rawUTXO.hash) + .findFirst(); + final tx = await mainDB.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) + .filter() + .txidEqualTo(rawUTXO.hash) + .findFirst(); final otherDataMap = { UTXOOtherDataKeys.keyImage: rawUTXO.keyImage, @@ -1112,7 +1119,7 @@ abstract class LibMoneroWallet // Slight possibility of race but should be irrelevant await refreshMutex.acquire(); - libMoneroWallet?.startSyncing(); + csMonero.startSyncing(walletId); _setSyncStatus(lib_monero_compat.StartingSyncStatus()); await updateTransactions(); @@ -1126,9 +1133,9 @@ abstract class LibMoneroWallet refreshMutex.release(); } - final synced = await libMoneroWallet?.isSynced(); + final synced = await csMonero.isSynced(walletId); - if (synced == true) { + if (synced) { _setSyncStatus(lib_monero_compat.SyncedSyncStatus()); } } @@ -1138,8 +1145,9 @@ abstract class LibMoneroWallet try { final currentReceiving = await getCurrentReceivingAddress(); - final newReceivingIndex = - currentReceiving == null ? 0 : currentReceiving.derivationIndex + 1; + final newReceivingIndex = currentReceiving == null + ? 0 + : currentReceiving.derivationIndex + 1; final newReceivingAddress = addressFor(index: newReceivingIndex); @@ -1174,17 +1182,14 @@ abstract class LibMoneroWallet try { int highestIndex = -1; - final entries = await libMoneroWallet?.getAllTxs(refresh: true); - if (entries != null) { - for (final element in entries) { - if (!element.isSpend) { - final int curAddressIndex = - element.addressIndexes.isEmpty - ? 0 - : element.addressIndexes.reduce(max); - if (curAddressIndex > highestIndex) { - highestIndex = curAddressIndex; - } + final entries = await csMonero.getAllTxs(walletId, refresh: true); + for (final element in entries) { + if (!element.isSpend) { + final int curAddressIndex = element.addressIndexes.isEmpty + ? 0 + : element.addressIndexes.reduce(max); + if (curAddressIndex > highestIndex) { + highestIndex = curAddressIndex; } } } @@ -1200,12 +1205,11 @@ abstract class LibMoneroWallet // Use new index to derive a new receiving address final newReceivingAddress = addressFor(index: newReceivingIndex); - final existing = - await mainDB - .getAddresses(walletId) - .filter() - .valueEqualTo(newReceivingAddress.value) - .findFirst(); + final existing = await mainDB + .getAddresses(walletId) + .filter() + .valueEqualTo(newReceivingAddress.value) + .findFirst(); if (existing == null) { // Add that new change address await mainDB.putAddress(newReceivingAddress); @@ -1241,9 +1245,9 @@ abstract class LibMoneroWallet numberOfBlocksFast: 10, numberOfBlocksAverage: 15, numberOfBlocksSlow: 20, - fast: BigInt.from(lib_monero.TransactionPriority.high.value), - medium: BigInt.from(lib_monero.TransactionPriority.medium.value), - slow: BigInt.from(lib_monero.TransactionPriority.normal.value), + fast: BigInt.from(csMonero.getTxPriorityHigh()), + medium: BigInt.from(csMonero.getTxPriorityMedium()), + slow: BigInt.from(csMonero.getTxPriorityNormal()), ); @override @@ -1269,16 +1273,16 @@ abstract class LibMoneroWallet try { final feeRate = txData.feeRateType; if (feeRate is FeeRateType) { - lib_monero.TransactionPriority feePriority; + final int feePriority; switch (feeRate) { case FeeRateType.fast: - feePriority = lib_monero.TransactionPriority.high; + feePriority = csMonero.getTxPriorityHigh(); break; case FeeRateType.average: - feePriority = lib_monero.TransactionPriority.medium; + feePriority = csMonero.getTxPriorityMedium(); break; case FeeRateType.slow: - feePriority = lib_monero.TransactionPriority.normal; + feePriority = csMonero.getTxPriorityNormal(); break; default: throw ArgumentError("Invalid use of custom fee"); @@ -1303,12 +1307,9 @@ abstract class LibMoneroWallet throw Exception("Send all not supported with multiple recipients"); } - final List outputs = []; + final List outputs = []; for (final recipient in txData.recipients!) { - final output = lib_monero.Recipient( - address: recipient.address, - amount: recipient.amount.raw, - ); + final output = CsRecipient(recipient.address, recipient.amount.raw); outputs.add(output); } @@ -1318,44 +1319,27 @@ abstract class LibMoneroWallet } final height = await chainHeight; - final inputs = - txData.utxos - ?.whereType() - .map( - (e) => lib_monero.Output( - address: e.address!, - hash: e.utxo.txid, - keyImage: e.utxo.keyImage!, - value: e.value, - isFrozen: e.utxo.isBlocked, - isUnlocked: - e.utxo.blockHeight != null && - (height - (e.utxo.blockHeight ?? 0)) >= - cryptoCurrency.minConfirms, - height: e.utxo.blockHeight ?? 0, - vout: e.utxo.vout, - spent: e.utxo.used ?? false, - spentHeight: null, // doesn't matter here - coinbase: e.utxo.isCoinbase, - ), - ) - .toList(); + final inputs = txData.utxos?.whereType().toList(); return await prepareSendMutex.protect(() async { - final lib_monero.PendingTransaction pendingTransaction; + final CsPendingTransaction pendingTransaction; if (outputs.length == 1) { - pendingTransaction = await libMoneroWallet!.createTx( + pendingTransaction = await csMonero.createTx( + walletId, + minConfirms: cryptoCurrency.minConfirms, + currentHeight: height, output: outputs.first, - paymentId: "", sweep: sweep, priority: feePriority, preferredInputs: inputs, accountIndex: 0, // sw only uses account 0 at this time ); } else { - pendingTransaction = await libMoneroWallet!.createTxMultiDest( + pendingTransaction = await csMonero.createTxMultiDest( + walletId, + minConfirms: cryptoCurrency.minConfirms, + currentHeight: height, outputs: outputs, - paymentId: "", priority: feePriority, preferredInputs: inputs, sweep: sweep, @@ -1398,7 +1382,7 @@ abstract class LibMoneroWallet Future confirmSend({required TxData txData}) async { try { try { - await libMoneroWallet!.commitTx(txData.pendingTransaction!); + await csMonero.commitTx(walletId, txData.pendingTransaction!); Logging.instance.d( "transaction ${txData.pendingTransaction!.txid} has been sent", @@ -1447,7 +1431,11 @@ abstract class LibMoneroWallet key: lib_monero_compat.libMoneroWalletPasswordKey(walletId), value: password, ); - final wallet = await getRestoredFromViewKeyWallet( + + if (csMonero.walletInstanceExists(walletId)) { + await exit(); + } + await getRestoredFromViewKeyWallet( path: path, password: password, address: data.address, @@ -1455,12 +1443,6 @@ abstract class LibMoneroWallet height: height, ); - if (libMoneroWallet != null) { - await exit(); - } - - libMoneroWallet = wallet; - _setListener(); final newReceivingAddress = @@ -1469,7 +1451,7 @@ abstract class LibMoneroWallet walletId: walletId, derivationIndex: 0, derivationPath: null, - value: wallet.getAddress().value, + value: csMonero.getAddress(walletId), publicKey: [], type: AddressType.cryptonote, subType: AddressSubType.receiving, @@ -1484,12 +1466,12 @@ abstract class LibMoneroWallet await updateNode(); _setListener(); - unawaited(libMoneroWallet?.rescanBlockchain()); - libMoneroWallet?.startSyncing(); + unawaited(csMonero.rescanBlockchain(walletId)); + csMonero.startSyncing(walletId); // await save(); - libMoneroWallet?.startListeners(); - libMoneroWallet?.startAutoSaving(); + csMonero.startListeners(walletId); + csMonero.startAutoSaving(walletId); } catch (e, s) { Logging.instance.e( "Exception rethrown from recoverViewOnly(): ", diff --git a/lib/widgets/desktop/desktop_fee_dialog.dart b/lib/widgets/desktop/desktop_fee_dialog.dart index 82cc82e0bd..82f92a5a25 100644 --- a/lib/widgets/desktop/desktop_fee_dialog.dart +++ b/lib/widgets/desktop/desktop_fee_dialog.dart @@ -1,4 +1,3 @@ -import 'package:cs_monero/cs_monero.dart' as lib_monero; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -16,6 +15,7 @@ import '../../utilities/text_styles.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../wallets/isar/providers/eth/current_token_wallet_provider.dart'; import '../../wallets/wallet/impl/firo_wallet.dart'; +import '../../wl_gen/interfaces/cs_monero_interface.dart'; import '../animated_text.dart'; import '../conditional_parent.dart'; import 'desktop_dialog.dart'; @@ -63,7 +63,7 @@ class _DesktopFeeDialogState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - BigInt.from(lib_monero.TransactionPriority.high.value), + BigInt.from(csMonero.getTxPriorityHigh()), ); ref.read(feeSheetSessionCacheProvider).fast[amount] = fee; } else if (coin is Firo) { @@ -113,7 +113,7 @@ class _DesktopFeeDialogState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - BigInt.from(lib_monero.TransactionPriority.medium.value), + BigInt.from(csMonero.getTxPriorityMedium()), ); ref.read(feeSheetSessionCacheProvider).average[amount] = fee; } else if (coin is Firo) { @@ -163,7 +163,7 @@ class _DesktopFeeDialogState extends ConsumerState { if (coin is Monero || coin is Wownero) { final fee = await wallet.estimateFeeFor( amount, - BigInt.from(lib_monero.TransactionPriority.normal.value), + BigInt.from(csMonero.getTxPriorityNormal()), ); ref.read(feeSheetSessionCacheProvider).slow[amount] = fee; } else if (coin is Firo) { @@ -335,18 +335,17 @@ class _DesktopFeeItemState extends ConsumerState { return ConditionalParent( condition: widget.isButton, - builder: - (child) => MaterialButton( - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - onPressed: () { - ref.read(feeRateTypeDesktopStateProvider.state).state = - widget.feeRateType; - Navigator.of( - context, - ).pop((widget.feeRateType, feeString, timeString)); - }, - child: child, - ), + builder: (child) => MaterialButton( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + onPressed: () { + ref.read(feeRateTypeDesktopStateProvider.state).state = + widget.feeRateType; + Navigator.of( + context, + ).pop((widget.feeRateType, feeString, timeString)); + }, + child: child, + ), child: Builder( builder: (_) { if (widget.feeRateType == FeeRateType.custom) { @@ -355,14 +354,12 @@ class _DesktopFeeItemState extends ConsumerState { children: [ Text( widget.feeRateType.prettyName, - style: STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of( + style: STextStyles.desktopTextExtraExtraSmall(context) + .copyWith( + color: Theme.of( context, ).extension()!.textFieldActiveText, - ), + ), textAlign: TextAlign.left, ), ], @@ -377,10 +374,9 @@ class _DesktopFeeItemState extends ConsumerState { return AnimatedText( stringsToLoopThrough: stringsToLoopThrough, style: STextStyles.desktopTextExtraExtraSmall(context).copyWith( - color: - Theme.of( - context, - ).extension()!.textFieldActiveText, + color: Theme.of( + context, + ).extension()!.textFieldActiveText, ), ); } else { @@ -388,12 +384,11 @@ class _DesktopFeeItemState extends ConsumerState { future: widget.feeFor( coin: wallet.info.coin, feeRateType: widget.feeRateType, - feeRate: - widget.feeRateType == FeeRateType.fast - ? widget.feeObject!.fast - : widget.feeRateType == FeeRateType.slow - ? widget.feeObject!.slow - : widget.feeObject!.medium, + feeRate: widget.feeRateType == FeeRateType.fast + ? widget.feeObject!.fast + : widget.feeRateType == FeeRateType.slow + ? widget.feeObject!.slow + : widget.feeObject!.medium, amount: ref.watch(sendAmountProvider.state).state, ), builder: (_, AsyncSnapshot snapshot) { @@ -403,58 +398,51 @@ class _DesktopFeeItemState extends ConsumerState { "${widget.feeRateType.prettyName} " "(~${ref.watch(pAmountFormatter(wallet.info.coin)).format(snapshot.data!, indicatePrecisionLoss: false)})"; - timeString = - wallet.info.coin is Ethereum - ? "" - : estimatedTimeToBeIncludedInNextBlock( - wallet.info.coin.targetBlockTimeSeconds, - widget.feeRateType == FeeRateType.fast - ? widget.feeObject!.numberOfBlocksFast - : widget.feeRateType == FeeRateType.slow - ? widget.feeObject!.numberOfBlocksSlow - : widget.feeObject!.numberOfBlocksAverage, - ); + timeString = wallet.info.coin is Ethereum + ? "" + : estimatedTimeToBeIncludedInNextBlock( + wallet.info.coin.targetBlockTimeSeconds, + widget.feeRateType == FeeRateType.fast + ? widget.feeObject!.numberOfBlocksFast + : widget.feeRateType == FeeRateType.slow + ? widget.feeObject!.numberOfBlocksSlow + : widget.feeObject!.numberOfBlocksAverage, + ); return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( feeString!, - style: STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of( + style: STextStyles.desktopTextExtraExtraSmall(context) + .copyWith( + color: Theme.of( context, ).extension()!.textFieldActiveText, - ), + ), textAlign: TextAlign.left, ), if (widget.feeObject != null) Text( timeString!, - style: STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) + style: STextStyles.desktopTextExtraExtraSmall(context) + .copyWith( + color: Theme.of(context) .extension()! .textFieldActiveSearchIconRight, - ), + ), ), ], ); } else { return AnimatedText( stringsToLoopThrough: stringsToLoopThrough, - style: STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of( + style: STextStyles.desktopTextExtraExtraSmall(context) + .copyWith( + color: Theme.of( context, ).extension()!.textFieldActiveText, - ), + ), ); } }, diff --git a/lib/wl_gen/interfaces/cs_monero_interface.dart b/lib/wl_gen/interfaces/cs_monero_interface.dart new file mode 100644 index 0000000000..7622962396 --- /dev/null +++ b/lib/wl_gen/interfaces/cs_monero_interface.dart @@ -0,0 +1,380 @@ +import '../../models/input.dart'; + +export '../generated/cs_monero_interface_impl.dart'; + +abstract class CsMoneroInterface { + void setUseCsMoneroLoggerInternal(bool enable); + + // tx prio forwarding + int getTxPriorityHigh(); + int getTxPriorityMedium(); + int getTxPriorityNormal(); + + bool walletInstanceExists(String walletId); + + bool walletExists(String path, {required CsCoin csCoin}); + + Future estimateFee(int rate, BigInt amount, {required String walletId}); + + Future loadWallet( + String walletId, { + required CsCoin csCoin, + required String path, + required String password, + }); + + String getAddress( + String walletId, { + int accountIndex = 0, + int addressIndex = 0, + }); + + Future getCreatedWallet({ + required CsCoin csCoin, + required String path, + required String password, + required int wordCount, + required String seedOffset, + required final void Function(int refreshFromBlockHeight, String seed) + onCreated, + }); + + Future getRestoredWallet({ + required String walletId, + required CsCoin csCoin, + required String path, + required String password, + required String mnemonic, + required String seedOffset, + int height = 0, + }); + + Future getRestoredFromViewKeyWallet({ + required String walletId, + required CsCoin csCoin, + required String path, + required String password, + required String address, + required String privateViewKey, + int height = 0, + }); + + String getTxKey(String walletId, String txid); + + Future save(String walletId); + + String getPublicViewKey(String walletId); + String getPrivateViewKey(String walletId); + String getPublicSpendKey(String walletId); + String getPrivateSpendKey(String walletId); + + Future isSynced(String walletId); + void startSyncing(String walletId); + void stopSyncing(String walletId); + + void startAutoSaving(String walletId); + void stopAutoSaving(String walletId); + + bool hasListeners(String walletId); + void addListener(String walletId, CsMoneroWalletListener listener); + void startListeners(String walletId); + void stopListeners(String walletId); + + Future rescanBlockchain(String walletId); + Future isConnectedToDaemon(String walletId); + + int getRefreshFromBlockHeight(String walletId); + void setRefreshFromBlockHeight(String walletId, int height); + + Future connect( + String walletId, { + required String daemonAddress, + required bool trusted, + String? daemonUsername, + String? daemonPassword, + bool useSSL = false, + bool isLightWallet = false, + String? socksProxyAddress, + }); + + Future> getAllTxids(String walletId, {bool refresh = false}); + + BigInt? getBalance(String walletId, {int accountIndex = 0}); + BigInt? getUnlockedBalance(String walletId, {int accountIndex = 0}); + + Future> getAllTxs( + String walletId, { + bool refresh = false, + }); + + Future> getTxs( + String walletId, { + required Set txids, + bool refresh = false, + }); + + Future createTx( + String walletId, { + required CsRecipient output, + required int priority, + required bool sweep, + List? preferredInputs, + required int accountIndex, + required int minConfirms, + required int currentHeight, + }); + + Future createTxMultiDest( + String walletId, { + required List outputs, + required int priority, + required bool sweep, + List? preferredInputs, + required int accountIndex, + required int minConfirms, + required int currentHeight, + }); + + Future commitTx(String walletId, CsPendingTransaction tx); + + Future> getOutputs( + String walletId, { + bool refresh = false, + bool includeSpent = false, + }); + + Future freezeOutput(String walletId, String keyImage); + Future thawOutput(String walletId, String keyImage); + + List getMoneroWordList(String language); + List getWowneroWordList(String language, int seedLength); +} + +enum CsCoin { monero, wownero } + +// forwarding class +final class CsMoneroWalletListener { + CsMoneroWalletListener({ + this.onSyncingUpdate, + this.onNewBlock, + this.onBalancesChanged, + this.onError, + }); + + /// Called when the wallet sync progress is updated. + /// + /// Parameters: + /// - [syncHeight]: The current syncing height of the wallet. + /// - [nodeHeight]: The height of the blockchain on the connected node. + /// - [message]: An optional message that may provide additional context. + final void Function({ + required int syncHeight, + required int nodeHeight, + String? message, + })? + onSyncingUpdate; + + /// Called when the daemon’s chain height changes, indicating new blocks. + /// + /// Parameters: + /// - [height]: The new height of the blockchain. + final void Function(int height)? onNewBlock; + + /// Called when the wallet balance or unlocked balance updates. + /// + /// Parameters: + /// - [newBalance]: The updated total wallet balance in atomic units. + /// - [newUnlockedBalance]: The updated unlocked balance in atomic units. + final void Function({ + required BigInt newBalance, + required BigInt newUnlockedBalance, + })? + onBalancesChanged; + + /// Called when an error occurs during synchronization/polling. + /// + /// Parameters: + /// - [error]: The error object describing what went wrong. + /// - [stackTrace]: The stack trace at the point where the error occurred. + final void Function(Object? error, StackTrace? stackTrace)? onError; +} + +// stupid +final class CsPendingTransaction { + /// should only ever be lib_monero.PendingTransaction but we can't strongly + /// type that because we cannot conditionally import in dart so now we have + /// this hack yay + final Object value; + + // stupid duplicates + final BigInt amount, fee; + + // stupid duplicate + final String txid; + + const CsPendingTransaction(this.value, this.amount, this.fee, this.txid); +} + +// forwarding class +final class CsTransaction { + CsTransaction({ + required this.displayLabel, + required this.description, + required this.fee, + required this.confirmations, + required this.blockHeight, + required this.accountIndex, + required this.addressIndexes, + required this.paymentId, + required this.amount, + required this.isSpend, + required this.hash, + required this.key, + required this.timeStamp, + required this.minConfirms, + }) { + if (fee.isNegative) throw Exception("negative fee"); + if (confirmations.isNegative) throw Exception("negative confirmations"); + if (accountIndex.isNegative) throw Exception("negative accountIndex"); + if (amount.isNegative) throw Exception("negative amount"); + } + + /// A label to display for the transaction, providing a human-readable identifier. + final String displayLabel; + + /// A description of the transaction, providing additional context or details. + final String description; + + /// The transaction fee in atomic units. + final BigInt fee; + + /// The number of confirmations this transaction has received. + final int confirmations; + + /// The block height at which this transaction was included. + final int blockHeight; + + /// A set of indexes corresponding to addresses associated with this transaction. + final Set addressIndexes; + + /// The index of the account associated with this transaction. + final int accountIndex; + + /// An optional payment identifier, used to associate this transaction with a payment. + final String paymentId; + + /// The amount of funds transferred in this transaction, represented in atomic units. + final BigInt amount; + + /// Flag indicating whether this transaction is a spend transaction. + final bool isSpend; + + /// The timestamp of when this transaction was created or recorded. + final DateTime timeStamp; + + /// The unique hash of this transaction (txid). + final String hash; + + /// A key used to prove a transaction was made and relayed and to verify its details. + final String key; + + /// The minimum number of confirmations required for this transaction. + final int minConfirms; + + /// Flag indicating whether the transaction is confirmed. + bool get isConfirmed => !isPending; + + /// Flag indicating whether the transaction is pending (i.e., not yet confirmed). + bool get isPending => confirmations < minConfirms; +} + +// forwarding class +final class CsRecipient { + final String address; + final BigInt amount; + + CsRecipient(this.address, this.amount); +} + +// forwarding class +final class CsOutput { + /// Creates an [CsOutput] with the specified Monero transaction details. NOTE: + /// No validation of any properties (besides a negative [value] or [vout], + /// and a non empty [keyImage]) occurs here. + /// + /// [address] is the receiving Monero address. + /// [hash] is the transaction hash of the output. + /// [keyImage] is the unique identifier of the output. + /// [value] represents the amount of Monero in atomic units. + /// [isFrozen] indicates if the output is currently frozen. + /// [isUnlocked] shows if the output is available for spending. + /// [height] is the blockchain height at which the output was created. + /// [spentHeight] is the blockchain height at which the output was spent, + /// or `null` if it is unspent. + /// [vout] represents the output index within the transaction. + /// [spent] indicates if the output has been spent. + /// [coinbase] identifies if the output is from a coinbase transaction. + CsOutput({ + required this.address, + required this.hash, + required this.keyImage, + required this.value, + required this.isFrozen, + required this.isUnlocked, + required this.height, + required this.spentHeight, + required this.vout, + required this.spent, + required this.coinbase, + }) : assert(!value.isNegative && !vout.isNegative && keyImage.isNotEmpty); + + /// The receiving Monero address. + final String address; + + /// The hash of the transaction in which this output was created. + final String hash; + + /// The value of the output, in atomic units. + final BigInt value; + + /// A unique identifier for this output. + /// See https://monero.stackexchange.com/questions/2883/what-is-a-key-image + final String keyImage; + + /// Whether this output is frozen, preventing it from being spent. + final bool isFrozen; + + /// Whether this output is unlocked and available for spending. + final bool isUnlocked; + + /// The blockchain height where this output was created. + final int height; + + /// The blockchain height where this output was spent, or `null` if unspent. + final int? spentHeight; + + /// The output index within the transaction. + final int vout; + + /// Whether this output has already been spent. + final bool spent; + + /// Whether this output originates from a coinbase transaction. + final bool coinbase; + + /// Returns a copy of this [Output] instance, with the [isFrozen] status + /// updated. + CsOutput copyWithFrozen(bool isFrozen) => CsOutput( + address: address, + hash: hash, + keyImage: keyImage, + value: value, + isFrozen: isFrozen, + isUnlocked: isUnlocked, + height: height, + spentHeight: spentHeight, + vout: vout, + spent: spent, + coinbase: coinbase, + ); +} diff --git a/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart b/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart new file mode 100644 index 0000000000..a90a4dbb17 --- /dev/null +++ b/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart @@ -0,0 +1,541 @@ +//ON +import 'package:cs_monero/cs_monero.dart' as lib_monero; + +//END_ON +import '../../models/input.dart'; +import '../interfaces/cs_monero_interface.dart'; + +CsMoneroInterface get csMonero => _getInterface(); + +//OFF +CsMoneroInterface _getInterface() => throw Exception("XMR/WOW not enabled!"); + +//END_OFF +//ON +CsMoneroInterface _getInterface() => _CsMoneroInterfaceImpl(); + +class _CsMoneroInterfaceImpl extends CsMoneroInterface { + final Map _wallets = {}; + + @override + void setUseCsMoneroLoggerInternal(bool enable) => + lib_monero.Logging.useLogger = enable; + + @override + bool walletInstanceExists(String walletId) => _wallets[walletId] != null; + + @override + bool walletExists(String path, {required CsCoin csCoin}) => switch (csCoin) { + CsCoin.monero => lib_monero.MoneroWallet.isWalletExist(path), + CsCoin.wownero => lib_monero.WowneroWallet.isWalletExist(path), + }; + + @override + Future estimateFee(int rate, BigInt amount, {required String walletId}) { + lib_monero.TransactionPriority priority; + switch (rate) { + case 1: + priority = lib_monero.TransactionPriority.low; + break; + case 2: + priority = lib_monero.TransactionPriority.medium; + break; + case 3: + priority = lib_monero.TransactionPriority.high; + break; + case 4: + priority = lib_monero.TransactionPriority.last; + break; + case 0: + default: + priority = lib_monero.TransactionPriority.normal; + break; + } + + return _wallets[walletId]!.estimateFee(priority, amount.toInt()); + } + + @override + Future loadWallet( + String walletId, { + required CsCoin csCoin, + required String path, + required String password, + }) async { + switch (csCoin) { + case CsCoin.monero: + _wallets[walletId] = await lib_monero.MoneroWallet.loadWallet( + path: path, + password: password, + ); + break; + + case CsCoin.wownero: + _wallets[walletId] = await lib_monero.WowneroWallet.loadWallet( + path: path, + password: password, + ); + break; + } + } + + @override + int getTxPriorityHigh() => lib_monero.TransactionPriority.high.value; + + @override + int getTxPriorityMedium() => lib_monero.TransactionPriority.medium.value; + + @override + int getTxPriorityNormal() => lib_monero.TransactionPriority.normal.value; + + @override + String getAddress( + String walletId, { + int accountIndex = 0, + int addressIndex = 0, + }) => _wallets[walletId]! + .getAddress(accountIndex: accountIndex, addressIndex: addressIndex) + .value; + + @override + Future getCreatedWallet({ + required CsCoin csCoin, + required String path, + required String password, + required int wordCount, + required String seedOffset, + required final void Function(int refreshFromBlockHeight, String seed) + onCreated, + }) async { + final lib_monero.Wallet wallet; + + switch (csCoin) { + case CsCoin.monero: + final type = switch (wordCount) { + 16 => lib_monero.MoneroSeedType.sixteen, + 25 => lib_monero.MoneroSeedType.twentyFive, + _ => throw Exception("Invalid mnemonic word count: $wordCount"), + }; + + wallet = await lib_monero.MoneroWallet.create( + path: path, + password: password, + seedType: type, + seedOffset: seedOffset, + ); + break; + + case CsCoin.wownero: + final type = switch (wordCount) { + 16 => lib_monero.WowneroSeedType.sixteen, + 25 => lib_monero.WowneroSeedType.twentyFive, + _ => throw Exception("Invalid mnemonic word count: $wordCount"), + }; + + wallet = await lib_monero.WowneroWallet.create( + path: path, + password: password, + seedType: type, + seedOffset: seedOffset, + ); + break; + } + + onCreated( + wallet.getRefreshFromBlockHeight(), + wallet.getSeed(seedOffset: seedOffset).trim(), + ); + } + + @override + Future getRestoredWallet({ + required String walletId, + required CsCoin csCoin, + required String path, + required String password, + required String mnemonic, + required String seedOffset, + int height = 0, + }) async { + _wallets[walletId] = switch (csCoin) { + CsCoin.monero => + _wallets[walletId] = + await lib_monero.MoneroWallet.restoreWalletFromSeed( + path: path, + password: password, + seed: mnemonic, + restoreHeight: height, + seedOffset: seedOffset, + ), + + CsCoin.wownero => + _wallets[walletId] = + await lib_monero.WowneroWallet.restoreWalletFromSeed( + path: path, + password: password, + seed: mnemonic, + restoreHeight: height, + seedOffset: seedOffset, + ), + }; + } + + @override + Future getRestoredFromViewKeyWallet({ + required String walletId, + required CsCoin csCoin, + required String path, + required String password, + required String address, + required String privateViewKey, + int height = 0, + }) async { + _wallets[walletId] = switch (csCoin) { + CsCoin.monero => await lib_monero.MoneroWallet.createViewOnlyWallet( + path: path, + password: password, + address: address, + viewKey: privateViewKey, + restoreHeight: height, + ), + + CsCoin.wownero => await lib_monero.WowneroWallet.createViewOnlyWallet( + path: path, + password: password, + address: address, + viewKey: privateViewKey, + restoreHeight: height, + ), + }; + } + + @override + String getTxKey(String walletId, String txid) => + _wallets[walletId]!.getTxKey(txid); + + @override + Future save(String walletId) => _wallets[walletId]!.save(); + + @override + String getPublicViewKey(String walletId) => + _wallets[walletId]!.getPublicViewKey(); + + @override + String getPrivateViewKey(String walletId) => + _wallets[walletId]!.getPrivateViewKey(); + + @override + String getPublicSpendKey(String walletId) => + _wallets[walletId]!.getPublicSpendKey(); + + @override + String getPrivateSpendKey(String walletId) => + _wallets[walletId]!.getPrivateSpendKey(); + + @override + Future isSynced(String walletId) => + _wallets[walletId]?.isSynced() ?? Future.value(false); + + @override + void startSyncing(String walletId) => _wallets[walletId]?.startSyncing(); + + @override + void stopSyncing(String walletId) => _wallets[walletId]?.stopSyncing(); + + @override + void startAutoSaving(String walletId) => + _wallets[walletId]?.startAutoSaving(); + + @override + void stopAutoSaving(String walletId) => _wallets[walletId]?.stopAutoSaving(); + + @override + bool hasListeners(String walletId) => + _wallets[walletId]!.getListeners().isNotEmpty; + + @override + void addListener(String walletId, CsMoneroWalletListener listener) => + _wallets[walletId]?.addListener( + lib_monero.WalletListener( + onSyncingUpdate: listener.onSyncingUpdate, + onNewBlock: listener.onNewBlock, + onBalancesChanged: listener.onBalancesChanged, + onError: listener.onError, + ), + ); + + @override + void startListeners(String walletId) => _wallets[walletId]?.startListeners(); + + @override + void stopListeners(String walletId) => _wallets[walletId]?.stopListeners(); + + @override + int getRefreshFromBlockHeight(String walletId) => + _wallets[walletId]!.getRefreshFromBlockHeight(); + + @override + void setRefreshFromBlockHeight(String walletId, int height) => + _wallets[walletId]!.setRefreshFromBlockHeight(height); + + @override + Future rescanBlockchain(String walletId) => + _wallets[walletId]?.rescanBlockchain() ?? Future.value(false); + + @override + Future isConnectedToDaemon(String walletId) => + _wallets[walletId]?.isConnectedToDaemon() ?? Future.value(false); + + @override + Future connect( + String walletId, { + required String daemonAddress, + required bool trusted, + String? daemonUsername, + String? daemonPassword, + bool useSSL = false, + bool isLightWallet = false, + String? socksProxyAddress, + }) async { + await _wallets[walletId]?.connect( + daemonAddress: daemonAddress, + trusted: trusted, + daemonUsername: daemonUsername, + daemonPassword: daemonPassword, + useSSL: useSSL, + socksProxyAddress: socksProxyAddress, + isLightWallet: isLightWallet, + ); + } + + @override + Future> getAllTxids(String walletId, {bool refresh = false}) => + _wallets[walletId]!.getAllTxids(refresh: refresh); + + @override + BigInt? getBalance(String walletId, {int accountIndex = 0}) => + _wallets[walletId]?.getBalance(accountIndex: accountIndex); + + @override + BigInt? getUnlockedBalance(String walletId, {int accountIndex = 0}) => + _wallets[walletId]?.getUnlockedBalance(accountIndex: accountIndex); + + @override + Future> getAllTxs( + String walletId, { + bool refresh = false, + }) async { + final transactions = await _wallets[walletId]?.getAllTxs(refresh: refresh); + if (transactions == null) return []; + return transactions + .map( + (e) => CsTransaction( + displayLabel: e.displayLabel, + description: e.description, + fee: e.fee, + confirmations: e.confirmations, + blockHeight: e.blockHeight, + accountIndex: e.accountIndex, + addressIndexes: e.addressIndexes, + paymentId: e.paymentId, + amount: e.amount, + isSpend: e.isSpend, + hash: e.hash, + key: e.key, + timeStamp: e.timeStamp, + minConfirms: e.minConfirms.value, + ), + ) + .toList(); + } + + @override + Future> getTxs( + String walletId, { + required Set txids, + bool refresh = false, + }) async { + final transactions = await _wallets[walletId]?.getTxs( + txids: txids, + refresh: refresh, + ); + if (transactions == null) return []; + return transactions + .map( + (e) => CsTransaction( + displayLabel: e.displayLabel, + description: e.description, + fee: e.fee, + confirmations: e.confirmations, + blockHeight: e.blockHeight, + accountIndex: e.accountIndex, + addressIndexes: e.addressIndexes, + paymentId: e.paymentId, + amount: e.amount, + isSpend: e.isSpend, + hash: e.hash, + key: e.key, + timeStamp: e.timeStamp, + minConfirms: e.minConfirms.value, + ), + ) + .toList(); + } + + @override + Future createTx( + String walletId, { + required CsRecipient output, + required int priority, + required bool sweep, + List? preferredInputs, + required int accountIndex, + required int minConfirms, + required int currentHeight, + }) async { + final pending = await _wallets[walletId]!.createTx( + output: lib_monero.Recipient( + address: output.address, + amount: output.amount, + ), + paymentId: "", + sweep: sweep, + priority: lib_monero.TransactionPriority.values.firstWhere( + (e) => e.value == priority, + ), + preferredInputs: preferredInputs + ?.map( + (e) => lib_monero.Output( + address: e.address!, + hash: e.utxo.txid, + keyImage: e.utxo.keyImage!, + value: e.value, + isFrozen: e.utxo.isBlocked, + isUnlocked: + e.utxo.blockHeight != null && + (currentHeight - (e.utxo.blockHeight ?? 0)) >= minConfirms, + height: e.utxo.blockHeight ?? 0, + vout: e.utxo.vout, + spent: e.utxo.used ?? false, + spentHeight: null, // doesn't matter here + coinbase: e.utxo.isCoinbase, + ), + ) + .toList(), + accountIndex: accountIndex, + ); + + return CsPendingTransaction( + pending, + pending.amount, + pending.fee, + pending.txid, + ); + } + + @override + Future createTxMultiDest( + String walletId, { + required List outputs, + required int priority, + required bool sweep, + List? preferredInputs, + required int accountIndex, + required int minConfirms, + required int currentHeight, + }) async { + final pending = await _wallets[walletId]!.createTxMultiDest( + outputs: outputs + .map( + (e) => lib_monero.Recipient(address: e.address, amount: e.amount), + ) + .toList(), + paymentId: "", + sweep: sweep, + priority: lib_monero.TransactionPriority.values.firstWhere( + (e) => e.value == priority, + ), + preferredInputs: preferredInputs + ?.map( + (e) => lib_monero.Output( + address: e.address!, + hash: e.utxo.txid, + keyImage: e.utxo.keyImage!, + value: e.value, + isFrozen: e.utxo.isBlocked, + isUnlocked: + e.utxo.blockHeight != null && + (currentHeight - (e.utxo.blockHeight ?? 0)) >= minConfirms, + height: e.utxo.blockHeight ?? 0, + vout: e.utxo.vout, + spent: e.utxo.used ?? false, + spentHeight: null, // doesn't matter here + coinbase: e.utxo.isCoinbase, + ), + ) + .toList(), + accountIndex: accountIndex, + ); + + return CsPendingTransaction( + pending, + pending.amount, + pending.fee, + pending.txid, + ); + } + + @override + Future commitTx(String walletId, CsPendingTransaction tx) => + _wallets[walletId]!.commitTx(tx.value as lib_monero.PendingTransaction); + + @override + Future> getOutputs( + String walletId, { + bool refresh = false, + bool includeSpent = false, + }) async { + final outputs = await _wallets[walletId]?.getOutputs( + includeSpent: includeSpent, + refresh: refresh, + ); + + if (outputs == null) return []; + + return outputs + .map( + (e) => CsOutput( + address: e.address, + hash: e.hash, + keyImage: e.keyImage, + value: e.value, + isFrozen: e.isFrozen, + isUnlocked: e.isUnlocked, + height: e.height, + spentHeight: e.spentHeight, + vout: e.vout, + spent: e.spent, + coinbase: e.coinbase, + ), + ) + .toList(); + } + + @override + Future freezeOutput(String walletId, String keyImage) => + _wallets[walletId]!.freezeOutput(keyImage); + + @override + Future thawOutput(String walletId, String keyImage) => + _wallets[walletId]!.thawOutput(keyImage); + + @override + List getMoneroWordList(String language) => + lib_monero.getMoneroWordList(language); + + @override + List getWowneroWordList(String language, int seedLength) => + lib_monero.getWowneroWordList(language, seedWordsLength: seedLength); +} + +//END_ON From 6b5966c58cfc1991c94248196b2203ca7b0965fa Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 6 Oct 2025 08:31:40 -0600 Subject: [PATCH 07/30] clean switch --- ...XMR_cs_monero_interface_impl.template.dart | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart b/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart index a90a4dbb17..5d6fbfc35c 100644 --- a/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart +++ b/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart @@ -158,25 +158,21 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface { int height = 0, }) async { _wallets[walletId] = switch (csCoin) { - CsCoin.monero => - _wallets[walletId] = - await lib_monero.MoneroWallet.restoreWalletFromSeed( - path: path, - password: password, - seed: mnemonic, - restoreHeight: height, - seedOffset: seedOffset, - ), + CsCoin.monero => await lib_monero.MoneroWallet.restoreWalletFromSeed( + path: path, + password: password, + seed: mnemonic, + restoreHeight: height, + seedOffset: seedOffset, + ), - CsCoin.wownero => - _wallets[walletId] = - await lib_monero.WowneroWallet.restoreWalletFromSeed( - path: path, - password: password, - seed: mnemonic, - restoreHeight: height, - seedOffset: seedOffset, - ), + CsCoin.wownero => await lib_monero.WowneroWallet.restoreWalletFromSeed( + path: path, + password: password, + seed: mnemonic, + restoreHeight: height, + seedOffset: seedOffset, + ), }; } From be92e869e7f7cbb46234fe567213913a87e81e50 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 6 Oct 2025 09:31:19 -0600 Subject: [PATCH 08/30] implement sal ffi package optional import and some xmr/wow fixes --- .../restore_options_view.dart | 563 +++++++++--------- .../verify_recovery_phrase_view.dart | 229 ++++--- lib/wallets/crypto_currency/coins/monero.dart | 13 +- .../crypto_currency/coins/salvium.dart | 13 +- .../crypto_currency/coins/wownero.dart | 13 +- lib/wallets/models/tx_data.dart | 5 +- lib/wallets/wallet/impl/salvium_wallet.dart | 98 +-- .../intermediate/lib_monero_wallet.dart | 2 +- .../intermediate/lib_salvium_wallet.dart | 401 ++++++------- .../interfaces/cs_monero_interface.dart | 10 +- .../interfaces/cs_salvium_interface.dart | 154 +++++ ...AL_cs_salvium_interface_impl.template.dart | 488 +++++++++++++++ ...XMR_cs_monero_interface_impl.template.dart | 24 +- 13 files changed, 1292 insertions(+), 721 deletions(-) create mode 100644 lib/wl_gen/interfaces/cs_salvium_interface.dart create mode 100644 tool/wl_templates/SAL_cs_salvium_interface_impl.template.dart diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart index c230578940..1972ae5d3a 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart @@ -8,8 +8,6 @@ * */ -import 'package:cs_monero/src/deprecated/get_height_by_date.dart' - as cs_monero_deprecated; import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -42,6 +40,8 @@ import '../../../../widgets/rounded_white_container.dart'; import '../../../../widgets/stack_text_field.dart'; import '../../../../widgets/textfield_icon_button.dart'; import '../../../../widgets/toggle.dart'; +import '../../../../wl_gen/interfaces/cs_monero_interface.dart'; +import '../../../../wl_gen/interfaces/cs_salvium_interface.dart'; import '../../create_or_restore_wallet_view/sub_widgets/coin_image.dart'; import '../restore_view_only_wallet_view.dart'; import '../restore_wallet_view.dart'; @@ -213,10 +213,15 @@ class _RestoreOptionsViewState extends ConsumerState { int height = 0; if (date != null) { if (widget.coin is Monero) { - height = cs_monero_deprecated.getMoneroHeightByDate(date: date); + height = csMonero.getHeightByDate(date, csCoin: CsCoin.monero); } if (widget.coin is Wownero) { - height = cs_monero_deprecated.getWowneroHeightByDate(date: date); + height = csMonero.getHeightByDate(date, csCoin: CsCoin.wownero); + } + if (widget.coin is Salvium) { + height = csSalvium.getHeightByDate( + DateTime.now().subtract(const Duration(days: 7)), + ); } if (height < 0) { height = 0; @@ -256,27 +261,26 @@ class _RestoreOptionsViewState extends ConsumerState { return MasterScaffold( isDesktop: isDesktop, - appBar: - isDesktop - ? const DesktopAppBar( - isCompactHeight: false, - leading: AppBarBackButton(), - trailing: ExitToMyStackButton(), - ) - : AppBar( - leading: AppBarBackButton( - onPressed: () { - if (textFieldFocusNode.hasFocus) { - textFieldFocusNode.unfocus(); - Future.delayed( - const Duration(milliseconds: 100), - ).then((value) => Navigator.of(context).pop()); - } else { - Navigator.of(context).pop(); - } - }, - ), + appBar: isDesktop + ? const DesktopAppBar( + isCompactHeight: false, + leading: AppBarBackButton(), + trailing: ExitToMyStackButton(), + ) + : AppBar( + leading: AppBarBackButton( + onPressed: () { + if (textFieldFocusNode.hasFocus) { + textFieldFocusNode.unfocus(); + Future.delayed( + const Duration(milliseconds: 100), + ).then((value) => Navigator.of(context).pop()); + } else { + Navigator.of(context).pop(); + } + }, ), + ), body: RestoreOptionsPlatformLayout( isDesktop: isDesktop, child: ConstrainedBox( @@ -292,10 +296,9 @@ class _RestoreOptionsViewState extends ConsumerState { Text( "Restore options", textAlign: TextAlign.center, - style: - isDesktop - ? STextStyles.desktopH2(context) - : STextStyles.pageTitleH1(context), + style: isDesktop + ? STextStyles.desktopH2(context) + : STextStyles.pageTitleH1(context), ), SizedBox(height: isDesktop ? 40 : 24), if (coin is ViewOnlyOptionCurrencyInterface) @@ -306,12 +309,12 @@ class _RestoreOptionsViewState extends ConsumerState { key: UniqueKey(), onText: "Seed", offText: "View Only", - onColor: - Theme.of(context).extension()!.popupBG, - offColor: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + onColor: Theme.of( + context, + ).extension()!.popupBG, + offColor: Theme.of( + context, + ).extension()!.textFieldDefaultBG, isOn: _showViewOnlyOption, onValueChanged: (value) { setState(() { @@ -330,32 +333,33 @@ class _RestoreOptionsViewState extends ConsumerState { SizedBox(height: isDesktop ? 40 : 24), _showViewOnlyOption ? ViewOnlyRestoreOption( - coin: coin, - dateController: _dateController, - dateChooserFunction: - isDesktop ? chooseDesktopDate : chooseDate, - blockHeightController: _blockHeightController, - blockHeightFocusNode: _blockHeightFocusNode, - ) + coin: coin, + dateController: _dateController, + dateChooserFunction: isDesktop + ? chooseDesktopDate + : chooseDate, + blockHeightController: _blockHeightController, + blockHeightFocusNode: _blockHeightFocusNode, + ) : SeedRestoreOption( - coin: coin, - dateController: _dateController, - blockHeightController: _blockHeightController, - blockHeightFocusNode: _blockHeightFocusNode, - pwController: passwordController, - pwFocusNode: passwordFocusNode, - dateChooserFunction: - isDesktop ? chooseDesktopDate : chooseDate, - chooseMnemonicLength: chooseMnemonicLength, - ), + coin: coin, + dateController: _dateController, + blockHeightController: _blockHeightController, + blockHeightFocusNode: _blockHeightFocusNode, + pwController: passwordController, + pwFocusNode: passwordFocusNode, + dateChooserFunction: isDesktop + ? chooseDesktopDate + : chooseDate, + chooseMnemonicLength: chooseMnemonicLength, + ), if (!isDesktop) const Spacer(flex: 3), SizedBox(height: isDesktop ? 32 : 12), RestoreOptionsNextButton( isDesktop: isDesktop, - onPressed: - ref.watch(_pIsUsingDate) || _hasBlockHeight - ? nextPressed - : null, + onPressed: ref.watch(_pIsUsingDate) || _hasBlockHeight + ? nextPressed + : null, ), if (isDesktop) const Spacer(flex: 15), ], @@ -427,24 +431,22 @@ class _SeedRestoreOptionState extends ConsumerState { children: [ Text( ref.watch(_pIsUsingDate) ? "Choose start date" : "Block height", - style: - Util.isDesktop - ? STextStyles.desktopTextExtraSmall(context).copyWith( - color: - Theme.of( - context, - ).extension()!.textDark3, - ) - : STextStyles.smallMed12(context), + style: Util.isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: Theme.of( + context, + ).extension()!.textDark3, + ) + : STextStyles.smallMed12(context), textAlign: TextAlign.left, ), CustomTextButton( - text: - ref.watch(_pIsUsingDate) ? "Use block height" : "Use date", - onTap: - () => - ref.read(_pIsUsingDate.notifier).state = - !ref.read(_pIsUsingDate), + text: ref.watch(_pIsUsingDate) + ? "Use block height" + : "Use date", + onTap: () => ref.read(_pIsUsingDate.notifier).state = !ref.read( + _pIsUsingDate, + ), ), ], ), @@ -457,60 +459,59 @@ class _SeedRestoreOptionState extends ConsumerState { widget.coin is Mimblewimblecoin) ref.watch(_pIsUsingDate) ? RestoreFromDatePicker( - onTap: widget.dateChooserFunction, - controller: widget.dateController, - ) + onTap: widget.dateChooserFunction, + controller: widget.dateController, + ) : ClipRRect( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - child: TextField( - focusNode: widget.blockHeightFocusNode, - controller: widget.blockHeightController, - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - textInputAction: TextInputAction.done, - style: - Util.isDesktop - ? STextStyles.desktopTextMedium( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + focusNode: widget.blockHeightFocusNode, + controller: widget.blockHeightController, + keyboardType: TextInputType.number, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + textInputAction: TextInputAction.done, + style: Util.isDesktop + ? STextStyles.desktopTextMedium( context, ).copyWith(height: 2) - : STextStyles.field(context), - onChanged: (value) { - setState(() { - _blockFieldEmpty = value.isEmpty; - }); - }, - decoration: standardInputDecoration( - "Start scanning from...", - widget.blockHeightFocusNode, - context, - ).copyWith( - suffixIcon: UnconstrainedBox( - child: TextFieldIconButton( - child: Semantics( - label: - "Clear Block Height Field Button. Clears the block height field", - excludeSemantics: true, - child: - !_blockFieldEmpty - ? XIcon( - width: Util.isDesktop ? 24 : 16, - height: Util.isDesktop ? 24 : 16, - ) - : const SizedBox.shrink(), + : STextStyles.field(context), + onChanged: (value) { + setState(() { + _blockFieldEmpty = value.isEmpty; + }); + }, + decoration: + standardInputDecoration( + "Start scanning from...", + widget.blockHeightFocusNode, + context, + ).copyWith( + suffixIcon: UnconstrainedBox( + child: TextFieldIconButton( + child: Semantics( + label: + "Clear Block Height Field Button. Clears the block height field", + excludeSemantics: true, + child: !_blockFieldEmpty + ? XIcon( + width: Util.isDesktop ? 24 : 16, + height: Util.isDesktop ? 24 : 16, + ) + : const SizedBox.shrink(), + ), + onTap: () { + widget.blockHeightController.text = ""; + setState(() { + _blockFieldEmpty = true; + }); + }, + ), + ), ), - onTap: () { - widget.blockHeightController.text = ""; - setState(() { - _blockFieldEmpty = true; - }); - }, - ), - ), ), ), - ), if (isCnAnd25 || widget.coin is Epiccash || widget.coin is Mimblewimblecoin) @@ -525,15 +526,13 @@ class _SeedRestoreOptionState extends ConsumerState { ref.watch(_pIsUsingDate) ? "Choose the date you made the wallet (approximate is fine)" : "Enter the initial block height of the wallet", - style: - Util.isDesktop - ? STextStyles.desktopTextExtraSmall(context).copyWith( - color: - Theme.of( - context, - ).extension()!.textSubtitle1, - ) - : STextStyles.smallMed12(context).copyWith(fontSize: 10), + style: Util.isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: Theme.of( + context, + ).extension()!.textSubtitle1, + ) + : STextStyles.smallMed12(context).copyWith(fontSize: 10), ), ), ), @@ -543,13 +542,11 @@ class _SeedRestoreOptionState extends ConsumerState { SizedBox(height: Util.isDesktop ? 24 : 16), Text( "Choose recovery phrase length", - style: - Util.isDesktop - ? STextStyles.desktopTextExtraSmall(context).copyWith( - color: - Theme.of(context).extension()!.textDark3, - ) - : STextStyles.smallMed12(context), + style: Util.isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: Theme.of(context).extension()!.textDark3, + ) + : STextStyles.smallMed12(context), textAlign: TextAlign.left, ), SizedBox(height: Util.isDesktop ? 16 : 8), @@ -577,19 +574,17 @@ class _SeedRestoreOptionState extends ConsumerState { iconStyleData: IconStyleData( icon: ConditionalParent( condition: Util.isDesktop, - builder: - (child) => Padding( - padding: const EdgeInsets.only(right: 10), - child: child, - ), + builder: (child) => Padding( + padding: const EdgeInsets.only(right: 10), + child: child, + ), child: SvgPicture.asset( Assets.svg.chevronDown, width: 12, height: 6, - color: - Theme.of(context) - .extension()! - .textFieldActiveSearchIconRight, + color: Theme.of( + context, + ).extension()!.textFieldActiveSearchIconRight, ), ), ), @@ -597,10 +592,9 @@ class _SeedRestoreOptionState extends ConsumerState { offset: const Offset(0, -10), elevation: 0, decoration: BoxDecoration( - color: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + color: Theme.of( + context, + ).extension()!.textFieldDefaultBG, borderRadius: BorderRadius.circular( Constants.size.circularBorderRadius, ), @@ -637,17 +631,15 @@ class _SeedRestoreOptionState extends ConsumerState { children: [ Text( "Advanced", - style: - Util.isDesktop - ? STextStyles.desktopTextExtraExtraSmall( + style: Util.isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of( context, - ).copyWith( - color: - Theme.of( - context, - ).extension()!.textDark3, - ) - : STextStyles.smallMed12(context), + ).extension()!.textDark3, + ) + : STextStyles.smallMed12(context), textAlign: TextAlign.left, ), SvgPicture.asset( @@ -656,10 +648,9 @@ class _SeedRestoreOptionState extends ConsumerState { : Assets.svg.chevronDown, width: 12, height: 6, - color: - Theme.of(context) - .extension()! - .textFieldActiveSearchIconRight, + color: Theme.of(context) + .extension()! + .textFieldActiveSearchIconRight, ), ], ), @@ -678,57 +669,56 @@ class _SeedRestoreOptionState extends ConsumerState { key: const Key("mnemonicPassphraseFieldKey1"), focusNode: widget.pwFocusNode, controller: widget.pwController, - style: - Util.isDesktop - ? STextStyles.desktopTextMedium( - context, - ).copyWith(height: 2) - : STextStyles.field(context), + style: Util.isDesktop + ? STextStyles.desktopTextMedium( + context, + ).copyWith(height: 2) + : STextStyles.field(context), obscureText: _hidePassword, enableSuggestions: false, autocorrect: false, - decoration: standardInputDecoration( - widget.coin is CryptonoteCurrency - ? "Seed Offset" - : "BIP39 passphrase", - widget.pwFocusNode, - context, - ).copyWith( - suffixIcon: UnconstrainedBox( - child: ConditionalParent( - condition: Util.isDesktop, - builder: - (child) => SizedBox(height: 70, child: child), - child: Row( - children: [ - SizedBox(width: Util.isDesktop ? 24 : 16), - GestureDetector( - key: const Key( - "mnemonicPassphraseFieldShowPasswordButtonKey", - ), - onTap: () async { - setState(() { - _hidePassword = !_hidePassword; - }); - }, - child: SvgPicture.asset( - _hidePassword - ? Assets.svg.eye - : Assets.svg.eyeSlash, - color: - Theme.of( + decoration: + standardInputDecoration( + widget.coin is CryptonoteCurrency + ? "Seed Offset" + : "BIP39 passphrase", + widget.pwFocusNode, + context, + ).copyWith( + suffixIcon: UnconstrainedBox( + child: ConditionalParent( + condition: Util.isDesktop, + builder: (child) => + SizedBox(height: 70, child: child), + child: Row( + children: [ + SizedBox(width: Util.isDesktop ? 24 : 16), + GestureDetector( + key: const Key( + "mnemonicPassphraseFieldShowPasswordButtonKey", + ), + onTap: () async { + setState(() { + _hidePassword = !_hidePassword; + }); + }, + child: SvgPicture.asset( + _hidePassword + ? Assets.svg.eye + : Assets.svg.eyeSlash, + color: Theme.of( context, ).extension()!.textDark3, - width: Util.isDesktop ? 24 : 16, - height: Util.isDesktop ? 24 : 16, - ), + width: Util.isDesktop ? 24 : 16, + height: Util.isDesktop ? 24 : 16, + ), + ), + const SizedBox(width: 12), + ], ), - const SizedBox(width: 12), - ], + ), ), ), - ), - ), ), ), const SizedBox(height: 8), @@ -737,23 +727,21 @@ class _SeedRestoreOptionState extends ConsumerState { child: Text( widget.coin is CryptonoteCurrency ? "(Optional) An offset used to derive a different " - "wallet from the given mnemonic, allowing recovery " - "of a hidden or alternate wallet based on the same " - "seed phrase." + "wallet from the given mnemonic, allowing recovery " + "of a hidden or alternate wallet based on the same " + "seed phrase." : "If the recovery phrase you are about to restore " - "was created with an optional BIP39 passphrase " - "you can enter it here.", - style: - Util.isDesktop - ? STextStyles.desktopTextExtraSmall( + "was created with an optional BIP39 passphrase " + "you can enter it here.", + style: Util.isDesktop + ? STextStyles.desktopTextExtraSmall( + context, + ).copyWith( + color: Theme.of( context, - ).copyWith( - color: - Theme.of( - context, - ).extension()!.textSubtitle1, - ) - : STextStyles.itemSubtitle(context), + ).extension()!.textSubtitle1, + ) + : STextStyles.itemSubtitle(context), ), ), ), @@ -810,25 +798,23 @@ class _ViewOnlyRestoreOptionState extends ConsumerState { children: [ Text( ref.watch(_pIsUsingDate) ? "Choose start date" : "Block height", - style: - Util.isDesktop - ? STextStyles.desktopTextExtraExtraSmall( + style: Util.isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context).copyWith( + color: Theme.of( context, - ).copyWith( - color: - Theme.of( - context, - ).extension()!.textDark3, - ) - : STextStyles.smallMed12(context), + ).extension()!.textDark3, + ) + : STextStyles.smallMed12(context), textAlign: TextAlign.left, ), CustomTextButton( - text: - ref.watch(_pIsUsingDate) ? "Use block height" : "Use date", + text: ref.watch(_pIsUsingDate) + ? "Use block height" + : "Use date", onTap: () { - ref.read(_pIsUsingDate.notifier).state = - !ref.read(_pIsUsingDate); + ref.read(_pIsUsingDate.notifier).state = !ref.read( + _pIsUsingDate, + ); }, ), ], @@ -837,60 +823,59 @@ class _ViewOnlyRestoreOptionState extends ConsumerState { if (showDateOption) ref.watch(_pIsUsingDate) ? RestoreFromDatePicker( - onTap: widget.dateChooserFunction, - controller: widget.dateController, - ) + onTap: widget.dateChooserFunction, + controller: widget.dateController, + ) : ClipRRect( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - child: TextField( - focusNode: widget.blockHeightFocusNode, - controller: widget.blockHeightController, - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - textInputAction: TextInputAction.done, - style: - Util.isDesktop - ? STextStyles.desktopTextMedium( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + focusNode: widget.blockHeightFocusNode, + controller: widget.blockHeightController, + keyboardType: TextInputType.number, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + textInputAction: TextInputAction.done, + style: Util.isDesktop + ? STextStyles.desktopTextMedium( context, ).copyWith(height: 2) - : STextStyles.field(context), - onChanged: (value) { - setState(() { - _blockFieldEmpty = value.isEmpty; - }); - }, - decoration: standardInputDecoration( - "Start scanning from...", - widget.blockHeightFocusNode, - context, - ).copyWith( - suffixIcon: UnconstrainedBox( - child: TextFieldIconButton( - child: Semantics( - label: - "Clear Block Height Field Button. Clears the block height field", - excludeSemantics: true, - child: - !_blockFieldEmpty - ? XIcon( - width: Util.isDesktop ? 24 : 16, - height: Util.isDesktop ? 24 : 16, - ) - : const SizedBox.shrink(), + : STextStyles.field(context), + onChanged: (value) { + setState(() { + _blockFieldEmpty = value.isEmpty; + }); + }, + decoration: + standardInputDecoration( + "Start scanning from...", + widget.blockHeightFocusNode, + context, + ).copyWith( + suffixIcon: UnconstrainedBox( + child: TextFieldIconButton( + child: Semantics( + label: + "Clear Block Height Field Button. Clears the block height field", + excludeSemantics: true, + child: !_blockFieldEmpty + ? XIcon( + width: Util.isDesktop ? 24 : 16, + height: Util.isDesktop ? 24 : 16, + ) + : const SizedBox.shrink(), + ), + onTap: () { + widget.blockHeightController.text = ""; + setState(() { + _blockFieldEmpty = true; + }); + }, + ), + ), ), - onTap: () { - widget.blockHeightController.text = ""; - setState(() { - _blockFieldEmpty = true; - }); - }, - ), - ), ), ), - ), if (showDateOption) const SizedBox(height: 8), if (showDateOption) RoundedWhiteContainer( @@ -899,17 +884,13 @@ class _ViewOnlyRestoreOptionState extends ConsumerState { ref.watch(_pIsUsingDate) ? "Choose the date you made the wallet (approximate is fine)" : "Enter the initial block height of the wallet", - style: - Util.isDesktop - ? STextStyles.desktopTextExtraSmall(context).copyWith( - color: - Theme.of( - context, - ).extension()!.textSubtitle1, - ) - : STextStyles.smallMed12( + style: Util.isDesktop + ? STextStyles.desktopTextExtraSmall(context).copyWith( + color: Theme.of( context, - ).copyWith(fontSize: 10), + ).extension()!.textSubtitle1, + ) + : STextStyles.smallMed12(context).copyWith(fontSize: 10), ), ), ), diff --git a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart index 9d79a315eb..9494b6630f 100644 --- a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart +++ b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart @@ -12,10 +12,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:math'; -import 'package:cs_monero/src/deprecated/get_height_by_date.dart' - as cs_monero_deprecated; -import 'package:cs_salvium/src/deprecated/get_height_by_date.dart' - as cs_salvium_deprecated; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -50,6 +46,8 @@ import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../widgets/desktop/desktop_app_bar.dart'; import '../../../widgets/desktop/desktop_scaffold.dart'; import '../../../widgets/stack_dialog.dart'; +import '../../../wl_gen/interfaces/cs_monero_interface.dart'; +import '../../../wl_gen/interfaces/cs_salvium_interface.dart'; import '../../home_view/home_view.dart'; import '../add_token_view/edit_wallet_tokens_view.dart'; import '../new_wallet_options/new_wallet_options_view.dart'; @@ -116,18 +114,20 @@ class _VerifyRecoveryPhraseViewState } else if (widget.wallet is LibMoneroWallet || widget.wallet is LibSalviumWallet) { if (widget.wallet.cryptoCurrency is Monero) { - height = cs_monero_deprecated.getMoneroHeightByDate( - date: DateTime.now().subtract(const Duration(days: 7)), + height = csMonero.getHeightByDate( + DateTime.now().subtract(const Duration(days: 7)), + csCoin: CsCoin.monero, ); } if (widget.wallet.cryptoCurrency is Wownero) { - height = cs_monero_deprecated.getWowneroHeightByDate( - date: DateTime.now().subtract(const Duration(days: 7)), + height = csMonero.getHeightByDate( + DateTime.now().subtract(const Duration(days: 7)), + csCoin: CsCoin.wownero, ); } if (widget.wallet.cryptoCurrency is Salvium) { - height = cs_salvium_deprecated.getSalviumHeightByDate( - date: DateTime.now().subtract(const Duration(days: 7)), + height = csSalvium.getHeightByDate( + DateTime.now().subtract(const Duration(days: 7)), ); } if (height < 0) height = 0; @@ -151,8 +151,8 @@ class _VerifyRecoveryPhraseViewState final ViewOnlyWalletData viewOnlyData; if (widget.wallet is ExtendedKeysInterface) { - final extendedKeyInfo = - await (widget.wallet as ExtendedKeysInterface).getXPubs(); + final extendedKeyInfo = await (widget.wallet as ExtendedKeysInterface) + .getXPubs(); final testPath = (_coin as Bip39HDCurrency).constructDerivePath( derivePathType: (_coin as Bip39HDCurrency).defaultDerivePathType, chain: 0, @@ -178,9 +178,8 @@ class _VerifyRecoveryPhraseViewState } else if (widget.wallet is LibMoneroWallet) { final w = widget.wallet as LibMoneroWallet; - final info = - await w - .hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing(); + final info = await w + .hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing(); final address = info.$1; final privateViewKey = info.$2; @@ -194,9 +193,8 @@ class _VerifyRecoveryPhraseViewState } else if (widget.wallet is LibSalviumWallet) { final w = widget.wallet as LibSalviumWallet; - final info = - await w - .hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing(); + final info = await w + .hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing(); final address = info.$1; final privateViewKey = info.$2; @@ -310,8 +308,8 @@ class _VerifyRecoveryPhraseViewState ref .read(newEthWalletTriggerTempUntilHiveCompletelyDeleted.state) .state = !ref - .read(newEthWalletTriggerTempUntilHiveCompletelyDeleted.state) - .state; + .read(newEthWalletTriggerTempUntilHiveCompletelyDeleted.state) + .state; } if (mounted && @@ -338,11 +336,10 @@ class _VerifyRecoveryPhraseViewState if (mounted) { await showDialog( context: context, - builder: - (_) => StackOkDialog( - title: e.toString(), - desktopPopRootNavigator: Util.isDesktop, - ), + builder: (_) => StackOkDialog( + title: e.toString(), + desktopPopRootNavigator: Util.isDesktop, + ), ); } @@ -485,53 +482,54 @@ class _VerifyRecoveryPhraseViewState @override Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); - final correctIndex = - ref.watch(verifyMnemonicWordIndexStateProvider.state).state; + final correctIndex = ref + .watch(verifyMnemonicWordIndexStateProvider.state) + .state; return WillPopScope( onWillPop: onWillPop, child: MasterScaffold( isDesktop: isDesktop, - appBar: - isDesktop - ? DesktopAppBar( - isCompactHeight: false, - leading: AppBarBackButton( - onPressed: () async { - Navigator.of(context).popUntil( - ModalRoute.withName( - NewWalletRecoveryPhraseView.routeName, - ), - ); - }, - ), - trailing: ExitToMyStackButton( - onPressed: () async { - await delete(); - if (context.mounted) { - Navigator.of(context).popUntil( - ModalRoute.withName(DesktopHomeView.routeName), - ); - } - }, - ), - ) - : AppBar( - leading: AppBarBackButton( - onPressed: () async { + appBar: isDesktop + ? DesktopAppBar( + isCompactHeight: false, + leading: AppBarBackButton( + onPressed: () async { + Navigator.of(context).popUntil( + ModalRoute.withName( + NewWalletRecoveryPhraseView.routeName, + ), + ); + }, + ), + trailing: ExitToMyStackButton( + onPressed: () async { + await delete(); + if (context.mounted) { Navigator.of(context).popUntil( - ModalRoute.withName( - NewWalletRecoveryPhraseView.routeName, - ), + ModalRoute.withName(DesktopHomeView.routeName), ); - }, - ), + } + }, + ), + ) + : AppBar( + leading: AppBarBackButton( + onPressed: () async { + Navigator.of(context).popUntil( + ModalRoute.withName( + NewWalletRecoveryPhraseView.routeName, + ), + ); + }, ), + ), body: SizedBox( width: isDesktop ? 410 : null, child: Padding( - padding: - isDesktop ? const EdgeInsets.all(0) : const EdgeInsets.all(16), + padding: isDesktop + ? const EdgeInsets.all(0) + : const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -540,27 +538,24 @@ class _VerifyRecoveryPhraseViewState Text( "Verify recovery phrase", textAlign: TextAlign.center, - style: - isDesktop - ? STextStyles.desktopH2(context) - : STextStyles.label(context).copyWith(fontSize: 12), + style: isDesktop + ? STextStyles.desktopH2(context) + : STextStyles.label(context).copyWith(fontSize: 12), ), SizedBox(height: isDesktop ? 16 : 4), Text( isDesktop ? "Select word number" : "Tap word number ", textAlign: TextAlign.center, - style: - isDesktop - ? STextStyles.desktopSubtitleH1(context) - : STextStyles.pageTitleH1(context), + style: isDesktop + ? STextStyles.desktopSubtitleH1(context) + : STextStyles.pageTitleH1(context), ), SizedBox(height: isDesktop ? 16 : 12), Container( decoration: BoxDecoration( - color: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + color: Theme.of( + context, + ).extension()!.textFieldDefaultBG, borderRadius: BorderRadius.circular( Constants.size.circularBorderRadius, ), @@ -591,61 +586,51 @@ class _VerifyRecoveryPhraseViewState Expanded( child: Consumer( builder: (_, ref, __) { - final selectedWord = - ref - .watch( - verifyMnemonicSelectedWordStateProvider - .state, - ) - .state; - final correctWord = - ref - .watch( - verifyMnemonicCorrectWordStateProvider - .state, - ) - .state; + final selectedWord = ref + .watch( + verifyMnemonicSelectedWordStateProvider.state, + ) + .state; + final correctWord = ref + .watch( + verifyMnemonicCorrectWordStateProvider.state, + ) + .state; return ConstrainedBox( constraints: BoxConstraints( minHeight: isDesktop ? 70 : 0, ), child: TextButton( - onPressed: - selectedWord.isNotEmpty - ? () async { - await _continue( - correctWord == selectedWord, - ); - } - : null, - style: - selectedWord.isNotEmpty - ? Theme.of(context) - .extension()! - .getPrimaryEnabledButtonStyle(context) - : Theme.of(context) - .extension()! - .getPrimaryDisabledButtonStyle( - context, - ), - child: - isDesktop - ? Text( - "Verify", - style: - selectedWord.isNotEmpty - ? STextStyles.desktopButtonEnabled( - context, - ) - : STextStyles.desktopButtonDisabled( - context, - ), - ) - : Text( - "Continue", - style: STextStyles.button(context), - ), + onPressed: selectedWord.isNotEmpty + ? () async { + await _continue( + correctWord == selectedWord, + ); + } + : null, + style: selectedWord.isNotEmpty + ? Theme.of(context) + .extension()! + .getPrimaryEnabledButtonStyle(context) + : Theme.of(context) + .extension()! + .getPrimaryDisabledButtonStyle(context), + child: isDesktop + ? Text( + "Verify", + style: selectedWord.isNotEmpty + ? STextStyles.desktopButtonEnabled( + context, + ) + : STextStyles.desktopButtonDisabled( + context, + ), + ) + : Text( + "Continue", + style: STextStyles.button(context), + ), ), ); }, diff --git a/lib/wallets/crypto_currency/coins/monero.dart b/lib/wallets/crypto_currency/coins/monero.dart index 4d1535179d..3a983cf9fe 100644 --- a/lib/wallets/crypto_currency/coins/monero.dart +++ b/lib/wallets/crypto_currency/coins/monero.dart @@ -1,9 +1,7 @@ -import 'package:cs_monero/src/ffi_bindings/monero_wallet_bindings.dart' - as xmr_wallet_ffi; - import '../../../models/node_model.dart'; import '../../../utilities/default_nodes.dart'; import '../../../utilities/enums/derive_path_type_enum.dart'; +import '../../../wl_gen/interfaces/cs_monero_interface.dart'; import '../crypto_currency.dart'; import '../intermediate/cryptonote_currency.dart'; @@ -54,7 +52,7 @@ class Monero extends CryptonoteCurrency { } switch (network) { case CryptoCurrencyNetwork.main: - return xmr_wallet_ffi.validateAddress(address, 0); + return csMonero.validateAddress(address, 0, csCoin: CsCoin.monero); default: throw Exception("Unsupported network: $network"); } @@ -107,10 +105,9 @@ class Monero extends CryptonoteCurrency { int get targetBlockTimeSeconds => 120; @override - DerivePathType get defaultDerivePathType => - throw UnsupportedError( - "$runtimeType does not use bitcoin style derivation paths", - ); + DerivePathType get defaultDerivePathType => throw UnsupportedError( + "$runtimeType does not use bitcoin style derivation paths", + ); @override Uri defaultBlockExplorer(String txid) { diff --git a/lib/wallets/crypto_currency/coins/salvium.dart b/lib/wallets/crypto_currency/coins/salvium.dart index db794dc53a..931208c6ad 100644 --- a/lib/wallets/crypto_currency/coins/salvium.dart +++ b/lib/wallets/crypto_currency/coins/salvium.dart @@ -1,9 +1,7 @@ -import 'package:cs_salvium/src/ffi_bindings/salvium_wallet_bindings.dart' - as sal_wallet_ffi; - import '../../../models/node_model.dart'; import '../../../utilities/default_nodes.dart'; import '../../../utilities/enums/derive_path_type_enum.dart'; +import '../../../wl_gen/interfaces/cs_salvium_interface.dart'; import '../crypto_currency.dart'; import '../intermediate/cryptonote_currency.dart'; @@ -54,7 +52,7 @@ class Salvium extends CryptonoteCurrency { } switch (network) { case CryptoCurrencyNetwork.main: - return sal_wallet_ffi.validateAddress(address, 0); + return csSalvium.validateAddress(address, 0); default: throw Exception("Unsupported network: $network"); } @@ -107,10 +105,9 @@ class Salvium extends CryptonoteCurrency { int get targetBlockTimeSeconds => 120; @override - DerivePathType get defaultDerivePathType => - throw UnsupportedError( - "$runtimeType does not use bitcoin style derivation paths", - ); + DerivePathType get defaultDerivePathType => throw UnsupportedError( + "$runtimeType does not use bitcoin style derivation paths", + ); @override Uri defaultBlockExplorer(String txid) { diff --git a/lib/wallets/crypto_currency/coins/wownero.dart b/lib/wallets/crypto_currency/coins/wownero.dart index 0c702af40a..66d27c3e08 100644 --- a/lib/wallets/crypto_currency/coins/wownero.dart +++ b/lib/wallets/crypto_currency/coins/wownero.dart @@ -1,9 +1,7 @@ -import 'package:cs_monero/src/ffi_bindings/wownero_wallet_bindings.dart' - as wow_wallet_ffi; - import '../../../models/node_model.dart'; import '../../../utilities/default_nodes.dart'; import '../../../utilities/enums/derive_path_type_enum.dart'; +import '../../../wl_gen/interfaces/cs_monero_interface.dart'; import '../crypto_currency.dart'; import '../intermediate/cryptonote_currency.dart'; @@ -54,7 +52,7 @@ class Wownero extends CryptonoteCurrency { } switch (network) { case CryptoCurrencyNetwork.main: - return wow_wallet_ffi.validateAddress(address, 0); + return csMonero.validateAddress(address, 0, csCoin: CsCoin.wownero); default: throw Exception("Unsupported network: $network"); } @@ -107,10 +105,9 @@ class Wownero extends CryptonoteCurrency { int get targetBlockTimeSeconds => 120; @override - DerivePathType get defaultDerivePathType => - throw UnsupportedError( - "$runtimeType does not use bitcoin style derivation paths", - ); + DerivePathType get defaultDerivePathType => throw UnsupportedError( + "$runtimeType does not use bitcoin style derivation paths", + ); @override Uri defaultBlockExplorer(String txid) { diff --git a/lib/wallets/models/tx_data.dart b/lib/wallets/models/tx_data.dart index 8ebed1e5e5..68ba7b7223 100644 --- a/lib/wallets/models/tx_data.dart +++ b/lib/wallets/models/tx_data.dart @@ -1,4 +1,3 @@ -import 'package:cs_salvium/cs_salvium.dart' as lib_salvium; import 'package:tezart/tezart.dart' as tezart; import 'package:web3dart/web3dart.dart' as web3dart; @@ -71,7 +70,7 @@ class TxData { final CsPendingTransaction? pendingTransaction; // salvium - final lib_salvium.PendingTransaction? pendingSalviumTransaction; + final CsPendingTransaction? pendingSalviumTransaction; // tezos specific final tezart.OperationsList? tezosOperationsList; @@ -260,7 +259,7 @@ class TxData { int? nonce, BigInt? chainId, CsPendingTransaction? pendingTransaction, - lib_salvium.PendingTransaction? pendingSalviumTransaction, + CsPendingTransaction? pendingSalviumTransaction, int? jMintValue, List? spendCoinIndexes, int? height, diff --git a/lib/wallets/wallet/impl/salvium_wallet.dart b/lib/wallets/wallet/impl/salvium_wallet.dart index f340776bea..94aef46f86 100644 --- a/lib/wallets/wallet/impl/salvium_wallet.dart +++ b/lib/wallets/wallet/impl/salvium_wallet.dart @@ -1,48 +1,26 @@ import 'dart:async'; -import 'package:compat/compat.dart' as lib_monero_compat; -import 'package:cs_salvium/cs_salvium.dart' as lib_salvium; - import '../../../utilities/amount/amount.dart'; +import '../../../wl_gen/generated/cs_salvium_interface_impl.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../intermediate/lib_salvium_wallet.dart'; class SalviumWallet extends LibSalviumWallet { - SalviumWallet(CryptoCurrencyNetwork network) - : super(Salvium(network)); + SalviumWallet(CryptoCurrencyNetwork network) : super(Salvium(network)); @override Future estimateFeeFor(Amount amount, BigInt feeRate) async { - if (libSalviumWallet == null /*|| - syncStatus is! lib_monero_compat.SyncedSyncStatus*/) { + if (!csSalvium.walletInstanceExists(walletId) /*|| + syncStatus is! lib_monero_compat.SyncedSyncStatus*/ ) { return Amount.zeroWith(fractionDigits: cryptoCurrency.fractionDigits); } - lib_salvium.TransactionPriority priority; - switch (feeRate.toInt()) { - case 1: - priority = lib_salvium.TransactionPriority.low; - break; - case 2: - priority = lib_salvium.TransactionPriority.medium; - break; - case 3: - priority = lib_salvium.TransactionPriority.high; - break; - case 4: - priority = lib_salvium.TransactionPriority.last; - break; - case 0: - default: - priority = lib_salvium.TransactionPriority.normal; - break; - } - int approximateFee = 0; await estimateFeeMutex.protect(() async { - approximateFee = await libSalviumWallet!.estimateFee( - priority, - amount.raw.toInt(), + approximateFee = await csSalvium.estimateFee( + feeRate.toInt(), + amount.raw, + walletId: walletId, ); }); @@ -53,72 +31,58 @@ class SalviumWallet extends LibSalviumWallet { } @override - bool walletExists(String path) => lib_salvium.SalviumWallet.isWalletExist(path); + bool walletExists(String path) => csSalvium.walletExists(path); @override - Future loadWallet({ - required String path, - required String password, - }) async { - libSalviumWallet = await lib_salvium.SalviumWallet.loadWallet( - path: path, - password: password, - ); - } + Future loadWallet({required String path, required String password}) => + csSalvium.loadWallet(walletId, path: path, password: password); @override - Future getCreatedWallet({ + Future getCreatedWallet({ required String path, required String password, required int wordCount, required String seedOffset, - }) async { - final lib_salvium.SalviumSeedType type; - switch (wordCount) { - case 25: - type = lib_salvium.SalviumSeedType.twentyFive; - break; - - default: - throw Exception("Invalid mnemonic word count: $wordCount"); - } - - return await lib_salvium.SalviumWallet.create( - path: path, - password: password, - seedType: type, - seedOffset: seedOffset, - ); - } + required final void Function(int refreshFromBlockHeight, String seed) + onCreated, + }) => csSalvium.getCreatedWallet( + path: path, + password: password, + wordCount: wordCount, + seedOffset: seedOffset, + onCreated: onCreated, + ); @override - Future getRestoredWallet({ + Future getRestoredWallet({ required String path, required String password, required String mnemonic, required String seedOffset, int height = 0, - }) async => await lib_salvium.SalviumWallet.restoreWalletFromSeed( + }) async => await csSalvium.getRestoredWallet( path: path, password: password, - seed: mnemonic, - restoreHeight: height, + mnemonic: mnemonic, + height: height, seedOffset: seedOffset, + walletId: walletId, ); @override - Future getRestoredFromViewKeyWallet({ + Future getRestoredFromViewKeyWallet({ required String path, required String password, required String address, required String privateViewKey, int height = 0, - }) async => lib_salvium.SalviumWallet.createViewOnlyWallet( + }) async => csSalvium.getRestoredFromViewKeyWallet( + walletId: walletId, path: path, password: password, address: address, - viewKey: privateViewKey, - restoreHeight: height, + privateViewKey: privateViewKey, + height: height, ); @override diff --git a/lib/wallets/wallet/intermediate/lib_monero_wallet.dart b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart index 943de13d23..1d8a5da7bd 100644 --- a/lib/wallets/wallet/intermediate/lib_monero_wallet.dart +++ b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart @@ -177,7 +177,7 @@ abstract class LibMoneroWallet !csMonero.hasListeners(walletId)) { csMonero.addListener( walletId, - CsMoneroWalletListener( + CsWalletListener( onSyncingUpdate: onSyncingUpdate, onNewBlock: onNewBlock, onBalancesChanged: onBalancesChanged, diff --git a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart index afc15e547a..784e12c51d 100644 --- a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart +++ b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'dart:io'; import 'dart:math'; -import 'package:cs_salvium/cs_salvium.dart' as lib_salvium; import 'package:isar_community/isar.dart'; import 'package:mutex/mutex.dart'; import 'package:stack_wallet_backup/generate_password.dart'; @@ -32,6 +31,8 @@ import '../../../utilities/amount/amount.dart'; import '../../../utilities/enums/fee_rate_type_enum.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/stack_file_system.dart'; +import '../../../wl_gen/interfaces/cs_monero_interface.dart'; +import '../../../wl_gen/interfaces/cs_salvium_interface.dart'; import '../../crypto_currency/intermediate/cryptonote_currency.dart'; import '../../isar/models/wallet_info.dart'; import '../../models/tx_data.dart'; @@ -97,14 +98,12 @@ abstract class LibSalviumWallet await onUTXOsChanged(utxos); await updateBalance(shouldUpdateUtxos: false); } catch (e, s) { - lib_salvium.Logging.log?.i("_startInit", error: e, stackTrace: s); + Logging.instance.e("_startInit", error: e, stackTrace: s); } }); }); } - lib_salvium.Wallet? libSalviumWallet; - SyncStatus? get syncStatus => _syncStatus; SyncStatus? _syncStatus; int _syncedCount = 0; @@ -134,14 +133,16 @@ abstract class LibSalviumWallet Future loadWallet({required String path, required String password}); - Future getCreatedWallet({ + Future getCreatedWallet({ required String path, required String password, required int wordCount, required String seedOffset, + required final void Function(int refreshFromBlockHeight, String seed) + onCreated, }); - Future getRestoredWallet({ + Future getRestoredWallet({ required String path, required String password, required String mnemonic, @@ -149,7 +150,7 @@ abstract class LibSalviumWallet int height = 0, }); - Future getRestoredFromViewKeyWallet({ + Future getRestoredFromViewKeyWallet({ required String path, required String password, required String address, @@ -162,16 +163,18 @@ abstract class LibSalviumWallet bool walletExists(String path); String getTxKeyFor({required String txid}) { - if (libSalviumWallet == null) { + if (!csSalvium.walletInstanceExists(walletId)) { throw Exception("Cannot get tx key in uninitialized libSalviumWallet"); } - return libSalviumWallet!.getTxKey(txid); + return csSalvium.getTxKey(walletId, txid); } void _setListener() { - if (libSalviumWallet != null && libSalviumWallet!.getListeners().isEmpty) { - libSalviumWallet?.addListener( - lib_salvium.WalletListener( + if (csSalvium.walletInstanceExists(walletId) && + !csSalvium.hasListeners(walletId)) { + csSalvium.addListener( + walletId, + CsWalletListener( onSyncingUpdate: onSyncingUpdate, onNewBlock: onNewBlock, onBalancesChanged: onBalancesChanged, @@ -187,17 +190,16 @@ abstract class LibSalviumWallet Future open() async { bool wasNull = false; - if (libSalviumWallet == null) { + if (!csSalvium.walletInstanceExists(walletId)) { wasNull = true; // await libSalviumWallet?.close(); final path = await pathForWallet(name: walletId); final String password; try { - password = - (await secureStorageInterface.read( - key: _libSalviumWalletPasswordKey(walletId.toUpperCase()), - ))!; + password = (await secureStorageInterface.read( + key: _libSalviumWalletPasswordKey(walletId.toUpperCase()), + ))!; } catch (e, s) { throw Exception("Password not found $e, $s"); } @@ -224,15 +226,15 @@ abstract class LibSalviumWallet if (wasNull) { try { _setSyncStatus(ConnectingSyncStatus()); - libSalviumWallet?.startSyncing(); + csSalvium.startSyncing(walletId); } catch (_) { _setSyncStatus(FailedSyncStatus()); // TODO log } } _setListener(); - libSalviumWallet?.startListeners(); - libSalviumWallet?.startAutoSaving(); + csSalvium.startListeners(walletId); + csSalvium.startAutoSaving(walletId); unawaited(refresh()); } @@ -242,16 +244,17 @@ abstract class LibSalviumWallet final appRoot = await StackFileSystem.applicationRootDirectory(); await _backupWalletFiles(name: walletId, appRoot: appRoot); } - await libSalviumWallet!.save(); + await csSalvium.save(walletId); } Address addressFor({required int index, int account = 0}) { - final address = libSalviumWallet!.getAddress( + final address = csSalvium.getAddress( + walletId, accountIndex: account, addressIndex: index, ); - if (address.value.contains("111")) { + if (address.contains("111")) { throw Exception("111 address found!"); } @@ -259,7 +262,7 @@ abstract class LibSalviumWallet walletId: walletId, derivationIndex: index, derivationPath: null, - value: address.value, + value: address, publicKey: [], type: AddressType.cryptonote, subType: AddressSubType.receiving, @@ -269,18 +272,16 @@ abstract class LibSalviumWallet } Future getKeys() async { - final base = libSalviumWallet; - - if (base == null) { + if (!csSalvium.walletInstanceExists(walletId)) { return null; } try { return CWKeyData( walletId: walletId, - publicViewKey: base.getPublicViewKey(), - privateViewKey: base.getPrivateViewKey(), - publicSpendKey: base.getPublicSpendKey(), - privateSpendKey: base.getPrivateSpendKey(), + publicViewKey: csSalvium.getPublicViewKey(walletId), + privateViewKey: csSalvium.getPrivateViewKey(walletId), + publicSpendKey: csSalvium.getPublicSpendKey(walletId), + privateSpendKey: csSalvium.getPrivateSpendKey(walletId), ); } catch (e, s) { Logging.instance.f("getKeys failed: ", error: e, stackTrace: s); @@ -299,16 +300,17 @@ abstract class LibSalviumWallet final path = await pathForWallet(name: walletId); final String password; try { - password = - (await secureStorageInterface.read( - key: _libSalviumWalletPasswordKey(walletId), - ))!; + password = (await secureStorageInterface.read( + key: _libSalviumWalletPasswordKey(walletId), + ))!; } catch (e, s) { throw Exception("Password not found $e, $s"); } await loadWallet(path: path, password: password); - final wallet = libSalviumWallet!; - return (wallet.getAddress().value, wallet.getPrivateViewKey()); + return ( + csSalvium.getAddress(walletId), + csSalvium.getPrivateViewKey(walletId), + ); } @override @@ -322,17 +324,23 @@ abstract class LibSalviumWallet key: _libSalviumWalletPasswordKey(walletId), value: password, ); - final wallet = await getCreatedWallet( + + late final int refreshFromBlockHeight; + late final String seedPhrase; + + await getCreatedWallet( path: path, password: password, wordCount: wordCount, seedOffset: "", // default for non restored wallets for now + onCreated: (height, seed) { + refreshFromBlockHeight = height; + seedPhrase = seed; + }, ); - final height = wallet.getRefreshFromBlockHeight(); - await info.updateRestoreHeight( - newRestoreHeight: height, + newRestoreHeight: refreshFromBlockHeight, isar: mainDB.isar, ); @@ -340,7 +348,7 @@ abstract class LibSalviumWallet // before wallet.init() is called await secureStorageInterface.write( key: Wallet.mnemonicKey(walletId: walletId), - value: wallet.getSeed().trim(), + value: seedPhrase, ); await secureStorageInterface.write( key: Wallet.mnemonicPassphraseKey(walletId: walletId), @@ -363,8 +371,8 @@ abstract class LibSalviumWallet await mainDB.deleteWalletBlockchainData(walletId); highestPercentCached = 0; - unawaited(libSalviumWallet?.rescanBlockchain()); - libSalviumWallet?.startSyncing(); + unawaited(csSalvium.rescanBlockchain(walletId)); + csSalvium.startSyncing(walletId); // unawaited(save()); }); unawaited(refresh()); @@ -401,7 +409,12 @@ abstract class LibSalviumWallet key: _libSalviumWalletPasswordKey(walletId), value: password, ); - final wallet = await getRestoredWallet( + + if (!csSalvium.walletInstanceExists(walletId)) { + await exit(); + } + + await getRestoredWallet( path: path, password: password, mnemonic: mnemonic, @@ -409,12 +422,6 @@ abstract class LibSalviumWallet seedOffset: seedOffset, ); - if (libSalviumWallet != null) { - await exit(); - } - - libSalviumWallet = wallet; - _setListener(); final newReceivingAddress = @@ -423,7 +430,7 @@ abstract class LibSalviumWallet walletId: walletId, derivationIndex: 0, derivationPath: null, - value: wallet.getAddress().value, + value: csSalvium.getAddress(walletId), publicKey: [], type: AddressType.cryptonote, subType: AddressSubType.receiving, @@ -442,12 +449,12 @@ abstract class LibSalviumWallet _setListener(); // libSalviumWallet?.setRecoveringFromSeed(isRecovery: true); - unawaited(libSalviumWallet?.rescanBlockchain()); - libSalviumWallet?.startSyncing(); + unawaited(csSalvium.rescanBlockchain(walletId)); + csSalvium.startSyncing(walletId); // await save(); - libSalviumWallet?.startListeners(); - libSalviumWallet?.startAutoSaving(); + csSalvium.startListeners(walletId); + csSalvium.startAutoSaving(walletId); } catch (e, s) { Logging.instance.e( "Exception rethrown from recoverFromMnemonic(): ", @@ -464,7 +471,7 @@ abstract class LibSalviumWallet @override Future pingCheck() async { if (_canPing) { - return (await libSalviumWallet?.isConnectedToDaemon()) ?? false; + return csSalvium.isConnectedToDaemon(walletId); } else { return false; } @@ -478,50 +485,50 @@ abstract class LibSalviumWallet throw Exception("TOR – clearnet mismatch"); } - final host = - node.host.endsWith(".onion") ? node.host : Uri.parse(node.host).host; + final host = node.host.endsWith(".onion") + ? node.host + : Uri.parse(node.host).host; ({InternetAddress host, int port})? proxy; - proxy = - prefs.useTor && !node.forceNoTor - ? TorService.sharedInstance.getProxyInfo() - : null; + proxy = prefs.useTor && !node.forceNoTor + ? TorService.sharedInstance.getProxyInfo() + : null; _setSyncStatus(ConnectingSyncStatus()); try { if (_requireMutex) { await _torConnectingLock.protect(() async { - await libSalviumWallet?.connect( + await csSalvium.connect( + walletId, daemonAddress: "$host:${node.port}", daemonUsername: node.loginName, daemonPassword: await node.getPassword(secureStorageInterface), trusted: node.trusted ?? false, useSSL: node.useSSL, - socksProxyAddress: - node.forceNoTor - ? null - : proxy == null - ? null - : "${proxy.host.address}:${proxy.port}", + socksProxyAddress: node.forceNoTor + ? null + : proxy == null + ? null + : "${proxy.host.address}:${proxy.port}", ); }); } else { - await libSalviumWallet?.connect( + await csSalvium.connect( + walletId, daemonAddress: "$host:${node.port}", daemonUsername: node.loginName, daemonPassword: await node.getPassword(secureStorageInterface), trusted: node.trusted ?? false, useSSL: node.useSSL, - socksProxyAddress: - node.forceNoTor - ? null - : proxy == null - ? null - : "${proxy.host.address}:${proxy.port}", + socksProxyAddress: node.forceNoTor + ? null + : proxy == null + ? null + : "${proxy.host.address}:${proxy.port}", ); } - libSalviumWallet?.startSyncing(); - libSalviumWallet?.startListeners(); - libSalviumWallet?.startAutoSaving(); + csSalvium.startSyncing(walletId); + csSalvium.startListeners(walletId); + csSalvium.startAutoSaving(walletId); // _setSyncStatus(ConnectedSyncStatus()); } catch (e, s) { @@ -538,22 +545,19 @@ abstract class LibSalviumWallet @override Future updateTransactions() async { - final base = libSalviumWallet; - - if (base == null) { + if (!csSalvium.walletInstanceExists(walletId)) { return; } - final localTxids = - await mainDB.isar.transactionV2s - .where() - .walletIdEqualTo(walletId) - .filter() - .heightGreaterThan(0) - .txidProperty() - .findAll(); + final localTxids = await mainDB.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) + .filter() + .heightGreaterThan(0) + .txidProperty() + .findAll(); - final allTxids = await base.getAllTxids(refresh: true); + final allTxids = await csSalvium.getAllTxids(walletId, refresh: true); final txidsToFetch = allTxids.toSet().difference(localTxids.toSet()); @@ -561,9 +565,17 @@ abstract class LibSalviumWallet return; } - final transactions = await base.getTxs(txids: txidsToFetch, refresh: false); + final transactions = await csSalvium.getTxs( + walletId, + txids: txidsToFetch, + refresh: false, + ); - final allOutputs = await base.getOutputs(includeSpent: true, refresh: true); + final allOutputs = await csSalvium.getOutputs( + walletId, + includeSpent: true, + refresh: true, + ); // final cachedTransactions = // DB.instance.get(boxName: walletId, key: 'latest_tx_model') @@ -649,16 +661,14 @@ abstract class LibSalviumWallet type: type, subType: TransactionSubType.none, otherData: jsonEncode({ - TxV2OdKeys.overrideFee: - Amount( - rawValue: tx.fee, - fractionDigits: cryptoCurrency.fractionDigits, - ).toJsonString(), - TxV2OdKeys.moneroAmount: - Amount( - rawValue: tx.amount, - fractionDigits: cryptoCurrency.fractionDigits, - ).toJsonString(), + TxV2OdKeys.overrideFee: Amount( + rawValue: tx.fee, + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), + TxV2OdKeys.moneroAmount: Amount( + rawValue: tx.amount, + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), TxV2OdKeys.moneroAccountIndex: tx.accountIndex, TxV2OdKeys.isMoneroTransaction: true, }), @@ -673,7 +683,7 @@ abstract class LibSalviumWallet Future get availableBalance async { try { return Amount( - rawValue: libSalviumWallet!.getUnlockedBalance(), + rawValue: csSalvium.getUnlockedBalance(walletId)!, fractionDigits: cryptoCurrency.fractionDigits, ); } catch (_) { @@ -683,14 +693,14 @@ abstract class LibSalviumWallet Future get totalBalance async { try { - final full = libSalviumWallet?.getBalance(); + final full = csSalvium.getBalance(walletId); if (full != null) { return Amount( rawValue: full, fractionDigits: cryptoCurrency.fractionDigits, ); } else { - final transactions = await libSalviumWallet!.getAllTxs(refresh: true); + final transactions = await csSalvium.getAllTxs(walletId, refresh: true); BigInt transactionBalance = BigInt.zero; for (final tx in transactions) { if (!tx.isSpend) { @@ -713,10 +723,10 @@ abstract class LibSalviumWallet @override Future exit() async { Logging.instance.i("exit called on $walletId"); - libSalviumWallet?.stopAutoSaving(); - libSalviumWallet?.stopListeners(); - libSalviumWallet?.stopSyncing(); - await libSalviumWallet?.save(); + csSalvium.stopAutoSaving(walletId); + csSalvium.stopListeners(walletId); + csSalvium.stopSyncing(walletId); + await csSalvium.save(walletId); } Future pathForWalletDir({required String name}) async { @@ -786,7 +796,7 @@ abstract class LibSalviumWallet final _utxosUpdateLock = Mutex(); Future onUTXOsChanged(List utxos) async { await _utxosUpdateLock.protect(() async { - final cwUtxos = await libSalviumWallet?.getOutputs(refresh: true) ?? []; + final cwUtxos = await csSalvium.getOutputs(walletId, refresh: true); // bool changed = false; @@ -803,12 +813,12 @@ abstract class LibSalviumWallet if (u.isBlocked) { if (!cw.isFrozen) { - await libSalviumWallet?.freezeOutput(cw.keyImage); + await csSalvium.freezeOutput(walletId, cw.keyImage); // changed = true; } } else { if (cw.isFrozen) { - await libSalviumWallet?.thawOutput(cw.keyImage); + await csSalvium.thawOutput(walletId, cw.keyImage); // changed = true; } } @@ -964,9 +974,9 @@ abstract class LibSalviumWallet if (mismatch) { _canPing = false; - libSalviumWallet?.stopAutoSaving(); - libSalviumWallet?.stopListeners(); - libSalviumWallet?.stopSyncing(); + csSalvium.stopAutoSaving(walletId); + csSalvium.stopListeners(walletId); + csSalvium.stopSyncing(walletId); _setSyncStatus(FailedSyncStatus()); } @@ -984,27 +994,23 @@ abstract class LibSalviumWallet @override Future updateUTXOs() async { final List outputArray = []; - final utxos = - await libSalviumWallet?.getOutputs(refresh: true) ?? - []; + final utxos = await csSalvium.getOutputs(walletId, refresh: true); for (final rawUTXO in utxos) { if (!rawUTXO.spent) { - final current = - await mainDB.isar.utxos - .where() - .walletIdEqualTo(walletId) - .filter() - .voutEqualTo(rawUTXO.vout) - .and() - .txidEqualTo(rawUTXO.hash) - .findFirst(); - final tx = - await mainDB.isar.transactionV2s - .where() - .walletIdEqualTo(walletId) - .filter() - .txidEqualTo(rawUTXO.hash) - .findFirst(); + final current = await mainDB.isar.utxos + .where() + .walletIdEqualTo(walletId) + .filter() + .voutEqualTo(rawUTXO.vout) + .and() + .txidEqualTo(rawUTXO.hash) + .findFirst(); + final tx = await mainDB.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) + .filter() + .txidEqualTo(rawUTXO.hash) + .findFirst(); final otherDataMap = { UTXOOtherDataKeys.keyImage: rawUTXO.keyImage, @@ -1077,7 +1083,7 @@ abstract class LibSalviumWallet // Slight possibility of race but should be irrelevant await refreshMutex.acquire(); - libSalviumWallet?.startSyncing(); + csSalvium.startSyncing(walletId); _setSyncStatus(StartingSyncStatus()); await updateTransactions(); @@ -1091,7 +1097,7 @@ abstract class LibSalviumWallet refreshMutex.release(); } - final synced = await libSalviumWallet?.isSynced(); + final synced = await csSalvium.isSynced(walletId); if (synced == true) { _setSyncStatus(SyncedSyncStatus()); @@ -1103,8 +1109,9 @@ abstract class LibSalviumWallet try { final currentReceiving = await getCurrentReceivingAddress(); - final newReceivingIndex = - currentReceiving == null ? 0 : currentReceiving.derivationIndex + 1; + final newReceivingIndex = currentReceiving == null + ? 0 + : currentReceiving.derivationIndex + 1; final newReceivingAddress = addressFor(index: newReceivingIndex); @@ -1139,17 +1146,15 @@ abstract class LibSalviumWallet try { int highestIndex = -1; - final entries = await libSalviumWallet?.getAllTxs(refresh: true); - if (entries != null) { - for (final element in entries) { - if (!element.isSpend) { - final int curAddressIndex = - element.addressIndexes.isEmpty - ? 0 - : element.addressIndexes.reduce(max); - if (curAddressIndex > highestIndex) { - highestIndex = curAddressIndex; - } + final entries = await csSalvium.getAllTxs(walletId, refresh: true); + + for (final element in entries) { + if (!element.isSpend) { + final int curAddressIndex = element.addressIndexes.isEmpty + ? 0 + : element.addressIndexes.reduce(max); + if (curAddressIndex > highestIndex) { + highestIndex = curAddressIndex; } } } @@ -1165,12 +1170,11 @@ abstract class LibSalviumWallet // Use new index to derive a new receiving address final newReceivingAddress = addressFor(index: newReceivingIndex); - final existing = - await mainDB - .getAddresses(walletId) - .filter() - .valueEqualTo(newReceivingAddress.value) - .findFirst(); + final existing = await mainDB + .getAddresses(walletId) + .filter() + .valueEqualTo(newReceivingAddress.value) + .findFirst(); if (existing == null) { // Add that new change address await mainDB.putAddress(newReceivingAddress); @@ -1206,9 +1210,9 @@ abstract class LibSalviumWallet numberOfBlocksFast: 10, numberOfBlocksAverage: 15, numberOfBlocksSlow: 20, - fast: BigInt.from(lib_salvium.TransactionPriority.high.value), - medium: BigInt.from(lib_salvium.TransactionPriority.medium.value), - slow: BigInt.from(lib_salvium.TransactionPriority.normal.value), + fast: BigInt.from(csSalvium.getTxPriorityHigh()), + medium: BigInt.from(csSalvium.getTxPriorityMedium()), + slow: BigInt.from(csSalvium.getTxPriorityNormal()), ); @override @@ -1234,16 +1238,16 @@ abstract class LibSalviumWallet try { final feeRate = txData.feeRateType; if (feeRate is FeeRateType) { - lib_salvium.TransactionPriority feePriority; + final int feePriority; switch (feeRate) { case FeeRateType.fast: - feePriority = lib_salvium.TransactionPriority.high; + feePriority = csSalvium.getTxPriorityHigh(); break; case FeeRateType.average: - feePriority = lib_salvium.TransactionPriority.medium; + feePriority = csSalvium.getTxPriorityMedium(); break; case FeeRateType.slow: - feePriority = lib_salvium.TransactionPriority.normal; + feePriority = csSalvium.getTxPriorityNormal(); break; default: throw ArgumentError("Invalid use of custom fee"); @@ -1268,12 +1272,9 @@ abstract class LibSalviumWallet throw Exception("Send all not supported with multiple recipients"); } - final List outputs = []; + final List outputs = []; for (final recipient in txData.recipients!) { - final output = lib_salvium.Recipient( - address: recipient.address, - amount: recipient.amount.raw, - ); + final output = CsRecipient(recipient.address, recipient.amount.raw); outputs.add(output); } @@ -1283,44 +1284,27 @@ abstract class LibSalviumWallet } final height = await chainHeight; - final inputs = - txData.utxos - ?.whereType() - .map( - (e) => lib_salvium.Output( - address: e.utxo.address!, - hash: e.utxo.txid, - keyImage: e.utxo.keyImage!, - value: e.value, - isFrozen: e.utxo.isBlocked, - isUnlocked: - e.utxo.blockHeight != null && - (height - (e.utxo.blockHeight ?? 0)) >= - cryptoCurrency.minConfirms, - height: e.utxo.blockHeight ?? 0, - vout: e.utxo.vout, - spent: e.utxo.used ?? false, - spentHeight: null, // doesn't matter here - coinbase: e.utxo.isCoinbase, - ), - ) - .toList(); + final inputs = txData.utxos?.whereType().toList(); return await prepareSendMutex.protect(() async { - final lib_salvium.PendingTransaction pendingTransaction; + final CsPendingTransaction pendingTransaction; if (outputs.length == 1) { - pendingTransaction = await libSalviumWallet!.createTx( + pendingTransaction = await csSalvium.createTx( + walletId, output: outputs.first, - paymentId: "", + minConfirms: cryptoCurrency.minConfirms, + currentHeight: height, sweep: sweep, priority: feePriority, preferredInputs: inputs, accountIndex: 0, // sw only uses account 0 at this time ); } else { - pendingTransaction = await libSalviumWallet!.createTxMultiDest( + pendingTransaction = await csSalvium.createTxMultiDest( + walletId, outputs: outputs, - paymentId: "", + minConfirms: cryptoCurrency.minConfirms, + currentHeight: height, priority: feePriority, preferredInputs: inputs, sweep: sweep, @@ -1363,7 +1347,7 @@ abstract class LibSalviumWallet Future confirmSend({required TxData txData}) async { try { try { - await libSalviumWallet!.commitTx(txData.pendingSalviumTransaction!); + await csSalvium.commitTx(walletId, txData.pendingSalviumTransaction!); Logging.instance.d( "transaction ${txData.pendingSalviumTransaction!.txid} has been sent", @@ -1412,7 +1396,12 @@ abstract class LibSalviumWallet key: _libSalviumWalletPasswordKey(walletId.toUpperCase()), value: password, ); - final wallet = await getRestoredFromViewKeyWallet( + + if (csSalvium.walletInstanceExists(walletId)) { + await exit(); + } + + await getRestoredFromViewKeyWallet( path: path, password: password, address: data.address, @@ -1420,12 +1409,6 @@ abstract class LibSalviumWallet height: height, ); - if (libSalviumWallet != null) { - await exit(); - } - - libSalviumWallet = wallet; - _setListener(); final newReceivingAddress = @@ -1434,7 +1417,7 @@ abstract class LibSalviumWallet walletId: walletId, derivationIndex: 0, derivationPath: null, - value: wallet.getAddress().value, + value: csSalvium.getAddress(walletId), publicKey: [], type: AddressType.cryptonote, subType: AddressSubType.receiving, @@ -1449,12 +1432,12 @@ abstract class LibSalviumWallet await updateNode(); _setListener(); - unawaited(libSalviumWallet?.rescanBlockchain()); - libSalviumWallet?.startSyncing(); + unawaited(csSalvium.rescanBlockchain(walletId)); + csSalvium.startSyncing(walletId); // await save(); - libSalviumWallet?.startListeners(); - libSalviumWallet?.startAutoSaving(); + csSalvium.startListeners(walletId); + csSalvium.startAutoSaving(walletId); } catch (e, s) { Logging.instance.e( "Exception rethrown from recoverViewOnly(): ", diff --git a/lib/wl_gen/interfaces/cs_monero_interface.dart b/lib/wl_gen/interfaces/cs_monero_interface.dart index 7622962396..9ed2bb4163 100644 --- a/lib/wl_gen/interfaces/cs_monero_interface.dart +++ b/lib/wl_gen/interfaces/cs_monero_interface.dart @@ -76,7 +76,7 @@ abstract class CsMoneroInterface { void stopAutoSaving(String walletId); bool hasListeners(String walletId); - void addListener(String walletId, CsMoneroWalletListener listener); + void addListener(String walletId, CsWalletListener listener); void startListeners(String walletId); void stopListeners(String walletId); @@ -148,13 +148,17 @@ abstract class CsMoneroInterface { List getMoneroWordList(String language); List getWowneroWordList(String language, int seedLength); + + int getHeightByDate(DateTime date, {required CsCoin csCoin}); + + bool validateAddress(String address, int network, {required CsCoin csCoin}); } enum CsCoin { monero, wownero } // forwarding class -final class CsMoneroWalletListener { - CsMoneroWalletListener({ +final class CsWalletListener { + CsWalletListener({ this.onSyncingUpdate, this.onNewBlock, this.onBalancesChanged, diff --git a/lib/wl_gen/interfaces/cs_salvium_interface.dart b/lib/wl_gen/interfaces/cs_salvium_interface.dart new file mode 100644 index 0000000000..108ff166b2 --- /dev/null +++ b/lib/wl_gen/interfaces/cs_salvium_interface.dart @@ -0,0 +1,154 @@ +import '../../models/input.dart'; +import 'cs_monero_interface.dart'; + +export '../generated/cs_salvium_interface_impl.dart'; + +abstract class CsSalviumInterface { + void setUseCsSalviumLoggerInternal(bool enable); + + // tx prio forwarding + int getTxPriorityHigh(); + int getTxPriorityMedium(); + int getTxPriorityNormal(); + + bool walletInstanceExists(String walletId); + + bool walletExists(String path); + + Future estimateFee(int rate, BigInt amount, {required String walletId}); + + Future loadWallet( + String walletId, { + + required String path, + required String password, + }); + + String getAddress( + String walletId, { + int accountIndex = 0, + int addressIndex = 0, + }); + + Future getCreatedWallet({ + required String path, + required String password, + required int wordCount, + required String seedOffset, + required final void Function(int refreshFromBlockHeight, String seed) + onCreated, + }); + + Future getRestoredWallet({ + required String walletId, + + required String path, + required String password, + required String mnemonic, + required String seedOffset, + int height = 0, + }); + + Future getRestoredFromViewKeyWallet({ + required String walletId, + + required String path, + required String password, + required String address, + required String privateViewKey, + int height = 0, + }); + + String getTxKey(String walletId, String txid); + + Future save(String walletId); + + String getPublicViewKey(String walletId); + String getPrivateViewKey(String walletId); + String getPublicSpendKey(String walletId); + String getPrivateSpendKey(String walletId); + + Future isSynced(String walletId); + void startSyncing(String walletId); + void stopSyncing(String walletId); + + void startAutoSaving(String walletId); + void stopAutoSaving(String walletId); + + bool hasListeners(String walletId); + void addListener(String walletId, CsWalletListener listener); + void startListeners(String walletId); + void stopListeners(String walletId); + + Future rescanBlockchain(String walletId); + Future isConnectedToDaemon(String walletId); + + int getRefreshFromBlockHeight(String walletId); + void setRefreshFromBlockHeight(String walletId, int height); + + Future connect( + String walletId, { + required String daemonAddress, + required bool trusted, + String? daemonUsername, + String? daemonPassword, + bool useSSL = false, + bool isLightWallet = false, + String? socksProxyAddress, + }); + + Future> getAllTxids(String walletId, {bool refresh = false}); + + BigInt? getBalance(String walletId, {int accountIndex = 0}); + BigInt? getUnlockedBalance(String walletId, {int accountIndex = 0}); + + Future> getAllTxs( + String walletId, { + bool refresh = false, + }); + + Future> getTxs( + String walletId, { + required Set txids, + bool refresh = false, + }); + + Future createTx( + String walletId, { + required CsRecipient output, + required int priority, + required bool sweep, + List? preferredInputs, + required int accountIndex, + required int minConfirms, + required int currentHeight, + }); + + Future createTxMultiDest( + String walletId, { + required List outputs, + required int priority, + required bool sweep, + List? preferredInputs, + required int accountIndex, + required int minConfirms, + required int currentHeight, + }); + + Future commitTx(String walletId, CsPendingTransaction tx); + + Future> getOutputs( + String walletId, { + bool refresh = false, + bool includeSpent = false, + }); + + Future freezeOutput(String walletId, String keyImage); + Future thawOutput(String walletId, String keyImage); + + List getSalviumWordList(String language); + + int getHeightByDate(DateTime date); + + bool validateAddress(String address, int network); +} diff --git a/tool/wl_templates/SAL_cs_salvium_interface_impl.template.dart b/tool/wl_templates/SAL_cs_salvium_interface_impl.template.dart new file mode 100644 index 0000000000..6d56cb075e --- /dev/null +++ b/tool/wl_templates/SAL_cs_salvium_interface_impl.template.dart @@ -0,0 +1,488 @@ +//ON +import 'package:cs_salvium/cs_salvium.dart' as lib_salvium; +import 'package:cs_salvium/src/deprecated/get_height_by_date.dart' + as cs_salvium_deprecated; +import 'package:cs_salvium/src/ffi_bindings/salvium_wallet_bindings.dart' + as sal_wallet_ffi; + +//END_ON +import '../../models/input.dart'; +import '../interfaces/cs_monero_interface.dart'; +import '../interfaces/cs_salvium_interface.dart'; + +CsSalviumInterface get csSalvium => _getInterface(); + +//OFF +CsSalviumInterface _getInterface() => throw Exception("XMR/WOW not enabled!"); + +//END_OFF +//ON +CsSalviumInterface _getInterface() => _CsSalviumInterfaceImpl(); + +class _CsSalviumInterfaceImpl extends CsSalviumInterface { + final Map _wallets = {}; + + @override + void setUseCsSalviumLoggerInternal(bool enable) => + lib_salvium.Logging.useLogger = enable; + + @override + bool walletInstanceExists(String walletId) => _wallets[walletId] != null; + + @override + bool walletExists(String path) => + lib_salvium.SalviumWallet.isWalletExist(path); + + @override + Future estimateFee(int rate, BigInt amount, {required String walletId}) { + lib_salvium.TransactionPriority priority; + switch (rate) { + case 1: + priority = lib_salvium.TransactionPriority.low; + break; + case 2: + priority = lib_salvium.TransactionPriority.medium; + break; + case 3: + priority = lib_salvium.TransactionPriority.high; + break; + case 4: + priority = lib_salvium.TransactionPriority.last; + break; + case 0: + default: + priority = lib_salvium.TransactionPriority.normal; + break; + } + + return _wallets[walletId]!.estimateFee(priority, amount.toInt()); + } + + @override + Future loadWallet( + String walletId, { + required String path, + required String password, + }) async { + _wallets[walletId] = await lib_salvium.SalviumWallet.loadWallet( + path: path, + password: password, + ); + } + + @override + int getTxPriorityHigh() => lib_salvium.TransactionPriority.high.value; + + @override + int getTxPriorityMedium() => lib_salvium.TransactionPriority.medium.value; + + @override + int getTxPriorityNormal() => lib_salvium.TransactionPriority.normal.value; + + @override + String getAddress( + String walletId, { + int accountIndex = 0, + int addressIndex = 0, + }) => _wallets[walletId]! + .getAddress(accountIndex: accountIndex, addressIndex: addressIndex) + .value; + + @override + Future getCreatedWallet({ + required String path, + required String password, + required int wordCount, + required String seedOffset, + required final void Function(int refreshFromBlockHeight, String seed) + onCreated, + }) async { + final type = switch (wordCount) { + 16 => lib_salvium.SalviumSeedType.sixteen, + 25 => lib_salvium.SalviumSeedType.twentyFive, + _ => throw Exception("Invalid mnemonic word count: $wordCount"), + }; + + final wallet = await lib_salvium.SalviumWallet.create( + path: path, + password: password, + seedType: type, + seedOffset: seedOffset, + ); + + onCreated( + wallet.getRefreshFromBlockHeight(), + wallet.getSeed(seedOffset: seedOffset).trim(), + ); + } + + @override + Future getRestoredWallet({ + required String walletId, + required String path, + required String password, + required String mnemonic, + required String seedOffset, + int height = 0, + }) async { + _wallets[walletId] = await lib_salvium.SalviumWallet.restoreWalletFromSeed( + path: path, + password: password, + seed: mnemonic, + restoreHeight: height, + seedOffset: seedOffset, + ); + } + + @override + Future getRestoredFromViewKeyWallet({ + required String walletId, + required String path, + required String password, + required String address, + required String privateViewKey, + int height = 0, + }) async { + _wallets[walletId] = await lib_salvium.SalviumWallet.createViewOnlyWallet( + path: path, + password: password, + address: address, + viewKey: privateViewKey, + restoreHeight: height, + ); + } + + @override + String getTxKey(String walletId, String txid) => + _wallets[walletId]!.getTxKey(txid); + + @override + Future save(String walletId) => _wallets[walletId]!.save(); + + @override + String getPublicViewKey(String walletId) => + _wallets[walletId]!.getPublicViewKey(); + + @override + String getPrivateViewKey(String walletId) => + _wallets[walletId]!.getPrivateViewKey(); + + @override + String getPublicSpendKey(String walletId) => + _wallets[walletId]!.getPublicSpendKey(); + + @override + String getPrivateSpendKey(String walletId) => + _wallets[walletId]!.getPrivateSpendKey(); + + @override + Future isSynced(String walletId) => + _wallets[walletId]?.isSynced() ?? Future.value(false); + + @override + void startSyncing(String walletId) => _wallets[walletId]?.startSyncing(); + + @override + void stopSyncing(String walletId) => _wallets[walletId]?.stopSyncing(); + + @override + void startAutoSaving(String walletId) => + _wallets[walletId]?.startAutoSaving(); + + @override + void stopAutoSaving(String walletId) => _wallets[walletId]?.stopAutoSaving(); + + @override + bool hasListeners(String walletId) => + _wallets[walletId]!.getListeners().isNotEmpty; + + @override + void addListener(String walletId, CsWalletListener listener) => + _wallets[walletId]?.addListener( + lib_salvium.WalletListener( + onSyncingUpdate: listener.onSyncingUpdate, + onNewBlock: listener.onNewBlock, + onBalancesChanged: listener.onBalancesChanged, + onError: listener.onError, + ), + ); + + @override + void startListeners(String walletId) => _wallets[walletId]?.startListeners(); + + @override + void stopListeners(String walletId) => _wallets[walletId]?.stopListeners(); + + @override + int getRefreshFromBlockHeight(String walletId) => + _wallets[walletId]!.getRefreshFromBlockHeight(); + + @override + void setRefreshFromBlockHeight(String walletId, int height) => + _wallets[walletId]!.setRefreshFromBlockHeight(height); + + @override + Future rescanBlockchain(String walletId) => + _wallets[walletId]?.rescanBlockchain() ?? Future.value(false); + + @override + Future isConnectedToDaemon(String walletId) => + _wallets[walletId]?.isConnectedToDaemon() ?? Future.value(false); + + @override + Future connect( + String walletId, { + required String daemonAddress, + required bool trusted, + String? daemonUsername, + String? daemonPassword, + bool useSSL = false, + bool isLightWallet = false, + String? socksProxyAddress, + }) async { + await _wallets[walletId]?.connect( + daemonAddress: daemonAddress, + trusted: trusted, + daemonUsername: daemonUsername, + daemonPassword: daemonPassword, + useSSL: useSSL, + socksProxyAddress: socksProxyAddress, + isLightWallet: isLightWallet, + ); + } + + @override + Future> getAllTxids(String walletId, {bool refresh = false}) => + _wallets[walletId]!.getAllTxids(refresh: refresh); + + @override + BigInt? getBalance(String walletId, {int accountIndex = 0}) => + _wallets[walletId]?.getBalance(accountIndex: accountIndex); + + @override + BigInt? getUnlockedBalance(String walletId, {int accountIndex = 0}) => + _wallets[walletId]?.getUnlockedBalance(accountIndex: accountIndex); + + @override + Future> getAllTxs( + String walletId, { + bool refresh = false, + }) async { + final transactions = await _wallets[walletId]?.getAllTxs(refresh: refresh); + if (transactions == null) return []; + return transactions + .map( + (e) => CsTransaction( + displayLabel: e.displayLabel, + description: e.description, + fee: e.fee, + confirmations: e.confirmations, + blockHeight: e.blockHeight, + accountIndex: e.accountIndex, + addressIndexes: e.addressIndexes, + paymentId: e.paymentId, + amount: e.amount, + isSpend: e.isSpend, + hash: e.hash, + key: e.key, + timeStamp: e.timeStamp, + minConfirms: e.minConfirms.value, + ), + ) + .toList(); + } + + @override + Future> getTxs( + String walletId, { + required Set txids, + bool refresh = false, + }) async { + final transactions = await _wallets[walletId]?.getTxs( + txids: txids, + refresh: refresh, + ); + if (transactions == null) return []; + return transactions + .map( + (e) => CsTransaction( + displayLabel: e.displayLabel, + description: e.description, + fee: e.fee, + confirmations: e.confirmations, + blockHeight: e.blockHeight, + accountIndex: e.accountIndex, + addressIndexes: e.addressIndexes, + paymentId: e.paymentId, + amount: e.amount, + isSpend: e.isSpend, + hash: e.hash, + key: e.key, + timeStamp: e.timeStamp, + minConfirms: e.minConfirms.value, + ), + ) + .toList(); + } + + @override + Future createTx( + String walletId, { + required CsRecipient output, + required int priority, + required bool sweep, + List? preferredInputs, + required int accountIndex, + required int minConfirms, + required int currentHeight, + }) async { + final pending = await _wallets[walletId]!.createTx( + output: lib_salvium.Recipient( + address: output.address, + amount: output.amount, + ), + paymentId: "", + sweep: sweep, + priority: lib_salvium.TransactionPriority.values.firstWhere( + (e) => e.value == priority, + ), + preferredInputs: preferredInputs + ?.map( + (e) => lib_salvium.Output( + address: e.address!, + hash: e.utxo.txid, + keyImage: e.utxo.keyImage!, + value: e.value, + isFrozen: e.utxo.isBlocked, + isUnlocked: + e.utxo.blockHeight != null && + (currentHeight - (e.utxo.blockHeight ?? 0)) >= minConfirms, + height: e.utxo.blockHeight ?? 0, + vout: e.utxo.vout, + spent: e.utxo.used ?? false, + spentHeight: null, // doesn't matter here + coinbase: e.utxo.isCoinbase, + ), + ) + .toList(), + accountIndex: accountIndex, + ); + + return CsPendingTransaction( + pending, + pending.amount, + pending.fee, + pending.txid, + ); + } + + @override + Future createTxMultiDest( + String walletId, { + required List outputs, + required int priority, + required bool sweep, + List? preferredInputs, + required int accountIndex, + required int minConfirms, + required int currentHeight, + }) async { + final pending = await _wallets[walletId]!.createTxMultiDest( + outputs: outputs + .map( + (e) => lib_salvium.Recipient(address: e.address, amount: e.amount), + ) + .toList(), + paymentId: "", + sweep: sweep, + priority: lib_salvium.TransactionPriority.values.firstWhere( + (e) => e.value == priority, + ), + preferredInputs: preferredInputs + ?.map( + (e) => lib_salvium.Output( + address: e.address!, + hash: e.utxo.txid, + keyImage: e.utxo.keyImage!, + value: e.value, + isFrozen: e.utxo.isBlocked, + isUnlocked: + e.utxo.blockHeight != null && + (currentHeight - (e.utxo.blockHeight ?? 0)) >= minConfirms, + height: e.utxo.blockHeight ?? 0, + vout: e.utxo.vout, + spent: e.utxo.used ?? false, + spentHeight: null, // doesn't matter here + coinbase: e.utxo.isCoinbase, + ), + ) + .toList(), + accountIndex: accountIndex, + ); + + return CsPendingTransaction( + pending, + pending.amount, + pending.fee, + pending.txid, + ); + } + + @override + Future commitTx(String walletId, CsPendingTransaction tx) => + _wallets[walletId]!.commitTx(tx.value as lib_salvium.PendingTransaction); + + @override + Future> getOutputs( + String walletId, { + bool refresh = false, + bool includeSpent = false, + }) async { + final outputs = await _wallets[walletId]?.getOutputs( + includeSpent: includeSpent, + refresh: refresh, + ); + + if (outputs == null) return []; + + return outputs + .map( + (e) => CsOutput( + address: e.address, + hash: e.hash, + keyImage: e.keyImage, + value: e.value, + isFrozen: e.isFrozen, + isUnlocked: e.isUnlocked, + height: e.height, + spentHeight: e.spentHeight, + vout: e.vout, + spent: e.spent, + coinbase: e.coinbase, + ), + ) + .toList(); + } + + @override + Future freezeOutput(String walletId, String keyImage) => + _wallets[walletId]!.freezeOutput(keyImage); + + @override + Future thawOutput(String walletId, String keyImage) => + _wallets[walletId]!.thawOutput(keyImage); + + @override + List getSalviumWordList(String language) => + lib_salvium.getSalviumWordList(language); + + @override + int getHeightByDate(DateTime date) => + cs_salvium_deprecated.getSalviumHeightByDate(date: date); + + @override + bool validateAddress(String address, int network) => + sal_wallet_ffi.validateAddress(address, network); +} + +//END_ON diff --git a/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart b/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart index 5d6fbfc35c..3ca3909e74 100644 --- a/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart +++ b/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart @@ -1,5 +1,11 @@ //ON import 'package:cs_monero/cs_monero.dart' as lib_monero; +import 'package:cs_monero/src/deprecated/get_height_by_date.dart' + as cs_monero_deprecated; +import 'package:cs_monero/src/ffi_bindings/monero_wallet_bindings.dart' + as xmr_wallet_ffi; +import 'package:cs_monero/src/ffi_bindings/wownero_wallet_bindings.dart' + as wow_wallet_ffi; //END_ON import '../../models/input.dart'; @@ -250,7 +256,7 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface { _wallets[walletId]!.getListeners().isNotEmpty; @override - void addListener(String walletId, CsMoneroWalletListener listener) => + void addListener(String walletId, CsWalletListener listener) => _wallets[walletId]?.addListener( lib_monero.WalletListener( onSyncingUpdate: listener.onSyncingUpdate, @@ -532,6 +538,22 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface { @override List getWowneroWordList(String language, int seedLength) => lib_monero.getWowneroWordList(language, seedWordsLength: seedLength); + + @override + int getHeightByDate(DateTime date, {required CsCoin csCoin}) => + switch (csCoin) { + CsCoin.monero => cs_monero_deprecated.getMoneroHeightByDate(date: date), + CsCoin.wownero => cs_monero_deprecated.getWowneroHeightByDate( + date: date, + ), + }; + + @override + bool validateAddress(String address, int network, {required CsCoin csCoin}) => + switch (csCoin) { + CsCoin.monero => xmr_wallet_ffi.validateAddress(address, network), + CsCoin.wownero => wow_wallet_ffi.validateAddress(address, network), + }; } //END_ON From fef43fb0a2fb224cd3c0e9462cc2f1903b8ea48d Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 6 Oct 2025 10:18:56 -0600 Subject: [PATCH 09/30] mwc git version --- lib/utilities/git_status.dart | 5 ++--- lib/wl_gen/interfaces/libmwc_interface.dart | 2 ++ tool/wl_templates/MWC_libmwc_interface_impl.template.dart | 5 ++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/utilities/git_status.dart b/lib/utilities/git_status.dart index 2b19e334e6..9efade7c9a 100644 --- a/lib/utilities/git_status.dart +++ b/lib/utilities/git_status.dart @@ -2,13 +2,13 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_libepiccash/git_versions.dart' as epic_versions; -import 'package:flutter_libmwc/git_versions.dart' as mimblewimblecoin_versions; import 'package:http/http.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/text_styles.dart'; import '../app_config.dart'; +import '../wl_gen/interfaces/libmwc_interface.dart'; const kGithubAPI = "https://api.github.com"; const kGithubSearch = "/search/commits"; @@ -19,8 +19,7 @@ enum CommitStatus { isHead, isOldCommit, notACommit, notLoaded } abstract class GitStatus { static String get epicCashCommit => epic_versions.getPluginVersion(); // static String get moneroCommit => monero_versions.getPluginVersion(); - static String get mimblewimblecoinCommit => - mimblewimblecoin_versions.getPluginVersion(); + static String get mimblewimblecoinCommit => libMwc.getPluginVersion(); static String get appCommitHash => AppConfig.commitHash; diff --git a/lib/wl_gen/interfaces/libmwc_interface.dart b/lib/wl_gen/interfaces/libmwc_interface.dart index 60536cfb96..acd5b2d795 100644 --- a/lib/wl_gen/interfaces/libmwc_interface.dart +++ b/lib/wl_gen/interfaces/libmwc_interface.dart @@ -139,6 +139,8 @@ abstract class LibMwcInterface { Future getChainHeight({required String config}); Future deleteWallet({required String wallet, required String config}); + + String getPluginVersion(); } class MwcTransaction { diff --git a/tool/wl_templates/MWC_libmwc_interface_impl.template.dart b/tool/wl_templates/MWC_libmwc_interface_impl.template.dart index 356c071ec9..0262cc5e35 100644 --- a/tool/wl_templates/MWC_libmwc_interface_impl.template.dart +++ b/tool/wl_templates/MWC_libmwc_interface_impl.template.dart @@ -1,4 +1,5 @@ //ON +import 'package:flutter_libmwc/git_versions.dart' as mimblewimblecoin_versions; import 'package:flutter_libmwc/lib.dart' as mimblewimblecoin; import 'package:flutter_libmwc/models/transaction.dart' as mimblewimblecoin_models; @@ -12,7 +13,6 @@ LibMwcInterface get libMwc => _getLibMwc(); LibMwcInterface _getLibMwc() => throw Exception("MWC not enabled!"); //END_OFF - //ON LibMwcInterface _getLibMwc() => const _LibMwcInterfaceImpl(); @@ -343,6 +343,9 @@ final class _LibMwcInterfaceImpl extends LibMwcInterface { bool validateSendAddress({required String address}) { return mimblewimblecoin.Libmwc.validateSendAddress(address: address); } + + @override + String getPluginVersion() => mimblewimblecoin_versions.getPluginVersion(); } //END_ON From 490a8fbd5fe41bf99b14f17f289ee3252e5c7b64 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 6 Oct 2025 10:21:23 -0600 Subject: [PATCH 10/30] implement epiccash optional ffi package import --- .../send_view/confirm_transaction_view.dart | 658 +++++++++--------- lib/utilities/git_status.dart | 4 +- .../crypto_currency/coins/epiccash.dart | 12 +- lib/wallets/wallet/impl/epiccash_wallet.dart | 95 ++- .../interfaces/libepiccash_interface.dart | 160 +++++ ...C_libepiccash_interface_impl.template.dart | 274 ++++++++ 6 files changed, 800 insertions(+), 403 deletions(-) create mode 100644 lib/wl_gen/interfaces/libepiccash_interface.dart create mode 100644 tool/wl_templates/EPIC_libepiccash_interface_impl.template.dart diff --git a/lib/pages/send_view/confirm_transaction_view.dart b/lib/pages/send_view/confirm_transaction_view.dart index 93a6f052ad..5cb12e02a6 100644 --- a/lib/pages/send_view/confirm_transaction_view.dart +++ b/lib/pages/send_view/confirm_transaction_view.dart @@ -14,7 +14,6 @@ import 'dart:io'; import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_libepiccash/lib.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; @@ -55,6 +54,7 @@ import '../../widgets/rounded_white_container.dart'; import '../../widgets/stack_dialog.dart'; import '../../widgets/stack_text_field.dart'; import '../../widgets/textfield_icon_button.dart'; +import '../../wl_gen/interfaces/libepiccash_interface.dart'; import '../pinpad_views/lock_screen_view.dart'; import '../wallet_view/wallet_view.dart'; import 'sub_widgets/mwc_slatepack_dialog.dart'; @@ -121,16 +121,15 @@ class _ConfirmTransactionViewState // Create slatepack. final slatepackResult = await wallet.createSlatepack( amount: recipient.amount, - recipientAddress: - recipient.address.isNotEmpty ? recipient.address : null, - message: - onChainNoteController.text.isNotEmpty - ? onChainNoteController.text - : null, - encrypt: - recipient - .address - .isNotEmpty, // Encrypt if we have a recipient address. + recipientAddress: recipient.address.isNotEmpty + ? recipient.address + : null, + message: onChainNoteController.text.isNotEmpty + ? onChainNoteController.text + : null, + encrypt: recipient + .address + .isNotEmpty, // Encrypt if we have a recipient address. ); if (!slatepackResult.success || slatepackResult.slatepack == null) { @@ -142,8 +141,8 @@ class _ConfirmTransactionViewState await showDialog( context: context, barrierDismissible: false, - builder: - (context) => MwcSlatepackDialog(slatepackResult: slatepackResult), + builder: (context) => + MwcSlatepackDialog(slatepackResult: slatepackResult), ); // After slatepack dialog is closed, navigate back to wallet. @@ -171,17 +170,16 @@ class _ConfirmTransactionViewState await showDialog( context: context, - builder: - (context) => AlertDialog( - title: const Text('Slatepack Creation Failed'), - content: Text('Failed to create slatepack: $e'), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: const Text('OK'), - ), - ], + builder: (context) => AlertDialog( + title: const Text('Slatepack Creation Failed'), + content: Text('Failed to create slatepack: $e'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('OK'), ), + ], + ), ); } } @@ -245,10 +243,9 @@ class _ConfirmTransactionViewState } else { if (coin is Mimblewimblecoin) { // Check if this is a slatepack transaction (manual exchange). - final otherDataMap = - widget.txData.otherData != null - ? jsonDecode(widget.txData.otherData!) - : null; + final otherDataMap = widget.txData.otherData != null + ? jsonDecode(widget.txData.otherData!) + : null; final transactionMethod = otherDataMap?['transactionMethod'] as String?; @@ -321,7 +318,7 @@ class _ConfirmTransactionViewState widget.onSuccessInsteadOfRouteOnSuccess!.call(); } } - } on BadEpicHttpAddressException catch (_) { + } on BadHttpAddressException catch (_) { if (context.mounted) { // pop building dialog Navigator.of(context).pop(); @@ -394,10 +391,9 @@ class _ConfirmTransactionViewState child: Text( "Ok", style: STextStyles.button(context).copyWith( - color: - Theme.of( - context, - ).extension()!.accentColorDark, + color: Theme.of( + context, + ).extension()!.accentColorDark, ), ), onPressed: () { @@ -492,81 +488,76 @@ class _ConfirmTransactionViewState return ConditionalParent( condition: !isDesktop, - builder: - (child) => Background( - child: Scaffold( - backgroundColor: - Theme.of(context).extension()!.background, - appBar: AppBar( - backgroundColor: - Theme.of(context).extension()!.background, - leading: AppBarBackButton( - onPressed: () async { - // if (FocusScope.of(context).hasFocus) { - // FocusScope.of(context).unfocus(); - // await Future.delayed(Duration(milliseconds: 50)); - // } - Navigator.of(context).pop(); - }, - ), - title: Text( - "Confirm transaction", - style: STextStyles.navBarTitle(context), - ), - ), - body: SafeArea( - child: LayoutBuilder( - builder: (builderContext, constraints) { - return Padding( - padding: const EdgeInsets.only( - left: 12, - top: 12, - right: 12, + builder: (child) => Background( + child: Scaffold( + backgroundColor: Theme.of( + context, + ).extension()!.background, + appBar: AppBar( + backgroundColor: Theme.of( + context, + ).extension()!.background, + leading: AppBarBackButton( + onPressed: () async { + // if (FocusScope.of(context).hasFocus) { + // FocusScope.of(context).unfocus(); + // await Future.delayed(Duration(milliseconds: 50)); + // } + Navigator.of(context).pop(); + }, + ), + title: Text( + "Confirm transaction", + style: STextStyles.navBarTitle(context), + ), + ), + body: SafeArea( + child: LayoutBuilder( + builder: (builderContext, constraints) { + return Padding( + padding: const EdgeInsets.only(left: 12, top: 12, right: 12), + child: SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight - 24, ), - child: SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: constraints.maxHeight - 24, - ), - child: IntrinsicHeight( - child: Padding( - padding: const EdgeInsets.all(4), - child: child, - ), - ), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.all(4), + child: child, ), ), - ); - }, - ), - ), + ), + ), + ); + }, ), ), + ), + ), child: ConditionalParent( condition: isDesktop, - builder: - (child) => Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisSize: MainAxisSize.min, + builder: (child) => Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + Row( children: [ - Row( - children: [ - AppBarBackButton( - size: 40, - iconSize: 24, - onPressed: - () => - Navigator.of(context, rootNavigator: true).pop(), - ), - Text( - "Confirm $unit transaction", - style: STextStyles.desktopH3(context), - ), - ], + AppBarBackButton( + size: 40, + iconSize: 24, + onPressed: () => + Navigator.of(context, rootNavigator: true).pop(), + ), + Text( + "Confirm $unit transaction", + style: STextStyles.desktopH3(context), ), - Flexible(child: SingleChildScrollView(child: child)), ], ), + Flexible(child: SingleChildScrollView(child: child)), + ], + ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: isDesktop ? MainAxisSize.min : MainAxisSize.max, @@ -592,7 +583,11 @@ class _ConfirmTransactionViewState widget.isPaynymTransaction ? widget.txData.paynymAccountLite!.nymName : widget.txData.recipients?.first.address ?? - widget.txData.sparkRecipients!.first.address, + widget + .txData + .sparkRecipients! + .first + .address, style: STextStyles.itemSubtitle12(context), ), ], @@ -609,12 +604,11 @@ class _ConfirmTransactionViewState .watch(pAmountFormatter(coin)) .format( amountWithoutChange, - ethContract: - widget.isTokenTx - ? ref - .watch(pCurrentTokenWallet)! - .tokenContract - : null, + ethContract: widget.isTokenTx + ? ref + .watch(pCurrentTokenWallet)! + .tokenContract + : null, ), style: STextStyles.itemSubtitle12(context), textAlign: TextAlign.right, @@ -728,18 +722,18 @@ class _ConfirmTransactionViewState ), child: RoundedWhiteContainer( padding: const EdgeInsets.all(0), - borderColor: - Theme.of(context).extension()!.background, + borderColor: Theme.of( + context, + ).extension()!.background, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Container( decoration: BoxDecoration( - color: - Theme.of( - context, - ).extension()!.background, + color: Theme.of( + context, + ).extension()!.background, borderRadius: BorderRadius.only( topLeft: Radius.circular( Constants.size.circularBorderRadius, @@ -799,37 +793,35 @@ class _ConfirmTransactionViewState String fiatAmount = "N/A"; if (externalCalls) { - final price = - widget.isTokenTx - ? ref - .read( - priceAnd24hChangeNotifierProvider, - ) - .getTokenPrice( - ref - .read(pCurrentTokenWallet)! - .tokenContract - .address, - ) - ?.value - : ref - .read( - priceAnd24hChangeNotifierProvider, - ) - .getPrice(coin) - ?.value; - if (price != null && price > Decimal.zero) { - fiatAmount = (amountWithoutChange.decimal * - price) - .toAmount(fractionDigits: 2) - .fiatString( - locale: + final price = widget.isTokenTx + ? ref + .read( + priceAnd24hChangeNotifierProvider, + ) + .getTokenPrice( ref + .read(pCurrentTokenWallet)! + .tokenContract + .address, + ) + ?.value + : ref + .read( + priceAnd24hChangeNotifierProvider, + ) + .getPrice(coin) + ?.value; + if (price != null && price > Decimal.zero) { + fiatAmount = + (amountWithoutChange.decimal * price) + .toAmount(fractionDigits: 2) + .fiatString( + locale: ref .read( localeServiceChangeNotifierProvider, ) .locale, - ); + ); } } @@ -840,23 +832,21 @@ class _ConfirmTransactionViewState .watch(pAmountFormatter(coin)) .format( amountWithoutChange, - ethContract: - widget.isTokenTx - ? ref - .watch( - pCurrentTokenWallet, - )! - .tokenContract - : null, + ethContract: widget.isTokenTx + ? ref + .watch( + pCurrentTokenWallet, + )! + .tokenContract + : null, ), style: STextStyles.desktopTextExtraExtraSmall( context, ).copyWith( - color: - Theme.of(context) - .extension()! - .textDark, + color: Theme.of(context) + .extension()! + .textDark, ), ), if (externalCalls) @@ -884,10 +874,9 @@ class _ConfirmTransactionViewState ), Container( height: 1, - color: - Theme.of( - context, - ).extension()!.background, + color: Theme.of( + context, + ).extension()!.background, ), Padding( padding: const EdgeInsets.all(12), @@ -909,19 +898,19 @@ class _ConfirmTransactionViewState widget.isPaynymTransaction ? widget.txData.paynymAccountLite!.nymName : widget.txData.recipients?.first.address ?? - widget - .txData - .sparkRecipients! - .first - .address, - style: STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of( + widget + .txData + .sparkRecipients! + .first + .address, + style: + STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of( context, ).extension()!.textDark, - ), + ), ), ], ), @@ -929,10 +918,9 @@ class _ConfirmTransactionViewState if (widget.isPaynymTransaction) Container( height: 1, - color: - Theme.of( - context, - ).extension()!.background, + color: Theme.of( + context, + ).extension()!.background, ), if (widget.isPaynymTransaction) Padding( @@ -950,14 +938,14 @@ class _ConfirmTransactionViewState const SizedBox(height: 2), SelectableText( ref.watch(pAmountFormatter(coin)).format(fee!), - style: STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of( + style: + STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of( context, ).extension()!.textDark, - ), + ), ), ], ), @@ -965,10 +953,9 @@ class _ConfirmTransactionViewState if (coin is Ethereum) Container( height: 1, - color: - Theme.of( - context, - ).extension()!.background, + color: Theme.of( + context, + ).extension()!.background, ), if (coin is Ethereum) Padding( @@ -986,14 +973,14 @@ class _ConfirmTransactionViewState const SizedBox(height: 2), SelectableText( widget.txData.nonce.toString(), - style: STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of( + style: + STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of( context, ).extension()!.textDark, - ), + ), ), ], ), @@ -1063,33 +1050,36 @@ class _ConfirmTransactionViewState focusNode: _onChainNoteFocusNode, style: STextStyles.field(context), onChanged: (_) => setState(() {}), - decoration: standardInputDecoration( - "Type something...", - _onChainNoteFocusNode, - context, - ).copyWith( - suffixIcon: - onChainNoteController.text.isNotEmpty + decoration: + standardInputDecoration( + "Type something...", + _onChainNoteFocusNode, + context, + ).copyWith( + suffixIcon: + onChainNoteController.text.isNotEmpty ? Padding( - padding: const EdgeInsets.only(right: 0), - child: UnconstrainedBox( - child: Row( - children: [ - TextFieldIconButton( - child: const XIcon(), - onTap: () async { - setState(() { - onChainNoteController.text = - ""; - }); - }, - ), - ], + padding: const EdgeInsets.only( + right: 0, ), - ), - ) + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + onChainNoteController.text = + ""; + }); + }, + ), + ], + ), + ), + ) : null, - ), + ), ), ), if (coin is Epiccash || coin is Mimblewimblecoin) @@ -1098,14 +1088,12 @@ class _ConfirmTransactionViewState (coin is Epiccash || coin is Mimblewimblecoin) ? "Local Note (optional)" : "Note (optional)", - style: STextStyles.desktopTextExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) + style: STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) .extension()! .textFieldActiveSearchIconRight, - ), + ), textAlign: TextAlign.left, ), const SizedBox(height: 10), @@ -1120,49 +1108,48 @@ class _ConfirmTransactionViewState enableSuggestions: isDesktop ? false : true, controller: noteController, focusNode: _noteFocusNode, - style: STextStyles.desktopTextExtraSmall( - context, - ).copyWith( - color: - Theme.of( + style: STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of( context, ).extension()!.textFieldActiveText, - height: 1.8, - ), + height: 1.8, + ), onChanged: (_) => setState(() {}), - decoration: standardInputDecoration( - "Type something...", - _noteFocusNode, - context, - desktopMed: true, - ).copyWith( - contentPadding: const EdgeInsets.only( - left: 16, - top: 11, - bottom: 12, - right: 5, - ), - suffixIcon: - noteController.text.isNotEmpty + decoration: + standardInputDecoration( + "Type something...", + _noteFocusNode, + context, + desktopMed: true, + ).copyWith( + contentPadding: const EdgeInsets.only( + left: 16, + top: 11, + bottom: 12, + right: 5, + ), + suffixIcon: noteController.text.isNotEmpty ? Padding( - padding: const EdgeInsets.only(right: 0), - child: UnconstrainedBox( - child: Row( - children: [ - TextFieldIconButton( - child: const XIcon(), - onTap: () async { - setState( - () => noteController.text = "", - ); - }, - ), - ], + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState( + () => + noteController.text = "", + ); + }, + ), + ], + ), ), - ), - ) + ) : null, - ), + ), ), ), const SizedBox(height: 20), @@ -1185,10 +1172,9 @@ class _ConfirmTransactionViewState horizontal: 16, vertical: 18, ), - color: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + color: Theme.of( + context, + ).extension()!.textFieldDefaultBG, child: SelectableText( ref.watch(pAmountFormatter(coin)).format(fee!), style: STextStyles.itemSubtitle(context), @@ -1217,10 +1203,9 @@ class _ConfirmTransactionViewState horizontal: 16, vertical: 18, ), - color: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + color: Theme.of( + context, + ).extension()!.textFieldDefaultBG, child: SelectableText( "~${fee!.raw.toInt() ~/ widget.txData.vSize!}", style: STextStyles.itemSubtitle(context), @@ -1231,64 +1216,52 @@ class _ConfirmTransactionViewState SizedBox(height: isDesktop ? 23 : 12), if (!widget.isTokenTx) Padding( - padding: - isDesktop - ? const EdgeInsets.symmetric(horizontal: 32) - : const EdgeInsets.all(0), + padding: isDesktop + ? const EdgeInsets.symmetric(horizontal: 32) + : const EdgeInsets.all(0), child: RoundedContainer( - padding: - isDesktop - ? const EdgeInsets.symmetric( - horizontal: 16, - vertical: 18, - ) - : const EdgeInsets.all(12), - color: - Theme.of( - context, - ).extension()!.snackBarBackSuccess, + padding: isDesktop + ? const EdgeInsets.symmetric(horizontal: 16, vertical: 18) + : const EdgeInsets.all(12), + color: Theme.of( + context, + ).extension()!.snackBarBackSuccess, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( isDesktop ? "Total amount to send" : "Total amount", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension()! - .textConfirmTotalAmount, - ) - : STextStyles.titleBold12(context).copyWith( - color: - Theme.of(context) - .extension()! - .textConfirmTotalAmount, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textConfirmTotalAmount, + ) + : STextStyles.titleBold12(context).copyWith( + color: Theme.of(context) + .extension()! + .textConfirmTotalAmount, + ), ), SelectableText( ref .watch(pAmountFormatter(coin)) .format(amountWithoutChange + fee!), - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension()! - .textConfirmTotalAmount, - ) - : STextStyles.itemSubtitle12(context).copyWith( - color: - Theme.of(context) - .extension()! - .textConfirmTotalAmount, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textConfirmTotalAmount, + ) + : STextStyles.itemSubtitle12(context).copyWith( + color: Theme.of(context) + .extension()! + .textConfirmTotalAmount, + ), textAlign: TextAlign.right, ), ], @@ -1297,10 +1270,9 @@ class _ConfirmTransactionViewState ), SizedBox(height: isDesktop ? 28 : 16), Padding( - padding: - isDesktop - ? const EdgeInsets.symmetric(horizontal: 32) - : const EdgeInsets.all(0), + padding: isDesktop + ? const EdgeInsets.symmetric(horizontal: 32) + : const EdgeInsets.all(0), child: PrimaryButton( label: "Send", buttonHeight: isDesktop ? ButtonHeight.l : null, @@ -1308,28 +1280,27 @@ class _ConfirmTransactionViewState if (isDesktop) { final unlocked = await showDialog( context: context, - builder: - (context) => DesktopDialog( - maxWidth: 580, - maxHeight: double.infinity, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [DesktopDialogCloseButton()], - ), - Padding( - padding: const EdgeInsets.only( - left: 32, - right: 32, - bottom: 32, - ), - child: DesktopAuthSend(coin: coin), - ), - ], + builder: (context) => DesktopDialog( + maxWidth: 580, + maxHeight: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [DesktopDialogCloseButton()], ), - ), + Padding( + padding: const EdgeInsets.only( + left: 32, + right: 32, + bottom: 32, + ), + child: DesktopAuthSend(coin: coin), + ), + ], + ), + ), ); if (context.mounted && unlocked is bool) { if (unlocked) { @@ -1350,18 +1321,16 @@ class _ConfirmTransactionViewState RouteGenerator.getRoute( shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute, - builder: - (_) => const LockscreenView( - showBackButton: true, - popOnSuccess: true, - routeOnSuccessArguments: true, - routeOnSuccess: "", - biometricsCancelButtonString: "CANCEL", - biometricsLocalizedReason: - "Authenticate to send transaction", - biometricsAuthenticationTitle: - "Confirm Transaction", - ), + builder: (_) => const LockscreenView( + showBackButton: true, + popOnSuccess: true, + routeOnSuccessArguments: true, + routeOnSuccess: "", + biometricsCancelButtonString: "CANCEL", + biometricsLocalizedReason: + "Authenticate to send transaction", + biometricsAuthenticationTitle: "Confirm Transaction", + ), settings: const RouteSettings( name: "/confirmsendlockscreen", ), @@ -1375,10 +1344,9 @@ class _ConfirmTransactionViewState unawaited( showFloatingFlushBar( type: FlushBarType.warning, - message: - Util.isDesktop - ? "Invalid passphrase" - : "Invalid PIN", + message: Util.isDesktop + ? "Invalid passphrase" + : "Invalid PIN", context: context, ), ); diff --git a/lib/utilities/git_status.dart b/lib/utilities/git_status.dart index 9efade7c9a..a6d79f3863 100644 --- a/lib/utilities/git_status.dart +++ b/lib/utilities/git_status.dart @@ -1,13 +1,13 @@ import 'dart:convert'; import 'package:flutter/material.dart'; -import 'package:flutter_libepiccash/git_versions.dart' as epic_versions; import 'package:http/http.dart'; import '../../../themes/stack_colors.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/text_styles.dart'; import '../app_config.dart'; +import '../wl_gen/generated/libepiccash_interface_impl.dart'; import '../wl_gen/interfaces/libmwc_interface.dart'; const kGithubAPI = "https://api.github.com"; @@ -17,7 +17,7 @@ const kGithubHead = "/repos"; enum CommitStatus { isHead, isOldCommit, notACommit, notLoaded } abstract class GitStatus { - static String get epicCashCommit => epic_versions.getPluginVersion(); + static String get epicCashCommit => libEpic.getPluginVersion(); // static String get moneroCommit => monero_versions.getPluginVersion(); static String get mimblewimblecoinCommit => libMwc.getPluginVersion(); diff --git a/lib/wallets/crypto_currency/coins/epiccash.dart b/lib/wallets/crypto_currency/coins/epiccash.dart index b715f9623b..e60f90f6d2 100644 --- a/lib/wallets/crypto_currency/coins/epiccash.dart +++ b/lib/wallets/crypto_currency/coins/epiccash.dart @@ -1,9 +1,8 @@ -import 'package:flutter_libepiccash/lib.dart' as epic; - import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/node_model.dart'; import '../../../utilities/default_nodes.dart'; import '../../../utilities/enums/derive_path_type_enum.dart'; +import '../../../wl_gen/interfaces/libepiccash_interface.dart'; import '../crypto_currency.dart'; import '../intermediate/bip39_currency.dart'; @@ -63,7 +62,7 @@ class Epiccash extends Bip39Currency { } } - return epic.LibEpiccash.validateSendAddress(address: address); + return libEpic.validateSendAddress(address: address); } @override @@ -115,10 +114,9 @@ class Epiccash extends Bip39Currency { int get targetBlockTimeSeconds => 60; @override - DerivePathType get defaultDerivePathType => - throw UnsupportedError( - "$runtimeType does not use bitcoin style derivation paths", - ); + DerivePathType get defaultDerivePathType => throw UnsupportedError( + "$runtimeType does not use bitcoin style derivation paths", + ); @override Uri defaultBlockExplorer(String txid) { diff --git a/lib/wallets/wallet/impl/epiccash_wallet.dart b/lib/wallets/wallet/impl/epiccash_wallet.dart index 6f961e2983..a734626f42 100644 --- a/lib/wallets/wallet/impl/epiccash_wallet.dart +++ b/lib/wallets/wallet/impl/epiccash_wallet.dart @@ -3,8 +3,6 @@ import 'dart:convert'; import 'dart:io'; import 'package:decimal/decimal.dart'; -import 'package:flutter_libepiccash/lib.dart' as epiccash; -import 'package:flutter_libepiccash/models/transaction.dart' as epic_models; import 'package:isar_community/isar.dart'; import 'package:mutex/mutex.dart'; import 'package:stack_wallet_backup/generate_password.dart'; @@ -33,6 +31,7 @@ import '../../../utilities/logger.dart'; import '../../../utilities/stack_file_system.dart'; import '../../../utilities/test_epic_box_connection.dart'; import '../../../utilities/tor_plain_net_option_enum.dart'; +import '../../../wl_gen/interfaces/libepiccash_interface.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/bip39_wallet.dart'; @@ -86,10 +85,11 @@ class EpiccashWallet extends Bip39Wallet { Future cancelPendingTransactionAndPost(String txSlateId) async { try { _hackedCheckTorNodePrefs(); - final String wallet = - (await secureStorageInterface.read(key: '${walletId}_wallet'))!; + final String wallet = (await secureStorageInterface.read( + key: '${walletId}_wallet', + ))!; - final result = await epiccash.LibEpiccash.cancelTransaction( + final result = await libEpic.cancelTransaction( wallet: wallet, transactionId: txSlateId, ); @@ -178,7 +178,7 @@ class EpiccashWallet extends Bip39Wallet { _hackedCheckTorNodePrefs(); final available = info.cachedBalance.spendable.raw.toInt(); - final transactionFees = await epiccash.LibEpiccash.getTransactionFees( + final transactionFees = await libEpic.getTransactionFees( wallet: wallet!, amount: satoshiAmount, minimumConfirmations: cryptoCurrency.minConfirms, @@ -187,8 +187,9 @@ class EpiccashWallet extends Bip39Wallet { int realFee = 0; try { - realFee = - (Decimal.parse(transactionFees.fee.toString())).toBigInt().toInt(); + realFee = (Decimal.parse( + transactionFees.fee.toString(), + )).toBigInt().toInt(); } catch (e, s) { //todo: come back to this Logging.instance.e("Error getting fees", error: e, stackTrace: s); @@ -208,7 +209,7 @@ class EpiccashWallet extends Bip39Wallet { if (!syncMutex.isLocked) { await syncMutex.protect(() async { // How does getWalletBalances start syncing???? - await epiccash.LibEpiccash.getWalletBalances( + await libEpic.getWalletBalances( wallet: wallet!, refreshFromNode: refreshFromNode, minimumConfirmations: 10, @@ -231,7 +232,7 @@ class EpiccashWallet extends Bip39Wallet { _hackedCheckTorNodePrefs(); final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); const refreshFromNode = 0; - return await epiccash.LibEpiccash.getWalletBalances( + return await libEpic.getWalletBalances( wallet: wallet!, refreshFromNode: refreshFromNode, minimumConfirmations: cryptoCurrency.minConfirms, @@ -329,7 +330,7 @@ class EpiccashWallet extends Bip39Wallet { ) async { final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); - final walletAddress = await epiccash.LibEpiccash.getAddressInfo( + final walletAddress = await libEpic.getAddressInfo( wallet: wallet!, index: index, epicboxConfig: epicboxConfig.toString(), @@ -353,7 +354,7 @@ class EpiccashWallet extends Bip39Wallet { Future _startScans() async { try { //First stop the current listener - epiccash.LibEpiccash.stopEpicboxListener(); + libEpic.stopEpicboxListener(); final wallet = await secureStorageInterface.read( key: '${walletId}_wallet', ); @@ -375,7 +376,7 @@ class EpiccashWallet extends Bip39Wallet { "chainHeight: $chainHeight, lastScannedBlock: $lastScannedBlock", ); - final int nextScannedBlock = await epiccash.LibEpiccash.scanOutputs( + final int nextScannedBlock = await libEpic.scanOutputs( wallet: wallet!, startHeight: lastScannedBlock, numberOfBlocks: scanChunkSize, @@ -408,7 +409,7 @@ class EpiccashWallet extends Bip39Wallet { Logging.instance.d("STARTING WALLET LISTENER ...."); final wallet = await secureStorageInterface.read(key: '${walletId}_wallet'); final EpicBoxConfigModel epicboxConfig = await getEpicBoxConfig(); - epiccash.LibEpiccash.startEpicboxListener( + libEpic.startEpicboxListener( wallet: wallet!, epicboxConfig: epicboxConfig.toString(), ); @@ -492,7 +493,7 @@ class EpiccashWallet extends Bip39Wallet { final String name = walletId; - await epiccash.LibEpiccash.initializeNewWallet( + await libEpic.initializeNewWallet( config: stringConfig, mnemonic: mnemonicString, password: password, @@ -500,7 +501,7 @@ class EpiccashWallet extends Bip39Wallet { ); //Open wallet - encodedWallet = await epiccash.LibEpiccash.openWallet( + encodedWallet = await libEpic.openWallet( config: stringConfig, password: password, ); @@ -542,7 +543,7 @@ class EpiccashWallet extends Bip39Wallet { key: '${walletId}_password', ); - final walletOpen = await epiccash.LibEpiccash.openWallet( + final walletOpen = await libEpic.openWallet( config: config, password: password!, ); @@ -591,7 +592,7 @@ class EpiccashWallet extends Bip39Wallet { if (receiverAddress.startsWith("http://") || receiverAddress.startsWith("https://")) { - transaction = await epiccash.LibEpiccash.txHttpSend( + transaction = await libEpic.txHttpSend( wallet: wallet!, selectionStrategyIsAll: 0, minimumConfirmations: cryptoCurrency.minConfirms, @@ -600,7 +601,7 @@ class EpiccashWallet extends Bip39Wallet { address: txData.recipients!.first.address, ); } else { - transaction = await epiccash.LibEpiccash.createTransaction( + transaction = await libEpic.createTransaction( wallet: wallet!, amount: txData.recipients!.first.amount.raw.toInt(), address: txData.recipients!.first.address, @@ -694,7 +695,7 @@ class EpiccashWallet extends Bip39Wallet { value: epicboxConfig.toString(), ); - await epiccash.LibEpiccash.recoverWallet( + await libEpic.recoverWallet( config: stringConfig, password: password, mnemonic: await getMnemonic(), @@ -717,7 +718,7 @@ class EpiccashWallet extends Bip39Wallet { ); //Open Wallet - final walletOpen = await epiccash.LibEpiccash.openWallet( + final walletOpen = await libEpic.openWallet( config: stringConfig, password: password, ); @@ -905,20 +906,19 @@ class EpiccashWallet extends Bip39Wallet { ); const refreshFromNode = 1; - final myAddresses = - await mainDB - .getAddresses(walletId) - .filter() - .typeEqualTo(AddressType.mimbleWimble) - .and() - .subTypeEqualTo(AddressSubType.receiving) - .and() - .valueIsNotEmpty() - .valueProperty() - .findAll(); + final myAddresses = await mainDB + .getAddresses(walletId) + .filter() + .typeEqualTo(AddressType.mimbleWimble) + .and() + .subTypeEqualTo(AddressSubType.receiving) + .and() + .valueIsNotEmpty() + .valueProperty() + .findAll(); final myAddressesSet = myAddresses.toSet(); - final transactions = await epiccash.LibEpiccash.getTransactions( + final transactions = await libEpic.getTransactions( wallet: wallet!, refreshFromNode: refreshFromNode, ); @@ -929,12 +929,12 @@ class EpiccashWallet extends Bip39Wallet { for (final tx in transactions) { final isIncoming = - tx.txType == epic_models.TransactionType.TxReceived || - tx.txType == epic_models.TransactionType.TxReceivedCancelled; + libEpic.txTypeIsReceived(tx.txType) || + libEpic.txTypeIsReceiveCancelled(tx.txType); final slateId = tx.txSlateId; final commitId = slatesToCommits[slateId]?['commitId'] as String?; - final numberOfMessages = tx.messages?.messages.length; - final onChainNote = tx.messages?.messages[0].message; + final numberOfMessages = tx.messages?.length; + final onChainNote = tx.messages?.first.message; final addressFrom = slatesToCommits[slateId]?["from"] as String?; final addressTo = slatesToCommits[slateId]?["to"] as String?; @@ -994,13 +994,12 @@ class EpiccashWallet extends Bip39Wallet { "slateId": slateId, "onChainNote": onChainNote, "isCancelled": - tx.txType == epic_models.TransactionType.TxSentCancelled || - tx.txType == epic_models.TransactionType.TxReceivedCancelled, - "overrideFee": - Amount( - rawValue: BigInt.from(fee), - fractionDigits: cryptoCurrency.fractionDigits, - ).toJsonString(), + libEpic.txTypeIsSentCancelled(tx.txType) || + libEpic.txTypeIsReceiveCancelled(tx.txType), + "overrideFee": Amount( + rawValue: BigInt.from(fee), + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), }; final txn = TransactionV2( @@ -1087,9 +1086,7 @@ class EpiccashWallet extends Bip39Wallet { Future updateChainHeight() async { _hackedCheckTorNodePrefs(); final config = await _getRealConfig(); - final latestHeight = await epiccash.LibEpiccash.getChainHeight( - config: config, - ); + final latestHeight = await libEpic.getChainHeight(config: config); await info.updateCachedChainHeight( newHeight: latestHeight, isar: mainDB.isar, @@ -1132,7 +1129,7 @@ class EpiccashWallet extends Bip39Wallet { @override Future exit() async { - epiccash.LibEpiccash.stopEpicboxListener(); + libEpic.stopEpicboxListener(); timer?.cancel(); timer = null; await super.exit(); @@ -1185,7 +1182,7 @@ Future deleteEpicWallet({ return "Tried to delete non existent epic wallet file with walletId=$walletId"; } else { try { - return epiccash.LibEpiccash.deleteWallet(wallet: wallet, config: config!); + return libEpic.deleteWallet(wallet: wallet, config: config!); } catch (e, s) { Logging.instance.e("$e\n$s", error: e, stackTrace: s); return "deleteEpicWallet($walletId) failed..."; diff --git a/lib/wl_gen/interfaces/libepiccash_interface.dart b/lib/wl_gen/interfaces/libepiccash_interface.dart new file mode 100644 index 0000000000..af5e65375b --- /dev/null +++ b/lib/wl_gen/interfaces/libepiccash_interface.dart @@ -0,0 +1,160 @@ +export '../generated/libepiccash_interface_impl.dart'; + +abstract class LibEpicCashInterface { + const LibEpicCashInterface(); + + bool txTypeIsReceived(Enum value); + bool txTypeIsReceiveCancelled(Enum value); + bool txTypeIsSentCancelled(Enum value); + + Future initializeNewWallet({ + required String config, + required String mnemonic, + required String password, + required String name, + }); + + Future openWallet({required String config, required String password}); + + Future recoverWallet({ + required String config, + required String password, + required String mnemonic, + required String name, + }); + + Future<({String commitId, String slateId})> txHttpSend({ + required String wallet, + required int selectionStrategyIsAll, + required int minimumConfirmations, + required String message, + required int amount, + required String address, + }); + + Future<({String commitId, String slateId})> createTransaction({ + required String wallet, + required int amount, + required String address, + required int secretKeyIndex, + required String epicboxConfig, + required int minimumConfirmations, + required String note, + }); + + Future cancelTransaction({ + required String wallet, + required String transactionId, + }); + + Future> getTransactions({ + required String wallet, + required int refreshFromNode, + }); + + void startEpicboxListener({ + required String wallet, + required String epicboxConfig, + }); + + void stopEpicboxListener(); + + bool validateSendAddress({required String address}); + + Future<({int fee, bool strategyUseAll, int total})> getTransactionFees({ + required String wallet, + required int amount, + required int minimumConfirmations, + required int available, + }); + + Future< + ({ + double awaitingFinalization, + double pending, + double spendable, + double total, + }) + > + getWalletBalances({ + required String wallet, + required int refreshFromNode, + required int minimumConfirmations, + }); + + Future getAddressInfo({ + required String wallet, + required int index, + required String epicboxConfig, + }); + + Future scanOutputs({ + required String wallet, + required int startHeight, + required int numberOfBlocks, + }); + + Future getChainHeight({required String config}); + + Future deleteWallet({required String wallet, required String config}); + + String getPluginVersion(); +} + +class EpicTransaction { + final String parentKeyId; + final int id; + final String? txSlateId; + final Enum txType; + final String creationTs; + final String confirmationTs; + final bool confirmed; + final int numInputs; + final int numOutputs; + final String amountCredited; + final String amountDebited; + final String? fee; + final String? ttlCutoffHeight; + final List? messages; + final String? storedTx; + final String? kernelExcess; + final int? kernelLookupMinHeight; + final String? paymentProof; + + EpicTransaction({ + required this.parentKeyId, + required this.id, + this.txSlateId, + required this.txType, + required this.creationTs, + required this.confirmationTs, + required this.confirmed, + required this.numInputs, + required this.numOutputs, + required this.amountCredited, + required this.amountDebited, + this.fee, + this.ttlCutoffHeight, + this.messages, + this.storedTx, + this.kernelExcess, + this.kernelLookupMinHeight, + this.paymentProof, + }); +} + +class EpicMessage { + final String id; + final String publicKey; + final String? message; + final String? messageSig; + + EpicMessage({ + required this.id, + required this.publicKey, + this.message, + this.messageSig, + }); +} + +class BadHttpAddressException implements Exception {} diff --git a/tool/wl_templates/EPIC_libepiccash_interface_impl.template.dart b/tool/wl_templates/EPIC_libepiccash_interface_impl.template.dart new file mode 100644 index 0000000000..579c322878 --- /dev/null +++ b/tool/wl_templates/EPIC_libepiccash_interface_impl.template.dart @@ -0,0 +1,274 @@ +//ON +import 'package:flutter_libepiccash/git_versions.dart' as epic_versions; +import 'package:flutter_libepiccash/lib.dart'; +import 'package:flutter_libepiccash/models/transaction.dart'; + +//END_ON +import '../interfaces/libepiccash_interface.dart'; + +LibEpicCashInterface get libEpic => _getLib(); + +//OFF +LibEpicCashInterface _getLib() => throw Exception("EPIC not enabled!"); + +//END_OFF +//ON +LibEpicCashInterface _getLib() => const _LibEpicCashInterfaceImpl(); + +final class _LibEpicCashInterfaceImpl extends LibEpicCashInterface { + const _LibEpicCashInterfaceImpl(); + + @override + Future cancelTransaction({ + required String wallet, + required String transactionId, + }) { + return LibEpiccash.cancelTransaction( + wallet: wallet, + transactionId: transactionId, + ); + } + + @override + Future<({String commitId, String slateId})> createTransaction({ + required String wallet, + required int amount, + required String address, + required int secretKeyIndex, + required String epicboxConfig, + required int minimumConfirmations, + required String note, + }) { + return LibEpiccash.createTransaction( + wallet: wallet, + amount: amount, + address: address, + secretKeyIndex: secretKeyIndex, + epicboxConfig: epicboxConfig, + minimumConfirmations: minimumConfirmations, + note: note, + ); + } + + @override + Future deleteWallet({ + required String wallet, + required String config, + }) { + return LibEpiccash.deleteWallet(wallet: wallet, config: config); + } + + @override + Future getAddressInfo({ + required String wallet, + required int index, + required String epicboxConfig, + }) { + return LibEpiccash.getAddressInfo( + wallet: wallet, + index: index, + epicboxConfig: epicboxConfig, + ); + } + + @override + Future getChainHeight({required String config}) { + return LibEpiccash.getChainHeight(config: config); + } + + @override + Future<({int fee, bool strategyUseAll, int total})> getTransactionFees({ + required String wallet, + required int amount, + required int minimumConfirmations, + required int available, + }) { + return LibEpiccash.getTransactionFees( + wallet: wallet, + amount: amount, + minimumConfirmations: minimumConfirmations, + available: available, + ); + } + + @override + Future> getTransactions({ + required String wallet, + required int refreshFromNode, + }) async { + final transactions = await LibEpiccash.getTransactions( + wallet: wallet, + refreshFromNode: refreshFromNode, + ); + + return transactions + .map( + (e) => EpicTransaction( + parentKeyId: e.parentKeyId, + id: e.id, + txType: e.txType, + creationTs: e.creationTs, + confirmationTs: e.confirmationTs, + confirmed: e.confirmed, + numInputs: e.numInputs, + numOutputs: e.numOutputs, + amountCredited: e.amountCredited, + amountDebited: e.amountDebited, + txSlateId: e.txSlateId, + fee: e.fee, + ttlCutoffHeight: e.ttlCutoffHeight, + messages: e.messages?.messages + .map( + (f) => EpicMessage( + id: f.id, + publicKey: f.publicKey, + message: f.message, + messageSig: f.messageSig, + ), + ) + .toList(), + storedTx: e.storedTx, + kernelExcess: e.kernelExcess, + kernelLookupMinHeight: e.kernelLookupMinHeight, + paymentProof: e.paymentProof, + ), + ) + .toList(); + } + + @override + Future< + ({ + double awaitingFinalization, + double pending, + double spendable, + double total, + }) + > + getWalletBalances({ + required String wallet, + required int refreshFromNode, + required int minimumConfirmations, + }) { + return LibEpiccash.getWalletBalances( + wallet: wallet, + refreshFromNode: refreshFromNode, + minimumConfirmations: minimumConfirmations, + ); + } + + @override + Future initializeNewWallet({ + required String config, + required String mnemonic, + required String password, + required String name, + }) { + return LibEpiccash.initializeNewWallet( + config: config, + mnemonic: mnemonic, + password: password, + name: name, + ); + } + + @override + Future openWallet({ + required String config, + required String password, + }) { + return LibEpiccash.openWallet(config: config, password: password); + } + + @override + Future recoverWallet({ + required String config, + required String password, + required String mnemonic, + required String name, + }) { + return LibEpiccash.recoverWallet( + config: config, + password: password, + mnemonic: mnemonic, + name: name, + ); + } + + @override + Future scanOutputs({ + required String wallet, + required int startHeight, + required int numberOfBlocks, + }) { + return LibEpiccash.scanOutputs( + wallet: wallet, + startHeight: startHeight, + numberOfBlocks: numberOfBlocks, + ); + } + + @override + void startEpicboxListener({ + required String wallet, + required String epicboxConfig, + }) { + return LibEpiccash.startEpicboxListener( + wallet: wallet, + epicboxConfig: epicboxConfig, + ); + } + + @override + void stopEpicboxListener() { + return LibEpiccash.stopEpicboxListener(); + } + + @override + Future<({String commitId, String slateId})> txHttpSend({ + required String wallet, + required int selectionStrategyIsAll, + required int minimumConfirmations, + required String message, + required int amount, + required String address, + }) { + try { + return LibEpiccash.txHttpSend( + wallet: wallet, + selectionStrategyIsAll: selectionStrategyIsAll, + minimumConfirmations: minimumConfirmations, + message: message, + amount: amount, + address: address, + ); + } on BadEpicHttpAddressException catch (_) { + throw BadHttpAddressException(); + } + } + + @override + bool txTypeIsReceiveCancelled(Enum value) { + return value == TransactionType.TxReceivedCancelled; + } + + @override + bool txTypeIsReceived(Enum value) { + return value == TransactionType.TxReceived; + } + + @override + bool txTypeIsSentCancelled(Enum value) { + return value == TransactionType.TxSentCancelled; + } + + @override + bool validateSendAddress({required String address}) { + return LibEpiccash.validateSendAddress(address: address); + } + + @override + String getPluginVersion() => epic_versions.getPluginVersion(); +} + +//END_ON From df966bd28538d0c698c913e9bc1ca3209428faae Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 6 Oct 2025 11:01:23 -0600 Subject: [PATCH 11/30] fix mwc usage --- .../crypto_currency/coins/mimblewimblecoin.dart | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/wallets/crypto_currency/coins/mimblewimblecoin.dart b/lib/wallets/crypto_currency/coins/mimblewimblecoin.dart index 6d670d5ff6..c9d57878a0 100644 --- a/lib/wallets/crypto_currency/coins/mimblewimblecoin.dart +++ b/lib/wallets/crypto_currency/coins/mimblewimblecoin.dart @@ -1,10 +1,9 @@ -import 'package:flutter_libmwc/lib.dart' as mimblewimblecoin; - import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/node_model.dart'; import '../../../utilities/default_nodes.dart'; import '../../../utilities/enums/derive_path_type_enum.dart'; import '../../../utilities/enums/mwc_transaction_method.dart'; +import '../../../wl_gen/interfaces/libmwc_interface.dart'; import '../crypto_currency.dart'; import '../intermediate/bip39_currency.dart'; @@ -54,7 +53,7 @@ class Mimblewimblecoin extends Bip39Currency { @override bool validateAddress(String address) { // Use libmwc for address validation. - return mimblewimblecoin.Libmwc.validateSendAddress(address: address); + return libMwc.validateSendAddress(address: address); } /// Check if data is a slatepack. @@ -134,10 +133,9 @@ class Mimblewimblecoin extends Bip39Currency { int get targetBlockTimeSeconds => 60; @override - DerivePathType get defaultDerivePathType => - throw UnsupportedError( - "$runtimeType does not use bitcoin style derivation paths", - ); + DerivePathType get defaultDerivePathType => throw UnsupportedError( + "$runtimeType does not use bitcoin style derivation paths", + ); @override Uri defaultBlockExplorer(String txid) { From c51e78427f13c43dc3f8e57ee32fb93e7c70f64a Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 6 Oct 2025 11:01:42 -0600 Subject: [PATCH 12/30] fix package import --- .../transaction_details_view.dart | 1508 ++++++++--------- 1 file changed, 699 insertions(+), 809 deletions(-) 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 e1b2c89f0e..1e3aa7c729 100644 --- a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart +++ b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart @@ -16,7 +16,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:stackwallet/wallets/wallet/impl/mimblewimblecoin_wallet.dart'; import 'package:tuple/tuple.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -39,6 +38,7 @@ import '../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../wallets/crypto_currency/intermediate/nano_currency.dart'; import '../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../wallets/wallet/impl/epiccash_wallet.dart'; +import '../../../wallets/wallet/impl/mimblewimblecoin_wallet.dart'; import '../../../widgets/background.dart'; import '../../../widgets/conditional_parent.dart'; import '../../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -100,12 +100,11 @@ class _TransactionDetailsViewState isTokenTx = _transaction.subType == TransactionSubType.ethToken; walletId = widget.walletId; - minConfirms = - ref - .read(pWallets) - .getWallet(widget.walletId) - .cryptoCurrency - .minConfirms; + minConfirms = ref + .read(pWallets) + .getWallet(widget.walletId) + .cryptoCurrency + .minConfirms; coin = widget.coin; amount = _transaction.realAmount; fee = _transaction.fee.toAmountAsRaw(fractionDigits: coin.fractionDigits); @@ -116,12 +115,9 @@ class _TransactionDetailsViewState amountPrefix = _transaction.type == TransactionType.outgoing ? "-" : "+"; } - ethContract = - isTokenTx - ? ref - .read(mainDBProvider) - .getEthContractSync(_transaction.otherData!) - : null; + ethContract = isTokenTx + ? ref.read(mainDBProvider).getEthContractSync(_transaction.otherData!) + : null; unit = isTokenTx ? ethContract!.symbol : coin.ticker; @@ -217,10 +213,9 @@ class _TransactionDetailsViewState .read(addressBookServiceProvider) .contacts .where( - (element) => - element.addresses - .where((element) => element.address == address) - .isNotEmpty, + (element) => element.addresses + .where((element) => element.address == address) + .isNotEmpty, ); if (contacts.isNotEmpty) { return contacts.first.name; @@ -256,8 +251,9 @@ class _TransactionDetailsViewState onChanged: (value) { if (value is bool) { ref - .read(prefsChangeNotifierProvider) - .hideBlockExplorerWarning = value; + .read(prefsChangeNotifierProvider) + .hideBlockExplorerWarning = + value; setState(() {}); } }, @@ -277,10 +273,9 @@ class _TransactionDetailsViewState child: Text( "Cancel", style: STextStyles.button(context).copyWith( - color: - Theme.of( - context, - ).extension()!.accentColorDark, + color: Theme.of( + context, + ).extension()!.accentColorDark, ), ), ), @@ -319,8 +314,9 @@ class _TransactionDetailsViewState onChanged: (value) { if (value is bool) { ref - .read(prefsChangeNotifierProvider) - .hideBlockExplorerWarning = value; + .read(prefsChangeNotifierProvider) + .hideBlockExplorerWarning = + value; setState(() {}); } }, @@ -383,10 +379,9 @@ class _TransactionDetailsViewState )) { price = ref.watch( priceAnd24hChangeNotifierProvider.select( - (value) => - isTokenTx - ? value.getTokenPrice(_transaction.otherData!)?.value - : value.getPrice(coin)?.value, + (value) => isTokenTx + ? value.getTokenPrice(_transaction.otherData!)?.value + : value.getPrice(coin)?.value, ), ); } @@ -395,38 +390,36 @@ class _TransactionDetailsViewState condition: !isDesktop, builder: (child) => Background(child: child), child: Scaffold( - backgroundColor: - isDesktop - ? Colors.transparent - : Theme.of(context).extension()!.background, - appBar: - isDesktop - ? null - : AppBar( - backgroundColor: - Theme.of(context).extension()!.background, - leading: AppBarBackButton( - onPressed: () async { - // if (FocusScope.of(context).hasFocus) { - // FocusScope.of(context).unfocus(); - // await Future.delayed(Duration(milliseconds: 50)); - // } - Navigator.of(context).pop(); - }, - ), - title: Text( - "Transaction details", - style: STextStyles.navBarTitle(context), - ), + backgroundColor: isDesktop + ? Colors.transparent + : Theme.of(context).extension()!.background, + appBar: isDesktop + ? null + : AppBar( + backgroundColor: Theme.of( + context, + ).extension()!.background, + leading: AppBarBackButton( + onPressed: () async { + // if (FocusScope.of(context).hasFocus) { + // FocusScope.of(context).unfocus(); + // await Future.delayed(Duration(milliseconds: 50)); + // } + Navigator.of(context).pop(); + }, + ), + title: Text( + "Transaction details", + style: STextStyles.navBarTitle(context), ), + ), body: ConditionalParent( condition: !isDesktop, builder: (child) => SafeArea(child: child), child: Padding( - padding: - isDesktop - ? const EdgeInsets.only(left: 32) - : const EdgeInsets.all(12), + padding: isDesktop + ? const EdgeInsets.only(left: 32) + : const EdgeInsets.all(12), child: Column( children: [ if (isDesktop) @@ -442,20 +435,18 @@ class _TransactionDetailsViewState ), Expanded( child: Padding( - padding: - isDesktop - ? const EdgeInsets.only(right: 32, bottom: 32) - : const EdgeInsets.all(0), + padding: isDesktop + ? const EdgeInsets.only(right: 32, bottom: 32) + : const EdgeInsets.all(0), child: ConditionalParent( condition: isDesktop, builder: (child) { return RoundedWhiteContainer( - borderColor: - isDesktop - ? Theme.of( - context, - ).extension()!.backgroundAppBar - : null, + borderColor: isDesktop + ? Theme.of( + context, + ).extension()!.backgroundAppBar + : null, padding: const EdgeInsets.all(0), child: child, ); @@ -463,40 +454,35 @@ class _TransactionDetailsViewState child: SingleChildScrollView( primary: isDesktop ? false : null, child: Padding( - padding: - isDesktop - ? const EdgeInsets.all(0) - : const EdgeInsets.all(4), + padding: isDesktop + ? const EdgeInsets.all(0) + : const EdgeInsets.all(4), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ RoundedWhiteContainer( - padding: - isDesktop - ? const EdgeInsets.all(0) - : const EdgeInsets.all(12), + padding: isDesktop + ? const EdgeInsets.all(0) + : const EdgeInsets.all(12), child: Container( - decoration: - isDesktop - ? BoxDecoration( - color: - Theme.of(context) - .extension()! - .backgroundAppBar, - borderRadius: BorderRadius.vertical( - top: Radius.circular( - Constants - .size - .circularBorderRadius, - ), + decoration: isDesktop + ? BoxDecoration( + color: Theme.of(context) + .extension()! + .backgroundAppBar, + borderRadius: BorderRadius.vertical( + top: Radius.circular( + Constants + .size + .circularBorderRadius, ), - ) - : null, + ), + ) + : null, child: Padding( - padding: - isDesktop - ? const EdgeInsets.all(12) - : const EdgeInsets.all(0), + padding: isDesktop + ? const EdgeInsets.all(12) + : const EdgeInsets.all(0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -513,12 +499,12 @@ class _TransactionDetailsViewState SelectableText( _transaction.isCancelled ? coin is Ethereum - ? "Failed" - : "Cancelled" + ? "Failed" + : "Cancelled" : whatIsIt( - _transaction, - currentHeight, - ), + _transaction, + currentHeight, + ), style: STextStyles.desktopTextMedium( context, @@ -527,41 +513,37 @@ class _TransactionDetailsViewState ], ), Column( - crossAxisAlignment: - isDesktop - ? CrossAxisAlignment.end - : CrossAxisAlignment.start, + crossAxisAlignment: isDesktop + ? CrossAxisAlignment.end + : CrossAxisAlignment.start, children: [ SelectableText( "$amountPrefix${ref.watch(pAmountFormatter(coin)).format(amount, ethContract: ethContract)}", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.titleBold12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension< + StackColors + >()! + .textDark, + ) + : STextStyles.titleBold12( + context, + ), ), const SizedBox(height: 2), if (price != null) SelectableText( "$amountPrefix${(amount.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), ), ], ), @@ -581,24 +563,20 @@ class _TransactionDetailsViewState ? const _Divider() : const SizedBox(height: 12), RoundedWhiteContainer( - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Status", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle(context), ), // Flexible( // child: FittedBox( @@ -607,35 +585,31 @@ class _TransactionDetailsViewState SelectableText( _transaction.isCancelled ? coin is Ethereum - ? "Failed" - : "Cancelled" + ? "Failed" + : "Cancelled" : whatIsIt( - _transaction, - currentHeight, - ), - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - _transaction.type == - TransactionType - .outgoing - ? Theme.of(context) - .extension< - StackColors - >()! - .accentColorOrange - : Theme.of(context) - .extension< - StackColors - >()! - .accentColorGreen, - ) - : STextStyles.itemSubtitle12( - context, - ), + _transaction, + currentHeight, + ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: + _transaction.type == + TransactionType.outgoing + ? Theme.of(context) + .extension< + StackColors + >()! + .accentColorOrange + : Theme.of(context) + .extension< + StackColors + >()! + .accentColorGreen, + ) + : STextStyles.itemSubtitle12(context), ), // ), // ), @@ -658,10 +632,9 @@ class _TransactionDetailsViewState _transaction.subType == TransactionSubType.mint)) RoundedWhiteContainer( - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -688,24 +661,19 @@ class _TransactionDetailsViewState if (isDesktop) { showDialog( context: context, - builder: - ( - _, - ) => DesktopDialog( - maxHeight: - double - .infinity, - child: AddressDetailsView( - addressId: - _transaction - .address - .value! - .id, - walletId: - widget - .walletId, - ), - ), + builder: (_) => DesktopDialog( + maxHeight: double + .infinity, + child: AddressDetailsView( + addressId: + _transaction + .address + .value! + .id, + walletId: widget + .walletId, + ), + ), ); } else { Navigator.of( @@ -732,74 +700,70 @@ class _TransactionDetailsViewState TransactionType.outgoing ? "Sent to" : "Receiving address", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), ), ), const SizedBox(height: 8), _transaction.type == TransactionType.incoming ? FutureBuilder( - future: fetchContactNameFor( - _transaction - .address - .value! - .value, - ), - builder: ( - builderContext, - AsyncSnapshot - snapshot, - ) { - String - addressOrContactName = - _transaction - .address - .value! - .value; - if (snapshot.connectionState == - ConnectionState - .done && - snapshot.hasData) { - addressOrContactName = - snapshot.data!; - } - return SelectableText( - addressOrContactName, - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of( - context, - ) + future: fetchContactNameFor( + _transaction + .address + .value! + .value, + ), + builder: + ( + builderContext, + AsyncSnapshot + snapshot, + ) { + String + addressOrContactName = + _transaction + .address + .value! + .value; + if (snapshot.connectionState == + ConnectionState + .done && + snapshot + .hasData) { + addressOrContactName = + snapshot.data!; + } + return SelectableText( + addressOrContactName, + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) .extension< StackColors >()! .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), - ); - }, - ) + ) + : STextStyles.itemSubtitle12( + context, + ), + ); + }, + ) : SelectableText( - _transaction - .address - .value! - .value, - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( + _transaction + .address + .value! + .value, + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( context, ).copyWith( color: @@ -811,10 +775,10 @@ class _TransactionDetailsViewState >()! .textDark, ) - : STextStyles.itemSubtitle12( + : STextStyles.itemSubtitle12( context, ), - ), + ), ], ), ), @@ -832,10 +796,9 @@ class _TransactionDetailsViewState : const SizedBox(height: 12), if (coin is Epiccash) RoundedWhiteContainer( - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -849,33 +812,30 @@ class _TransactionDetailsViewState children: [ Text( "On chain note", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), ), const SizedBox(height: 8), SelectableText( _transaction.otherData ?? "", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension< + StackColors + >()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), ), ], ), @@ -891,10 +851,9 @@ class _TransactionDetailsViewState ? const _Divider() : const SizedBox(height: 12), RoundedWhiteContainer( - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -906,66 +865,67 @@ class _TransactionDetailsViewState (coin is Epiccash) ? "Local Note" : "Note ", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), ), isDesktop ? IconPencilButton( - onPressed: () { - showDialog( - context: context, - builder: (context) { - return DesktopDialog( - maxWidth: 580, - maxHeight: 360, - child: EditNoteView( - txid: _transaction.txid, - walletId: walletId, - ), - ); - }, - ); - }, - ) + onPressed: () { + showDialog( + context: context, + builder: (context) { + return DesktopDialog( + maxWidth: 580, + maxHeight: 360, + child: EditNoteView( + txid: + _transaction.txid, + walletId: walletId, + ), + ); + }, + ); + }, + ) : GestureDetector( - onTap: () { - Navigator.of(context).pushNamed( - EditNoteView.routeName, - arguments: Tuple2( - _transaction.txid, - walletId, - ), - ); - }, - child: Row( - children: [ - SvgPicture.asset( - Assets.svg.pencil, - width: 10, - height: 10, - color: - Theme.of(context) - .extension< - StackColors - >()! - .infoItemIcons, - ), - const SizedBox(width: 4), - Text( - "Edit", - style: STextStyles.link2( - context, + onTap: () { + Navigator.of( + context, + ).pushNamed( + EditNoteView.routeName, + arguments: Tuple2( + _transaction.txid, + walletId, ), - ), - ], + ); + }, + child: Row( + children: [ + SvgPicture.asset( + Assets.svg.pencil, + width: 10, + height: 10, + color: Theme.of(context) + .extension< + StackColors + >()! + .infoItemIcons, + ), + const SizedBox(width: 4), + Text( + "Edit", + style: STextStyles.link2( + context, + ), + ), + ], + ), ), - ), ], ), const SizedBox(height: 8), @@ -979,21 +939,15 @@ class _TransactionDetailsViewState ) ?.value ?? "", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ) + : STextStyles.itemSubtitle12(context), ), ], ), @@ -1002,10 +956,9 @@ class _TransactionDetailsViewState ? const _Divider() : const SizedBox(height: 12), RoundedWhiteContainer( - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -1017,14 +970,13 @@ class _TransactionDetailsViewState children: [ Text( "Date", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), ), if (isDesktop) const SizedBox(height: 2), @@ -1033,21 +985,19 @@ class _TransactionDetailsViewState Format.extractDateFrom( _transaction.timestamp, ), - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension< + StackColors + >()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), ), ], ), @@ -1056,21 +1006,17 @@ class _TransactionDetailsViewState Format.extractDateFrom( _transaction.timestamp, ), - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), ), if (isDesktop) IconCopyButton( @@ -1087,19 +1033,17 @@ class _TransactionDetailsViewState : const SizedBox(height: 12), if (coin is! NanoCurrency) RoundedWhiteContainer( - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Builder( builder: (context) { - final String feeString = - showFeePending - ? _transaction.isConfirmed( - currentHeight, - minConfirms, - ) - ? ref + final String feeString = showFeePending + ? _transaction.isConfirmed( + currentHeight, + minConfirms, + ) + ? ref .watch( pAmountFormatter(coin), ) @@ -1107,13 +1051,13 @@ class _TransactionDetailsViewState fee, withUnitName: isTokenTx, ) - : "Pending" - : ref - .watch(pAmountFormatter(coin)) - .format( - fee, - withUnitName: isTokenTx, - ); + : "Pending" + : ref + .watch(pAmountFormatter(coin)) + .format( + fee, + withUnitName: isTokenTx, + ); return Row( mainAxisAlignment: @@ -1127,58 +1071,52 @@ class _TransactionDetailsViewState children: [ Text( "Transaction fee", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), - ), - if (isDesktop) - const SizedBox(height: 2), - if (isDesktop) + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), + ), + if (isDesktop) + const SizedBox(height: 2), + if (isDesktop) SelectableText( feeString, - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of( - context, - ) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: + Theme.of(context) + .extension< + StackColors + >()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), ), ], ), if (!isDesktop) SelectableText( feeString, - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension< + StackColors + >()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), ), if (isDesktop) IconCopyButton(data: feeString), @@ -1201,9 +1139,9 @@ class _TransactionDetailsViewState widget.coin is Ecash) { height = _transaction.height != null && - _transaction.height! > 0 - ? "${_transaction.height!}" - : "Pending"; + _transaction.height! > 0 + ? "${_transaction.height!}" + : "Pending"; confirmations = confirms.toString(); } else if (widget.coin is Epiccash && _transaction.slateId == null) { @@ -1218,10 +1156,9 @@ class _TransactionDetailsViewState height = "${_transaction.height == 0 ? "Unknown" : _transaction.height}"; } else { - height = - confirms > 0 - ? "${_transaction.height}" - : "Pending"; + height = confirms > 0 + ? "${_transaction.height}" + : "Pending"; } confirmations = confirms.toString(); @@ -1230,10 +1167,9 @@ class _TransactionDetailsViewState return Column( children: [ RoundedWhiteContainer( - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -1246,58 +1182,54 @@ class _TransactionDetailsViewState children: [ Text( "Block height", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), ), if (isDesktop) const SizedBox(height: 2), if (isDesktop) SelectableText( height, - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of( - context, - ) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: + Theme.of( + context, + ) + .extension< + StackColors + >()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), ), ], ), if (!isDesktop) SelectableText( height, - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension< + StackColors + >()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), ), if (isDesktop) IconCopyButton(data: height), @@ -1308,10 +1240,9 @@ class _TransactionDetailsViewState ? const _Divider() : const SizedBox(height: 12), RoundedWhiteContainer( - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -1324,58 +1255,54 @@ class _TransactionDetailsViewState children: [ Text( "Confirmations", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), ), if (isDesktop) const SizedBox(height: 2), if (isDesktop) SelectableText( confirmations, - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of( - context, - ) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: + Theme.of( + context, + ) + .extension< + StackColors + >()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), ), ], ), if (!isDesktop) SelectableText( confirmations, - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension< + StackColors + >()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), ), if (isDesktop) IconCopyButton(data: height), @@ -1392,10 +1319,9 @@ class _TransactionDetailsViewState : const SizedBox(height: 12), if (coin is Ethereum) RoundedWhiteContainer( - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -1404,32 +1330,25 @@ class _TransactionDetailsViewState children: [ Text( "Nonce", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle(context), ), SelectableText( _transaction.nonce.toString(), - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), ), ], ), @@ -1440,10 +1359,9 @@ class _TransactionDetailsViewState : const SizedBox(height: 12), if (kDebugMode) RoundedWhiteContainer( - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -1452,32 +1370,25 @@ class _TransactionDetailsViewState children: [ Text( "Tx sub type", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle(context), ), SelectableText( _transaction.subType.toString(), - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), ), ], ), @@ -1486,10 +1397,9 @@ class _TransactionDetailsViewState ? const _Divider() : const SizedBox(height: 12), RoundedWhiteContainer( - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: @@ -1502,14 +1412,13 @@ class _TransactionDetailsViewState children: [ Text( "Transaction ID", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), ), const SizedBox(height: 8), // Flexible( @@ -1518,21 +1427,19 @@ class _TransactionDetailsViewState // child: SelectableText( _transaction.txid, - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension< + StackColors + >()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), ), if (coin is! Epiccash) const SizedBox(height: 8), @@ -1570,28 +1477,24 @@ class _TransactionDetailsViewState try { await launchUrl( uri, - mode: - LaunchMode - .externalApplication, + mode: LaunchMode + .externalApplication, ); } catch (_) { if (context.mounted) { unawaited( showDialog( context: context, - builder: - ( - _, - ) => StackOkDialog( - title: - "Could not open in block explorer", - message: - "Failed to open \"${uri.toString()}\"", - maxWidth: - Util.isDesktop - ? 400 - : null, - ), + builder: (_) => StackOkDialog( + title: + "Could not open in block explorer", + message: + "Failed to open \"${uri.toString()}\"", + maxWidth: + Util.isDesktop + ? 400 + : null, + ), ), ); } @@ -1701,10 +1604,9 @@ class _TransactionDetailsViewState : const SizedBox(height: 12), if (coin is Epiccash) RoundedWhiteContainer( - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -1717,14 +1619,13 @@ class _TransactionDetailsViewState children: [ Text( "Slate ID", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ) + : STextStyles.itemSubtitle( + context, + ), ), // Flexible( // child: FittedBox( @@ -1732,21 +1633,19 @@ class _TransactionDetailsViewState // child: SelectableText( _transaction.slateId ?? "Unknown", - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension< + StackColors + >()! + .textDark, + ) + : STextStyles.itemSubtitle12( + context, + ), ), // ), // ), @@ -1776,165 +1675,154 @@ class _TransactionDetailsViewState floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, floatingActionButton: ((coin is Epiccash || coin is Mimblewimblecoin) && - _transaction.getConfirmations(currentHeight) < 1 && - _transaction.isCancelled == false) - ? ConditionalParent( - condition: isDesktop, - builder: - (child) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 32, - vertical: 16, - ), - child: child, - ), - child: SizedBox( - width: MediaQuery.of(context).size.width - 32, - child: TextButton( - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all( - Theme.of(context).extension()!.textError, - ), + _transaction.getConfirmations(currentHeight) < 1 && + _transaction.isCancelled == false) + ? ConditionalParent( + condition: isDesktop, + builder: (child) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 32, + vertical: 16, + ), + child: child, + ), + child: SizedBox( + width: MediaQuery.of(context).size.width - 32, + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + Theme.of(context).extension()!.textError, ), - onPressed: () async { - final wallet = ref.read(pWallets).getWallet(walletId); - - if (wallet is EpiccashWallet) { - final String? id = _transaction.slateId; - if (id == null) { - unawaited( - showFloatingFlushBar( - type: FlushBarType.warning, - message: "Could not find Epic transaction ID", - context: context, - ), - ); - return; - } + ), + onPressed: () async { + final wallet = ref.read(pWallets).getWallet(walletId); + if (wallet is EpiccashWallet) { + final String? id = _transaction.slateId; + if (id == null) { unawaited( - showDialog( - barrierDismissible: false, + showFloatingFlushBar( + type: FlushBarType.warning, + message: "Could not find Epic transaction ID", context: context, - builder: - (_) => - const CancellingTransactionProgressDialog(), ), ); + return; + } - final result = await wallet - .cancelPendingTransactionAndPost(id); + unawaited( + showDialog( + barrierDismissible: false, + context: context, + builder: (_) => + const CancellingTransactionProgressDialog(), + ), + ); - if (context.mounted) { - // pop progress dialog - Navigator.of(context).pop(); + final result = await wallet + .cancelPendingTransactionAndPost(id); - if (result.isEmpty) { - await showDialog( - context: context, - builder: - (_) => StackOkDialog( - title: "Transaction cancelled", - maxWidth: Util.isDesktop ? 400 : null, - onOkPressed: (_) { - Navigator.of(context).popUntil( - ModalRoute.withName( - WalletView.routeName, - ), - ); - }, - ), - ); - } else { - await showDialog( - context: context, - builder: - (_) => StackOkDialog( - title: "Failed to cancel transaction", - message: result, - maxWidth: Util.isDesktop ? 400 : null, - ), - ); - } - } - } else if (wallet is MimblewimblecoinWallet) { - final String? id = _transaction.slateId; - if (id == null) { - unawaited( - showFloatingFlushBar( - type: FlushBarType.warning, - message: "Could not find MWC transaction ID", - context: context, + if (context.mounted) { + // pop progress dialog + Navigator.of(context).pop(); + + if (result.isEmpty) { + await showDialog( + context: context, + builder: (_) => StackOkDialog( + title: "Transaction cancelled", + maxWidth: Util.isDesktop ? 400 : null, + onOkPressed: (_) { + Navigator.of(context).popUntil( + ModalRoute.withName(WalletView.routeName), + ); + }, ), ); - return; - } - - unawaited( - showDialog( - barrierDismissible: false, + } else { + await showDialog( context: context, - builder: - (_) => - const CancellingTransactionProgressDialog(), - ), - ); - - final result = await wallet - .cancelPendingTransactionAndPost(id); - - if (context.mounted) { - // pop progress dialog - Navigator.of(context).pop(); - - if (result.isEmpty) { - await showDialog( - context: context, - builder: - (_) => StackOkDialog( - title: "Transaction cancelled", - maxWidth: Util.isDesktop ? 400 : null, - onOkPressed: (_) { - Navigator.of(context).popUntil( - ModalRoute.withName( - WalletView.routeName, - ), - ); - }, - ), - ); - } else { - await showDialog( - context: context, - builder: - (_) => StackOkDialog( - title: "Failed to cancel transaction", - message: result, - maxWidth: Util.isDesktop ? 400 : null, - ), - ); - } + builder: (_) => StackOkDialog( + title: "Failed to cancel transaction", + message: result, + maxWidth: Util.isDesktop ? 400 : null, + ), + ); } - } else { + } + } else if (wallet is MimblewimblecoinWallet) { + final String? id = _transaction.slateId; + if (id == null) { unawaited( showFloatingFlushBar( type: FlushBarType.warning, - message: - "ERROR: Wallet type is not Epic Cash or MimbleWimbleCoin", + message: "Could not find MWC transaction ID", context: context, ), ); return; } - }, - child: Text( - "Cancel Transaction", - style: STextStyles.button(context), - ), + + unawaited( + showDialog( + barrierDismissible: false, + context: context, + builder: (_) => + const CancellingTransactionProgressDialog(), + ), + ); + + final result = await wallet + .cancelPendingTransactionAndPost(id); + + if (context.mounted) { + // pop progress dialog + Navigator.of(context).pop(); + + if (result.isEmpty) { + await showDialog( + context: context, + builder: (_) => StackOkDialog( + title: "Transaction cancelled", + maxWidth: Util.isDesktop ? 400 : null, + onOkPressed: (_) { + Navigator.of(context).popUntil( + ModalRoute.withName(WalletView.routeName), + ); + }, + ), + ); + } else { + await showDialog( + context: context, + builder: (_) => StackOkDialog( + title: "Failed to cancel transaction", + message: result, + maxWidth: Util.isDesktop ? 400 : null, + ), + ); + } + } + } else { + unawaited( + showFloatingFlushBar( + type: FlushBarType.warning, + message: + "ERROR: Wallet type is not Epic Cash or MimbleWimbleCoin", + context: context, + ), + ); + return; + } + }, + child: Text( + "Cancel Transaction", + style: STextStyles.button(context), ), ), - ) - : null, + ), + ) + : null, ), ); } @@ -1963,8 +1851,9 @@ class IconCopyButton extends StatelessWidget { height: 26, width: 26, child: RawMaterialButton( - fillColor: - Theme.of(context).extension()!.buttonBackSecondary, + fillColor: Theme.of( + context, + ).extension()!.buttonBackSecondary, elevation: 0, hoverElevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)), @@ -2004,8 +1893,9 @@ class IconPencilButton extends StatelessWidget { height: 26, width: 26, child: RawMaterialButton( - fillColor: - Theme.of(context).extension()!.buttonBackSecondary, + fillColor: Theme.of( + context, + ).extension()!.buttonBackSecondary, elevation: 0, hoverElevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)), From 69d143c26c4f25de3253de527955b7d60af65614 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 6 Oct 2025 14:49:28 -0600 Subject: [PATCH 13/30] implement optional import of frostdart --- .../new/create_new_frost_ms_wallet_view.dart | 98 +-- .../new/steps/frost_create_step_1a.dart | 89 +- .../new/steps/frost_create_step_1b.dart | 48 +- .../new/steps/frost_create_step_2.dart | 55 +- .../new/steps/frost_create_step_3.dart | 34 +- .../new/steps/frost_create_step_5.dart | 44 +- .../reshare/frost_reshare_step_1a.dart | 91 +- .../reshare/frost_reshare_step_1b.dart | 49 +- .../reshare/frost_reshare_step_2abd.dart | 55 +- .../reshare/frost_reshare_step_2c.dart | 21 +- .../reshare/frost_reshare_step_3abd.dart | 54 +- .../reshare/frost_reshare_step_4.dart | 64 +- .../restore/restore_frost_ms_wallet_view.dart | 375 +++++---- .../send_steps/frost_send_step_1b.dart | 66 +- .../send_steps/frost_send_step_2.dart | 102 +-- .../send_steps/frost_send_step_3.dart | 44 +- .../helpers/restore_create_backup.dart | 101 ++- .../complete_reshare_config_view.dart | 120 ++- .../frost_wallet/frost_wallet_providers.dart | 87 +- .../wallet/impl/bitcoin_frost_wallet.dart | 334 ++++---- lib/wl_gen/interfaces/frost_interface.dart | 237 ++++++ .../FROST_frost_interface_impl.template.dart | 796 ++++++++++-------- 22 files changed, 1496 insertions(+), 1468 deletions(-) create mode 100644 lib/wl_gen/interfaces/frost_interface.dart rename lib/services/frost.dart => tool/wl_templates/FROST_frost_interface_impl.template.dart (64%) diff --git a/lib/pages/add_wallet_views/frost_ms/new/create_new_frost_ms_wallet_view.dart b/lib/pages/add_wallet_views/frost_ms/new/create_new_frost_ms_wallet_view.dart index a97c7b7859..74c31d872b 100644 --- a/lib/pages/add_wallet_views/frost_ms/new/create_new_frost_ms_wallet_view.dart +++ b/lib/pages/add_wallet_views/frost_ms/new/create_new_frost_ms_wallet_view.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../frost_route_generator.dart'; import '../../../../providers/frost_wallet/frost_wallet_providers.dart'; -import '../../../../services/frost.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; @@ -20,6 +20,7 @@ import '../../../../widgets/frost_mascot.dart'; import '../../../../widgets/frost_scaffold.dart'; import '../../../../widgets/rounded_white_container.dart'; import '../../../../widgets/stack_dialog.dart'; +import '../../../../wl_gen/interfaces/frost_interface.dart'; class CreateNewFrostMsWalletView extends ConsumerStatefulWidget { const CreateNewFrostMsWalletView({ @@ -114,31 +115,22 @@ class _NewFrostMsWalletViewState child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "What is a threshold?", - style: STextStyles.w600_20(context), - ), - const SizedBox( - height: 12, - ), + Text("What is a threshold?", style: STextStyles.w600_20(context)), + const SizedBox(height: 12), Text( "A threshold is the amount of people required to perform an " "action. This does not have to be the same number as the " "total number in the group.", style: STextStyles.w400_16(context), ), - const SizedBox( - height: 6, - ), + const SizedBox(height: 6), Text( "For example, if you have 3 people in the group, but a threshold " "of 2, then you only need 2 out of the 3 people to sign for an " "action to take place.", style: STextStyles.w400_16(context), ), - const SizedBox( - height: 6, - ), + const SizedBox(height: 6), Text( "Conversely if you have a group of 3 AND a threshold of 3, you " "will need all 3 people in the group to sign to approve any " @@ -177,17 +169,15 @@ class _NewFrostMsWalletViewState 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam est justo, ', ), ), - body: SizedBox( - width: 480, - child: child, - ), + body: SizedBox(width: 480, child: child), ), child: ConditionalParent( condition: !Util.isDesktop, builder: (child) => Background( child: Scaffold( - backgroundColor: - Theme.of(context).extension()!.background, + backgroundColor: Theme.of( + context, + ).extension()!.background, appBar: AppBar( leading: AppBarBackButton( onPressed: () { @@ -229,8 +219,9 @@ class _NewFrostMsWalletViewState Text( "Threshold", style: STextStyles.w500_14(context).copyWith( - color: - Theme.of(context).extension()!.textDark3, + color: Theme.of( + context, + ).extension()!.textDark3, ), ), CustomTextButton( @@ -239,9 +230,7 @@ class _NewFrostMsWalletViewState ), ], ), - const SizedBox( - height: 10, - ), + const SizedBox(height: 10), TextField( keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], @@ -251,18 +240,14 @@ class _NewFrostMsWalletViewState hintStyle: STextStyles.fieldLabel(context), ), ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), Text( "Number of participants", style: STextStyles.w500_14(context).copyWith( color: Theme.of(context).extension()!.textDark3, ), ), - const SizedBox( - height: 10, - ), + const SizedBox(height: 10), Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, @@ -277,9 +262,7 @@ class _NewFrostMsWalletViewState hintStyle: STextStyles.fieldLabel(context), ), ), - const SizedBox( - height: 6, - ), + const SizedBox(height: 6), Row( children: [ Expanded( @@ -294,9 +277,7 @@ class _NewFrostMsWalletViewState ), ], ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), if (controllers.isNotEmpty) Text( "My name", @@ -304,10 +285,7 @@ class _NewFrostMsWalletViewState color: Theme.of(context).extension()!.textDark3, ), ), - if (controllers.isNotEmpty) - const SizedBox( - height: 10, - ), + if (controllers.isNotEmpty) const SizedBox(height: 10), if (controllers.isNotEmpty) Column( mainAxisSize: MainAxisSize.min, @@ -320,9 +298,7 @@ class _NewFrostMsWalletViewState hintStyle: STextStyles.fieldLabel(context), ), ), - const SizedBox( - height: 6, - ), + const SizedBox(height: 6), Row( children: [ Expanded( @@ -337,10 +313,7 @@ class _NewFrostMsWalletViewState ), ], ), - if (controllers.length > 1) - const SizedBox( - height: 16, - ), + if (controllers.length > 1) const SizedBox(height: 16), if (controllers.length > 1) Column( mainAxisSize: MainAxisSize.min, @@ -349,13 +322,12 @@ class _NewFrostMsWalletViewState Text( "Remaining participants", style: STextStyles.w500_14(context).copyWith( - color: - Theme.of(context).extension()!.textDark3, + color: Theme.of( + context, + ).extension()!.textDark3, ), ), - const SizedBox( - height: 6, - ), + const SizedBox(height: 6), Row( children: [ Expanded( @@ -375,9 +347,7 @@ class _NewFrostMsWalletViewState children: [ for (int i = 1; i < controllers.length; i++) Padding( - padding: const EdgeInsets.only( - top: 10, - ), + padding: const EdgeInsets.only(top: 10), child: TextField( controller: controllers[i], decoration: InputDecoration( @@ -389,9 +359,7 @@ class _NewFrostMsWalletViewState ], ), if (!Util.isDesktop) const Spacer(), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), PrimaryButton( label: "Create new group", onPressed: () async { @@ -411,14 +379,14 @@ class _NewFrostMsWalletViewState ); } - final config = Frost.createMultisigConfig( + final config = frostInterface.createMultisigConfig( name: controllers.first.text.trim(), threshold: int.parse(_thresholdController.text), participants: controllers.map((e) => e.text.trim()).toList(), ); - ref.read(pFrostMyName.notifier).state = - controllers.first.text.trim(); + ref.read(pFrostMyName.notifier).state = controllers.first.text + .trim(); ref.read(pFrostMultisigConfig.notifier).state = config; ref.read(pFrostScaffoldArgs.state).state = ( @@ -434,9 +402,9 @@ class _NewFrostMsWalletViewState callerRouteName: CreateNewFrostMsWalletView.routeName, ); - await Navigator.of(context).pushNamed( - FrostStepScaffold.routeName, - ); + await Navigator.of( + context, + ).pushNamed(FrostStepScaffold.routeName); }, ), ], diff --git a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1a.dart b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1a.dart index 7b9efb7aa8..5273014115 100644 --- a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1a.dart +++ b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1a.dart @@ -4,7 +4,6 @@ import 'package:flutter_svg/flutter_svg.dart'; import '../../../../../frost_route_generator.dart'; import '../../../../../providers/frost_wallet/frost_wallet_providers.dart'; -import '../../../../../services/frost.dart'; import '../../../../../themes/stack_colors.dart'; import '../../../../../utilities/assets.dart'; import '../../../../../utilities/text_styles.dart'; @@ -17,6 +16,7 @@ import '../../../../../widgets/detail_item.dart'; import '../../../../../widgets/dialogs/simple_mobile_dialog.dart'; import '../../../../../widgets/frost_step_user_steps.dart'; import '../../../../../widgets/qr.dart'; +import '../../../../../wl_gen/interfaces/frost_interface.dart'; import '../../../../wallet_view/transaction_views/transaction_details_view.dart'; class FrostCreateStep1a extends ConsumerStatefulWidget { @@ -41,7 +41,7 @@ class _FrostCreateStep1aState extends ConsumerState { bool _userVerifyContinue = false; void _showParticipantsDialog() { - final participants = Frost.getParticipants( + final participants = frostInterface.getParticipants( multisigConfig: ref.read(pFrostMultisigConfig.state).state!, ); @@ -53,9 +53,7 @@ class _FrostCreateStep1aState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox( - height: 24, - ), + const SizedBox(height: 24), Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Text( @@ -63,9 +61,7 @@ class _FrostCreateStep1aState extends ConsumerState { style: STextStyles.w600_20(context), ), ), - const SizedBox( - height: 12, - ), + const SizedBox(height: 12), Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Text( @@ -75,9 +71,7 @@ class _FrostCreateStep1aState extends ConsumerState { ), ), ), - const SizedBox( - height: 12, - ), + const SizedBox(height: 12), for (final participant in participants) Column( mainAxisSize: MainAxisSize.min, @@ -85,12 +79,11 @@ class _FrostCreateStep1aState extends ConsumerState { Container( width: double.infinity, height: 1.5, - color: - Theme.of(context).extension()!.background, - ), - const SizedBox( - height: 12, + color: Theme.of( + context, + ).extension()!.background, ), + const SizedBox(height: 12), Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Row( @@ -99,12 +92,10 @@ class _FrostCreateStep1aState extends ConsumerState { width: 26, height: 26, decoration: BoxDecoration( - color: Theme.of(context) - .extension()! - .textFieldActiveBG, - borderRadius: BorderRadius.circular( - 200, - ), + color: Theme.of( + context, + ).extension()!.textFieldActiveBG, + borderRadius: BorderRadius.circular(200), ), child: Center( child: SvgPicture.asset( @@ -114,32 +105,22 @@ class _FrostCreateStep1aState extends ConsumerState { ), ), ), - const SizedBox( - width: 8, - ), + const SizedBox(width: 8), Expanded( child: Text( participant, style: STextStyles.w500_14(context), ), ), - const SizedBox( - width: 8, - ), - IconCopyButton( - data: participant, - ), + const SizedBox(width: 8), + IconCopyButton(data: participant), ], ), ), - const SizedBox( - height: 12, - ), + const SizedBox(height: 12), ], ), - const SizedBox( - height: 24, - ), + const SizedBox(height: 24), ], ), ), @@ -152,12 +133,8 @@ class _FrostCreateStep1aState extends ConsumerState { padding: const EdgeInsets.all(16), child: Column( children: [ - const FrostStepUserSteps( - userSteps: info, - ), - const SizedBox( - height: 20, - ), + const FrostStepUserSteps(userSteps: info), + const SizedBox(height: 20), SizedBox( height: 220, child: Row( @@ -170,9 +147,7 @@ class _FrostCreateStep1aState extends ConsumerState { ], ), ), - const SizedBox( - height: 20, - ), + const SizedBox(height: 20), DetailItem( title: "Encoded config", detail: ref.watch(pFrostMultisigConfig.state).state ?? "Error", @@ -186,9 +161,7 @@ class _FrostCreateStep1aState extends ConsumerState { ref.watch(pFrostMultisigConfig.state).state ?? "Error", ), ), - SizedBox( - height: Util.isDesktop ? 64 : 16, - ), + SizedBox(height: Util.isDesktop ? 64 : 16), Row( children: [ Expanded( @@ -199,13 +172,8 @@ class _FrostCreateStep1aState extends ConsumerState { ), ], ), - if (!Util.isDesktop) - const Spacer( - flex: 2, - ), - const SizedBox( - height: 16, - ), + if (!Util.isDesktop) const Spacer(flex: 2), + const SizedBox(height: 16), CheckboxTextButton( label: "I have verified that everyone has joined the group", onChanged: (value) { @@ -214,15 +182,14 @@ class _FrostCreateStep1aState extends ConsumerState { }); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), PrimaryButton( label: "Start key generation", enabled: _userVerifyContinue, onPressed: () async { - ref.read(pFrostStartKeyGenData.notifier).state = - Frost.startKeyGeneration( + ref + .read(pFrostStartKeyGenData.notifier) + .state = frostInterface.startKeyGeneration( multisigConfig: ref.watch(pFrostMultisigConfig.state).state!, myName: ref.read(pFrostMyName.state).state!, ); diff --git a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1b.dart b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1b.dart index 59597c659f..3661e853a9 100644 --- a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1b.dart +++ b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1b.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '../../../../../frost_route_generator.dart'; import '../../../../../providers/frost_wallet/frost_wallet_providers.dart'; -import '../../../../../services/frost.dart'; import '../../../../../utilities/text_styles.dart'; import '../../../../../utilities/util.dart'; import '../../../../../widgets/custom_buttons/checkbox_text_button.dart'; @@ -11,6 +11,7 @@ import '../../../../../widgets/frost_step_user_steps.dart'; import '../../../../../widgets/rounded_white_container.dart'; import '../../../../../widgets/stack_dialog.dart'; import '../../../../../widgets/textfields/frost_step_field.dart'; +import '../../../../../wl_gen/interfaces/frost_interface.dart'; class FrostCreateStep1b extends ConsumerStatefulWidget { const FrostCreateStep1b({super.key}); @@ -62,12 +63,8 @@ class _FrostCreateStep1bState extends ConsumerState { padding: const EdgeInsets.all(16), child: Column( children: [ - const FrostStepUserSteps( - userSteps: info, - ), - const SizedBox( - height: 16, - ), + const FrostStepUserSteps(userSteps: info), + const SizedBox(height: 16), FrostStepField( controller: configFieldController, focusNode: configFocusNode, @@ -80,9 +77,7 @@ class _FrostCreateStep1bState extends ConsumerState { }); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), FrostStepField( controller: myNameFieldController, focusNode: myNameFocusNode, @@ -95,9 +90,7 @@ class _FrostCreateStep1bState extends ConsumerState { }); }, ), - const SizedBox( - height: 6, - ), + const SizedBox(height: 6), Row( children: [ Expanded( @@ -111,13 +104,9 @@ class _FrostCreateStep1bState extends ConsumerState { ), ], ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), if (!Util.isDesktop) const Spacer(), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), CheckboxTextButton( label: "I have verified that everyone has joined the group", onChanged: (value) { @@ -126,9 +115,7 @@ class _FrostCreateStep1bState extends ConsumerState { }); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), PrimaryButton( label: "Start key generation", enabled: _userVerifyContinue && !_nameEmpty && !_configEmpty, @@ -139,7 +126,9 @@ class _FrostCreateStep1bState extends ConsumerState { final config = configFieldController.text; - if (!Frost.validateEncodedMultisigConfig(encodedConfig: config)) { + if (!frostInterface.validateEncodedMultisigConfig( + encodedConfig: config, + )) { return await showDialog( context: context, builder: (_) => StackOkDialog( @@ -149,7 +138,8 @@ class _FrostCreateStep1bState extends ConsumerState { ); } - if (!Frost.getParticipants(multisigConfig: config) + if (!frostInterface + .getParticipants(multisigConfig: config) .contains(myNameFieldController.text)) { return await showDialog( context: context, @@ -163,11 +153,11 @@ class _FrostCreateStep1bState extends ConsumerState { ref.read(pFrostMyName.state).state = myNameFieldController.text; ref.read(pFrostMultisigConfig.notifier).state = config; - ref.read(pFrostStartKeyGenData.state).state = - Frost.startKeyGeneration( - multisigConfig: ref.read(pFrostMultisigConfig.state).state!, - myName: ref.read(pFrostMyName.state).state!, - ); + ref.read(pFrostStartKeyGenData.state).state = frostInterface + .startKeyGeneration( + multisigConfig: ref.read(pFrostMultisigConfig.state).state!, + myName: ref.read(pFrostMyName.state).state!, + ); ref.read(pFrostCreateCurrentStep.state).state = 2; await Navigator.of(context).pushNamed( ref diff --git a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_2.dart b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_2.dart index d8aae6d6d5..9bef38d226 100644 --- a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_2.dart +++ b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_2.dart @@ -3,7 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../../frost_route_generator.dart'; import '../../../../../providers/frost_wallet/frost_wallet_providers.dart'; -import '../../../../../services/frost.dart'; import '../../../../../utilities/logger.dart'; import '../../../../../utilities/util.dart'; import '../../../../../widgets/custom_buttons/checkbox_text_button.dart'; @@ -15,12 +14,11 @@ import '../../../../../widgets/dialogs/frost/frost_error_dialog.dart'; import '../../../../../widgets/frost_step_user_steps.dart'; import '../../../../../widgets/stack_dialog.dart'; import '../../../../../widgets/textfields/frost_step_field.dart'; +import '../../../../../wl_gen/interfaces/frost_interface.dart'; import '../../../../wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart'; class FrostCreateStep2 extends ConsumerStatefulWidget { - const FrostCreateStep2({ - super.key, - }); + const FrostCreateStep2({super.key}); static const String routeName = "/frostCreateStep2"; static const String title = "Commitments"; @@ -47,7 +45,7 @@ class _FrostCreateStep2State extends ConsumerState { @override void initState() { - participants = Frost.getParticipants( + participants = frostInterface.getParticipants( multisigConfig: ref.read(pFrostMultisigConfig.state).state!, ); myIndex = participants.indexOf(ref.read(pFrostMyName.state).state!); @@ -81,9 +79,7 @@ class _FrostCreateStep2State extends ConsumerState { padding: const EdgeInsets.all(16), child: Column( children: [ - const FrostStepUserSteps( - userSteps: info, - ), + const FrostStepUserSteps(userSteps: info), const SizedBox(height: 12), DetailItem( title: "My name", @@ -94,17 +90,11 @@ class _FrostCreateStep2State extends ConsumerState { title: "My commitment", detail: myCommitment, button: Util.isDesktop - ? IconCopyButton( - data: myCommitment, - ) - : SimpleCopyButton( - data: myCommitment, - ), + ? IconCopyButton(data: myCommitment) + : SimpleCopyButton(data: myCommitment), ), const SizedBox(height: 12), - FrostQrDialogPopupButton( - data: myCommitment, - ), + FrostQrDialogPopupButton(data: myCommitment), const SizedBox(height: 12), for (int i = 0; i < participants.length; i++) Padding( @@ -135,7 +125,8 @@ class _FrostCreateStep2State extends ConsumerState { const SizedBox(height: 12), PrimaryButton( label: "Generate shares", - enabled: _userVerifyContinue && + enabled: + _userVerifyContinue && !fieldIsEmptyFlags.reduce((v, e) => v |= e), onPressed: () async { // check for empty commitments @@ -156,19 +147,19 @@ class _FrostCreateStep2State extends ConsumerState { commitments.insert(myIndex, myCommitment); try { - ref.read(pFrostSecretSharesData.notifier).state = - Frost.generateSecretShares( - multisigConfigWithNamePtr: ref - .read(pFrostStartKeyGenData.state) - .state! - .multisigConfigWithNamePtr, - mySeed: ref.read(pFrostStartKeyGenData.state).state!.seed, - secretShareMachineWrapperPtr: ref - .read(pFrostStartKeyGenData.state) - .state! - .secretShareMachineWrapperPtr, - commitments: commitments, - ); + ref.read(pFrostSecretSharesData.notifier).state = frostInterface + .generateSecretShares( + multisigConfigWithNamePtr: ref + .read(pFrostStartKeyGenData.state) + .state! + .multisigConfigWithNamePtr, + mySeed: ref.read(pFrostStartKeyGenData.state).state!.seed, + secretShareMachineWrapperPtr: ref + .read(pFrostStartKeyGenData.state) + .state! + .secretShareMachineWrapperPtr, + commitments: commitments, + ); ref.read(pFrostCreateCurrentStep.state).state = 3; await Navigator.of(context).pushNamed( @@ -178,7 +169,7 @@ class _FrostCreateStep2State extends ConsumerState { .routeName, ); } catch (e, s) { - Logging.instance.f("$e\n$s", error: e, stackTrace: s,); + Logging.instance.f("$e\n$s", error: e, stackTrace: s); if (context.mounted) { return await showDialog( context: context, diff --git a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_3.dart b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_3.dart index ec565cb597..1b2ba0c6ac 100644 --- a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_3.dart +++ b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_3.dart @@ -3,7 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../../frost_route_generator.dart'; import '../../../../../providers/frost_wallet/frost_wallet_providers.dart'; -import '../../../../../services/frost.dart'; import '../../../../../utilities/logger.dart'; import '../../../../../utilities/util.dart'; import '../../../../../widgets/custom_buttons/checkbox_text_button.dart'; @@ -15,6 +14,7 @@ import '../../../../../widgets/dialogs/frost/frost_error_dialog.dart'; import '../../../../../widgets/frost_step_user_steps.dart'; import '../../../../../widgets/stack_dialog.dart'; import '../../../../../widgets/textfields/frost_step_field.dart'; +import '../../../../../wl_gen/interfaces/frost_interface.dart'; import '../../../../wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart'; class FrostCreateStep3 extends ConsumerStatefulWidget { @@ -46,7 +46,7 @@ class _FrostCreateStep3State extends ConsumerState { @override void initState() { - participants = Frost.getParticipants( + participants = frostInterface.getParticipants( multisigConfig: ref.read(pFrostMultisigConfig.state).state!, ); myIndex = participants.indexOf(ref.read(pFrostMyName.state).state!); @@ -80,9 +80,7 @@ class _FrostCreateStep3State extends ConsumerState { padding: const EdgeInsets.all(16), child: Column( children: [ - const FrostStepUserSteps( - userSteps: info, - ), + const FrostStepUserSteps(userSteps: info), const SizedBox(height: 12), DetailItem( title: "My name", @@ -93,17 +91,11 @@ class _FrostCreateStep3State extends ConsumerState { title: "My share", detail: myShare, button: Util.isDesktop - ? IconCopyButton( - data: myShare, - ) - : SimpleCopyButton( - data: myShare, - ), + ? IconCopyButton(data: myShare) + : SimpleCopyButton(data: myShare), ), const SizedBox(height: 12), - FrostQrDialogPopupButton( - data: myShare, - ), + FrostQrDialogPopupButton(data: myShare), const SizedBox(height: 12), for (int i = 0; i < participants.length; i++) Padding( @@ -131,12 +123,11 @@ class _FrostCreateStep3State extends ConsumerState { }); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), PrimaryButton( label: "Generate", - enabled: _userVerifyContinue && + enabled: + _userVerifyContinue && !fieldIsEmptyFlags.reduce((v, e) => v |= e), onPressed: () async { // check for empty commitments @@ -157,8 +148,9 @@ class _FrostCreateStep3State extends ConsumerState { shares.insert(myIndex, myShare); try { - ref.read(pFrostCompletedKeyGenData.notifier).state = - Frost.completeKeyGeneration( + ref + .read(pFrostCompletedKeyGenData.notifier) + .state = frostInterface.completeKeyGeneration( multisigConfigWithNamePtr: ref .read(pFrostStartKeyGenData.state) .state! @@ -178,7 +170,7 @@ class _FrostCreateStep3State extends ConsumerState { .routeName, ); } catch (e, s) { - Logging.instance.f("$e\n$s", error: e, stackTrace: s,); + Logging.instance.f("$e\n$s", error: e, stackTrace: s); if (context.mounted) { return await showDialog( diff --git a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_5.dart b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_5.dart index 0d7c53e8b0..6516690760 100644 --- a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_5.dart +++ b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_5.dart @@ -11,7 +11,6 @@ import '../../../../../pages_desktop_specific/desktop_home_view.dart'; import '../../../../../providers/frost_wallet/frost_wallet_providers.dart'; import '../../../../../providers/global/secure_store_provider.dart'; import '../../../../../providers/providers.dart'; -import '../../../../../services/frost.dart'; import '../../../../../themes/stack_colors.dart'; import '../../../../../utilities/assets.dart'; import '../../../../../utilities/logger.dart'; @@ -26,6 +25,7 @@ import '../../../../../widgets/desktop/primary_button.dart'; import '../../../../../widgets/detail_item.dart'; import '../../../../../widgets/loading_indicator.dart'; import '../../../../../widgets/rounded_container.dart'; +import '../../../../../wl_gen/interfaces/frost_interface.dart'; import '../../../../home_view/home_view.dart'; import '../../../../wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart' as tvd; @@ -56,10 +56,14 @@ class _FrostCreateStep5State extends ConsumerState { @override void initState() { seed = ref.read(pFrostStartKeyGenData.state).state!.seed; - serializedKeys = - ref.read(pFrostCompletedKeyGenData.state).state!.serializedKeys; - recoveryString = - ref.read(pFrostCompletedKeyGenData.state).state!.recoveryString; + serializedKeys = ref + .read(pFrostCompletedKeyGenData.state) + .state! + .serializedKeys; + recoveryString = ref + .read(pFrostCompletedKeyGenData.state) + .state! + .recoveryString; multisigConfig = ref.read(pFrostMultisigConfig.state).state!; multisigId = ref.read(pFrostCompletedKeyGenData.state).state!.multisigId; @@ -73,15 +77,15 @@ class _FrostCreateStep5State extends ConsumerState { child: Column( children: [ RoundedContainer( - color: - Theme.of(context).extension()!.warningBackground, + color: Theme.of( + context, + ).extension()!.warningBackground, child: Text( _warning, style: STextStyles.w500_14(context).copyWith( - color: - Theme.of( - context, - ).extension()!.warningForeground, + color: Theme.of( + context, + ).extension()!.warningForeground, ), ), ), @@ -89,19 +93,17 @@ class _FrostCreateStep5State extends ConsumerState { DetailItem( title: "Multisig Config", detail: multisigConfig, - button: - Util.isDesktop - ? tvd.IconCopyButton(data: multisigConfig) - : SimpleCopyButton(data: multisigConfig), + button: Util.isDesktop + ? tvd.IconCopyButton(data: multisigConfig) + : SimpleCopyButton(data: multisigConfig), ), const SizedBox(height: 12), DetailItem( title: "Keys", detail: serializedKeys, - button: - Util.isDesktop - ? tvd.IconCopyButton(data: serializedKeys) - : SimpleCopyButton(data: serializedKeys), + button: Util.isDesktop + ? tvd.IconCopyButton(data: serializedKeys) + : SimpleCopyButton(data: serializedKeys), ), if (!Util.isDesktop) const Spacer(), const SizedBox(height: 12), @@ -156,10 +158,10 @@ class _FrostCreateStep5State extends ConsumerState { serializedKeys: serializedKeys, multisigId: multisigId, myName: ref.read(pFrostMyName.state).state!, - participants: Frost.getParticipants( + participants: frostInterface.getParticipants( multisigConfig: ref.read(pFrostMultisigConfig.state).state!, ), - threshold: Frost.getThreshold( + threshold: frostInterface.getThreshold( multisigConfig: ref.read(pFrostMultisigConfig.state).state!, ), ); diff --git a/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1a.dart b/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1a.dart index 740baafc3a..9fe973bce6 100644 --- a/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1a.dart +++ b/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1a.dart @@ -6,7 +6,6 @@ import '../../../../frost_route_generator.dart'; import '../../../../providers/db/main_db_provider.dart'; import '../../../../providers/frost_wallet/frost_wallet_providers.dart'; import '../../../../providers/global/wallets_provider.dart'; -import '../../../../services/frost.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; import '../../../../utilities/logger.dart'; @@ -23,6 +22,7 @@ import '../../../../widgets/dialogs/frost/frost_error_dialog.dart'; import '../../../../widgets/dialogs/simple_mobile_dialog.dart'; import '../../../../widgets/frost_step_user_steps.dart'; import '../../../../widgets/qr.dart'; +import '../../../../wl_gen/interfaces/frost_interface.dart'; import '../../../wallet_view/transaction_views/transaction_details_view.dart'; class FrostReshareStep1a extends ConsumerStatefulWidget { @@ -63,9 +63,9 @@ class _FrostReshareStep1aState extends ConsumerState { final serializedKeys = await wallet.getSerializedKeys(); if (mounted) { - final result = Frost.beginResharer( + final result = frostInterface.beginResharer( serializedKeys: serializedKeys!, - config: Frost.decodeRConfig( + config: frostInterface.decodeRConfig( ref.read(pFrostResharingData).resharerRConfig!, ), ); @@ -81,14 +81,12 @@ class _FrostReshareStep1aState extends ConsumerState { ); } } catch (e, s) { - Logging.instance.f("$e\n$s", error: e, stackTrace: s,); + Logging.instance.f("$e\n$s", error: e, stackTrace: s); if (mounted) { await showDialog( context: context, - builder: (_) => FrostErrorDialog( - title: e.toString(), - ), + builder: (_) => FrostErrorDialog(title: e.toString()), ); } } finally { @@ -97,8 +95,10 @@ class _FrostReshareStep1aState extends ConsumerState { } void _showParticipantsDialog() { - final participants = - ref.read(pFrostResharingData).configData!.newParticipants; + final participants = ref + .read(pFrostResharingData) + .configData! + .newParticipants; showDialog( context: context, @@ -108,9 +108,7 @@ class _FrostReshareStep1aState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox( - height: 24, - ), + const SizedBox(height: 24), Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Text( @@ -118,9 +116,7 @@ class _FrostReshareStep1aState extends ConsumerState { style: STextStyles.w600_20(context), ), ), - const SizedBox( - height: 12, - ), + const SizedBox(height: 12), Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Text( @@ -130,9 +126,7 @@ class _FrostReshareStep1aState extends ConsumerState { ), ), ), - const SizedBox( - height: 12, - ), + const SizedBox(height: 12), for (final participant in participants) Column( mainAxisSize: MainAxisSize.min, @@ -140,12 +134,11 @@ class _FrostReshareStep1aState extends ConsumerState { Container( width: double.infinity, height: 1.5, - color: - Theme.of(context).extension()!.background, - ), - const SizedBox( - height: 12, + color: Theme.of( + context, + ).extension()!.background, ), + const SizedBox(height: 12), Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Row( @@ -154,12 +147,10 @@ class _FrostReshareStep1aState extends ConsumerState { width: 26, height: 26, decoration: BoxDecoration( - color: Theme.of(context) - .extension()! - .textFieldActiveBG, - borderRadius: BorderRadius.circular( - 200, - ), + color: Theme.of( + context, + ).extension()!.textFieldActiveBG, + borderRadius: BorderRadius.circular(200), ), child: Center( child: SvgPicture.asset( @@ -169,32 +160,22 @@ class _FrostReshareStep1aState extends ConsumerState { ), ), ), - const SizedBox( - width: 8, - ), + const SizedBox(width: 8), Expanded( child: Text( participant, style: STextStyles.w500_14(context), ), ), - const SizedBox( - width: 8, - ), - IconCopyButton( - data: participant, - ), + const SizedBox(width: 8), + IconCopyButton(data: participant), ], ), ), - const SizedBox( - height: 12, - ), + const SizedBox(height: 12), ], ), - const SizedBox( - height: 24, - ), + const SizedBox(height: 24), ], ), ), @@ -227,9 +208,7 @@ class _FrostReshareStep1aState extends ConsumerState { padding: const EdgeInsets.all(16), child: Column( children: [ - const FrostStepUserSteps( - userSteps: info, - ), + const FrostStepUserSteps(userSteps: info), const SizedBox(height: 20), SizedBox( height: 220, @@ -243,9 +222,7 @@ class _FrostReshareStep1aState extends ConsumerState { ], ), ), - const SizedBox( - height: 32, - ), + const SizedBox(height: 32), DetailItem( title: "Config", detail: ref.watch(pFrostResharingData).resharerRConfig!, @@ -257,9 +234,7 @@ class _FrostReshareStep1aState extends ConsumerState { data: ref.watch(pFrostResharingData).resharerRConfig!, ), ), - SizedBox( - height: Util.isDesktop ? 64 : 16, - ), + SizedBox(height: Util.isDesktop ? 64 : 16), Row( children: [ Expanded( @@ -271,10 +246,7 @@ class _FrostReshareStep1aState extends ConsumerState { ], ), if (iAmInvolved && !Util.isDesktop) const Spacer(), - if (iAmInvolved) - const SizedBox( - height: 16, - ), + if (iAmInvolved) const SizedBox(height: 16), if (iAmInvolved) CheckboxTextButton( label: "I have verified that everyone has imported the config", @@ -284,10 +256,7 @@ class _FrostReshareStep1aState extends ConsumerState { }); }, ), - if (iAmInvolved) - const SizedBox( - height: 16, - ), + if (iAmInvolved) const SizedBox(height: 16), if (iAmInvolved) PrimaryButton( label: "Start resharing", diff --git a/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1b.dart b/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1b.dart index 7224e2acb0..b4971d6f5e 100644 --- a/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1b.dart +++ b/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_1b.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:frostdart/frostdart.dart'; + +// import 'package:frostdart/frostdart.dart'; import '../../../../frost_route_generator.dart'; import '../../../../providers/db/main_db_provider.dart'; import '../../../../providers/frost_wallet/frost_wallet_providers.dart'; import '../../../../providers/global/secure_store_provider.dart'; -import '../../../../services/frost.dart'; import '../../../../utilities/format.dart'; import '../../../../utilities/logger.dart'; import '../../../../utilities/util.dart'; @@ -16,11 +16,10 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/dialogs/frost/frost_error_dialog.dart'; import '../../../../widgets/frost_step_user_steps.dart'; import '../../../../widgets/textfields/frost_step_field.dart'; +import '../../../../wl_gen/interfaces/frost_interface.dart'; class FrostReshareStep1b extends ConsumerStatefulWidget { - const FrostReshareStep1b({ - super.key, - }); + const FrostReshareStep1b({super.key}); static const String routeName = "/frostReshareStep1b"; static const String title = "Import reshare config"; @@ -70,8 +69,8 @@ class _FrostReshareStep1bState extends ConsumerState { String? salt; try { salt = Format.uint8listToString( - resharerSalt( - resharerConfig: Frost.decodeRConfig( + frostInterface.getResharerSalt( + resharerConfig: frostInterface.decodeRConfig( ref.read(pFrostResharingData).resharerRConfig!, ), ), @@ -95,13 +94,13 @@ class _FrostReshareStep1bState extends ConsumerState { }); } - final serializedKeys = await ref.read(secureStoreProvider).read( - key: "{$walletId}_serializedFROSTKeys", - ); + final serializedKeys = await ref + .read(secureStoreProvider) + .read(key: "{$walletId}_serializedFROSTKeys"); if (mounted) { - final result = Frost.beginResharer( + final result = frostInterface.beginResharer( serializedKeys: serializedKeys!, - config: Frost.decodeRConfig( + config: frostInterface.decodeRConfig( ref.read(pFrostResharingData).resharerRConfig!, ), ); @@ -117,14 +116,12 @@ class _FrostReshareStep1bState extends ConsumerState { ); } } catch (e, s) { - Logging.instance.f("$e\n$s", error: e, stackTrace: s,); + Logging.instance.f("$e\n$s", error: e, stackTrace: s); if (mounted) { await showDialog( context: context, - builder: (_) => FrostErrorDialog( - title: e.toString(), - ), + builder: (_) => FrostErrorDialog(title: e.toString()), ); } } finally { @@ -153,12 +150,8 @@ class _FrostReshareStep1bState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox( - height: 16, - ), - const FrostStepUserSteps( - userSteps: info, - ), + const SizedBox(height: 16), + const FrostStepUserSteps(userSteps: info), const SizedBox(height: 20), FrostStepField( controller: configFieldController, @@ -172,13 +165,9 @@ class _FrostReshareStep1bState extends ConsumerState { }); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), if (!Util.isDesktop) const Spacer(), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), CheckboxTextButton( label: "I have verified that everyone has imported the config", onChanged: (value) { @@ -187,9 +176,7 @@ class _FrostReshareStep1bState extends ConsumerState { }); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), PrimaryButton( label: "Start resharing", enabled: !_configEmpty && _userVerifyContinue, diff --git a/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_2abd.dart b/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_2abd.dart index b54b6041b3..7cc2ce539a 100644 --- a/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_2abd.dart +++ b/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_2abd.dart @@ -6,7 +6,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../frost_route_generator.dart'; import '../../../../providers/db/main_db_provider.dart'; import '../../../../providers/frost_wallet/frost_wallet_providers.dart'; -import '../../../../services/frost.dart'; import '../../../../utilities/logger.dart'; import '../../../../utilities/util.dart'; import '../../../../wallets/isar/models/frost_wallet_info.dart'; @@ -17,6 +16,7 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/detail_item.dart'; import '../../../../widgets/dialogs/frost/frost_error_dialog.dart'; import '../../../../widgets/textfields/frost_step_field.dart'; +import '../../../../wl_gen/interfaces/frost_interface.dart'; import '../../../wallet_view/transaction_views/transaction_details_view.dart'; class FrostReshareStep2abd extends ConsumerStatefulWidget { @@ -60,9 +60,9 @@ class _FrostReshareStep2abdState extends ConsumerState { resharerStarts.insert(myResharerIndexIndex, myResharerStart); } - final result = Frost.beginReshared( + final result = frostInterface.beginReshared( myName: ref.read(pFrostResharingData).myName!, - resharerConfig: Frost.decodeRConfig( + resharerConfig: frostInterface.decodeRConfig( ref.read(pFrostResharingData).resharerRConfig!, ), resharerStarts: resharerStarts, @@ -79,15 +79,13 @@ class _FrostReshareStep2abdState extends ConsumerState { .routeName, ); } catch (e, s) { - Logging.instance.f("$e\n$s", error: e, stackTrace: s,); + Logging.instance.f("$e\n$s", error: e, stackTrace: s); if (mounted) { await showDialog( context: context, - builder: (_) => FrostErrorDialog( - title: "Error", - message: e.toString(), - ), + builder: (_) => + FrostErrorDialog(title: "Error", message: e.toString()), ); } } finally { @@ -103,11 +101,14 @@ class _FrostReshareStep2abdState extends ConsumerState { .isar .frostWalletInfo .getByWalletIdSync(ref.read(pFrostScaffoldArgs)!.walletId!)!; - final myOldIndex = - frostInfo.participants.indexOf(ref.read(pFrostResharingData).myName!); + final myOldIndex = frostInfo.participants.indexOf( + ref.read(pFrostResharingData).myName!, + ); - myResharerStart = - ref.read(pFrostResharingData).startResharerData!.resharerStart; + myResharerStart = ref + .read(pFrostResharingData) + .startResharerData! + .resharerStart; resharers = ref.read(pFrostResharingData).configData!.resharers; myResharerIndexIndex = resharers.values.toList().indexOf(myOldIndex); @@ -151,20 +152,12 @@ class _FrostReshareStep2abdState extends ConsumerState { title: "My resharer", detail: myResharerStart, button: Util.isDesktop - ? IconCopyButton( - data: myResharerStart, - ) - : SimpleCopyButton( - data: myResharerStart, - ), + ? IconCopyButton(data: myResharerStart) + : SimpleCopyButton(data: myResharerStart), ), const SizedBox(height: 12), - FrostQrDialogPopupButton( - data: myResharerStart, - ), - const SizedBox( - height: 12, - ), + FrostQrDialogPopupButton(data: myResharerStart), + const SizedBox(height: 12), Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -175,7 +168,8 @@ class _FrostReshareStep2abdState extends ConsumerState { focusNode: focusNodes[i], showQrScanOption: true, label: resharers.keys.elementAt(i), - hint: "Enter " + hint: + "Enter " "${resharers.keys.elementAt(i)}" "'s resharer", onChanged: (_) { @@ -187,9 +181,7 @@ class _FrostReshareStep2abdState extends ConsumerState { ], ), if (!Util.isDesktop) const Spacer(), - const SizedBox( - height: 12, - ), + const SizedBox(height: 12), CheckboxTextButton( label: "I have verified that everyone has my resharer", onChanged: (value) { @@ -198,12 +190,11 @@ class _FrostReshareStep2abdState extends ConsumerState { }); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), PrimaryButton( label: "Continue", - enabled: _userVerifyContinue && + enabled: + _userVerifyContinue && (amOutgoingParticipant || !fieldIsEmptyFlags.fold(false, (v, e) => v || e)), onPressed: _onPressed, diff --git a/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_2c.dart b/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_2c.dart index 92a3a195b4..3717188fd9 100644 --- a/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_2c.dart +++ b/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_2c.dart @@ -5,12 +5,12 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../frost_route_generator.dart'; import '../../../../providers/frost_wallet/frost_wallet_providers.dart'; -import '../../../../services/frost.dart'; import '../../../../utilities/logger.dart'; import '../../../../utilities/util.dart'; import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/dialogs/frost/frost_error_dialog.dart'; import '../../../../widgets/textfields/frost_step_field.dart'; +import '../../../../wl_gen/interfaces/frost_interface.dart'; class FrostReshareStep2c extends ConsumerStatefulWidget { const FrostReshareStep2c({super.key}); @@ -41,9 +41,9 @@ class _FrostReshareStep2cState extends ConsumerState { // collect resharer strings final resharerStarts = controllers.map((e) => e.text).toList(); - final result = Frost.beginReshared( + final result = frostInterface.beginReshared( myName: ref.read(pFrostResharingData).myName!, - resharerConfig: Frost.decodeRConfig( + resharerConfig: frostInterface.decodeRConfig( ref.read(pFrostResharingData).resharerRConfig!, ), resharerStarts: resharerStarts, @@ -59,15 +59,13 @@ class _FrostReshareStep2cState extends ConsumerState { .routeName, ); } catch (e, s) { - Logging.instance.f("$e\n$s", error: e, stackTrace: s,); + Logging.instance.f("$e\n$s", error: e, stackTrace: s); if (mounted) { await showDialog( context: context, - builder: (_) => FrostErrorDialog( - title: "Error", - message: e.toString(), - ), + builder: (_) => + FrostErrorDialog(title: "Error", message: e.toString()), ); } } finally { @@ -116,7 +114,8 @@ class _FrostReshareStep2cState extends ConsumerState { focusNode: focusNodes[i], showQrScanOption: true, label: resharers.keys.elementAt(i), - hint: "Enter " + hint: + "Enter " "${resharers.keys.elementAt(i)}" "'s resharer", onChanged: (_) { @@ -129,9 +128,7 @@ class _FrostReshareStep2cState extends ConsumerState { ], ), if (!Util.isDesktop) const Spacer(), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), PrimaryButton( label: "Continue", enabled: !fieldIsEmptyFlags.reduce((v, e) => v |= e), diff --git a/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_3abd.dart b/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_3abd.dart index d4fc876c12..edd5d5140c 100644 --- a/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_3abd.dart +++ b/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_3abd.dart @@ -1,11 +1,8 @@ -import 'dart:ffi'; - import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../frost_route_generator.dart'; import '../../../../providers/frost_wallet/frost_wallet_providers.dart'; -import '../../../../services/frost.dart'; import '../../../../utilities/logger.dart'; import '../../../../utilities/util.dart'; import '../../../../widgets/custom_buttons/checkbox_text_button.dart'; @@ -15,6 +12,7 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/detail_item.dart'; import '../../../../widgets/dialogs/frost/frost_error_dialog.dart'; import '../../../../widgets/textfields/frost_step_field.dart'; +import '../../../../wl_gen/interfaces/frost_interface.dart'; import '../../../wallet_view/transaction_views/transaction_details_view.dart'; class FrostReshareStep3abd extends ConsumerStatefulWidget { @@ -55,8 +53,8 @@ class _FrostReshareStep3abdState extends ConsumerState { encryptionKeys.insert(myIndex, myEncryptionKey!); } - final result = Frost.finishResharer( - machine: ref.read(pFrostResharingData).startResharerData!.machine.ref, + final result = frostInterface.finishResharer( + machine: ref.read(pFrostResharingData).startResharerData!.machine, encryptionKeysOfResharedTo: encryptionKeys, ); @@ -70,14 +68,12 @@ class _FrostReshareStep3abdState extends ConsumerState { .routeName, ); } catch (e, s) { - Logging.instance.f("$e\n$s", error: e, stackTrace: s,); + Logging.instance.f("$e\n$s", error: e, stackTrace: s); if (mounted) { await showDialog( context: context, - builder: (_) => FrostErrorDialog( - title: "Error", - message: e.toString(), - ), + builder: (_) => + FrostErrorDialog(title: "Error", message: e.toString()), ); } } finally { @@ -87,8 +83,10 @@ class _FrostReshareStep3abdState extends ConsumerState { @override void initState() { - myEncryptionKey = - ref.read(pFrostResharingData).startResharedData?.resharedStart; + myEncryptionKey = ref + .read(pFrostResharingData) + .startResharedData + ?.resharedStart; newParticipants = ref.read(pFrostResharingData).configData!.newParticipants; myIndex = newParticipants.indexOf(ref.read(pFrostResharingData).myName!); @@ -136,22 +134,13 @@ class _FrostReshareStep3abdState extends ConsumerState { title: "My encryption key", detail: myEncryptionKey!, button: Util.isDesktop - ? IconCopyButton( - data: myEncryptionKey!, - ) - : SimpleCopyButton( - data: myEncryptionKey!, - ), + ? IconCopyButton(data: myEncryptionKey!) + : SimpleCopyButton(data: myEncryptionKey!), ), if (!amOutgoingParticipant) const SizedBox(height: 12), if (!amOutgoingParticipant) - FrostQrDialogPopupButton( - data: myEncryptionKey!, - ), - if (!amOutgoingParticipant) - const SizedBox( - height: 12, - ), + FrostQrDialogPopupButton(data: myEncryptionKey!), + if (!amOutgoingParticipant) const SizedBox(height: 12), Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -164,7 +153,8 @@ class _FrostReshareStep3abdState extends ConsumerState { focusNode: focusNodes[i], showQrScanOption: true, label: newParticipants[i], - hint: "Enter " + hint: + "Enter " "${newParticipants[i]}" "'s encryption key", onChanged: (_) { @@ -177,10 +167,7 @@ class _FrostReshareStep3abdState extends ConsumerState { ], ), if (!Util.isDesktop) const Spacer(), - if (!amOutgoingParticipant) - const SizedBox( - height: 12, - ), + if (!amOutgoingParticipant) const SizedBox(height: 12), if (!amOutgoingParticipant) CheckboxTextButton( label: "I have verified that everyone has my encryption key", @@ -190,12 +177,11 @@ class _FrostReshareStep3abdState extends ConsumerState { }); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), PrimaryButton( label: "Continue", - enabled: (amOutgoingParticipant || _userVerifyContinue) && + enabled: + (amOutgoingParticipant || _userVerifyContinue) && !fieldIsEmptyFlags.reduce((v, e) => v |= e), onPressed: _onPressed, ), diff --git a/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_4.dart b/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_4.dart index dd081d121b..00765f9212 100644 --- a/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_4.dart +++ b/lib/pages/add_wallet_views/frost_ms/reshare/frost_reshare_step_4.dart @@ -1,5 +1,3 @@ -import 'dart:ffi'; - import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -7,7 +5,6 @@ import '../../../../frost_route_generator.dart'; import '../../../../pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart'; import '../../../../providers/db/main_db_provider.dart'; import '../../../../providers/frost_wallet/frost_wallet_providers.dart'; -import '../../../../services/frost.dart'; import '../../../../utilities/logger.dart'; import '../../../../utilities/util.dart'; import '../../../../wallets/isar/models/frost_wallet_info.dart'; @@ -18,6 +15,7 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/detail_item.dart'; import '../../../../widgets/dialogs/frost/frost_error_dialog.dart'; import '../../../../widgets/textfields/frost_step_field.dart'; +import '../../../../wl_gen/interfaces/frost_interface.dart'; import '../../../wallet_view/transaction_views/transaction_details_view.dart'; import '../../../wallet_view/wallet_view.dart'; @@ -58,7 +56,10 @@ class _FrostReshareStep4State extends ConsumerState { if (amOutgoingParticipant) { ref.read(pFrostResharingData).reset(); ref.read(pFrostScaffoldCanPopDesktop.notifier).state = true; - ref.read(pFrostScaffoldArgs)?.parentNav.popUntil( + ref + .read(pFrostScaffoldArgs) + ?.parentNav + .popUntil( ModalRoute.withName( Util.isDesktop ? DesktopWalletView.routeName @@ -72,8 +73,8 @@ class _FrostReshareStep4State extends ConsumerState { resharerCompletes.insert(myResharerIndexIndex!, myResharerComplete!); } - final data = Frost.finishReshared( - prior: ref.read(pFrostResharingData).startResharedData!.prior.ref, + final data = frostInterface.finishReshared( + prior: ref.read(pFrostResharingData).startResharedData!.prior, resharerCompletes: resharerCompletes, ); @@ -88,14 +89,12 @@ class _FrostReshareStep4State extends ConsumerState { ); } } catch (e, s) { - Logging.instance.f("$e\n$s", error: e, stackTrace: s,); + Logging.instance.f("$e\n$s", error: e, stackTrace: s); if (mounted) { await showDialog( context: context, - builder: (_) => FrostErrorDialog( - title: "Error", - message: e.toString(), - ), + builder: (_) => + FrostErrorDialog(title: "Error", message: e.toString()), ); } } finally { @@ -107,9 +106,9 @@ class _FrostReshareStep4State extends ConsumerState { void initState() { amNewParticipant = ref.read(pFrostResharingData).startResharerData == null && - ref.read(pFrostResharingData).incompleteWallet != null && - ref.read(pFrostResharingData).incompleteWallet?.walletId == - ref.read(pFrostScaffoldArgs)!.walletId!; + ref.read(pFrostResharingData).incompleteWallet != null && + ref.read(pFrostResharingData).incompleteWallet?.walletId == + ref.read(pFrostScaffoldArgs)!.walletId!; myName = ref.read(pFrostResharingData).myName!; @@ -127,8 +126,9 @@ class _FrostReshareStep4State extends ConsumerState { .isar .frostWalletInfo .getByWalletIdSync(ref.read(pFrostScaffoldArgs)!.walletId!)!; - final myOldIndex = - frostInfo.participants.indexOf(ref.read(pFrostResharingData).myName!); + final myOldIndex = frostInfo.participants.indexOf( + ref.read(pFrostResharingData).myName!, + ); myResharerIndexIndex = resharers.values.toList().indexOf(myOldIndex); if (myResharerIndexIndex! >= 0) { @@ -173,22 +173,13 @@ class _FrostReshareStep4State extends ConsumerState { title: "My resharer complete", detail: myResharerComplete!, button: Util.isDesktop - ? IconCopyButton( - data: myResharerComplete!, - ) - : SimpleCopyButton( - data: myResharerComplete!, - ), + ? IconCopyButton(data: myResharerComplete!) + : SimpleCopyButton(data: myResharerComplete!), ), if (myResharerComplete != null) const SizedBox(height: 12), if (myResharerComplete != null) - FrostQrDialogPopupButton( - data: myResharerComplete!, - ), - if (!amOutgoingParticipant) - const SizedBox( - height: 16, - ), + FrostQrDialogPopupButton(data: myResharerComplete!), + if (!amOutgoingParticipant) const SizedBox(height: 16), if (!amOutgoingParticipant) Column( mainAxisSize: MainAxisSize.min, @@ -202,7 +193,8 @@ class _FrostReshareStep4State extends ConsumerState { focusNode: focusNodes[i], showQrScanOption: true, label: resharers.keys.elementAt(i), - hint: "Enter " + hint: + "Enter " "${resharers.keys.elementAt(i)}" "'s resharer", onChanged: (_) { @@ -215,9 +207,7 @@ class _FrostReshareStep4State extends ConsumerState { ], ), if (!Util.isDesktop) const Spacer(), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), if (!amNewParticipant) CheckboxTextButton( label: "I have verified that everyone has my resharer complete", @@ -227,13 +217,11 @@ class _FrostReshareStep4State extends ConsumerState { }); }, ), - if (!amNewParticipant) - const SizedBox( - height: 16, - ), + if (!amNewParticipant) const SizedBox(height: 16), PrimaryButton( label: amOutgoingParticipant ? "Done" : "Complete", - enabled: (amNewParticipant || _userVerifyContinue) && + enabled: + (amNewParticipant || _userVerifyContinue) && (amOutgoingParticipant || !fieldIsEmptyFlags.fold(false, (v, e) => v || e)), onPressed: _onPressed, diff --git a/lib/pages/add_wallet_views/frost_ms/restore/restore_frost_ms_wallet_view.dart b/lib/pages/add_wallet_views/frost_ms/restore/restore_frost_ms_wallet_view.dart index facaeed444..33eddc54b8 100644 --- a/lib/pages/add_wallet_views/frost_ms/restore/restore_frost_ms_wallet_view.dart +++ b/lib/pages/add_wallet_views/frost_ms/restore/restore_frost_ms_wallet_view.dart @@ -4,13 +4,13 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:frostdart/frostdart.dart' as frost; + +// import 'package:frostdart/frostdart.dart' as frost; import '../../../../notifications/show_flush_bar.dart'; import '../../../../pages_desktop_specific/desktop_home_view.dart'; import '../../../../providers/global/secure_store_provider.dart'; import '../../../../providers/providers.dart'; -import '../../../../services/frost.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; import '../../../../utilities/barcode_scanner_interface.dart'; @@ -38,6 +38,7 @@ import '../../../../widgets/icon_widgets/x_icon.dart'; import '../../../../widgets/stack_dialog.dart'; import '../../../../widgets/stack_text_field.dart'; import '../../../../widgets/textfield_icon_button.dart'; +import '../../../../wl_gen/interfaces/frost_interface.dart'; import '../../../home_view/home_view.dart'; class RestoreFrostMsWalletView extends ConsumerStatefulWidget { @@ -70,8 +71,10 @@ class _RestoreFrostMsWalletViewState final keys = keysFieldController.text; final config = configFieldController.text; - final myNameIndex = frost.getParticipantIndexFromKeys(serializedKeys: keys); - final participants = Frost.getParticipants(multisigConfig: config); + final myNameIndex = frostInterface.participantIndexFromKeys( + serializedKeys: keys, + ); + final participants = frostInterface.getParticipants(multisigConfig: config); final myName = participants[myNameIndex]; final info = WalletInfo.createNew( @@ -92,7 +95,7 @@ class _RestoreFrostMsWalletViewState knownSalts: [], participants: participants, myName: myName, - threshold: frost.multisigThreshold(multisigConfig: config), + threshold: frostInterface.getMultisigThreshold(multisigConfig: config), ); await ref.read(mainDBProvider).isar.writeTxn(() async { @@ -173,12 +176,11 @@ class _RestoreFrostMsWalletViewState if (mounted) { await showDialog( context: context, - builder: - (_) => StackOkDialog( - title: "Failed to restore", - message: e.toString(), - desktopPopRootNavigator: Util.isDesktop, - ), + builder: (_) => StackOkDialog( + title: "Failed to restore", + message: e.toString(), + desktopPopRootNavigator: Util.isDesktop, + ), ); } } finally { @@ -265,60 +267,59 @@ class _RestoreFrostMsWalletViewState Widget build(BuildContext context) { return ConditionalParent( condition: Util.isDesktop, - builder: - (child) => DesktopScaffold( - background: Theme.of(context).extension()!.background, - appBar: const DesktopAppBar( - isCompactHeight: false, - leading: AppBarBackButton(), - // TODO: [prio=high] get rid of placeholder text?? - trailing: FrostMascot( - title: 'Lorem ipsum', - body: - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam est justo, ', - ), - ), - body: SizedBox(width: 480, child: child), + builder: (child) => DesktopScaffold( + background: Theme.of(context).extension()!.background, + appBar: const DesktopAppBar( + isCompactHeight: false, + leading: AppBarBackButton(), + // TODO: [prio=high] get rid of placeholder text?? + trailing: FrostMascot( + title: 'Lorem ipsum', + body: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam est justo, ', ), + ), + body: SizedBox(width: 480, child: child), + ), child: ConditionalParent( condition: !Util.isDesktop, - builder: - (child) => Background( - child: Scaffold( - backgroundColor: - Theme.of(context).extension()!.background, - appBar: AppBar( - leading: AppBarBackButton( - onPressed: () { - Navigator.of(context).pop(); - }, - ), - title: Text( - "Restore FROST multisig wallet", - style: STextStyles.navBarTitle(context), - ), - ), - body: SafeArea( - child: LayoutBuilder( - builder: (context, constraints) { - return SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: constraints.maxHeight, - ), - child: IntrinsicHeight( - child: Padding( - padding: const EdgeInsets.all(16), - child: child, - ), - ), + builder: (child) => Background( + child: Scaffold( + backgroundColor: Theme.of( + context, + ).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: Text( + "Restore FROST multisig wallet", + style: STextStyles.navBarTitle(context), + ), + ), + body: SafeArea( + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.all(16), + child: child, ), - ); - }, - ), - ), + ), + ), + ); + }, ), ), + ), + ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -340,78 +341,79 @@ class _RestoreFrostMsWalletViewState autocorrect: false, enableSuggestions: false, style: STextStyles.field(context), - decoration: standardInputDecoration( - "Enter config", - configFocusNode, - context, - ).copyWith( - contentPadding: const EdgeInsets.only( - left: 16, - top: 6, - bottom: 8, - right: 5, - ), - suffixIcon: Padding( - padding: - _configEmpty + decoration: + standardInputDecoration( + "Enter config", + configFocusNode, + context, + ).copyWith( + contentPadding: const EdgeInsets.only( + left: 16, + top: 6, + bottom: 8, + right: 5, + ), + suffixIcon: Padding( + padding: _configEmpty ? const EdgeInsets.only(right: 8) : const EdgeInsets.only(right: 0), - child: UnconstrainedBox( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - !_configEmpty - ? TextFieldIconButton( - semanticsLabel: - "Clear Button. Clears The Config Field.", - key: const Key("frConfigClearButtonKey"), - onTap: () { - configFieldController.text = ""; - - setState(() { - _configEmpty = true; - }); - }, - child: const XIcon(), - ) - : TextFieldIconButton( - semanticsLabel: - "Paste Button. Pastes From Clipboard To Config Field Input.", - key: const Key("frConfigPasteButtonKey"), - onTap: () async { - final ClipboardData? data = - await Clipboard.getData( - Clipboard.kTextPlain, - ); - if (data?.text != null && - data!.text!.isNotEmpty) { - configFieldController.text = - data.text!.trim(); - } - - setState(() { - _configEmpty = - configFieldController.text.isEmpty; - }); - }, - child: - _configEmpty - ? const ClipboardIcon() - : const XIcon(), - ), - if (_configEmpty) - TextFieldIconButton( - semanticsLabel: - "Scan QR Button. Opens Camera For Scanning QR Code.", - key: const Key("frConfigScanQrButtonKey"), - onTap: scanQr, - child: const QrCodeIcon(), - ), - ], + child: UnconstrainedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + !_configEmpty + ? TextFieldIconButton( + semanticsLabel: + "Clear Button. Clears The Config Field.", + key: const Key("frConfigClearButtonKey"), + onTap: () { + configFieldController.text = ""; + + setState(() { + _configEmpty = true; + }); + }, + child: const XIcon(), + ) + : TextFieldIconButton( + semanticsLabel: + "Paste Button. Pastes From Clipboard To Config Field Input.", + key: const Key("frConfigPasteButtonKey"), + onTap: () async { + final ClipboardData? data = + await Clipboard.getData( + Clipboard.kTextPlain, + ); + if (data?.text != null && + data!.text!.isNotEmpty) { + configFieldController.text = data + .text! + .trim(); + } + + setState(() { + _configEmpty = configFieldController + .text + .isEmpty; + }); + }, + child: _configEmpty + ? const ClipboardIcon() + : const XIcon(), + ), + if (_configEmpty) + TextFieldIconButton( + semanticsLabel: + "Scan QR Button. Opens Camera For Scanning QR Code.", + key: const Key("frConfigScanQrButtonKey"), + onTap: scanQr, + child: const QrCodeIcon(), + ), + ], + ), + ), ), ), - ), - ), ), ), const SizedBox(height: 16), @@ -432,70 +434,69 @@ class _RestoreFrostMsWalletViewState autocorrect: false, enableSuggestions: false, style: STextStyles.field(context), - decoration: standardInputDecoration( - "Keys", - keysFocusNode, - context, - ).copyWith( - contentPadding: const EdgeInsets.only( - left: 16, - top: 6, - bottom: 8, - right: 5, - ), - suffixIcon: Padding( - padding: - _keysEmpty + decoration: + standardInputDecoration( + "Keys", + keysFocusNode, + context, + ).copyWith( + contentPadding: const EdgeInsets.only( + left: 16, + top: 6, + bottom: 8, + right: 5, + ), + suffixIcon: Padding( + padding: _keysEmpty ? const EdgeInsets.only(right: 8) : const EdgeInsets.only(right: 0), - child: UnconstrainedBox( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - !_keysEmpty - ? TextFieldIconButton( - semanticsLabel: - "Clear Button. Clears The Keys Field.", - key: const Key("frMyNameClearButtonKey"), - onTap: () { - keysFieldController.text = ""; - - setState(() { - _keysEmpty = true; - }); - }, - child: const XIcon(), - ) - : TextFieldIconButton( - semanticsLabel: - "Paste Button. Pastes From Clipboard To Keys Field.", - key: const Key("frKeysPasteButtonKey"), - onTap: () async { - final ClipboardData? data = - await Clipboard.getData( - Clipboard.kTextPlain, - ); - if (data?.text != null && - data!.text!.isNotEmpty) { - keysFieldController.text = - data.text!.trim(); - } - - setState(() { - _keysEmpty = - keysFieldController.text.isEmpty; - }); - }, - child: - _keysEmpty - ? const ClipboardIcon() - : const XIcon(), - ), - ], + child: UnconstrainedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + !_keysEmpty + ? TextFieldIconButton( + semanticsLabel: + "Clear Button. Clears The Keys Field.", + key: const Key("frMyNameClearButtonKey"), + onTap: () { + keysFieldController.text = ""; + + setState(() { + _keysEmpty = true; + }); + }, + child: const XIcon(), + ) + : TextFieldIconButton( + semanticsLabel: + "Paste Button. Pastes From Clipboard To Keys Field.", + key: const Key("frKeysPasteButtonKey"), + onTap: () async { + final ClipboardData? data = + await Clipboard.getData( + Clipboard.kTextPlain, + ); + if (data?.text != null && + data!.text!.isNotEmpty) { + keysFieldController.text = data.text! + .trim(); + } + + setState(() { + _keysEmpty = + keysFieldController.text.isEmpty; + }); + }, + child: _keysEmpty + ? const ClipboardIcon() + : const XIcon(), + ), + ], + ), + ), ), ), - ), - ), ), ), const SizedBox(height: 16), diff --git a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_1b.dart b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_1b.dart index f7e6d0b438..2ce537697f 100644 --- a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_1b.dart +++ b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_1b.dart @@ -8,7 +8,6 @@ import '../../../../models/isar/models/isar_models.dart'; import '../../../../providers/db/main_db_provider.dart'; import '../../../../providers/frost_wallet/frost_wallet_providers.dart'; import '../../../../providers/global/wallets_provider.dart'; -import '../../../../services/frost.dart'; import '../../../../utilities/format.dart'; import '../../../../utilities/logger.dart'; import '../../../../utilities/util.dart'; @@ -19,6 +18,7 @@ import '../../../../widgets/desktop/primary_button.dart'; import '../../../../widgets/frost_step_user_steps.dart'; import '../../../../widgets/stack_dialog.dart'; import '../../../../widgets/textfields/frost_step_field.dart'; +import '../../../../wl_gen/interfaces/frost_interface.dart'; class FrostSendStep1b extends ConsumerStatefulWidget { const FrostSendStep1b({super.key}); @@ -65,43 +65,40 @@ class _FrostSendStep1bState extends ConsumerState { ref.read(pWallets).getWallet(ref.read(pFrostScaffoldArgs)!.walletId!) as BitcoinFrostWallet; - final data = Frost.extractDataFromSignConfig( + final data = frostInterface.extractDataFromSignConfig( signConfig: config, coin: wallet.cryptoCurrency, serializedKeys: (await wallet.getSerializedKeys())!, ); - final utxos = - await ref - .read(mainDBProvider) - .getUTXOs(wallet.walletId) - .filter() - .anyOf( - data.inputs, - (q, e) => q - .txidEqualTo(Format.uint8listToString(e.hash)) - .and() - .valueEqualTo(e.value) - .and() - .voutEqualTo(e.vout), - ) - .findAll(); + final utxos = await ref + .read(mainDBProvider) + .getUTXOs(wallet.walletId) + .filter() + .anyOf( + data.inputs, + (q, e) => q + .txidEqualTo(Format.uint8listToString(e.hash)) + .and() + .valueEqualTo(e.value) + .and() + .voutEqualTo(e.vout), + ) + .findAll(); // TODO add more data from 'data' and display to user ? ref.read(pFrostTxData.notifier).state = TxData( frostMSConfig: config, - recipients: - data.recipients - .map( - (e) => TxRecipient( - address: e.address, - amount: e.amount, - isChange: false, - addressType: - wallet.cryptoCurrency.getAddressType(e.address)!, - ), - ) - .toList(), + recipients: data.recipients + .map( + (e) => TxRecipient( + address: e.address, + amount: e.amount, + isChange: false, + addressType: wallet.cryptoCurrency.getAddressType(e.address)!, + ), + ) + .toList(), utxos: utxos.map((e) => StandardInput(e)).toSet(), ); @@ -123,12 +120,11 @@ class _FrostSendStep1bState extends ConsumerState { if (mounted) { await showDialog( context: context, - builder: - (_) => StackOkDialog( - title: "Import and attempt sign config failed", - message: e.toString(), - desktopPopRootNavigator: Util.isDesktop, - ), + builder: (_) => StackOkDialog( + title: "Import and attempt sign config failed", + message: e.toString(), + desktopPopRootNavigator: Util.isDesktop, + ), ); } } finally { diff --git a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_2.dart b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_2.dart index febae8b540..b0a5c66957 100644 --- a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_2.dart +++ b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_2.dart @@ -4,7 +4,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../frost_route_generator.dart'; import '../../../../providers/frost_wallet/frost_wallet_providers.dart'; import '../../../../providers/global/wallets_provider.dart'; -import '../../../../services/frost.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/logger.dart'; import '../../../../utilities/text_styles.dart'; @@ -17,6 +16,7 @@ import '../../../../widgets/detail_item.dart'; import '../../../../widgets/dialogs/frost/frost_error_dialog.dart'; import '../../../../widgets/rounded_white_container.dart'; import '../../../../widgets/textfields/frost_step_field.dart'; +import '../../../../wl_gen/interfaces/frost_interface.dart'; import '../../../wallet_view/transaction_views/transaction_details_view.dart'; class FrostSendStep2 extends ConsumerStatefulWidget { @@ -56,15 +56,16 @@ class _FrostSendStep2State extends ConsumerState { @override void initState() { - final wallet = ref.read(pWallets).getWallet( - ref.read(pFrostScaffoldArgs)!.walletId!, - ) as BitcoinFrostWallet; + final wallet = + ref.read(pWallets).getWallet(ref.read(pFrostScaffoldArgs)!.walletId!) + as BitcoinFrostWallet; final frostInfo = wallet.frostInfo; myName = frostInfo.myName; threshold = frostInfo.threshold; - participantsWithoutMe = - List.from(frostInfo.participants); // Copy so it isn't fixed-length. + participantsWithoutMe = List.from( + frostInfo.participants, + ); // Copy so it isn't fixed-length. myIndex = participantsWithoutMe.indexOf(frostInfo.myName); myPreprocess = ref.read(pFrostAttemptSignData.state).state!.preprocess; @@ -103,13 +104,8 @@ class _FrostSendStep2State extends ConsumerState { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "1.", - style: STextStyles.w500_12(context), - ), - const SizedBox( - width: 4, - ), + Text("1.", style: STextStyles.w500_12(context)), + const SizedBox(width: 4), Expanded( child: Text( "Share your preprocess with other signing group members.", @@ -118,19 +114,12 @@ class _FrostSendStep2State extends ConsumerState { ), ], ), - const SizedBox( - height: 4, - ), + const SizedBox(height: 4), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "2.", - style: STextStyles.w500_12(context), - ), - const SizedBox( - width: 4, - ), + Text("2.", style: STextStyles.w500_12(context)), + const SizedBox(width: 4), Expanded( child: RichText( text: TextSpan( @@ -141,7 +130,8 @@ class _FrostSendStep2State extends ConsumerState { style: STextStyles.w500_12(context), ), TextSpan( - text: "You must have the threshold number of " + text: + "You must have the threshold number of " "preprocesses (including yours) to send this transaction.", style: STextStyles.w600_12(context).copyWith( color: Theme.of(context) @@ -158,58 +148,38 @@ class _FrostSendStep2State extends ConsumerState { ], ), ), - const SizedBox( - height: 12, - ), + const SizedBox(height: 12), DetailItem( title: "Threshold", detail: "$threshold signatures", horizontal: true, ), - const SizedBox( - height: 12, - ), + const SizedBox(height: 12), DetailItem( title: "My name", detail: myName, button: Util.isDesktop - ? IconCopyButton( - data: myName, - ) - : SimpleCopyButton( - data: myName, - ), - ), - const SizedBox( - height: 12, + ? IconCopyButton(data: myName) + : SimpleCopyButton(data: myName), ), + const SizedBox(height: 12), DetailItem( title: "My preprocess", detail: myPreprocess, button: Util.isDesktop - ? IconCopyButton( - data: myPreprocess, - ) - : SimpleCopyButton( - data: myPreprocess, - ), + ? IconCopyButton(data: myPreprocess) + : SimpleCopyButton(data: myPreprocess), ), const SizedBox(height: 12), - FrostQrDialogPopupButton( - data: myPreprocess, - ), - const SizedBox( - height: 12, - ), + FrostQrDialogPopupButton(data: myPreprocess), + const SizedBox(height: 12), RoundedWhiteContainer( child: Text( "You need to obtain ${threshold - 1} preprocess from signing members to send this transaction.", style: STextStyles.label(context), ), ), - const SizedBox( - height: 12, - ), + const SizedBox(height: 12), Builder( builder: (context) { final count = countPreprocesses(); @@ -224,9 +194,7 @@ class _FrostSendStep2State extends ConsumerState { ); }, ), - const SizedBox( - height: 12, - ), + const SizedBox(height: 12), Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -247,9 +215,7 @@ class _FrostSendStep2State extends ConsumerState { ], ), if (!Util.isDesktop) const Spacer(), - const SizedBox( - height: 12, - ), + const SizedBox(height: 12), PrimaryButton( label: "Generate shares", enabled: countPreprocesses() >= threshold, @@ -271,12 +237,14 @@ class _FrostSendStep2State extends ConsumerState { preprocesses.insert(myIndex, ""); try { - ref.read(pFrostContinueSignData.notifier).state = - Frost.continueSigning( - machinePtr: - ref.read(pFrostAttemptSignData.state).state!.machinePtr, - preprocesses: preprocesses, - ); + ref.read(pFrostContinueSignData.notifier).state = frostInterface + .continueSigning( + machinePtr: ref + .read(pFrostAttemptSignData.state) + .state! + .machinePtr, + preprocesses: preprocesses, + ); ref.read(pFrostCreateCurrentStep.state).state = 3; await Navigator.of(context).pushNamed( @@ -291,7 +259,7 @@ class _FrostSendStep2State extends ConsumerState { // arguments: widget.walletId, // ); } catch (e, s) { - Logging.instance.f("$e\n$s", error: e, stackTrace: s,); + Logging.instance.f("$e\n$s", error: e, stackTrace: s); if (context.mounted) { return await showDialog( diff --git a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_3.dart b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_3.dart index f3ddc90cc9..d3e9f91a29 100644 --- a/lib/pages/send_view/frost_ms/send_steps/frost_send_step_3.dart +++ b/lib/pages/send_view/frost_ms/send_steps/frost_send_step_3.dart @@ -5,7 +5,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../frost_route_generator.dart'; import '../../../../providers/frost_wallet/frost_wallet_providers.dart'; import '../../../../providers/global/wallets_provider.dart'; -import '../../../../services/frost.dart'; import '../../../../utilities/amount/amount.dart'; import '../../../../utilities/logger.dart'; import '../../../../utilities/util.dart'; @@ -18,6 +17,7 @@ import '../../../../widgets/detail_item.dart'; import '../../../../widgets/dialogs/frost/frost_error_dialog.dart'; import '../../../../widgets/frost_step_user_steps.dart'; import '../../../../widgets/textfields/frost_step_field.dart'; +import '../../../../wl_gen/interfaces/frost_interface.dart'; import '../../../wallet_view/transaction_views/transaction_details_view.dart'; class FrostSendStep3 extends ConsumerStatefulWidget { @@ -62,13 +62,12 @@ class _FrostSendStep3State extends ConsumerState { myIndex = frostInfo.participants.indexOf(frostInfo.myName); myShare = ref.read(pFrostContinueSignData.state).state!.share; - participantsWithoutMe = - frostInfo.participants - .toSet() - .intersection( - ref.read(pFrostSelectParticipantsUnordered.state).state!.toSet(), - ) - .toList(); + participantsWithoutMe = frostInfo.participants + .toSet() + .intersection( + ref.read(pFrostSelectParticipantsUnordered.state).state!.toSet(), + ) + .toList(); participantsWithoutMe.remove(myName); @@ -104,19 +103,17 @@ class _FrostSendStep3State extends ConsumerState { DetailItem( title: "My name", detail: myName, - button: - Util.isDesktop - ? IconCopyButton(data: myName) - : SimpleCopyButton(data: myName), + button: Util.isDesktop + ? IconCopyButton(data: myName) + : SimpleCopyButton(data: myName), ), const SizedBox(height: 12), DetailItem( title: "My share", detail: myShare, - button: - Util.isDesktop - ? IconCopyButton(data: myShare) - : SimpleCopyButton(data: myShare), + button: Util.isDesktop + ? IconCopyButton(data: myShare) + : SimpleCopyButton(data: myShare), ), const SizedBox(height: 12), FrostQrDialogPopupButton(data: myShare), @@ -172,9 +169,11 @@ class _FrostSendStep3State extends ConsumerState { } try { - final rawTx = Frost.completeSigning( - machinePtr: - ref.read(pFrostContinueSignData.state).state!.machinePtr, + final rawTx = frostInterface.completeSigning( + machinePtr: ref + .read(pFrostContinueSignData.state) + .state! + .machinePtr, shares: shares, ); @@ -216,10 +215,9 @@ class _FrostSendStep3State extends ConsumerState { if (context.mounted) { return await showDialog( context: context, - builder: - (_) => const FrostErrorDialog( - title: "Failed to complete signing process", - ), + builder: (_) => const FrostErrorDialog( + title: "Failed to complete signing process", + ), ); } } diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart index 4069f06f6e..355fc6643a 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart @@ -13,7 +13,6 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:frostdart/frostdart.dart' as frost; import 'package:isar_community/isar.dart'; import 'package:stack_wallet_backup/stack_wallet_backup.dart'; import 'package:tuple/tuple.dart'; @@ -33,7 +32,6 @@ import '../../../../../models/stack_restoring_ui_state.dart'; import '../../../../../models/trade_wallet_lookup.dart'; import '../../../../../models/wallet_restore_state.dart'; import '../../../../../services/address_book_service.dart'; -import '../../../../../services/frost.dart'; import '../../../../../services/node_service.dart'; import '../../../../../services/trade_notes_service.dart'; import '../../../../../services/trade_sent_from_stack_service.dart'; @@ -62,6 +60,7 @@ import '../../../../../wallets/wallet/wallet.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/private_key_interface.dart'; import '../../../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart'; +import '../../../../../wl_gen/interfaces/frost_interface.dart'; class PreRestoreState { final Set walletIds; @@ -243,12 +242,11 @@ abstract class SWB { Logging.instance.i("...createStackWalletJSON DB.instance.mutex acquired"); Logging.instance.i("SWB backing up nodes"); try { - final nodesFuture = - nodeService.nodes.map((e) async { - final map = e.toMap(); - map["password"] = await e.getPassword(_secureStore); - return map; - }).toList(); + final nodesFuture = nodeService.nodes.map((e) async { + final map = e.toMap(); + map["password"] = await e.getPassword(_secureStore); + return map; + }).toList(); final nodes = await Future.wait(nodesFuture); backupJson['nodes'] = nodes; } catch (e, s) { @@ -280,8 +278,9 @@ abstract class SWB { final AddressBookService addressBookService = AddressBookService(); final addresses = addressBookService.contacts; - backupJson['addressBookEntries'] = - addresses.map((e) => e.toMap()).toList(); + backupJson['addressBookEntries'] = addresses + .map((e) => e.toMap()) + .toList(); Logging.instance.d("SWB backing up wallets"); @@ -298,8 +297,8 @@ abstract class SWB { (await wallet.getViewOnlyWalletData()).toJsonEncodedString(); } else if (wallet is MnemonicInterface) { backupWallet['mnemonic'] = await wallet.getMnemonic(); - backupWallet['mnemonicPassphrase'] = - await wallet.getMnemonicPassphrase(); + backupWallet['mnemonicPassphrase'] = await wallet + .getMnemonicPassphrase(); } else if (wallet is PrivateKeyInterface) { backupWallet['privateKey'] = await wallet.getPrivateKey(); } else if (wallet is BitcoinFrostWallet) { @@ -332,11 +331,10 @@ abstract class SWB { backupWallet['restoreHeight'] = wallet.info.restoreHeight; - final isarNotes = - await MainDB.instance.isar.transactionNotes - .where() - .walletIdEqualTo(wallet.walletId) - .findAll(); + final isarNotes = await MainDB.instance.isar.transactionNotes + .where() + .walletIdEqualTo(wallet.walletId) + .findAll(); final notes = isarNotes.asMap().map( (key, value) => MapEntry(value.txid, value.value), @@ -359,8 +357,9 @@ abstract class SWB { // back up trade history lookup data for trades send from stack wallet final tradeTxidLookupDataService = TradeSentFromStackService(); - final lookupData = - tradeTxidLookupDataService.all.map((e) => e.toMap()).toList(); + final lookupData = tradeTxidLookupDataService.all + .map((e) => e.toMap()) + .toList(); backupJson["tradeTxidLookupData"] = lookupData; Logging.instance.d("SWB backing up trade notes"); @@ -439,10 +438,10 @@ abstract class SWB { serializedKeys = frostData["keys"] as String; multisigConfig = frostData["config"] as String; - final myNameIndex = frost.getParticipantIndexFromKeys( + final myNameIndex = frostInterface.participantIndexFromKeys( serializedKeys: serializedKeys, ); - final participants = Frost.getParticipants( + final participants = frostInterface.getParticipants( multisigConfig: multisigConfig, ); final myName = participants[myNameIndex]; @@ -452,7 +451,9 @@ abstract class SWB { knownSalts: [], participants: participants, myName: myName, - threshold: frost.multisigThreshold(multisigConfig: multisigConfig), + threshold: frostInterface.getMultisigThreshold( + multisigConfig: multisigConfig, + ), ); await MainDB.instance.isar.writeTxn(() async { @@ -693,11 +694,10 @@ abstract class SWB { ); Logging.instance.d("SWB temp backup created"); - final List _currentWalletIds = - await MainDB.instance.isar.walletInfo - .where() - .walletIdProperty() - .findAll(); + final List _currentWalletIds = await MainDB.instance.isar.walletInfo + .where() + .walletIdProperty() + .findAll(); final preRestoreState = PreRestoreState( _currentWalletIds.toSet(), @@ -926,12 +926,11 @@ abstract class SWB { // ensure this contact's data matches the pre restore state final List addresses = []; for (final address in (contact['addresses'] as List)) { - final entry = - ContactAddressEntry() - ..coinName = address['coin'] as String - ..address = address['address'] as String - ..label = address['label'] as String - ..other = address['other'] as String?; + final entry = ContactAddressEntry() + ..coinName = address['coin'] as String + ..address = address['address'] as String + ..label = address['label'] as String + ..other = address['other'] as String?; try { entry.coin; @@ -1107,12 +1106,11 @@ abstract class SWB { _prefs.language = prefs['language'] as String; _prefs.showFavoriteWallets = prefs['showFavoriteWallets'] as bool; _prefs.wifiOnly = prefs['wifiOnly'] as bool; - _prefs.syncType = - prefs['syncType'] == "currentWalletOnly" - ? SyncingType.currentWalletOnly - : prefs['syncType'] == "selectedWalletsAtStartup" - ? SyncingType.currentWalletOnly - : SyncingType.allWalletsOnStartup; // + _prefs.syncType = prefs['syncType'] == "currentWalletOnly" + ? SyncingType.currentWalletOnly + : prefs['syncType'] == "selectedWalletsAtStartup" + ? SyncingType.currentWalletOnly + : SyncingType.allWalletsOnStartup; // _prefs.walletIdsSyncOnStartup = (prefs['walletIdsSyncOnStartup'] as List) .map((e) => e as String) @@ -1136,12 +1134,11 @@ abstract class SWB { for (final contact in addressBookEntries) { final List addresses = []; for (final address in (contact['addresses'] as List)) { - final entry = - ContactAddressEntry() - ..coinName = address['coin'] as String - ..address = address['address'] as String - ..label = address['label'] as String - ..other = address['other'] as String?; + final entry = ContactAddressEntry() + ..coinName = address['coin'] as String + ..address = address['address'] as String + ..label = address['label'] as String + ..other = address['other'] as String?; try { entry.coin; @@ -1175,11 +1172,10 @@ abstract class SWB { secureStorageInterface: secureStorageInterface, ); if (nodes != null) { - final primaryIds = - primaryNodes - ?.map((e) => e["id"] as String?) - .whereType() - .toSet(); + final primaryIds = primaryNodes + ?.map((e) => e["id"] as String?) + .whereType() + .toSet(); for (final node in nodes) { final id = node['id'] as String; @@ -1250,8 +1246,9 @@ abstract class SWB { final json = Map.from(tradeTxidLookupData[i] as Map); TradeWalletLookup lookup = TradeWalletLookup.fromJson(json); // update walletIds - final List walletIds = - lookup.walletIds.map((e) => oldToNewWalletIdMap[e]!).toList(); + final List walletIds = lookup.walletIds + .map((e) => oldToNewWalletIdMap[e]!) + .toList(); lookup = lookup.copyWith(walletIds: walletIds); final oldLookup = DB.instance.get( diff --git a/lib/pages/settings_views/wallet_settings_view/frost_ms/initiate_resharing/complete_reshare_config_view.dart b/lib/pages/settings_views/wallet_settings_view/frost_ms/initiate_resharing/complete_reshare_config_view.dart index afc48647d2..1198e59b88 100644 --- a/lib/pages/settings_views/wallet_settings_view/frost_ms/initiate_resharing/complete_reshare_config_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/frost_ms/initiate_resharing/complete_reshare_config_view.dart @@ -3,14 +3,14 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:frostdart/frostdart.dart'; + +// import 'package:frostdart/frostdart.dart'; import '../../../../../frost_route_generator.dart'; import '../../../../../pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart'; import '../../../../../providers/db/main_db_provider.dart'; import '../../../../../providers/frost_wallet/frost_wallet_providers.dart'; import '../../../../../providers/global/wallets_provider.dart'; -import '../../../../../services/frost.dart'; import '../../../../../themes/stack_colors.dart'; import '../../../../../utilities/format.dart'; import '../../../../../utilities/logger.dart'; @@ -27,6 +27,7 @@ import '../../../../../widgets/desktop/primary_button.dart'; import '../../../../../widgets/frost_scaffold.dart'; import '../../../../../widgets/rounded_white_container.dart'; import '../../../../../widgets/stack_dialog.dart'; +import '../../../../../wl_gen/interfaces/frost_interface.dart'; final class CompleteReshareConfigView extends ConsumerStatefulWidget { const CompleteReshareConfigView({ @@ -84,20 +85,21 @@ class _CompleteReshareConfigViewState ); } - final List newParticipants = - controllers.map((e) => e.text.trim()).toList(); + final List newParticipants = controllers + .map((e) => e.text.trim()) + .toList(); if (_includeMeInReshare) { newParticipants.insert(0, myName); } - final config = Frost.createResharerConfig( + final config = frostInterface.createResharerConfig( newThreshold: int.parse(_newThresholdController.text), resharers: widget.resharers.values.toList(), newParticipants: newParticipants, ); final salt = Format.uint8listToString( - resharerSalt(resharerConfig: config), + frostInterface.getResharerSalt(resharerConfig: config), ); if (frostInfo.knownSalts.contains(salt)) { @@ -122,10 +124,8 @@ class _CompleteReshareConfigViewState } ref.read(pFrostResharingData).myName = myName; - ref.read(pFrostResharingData).resharerRConfig = Frost.encodeRConfig( - config, - widget.resharers, - ); + ref.read(pFrostResharingData).resharerRConfig = frostInterface + .encodeRConfig(config, widget.resharers); final wallet = ref.read(pWallets).getWallet(widget.walletId) as BitcoinFrostWallet; @@ -143,12 +143,10 @@ class _CompleteReshareConfigViewState callerRouteName: CompleteReshareConfigView.routeName, ); - await Navigator.of(context).pushNamed( - FrostStepScaffold.routeName, - ); + await Navigator.of(context).pushNamed(FrostStepScaffold.routeName); } } catch (e, s) { - Logging.instance.f("$e\n$s", error: e, stackTrace: s,); + Logging.instance.f("$e\n$s", error: e, stackTrace: s); if (mounted) { await showDialog( context: context, @@ -269,17 +267,15 @@ class _CompleteReshareConfigViewState leading: AppBarBackButton(), trailing: ExitToMyStackButton(), ), - body: SizedBox( - width: 480, - child: child, - ), + body: SizedBox(width: 480, child: child), ), child: ConditionalParent( condition: !Util.isDesktop, builder: (child) => Background( child: Scaffold( - backgroundColor: - Theme.of(context).extension()!.background, + backgroundColor: Theme.of( + context, + ).extension()!.background, appBar: AppBar( leading: AppBarBackButton( onPressed: () { @@ -318,9 +314,7 @@ class _CompleteReshareConfigViewState ? CrossAxisAlignment.start : CrossAxisAlignment.stretch, children: [ - const SizedBox( - height: 8, - ), + const SizedBox(height: 8), GestureDetector( onTap: () { setState(() { @@ -340,18 +334,14 @@ class _CompleteReshareConfigViewState materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, value: _includeMeInReshare, onChanged: (value) { - setState( - () => _includeMeInReshare = value == true, - ); + setState(() => _includeMeInReshare = value == true); _participantsCountChanged( _newParticipantsCountController.text, ); }, ), ), - const SizedBox( - width: 12, - ), + const SizedBox(width: 12), Expanded( child: Text( "I will be a signer in the new config", @@ -362,19 +352,16 @@ class _CompleteReshareConfigViewState ), ), ), - const SizedBox( - height: 20, - ), + const SizedBox(height: 20), Text( "New threshold", style: STextStyles.w500_14(context).copyWith( - color: - Theme.of(context).extension()!.textSubtitle1, + color: Theme.of( + context, + ).extension()!.textSubtitle1, ), ), - const SizedBox( - height: 10, - ), + const SizedBox(height: 10), TextField( keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], @@ -384,31 +371,27 @@ class _CompleteReshareConfigViewState hintStyle: STextStyles.fieldLabel(context), ), ), - const SizedBox( - height: 6, - ), + const SizedBox(height: 6), RoundedWhiteContainer( child: Text( "Enter number of signatures required for fund management.", style: STextStyles.w500_12(context).copyWith( - color: - Theme.of(context).extension()!.textSubtitle2, + color: Theme.of( + context, + ).extension()!.textSubtitle2, ), ), ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), Text( "New number of participants", style: STextStyles.w500_14(context).copyWith( - color: - Theme.of(context).extension()!.textSubtitle1, + color: Theme.of( + context, + ).extension()!.textSubtitle1, ), ), - const SizedBox( - height: 10, - ), + const SizedBox(height: 10), TextField( keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], @@ -419,42 +402,37 @@ class _CompleteReshareConfigViewState hintStyle: STextStyles.fieldLabel(context), ), ), - const SizedBox( - height: 6, - ), + const SizedBox(height: 6), RoundedWhiteContainer( child: Text( "The number of participants must be equal to or greater than the" " number of required signatures.", style: STextStyles.w500_12(context).copyWith( - color: - Theme.of(context).extension()!.textSubtitle2, + color: Theme.of( + context, + ).extension()!.textSubtitle2, ), ), ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), if (controllers.isNotEmpty) Text( "Participants", style: STextStyles.w500_14(context).copyWith( - color: - Theme.of(context).extension()!.textSubtitle1, + color: Theme.of( + context, + ).extension()!.textSubtitle1, ), ), - if (controllers.isNotEmpty) - const SizedBox( - height: 10, - ), + if (controllers.isNotEmpty) const SizedBox(height: 10), if (controllers.isNotEmpty) RoundedWhiteContainer( child: Text( "Type each name in one word without spaces.", style: STextStyles.w500_12(context).copyWith( - color: Theme.of(context) - .extension()! - .textSubtitle2, + color: Theme.of( + context, + ).extension()!.textSubtitle2, ), ), ), @@ -463,9 +441,7 @@ class _CompleteReshareConfigViewState children: [ for (int i = 0; i < controllers.length; i++) Padding( - padding: const EdgeInsets.only( - top: 10, - ), + padding: const EdgeInsets.only(top: 10), child: TextField( controller: controllers[i], decoration: InputDecoration( @@ -477,9 +453,7 @@ class _CompleteReshareConfigViewState ], ), if (!Util.isDesktop) const Spacer(), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), PrimaryButton( label: "Generate config", onPressed: () async { diff --git a/lib/providers/frost_wallet/frost_wallet_providers.dart b/lib/providers/frost_wallet/frost_wallet_providers.dart index c5b934e8ee..bee42aa284 100644 --- a/lib/providers/frost_wallet/frost_wallet_providers.dart +++ b/lib/providers/frost_wallet/frost_wallet_providers.dart @@ -1,55 +1,50 @@ -import 'dart:ffi'; import 'dart:typed_data'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:frostdart/frostdart_bindings_generated.dart'; -import '../../services/frost.dart'; + import '../../wallets/models/incomplete_frost_wallet.dart'; import '../../wallets/models/tx_data.dart'; +import '../../wl_gen/interfaces/frost_interface.dart'; // =================== wallet creation ========================================= final pFrostMultisigConfig = StateProvider((ref) => null); final pFrostMyName = StateProvider((ref) => null); -final pFrostStartKeyGenData = StateProvider< - ({ - String seed, - String commitments, - Pointer multisigConfigWithNamePtr, - Pointer secretShareMachineWrapperPtr, - })?>((_) => null); - -final pFrostSecretSharesData = StateProvider< - ({ - String share, - Pointer secretSharesResPtr, - })?>((ref) => null); - -final pFrostCompletedKeyGenData = StateProvider< - ({ - Uint8List multisigId, - String recoveryString, - String serializedKeys, - })?>((ref) => null); +final pFrostStartKeyGenData = + StateProvider< + ({ + String seed, + String commitments, + OpaqueWrapper multisigConfigWithNamePtr, + OpaqueWrapper secretShareMachineWrapperPtr, + })? + >((_) => null); + +final pFrostSecretSharesData = + StateProvider<({String share, OpaqueWrapper secretSharesResPtr})?>( + (ref) => null, + ); + +final pFrostCompletedKeyGenData = + StateProvider< + ({Uint8List multisigId, String recoveryString, String serializedKeys})? + >((ref) => null); // ================= transaction creation ====================================== final pFrostTxData = StateProvider((ref) => null); -final pFrostAttemptSignData = StateProvider< - ({ - Pointer machinePtr, - String preprocess, - })?>((ref) => null); +final pFrostAttemptSignData = + StateProvider<({OpaqueWrapper machinePtr, String preprocess})?>( + (ref) => null, + ); -final pFrostContinueSignData = StateProvider< - ({ - Pointer machinePtr, - String share, - })?>((ref) => null); +final pFrostContinueSignData = + StateProvider<({OpaqueWrapper machinePtr, String share})?>((ref) => null); // ===================== shared/util =========================================== -final pFrostSelectParticipantsUnordered = - StateProvider?>((ref) => null); +final pFrostSelectParticipantsUnordered = StateProvider?>( + (ref) => null, +); // ========================= resharing ========================================= final pFrostResharingData = Provider((ref) => _ResharingData()); @@ -66,31 +61,23 @@ class _ResharingData { int newThreshold, Map resharers, List newParticipants, - })? get configData => resharerRConfig != null - ? Frost.extractResharerConfigData(rConfig: resharerRConfig!) + })? + get configData => resharerRConfig != null + ? frostInterface.extractResharerConfigData(rConfig: resharerRConfig!) : null; // resharer start string (for sharing) and machine - ({ - String resharerStart, - Pointer machine, - })? startResharerData; + ({String resharerStart, OpaqueWrapper machine})? startResharerData; // reshared start string (for sharing) and machine - ({ - String resharedStart, - Pointer prior, - })? startResharedData; + ({String resharedStart, OpaqueWrapper prior})? startResharedData; // resharer complete string (for sharing) String? resharerComplete; // new keys and config with an ID - ({ - String multisigConfig, - String serializedKeys, - String resharedId, - })? newWalletData; + ({String multisigConfig, String serializedKeys, String resharedId})? + newWalletData; // reset/clear all data void reset() { diff --git a/lib/wallets/wallet/impl/bitcoin_frost_wallet.dart b/lib/wallets/wallet/impl/bitcoin_frost_wallet.dart index dc0561429f..00537b12b8 100644 --- a/lib/wallets/wallet/impl/bitcoin_frost_wallet.dart +++ b/lib/wallets/wallet/impl/bitcoin_frost_wallet.dart @@ -1,11 +1,10 @@ import 'dart:async'; -import 'dart:ffi'; import 'dart:math'; import 'package:flutter/foundation.dart'; -import 'package:frostdart/frostdart.dart' as frost; -import 'package:frostdart/frostdart_bindings_generated.dart'; -import 'package:frostdart/util.dart'; +// import 'package:frostdart/frostdart.dart' as frost; +// import 'package:frostdart/frostdart_bindings_generated.dart'; +// import 'package:frostdart/util.dart'; import 'package:isar_community/isar.dart'; import '../../../electrumx_rpc/cached_electrumx_client.dart'; @@ -21,10 +20,10 @@ import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../models/paymint/fee_object_model.dart'; import '../../../services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import '../../../services/event_bus/global_event_bus.dart'; -import '../../../services/frost.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/extensions/extensions.dart'; import '../../../utilities/logger.dart'; +import '../../../wl_gen/interfaces/frost_interface.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/intermediate/frost_currency.dart'; import '../../isar/models/frost_wallet_info.dart'; @@ -40,11 +39,10 @@ class BitcoinFrostWallet extends Wallet BitcoinFrostWallet(CryptoCurrencyNetwork network) : super(BitcoinFrost(network) as T); - FrostWalletInfo get frostInfo => - mainDB.isar.frostWalletInfo - .where() - .walletIdEqualTo(walletId) - .findFirstSync()!; + FrostWalletInfo get frostInfo => mainDB.isar.frostWalletInfo + .where() + .walletIdEqualTo(walletId) + .findFirstSync()!; late ElectrumXClient electrumXClient; late CachedElectrumXClient electrumXCachedClient; @@ -61,7 +59,9 @@ class BitcoinFrostWallet extends Wallet Logging.instance.i("Generating new FROST wallet."); try { - final salt = frost.multisigSalt(multisigConfig: multisigConfig).toHex; + final salt = frostInterface + .getMultisigSalt(multisigConfig: multisigConfig) + .toHex; final FrostWalletInfo frostWalletInfo = FrostWalletInfo( walletId: info.walletId, @@ -90,14 +90,10 @@ class BitcoinFrostWallet extends Wallet serializedKeys: serializedKeys, secure: true, ); - } on FrostdartException catch (e) { - if (e.errorCode == 72) { - // rust doesn't like the addressDerivationData - index++; - continue; - } else { - rethrow; - } + } on FrostBadIndexException catch (_) { + // rust doesn't like the addressDerivationData + index++; + continue; } } @@ -125,24 +121,22 @@ class BitcoinFrostWallet extends Wallet .map((e) => e.amount) .reduce((value, e) => value += e); - final utxos = - await mainDB - .getUTXOs(walletId) - .filter() - .isBlockedEqualTo(false) - .findAll(); + final utxos = await mainDB + .getUTXOs(walletId) + .filter() + .isBlockedEqualTo(false) + .findAll(); if (utxos.isEmpty) { throw Exception("No UTXOs found"); } else { final currentHeight = await chainHeight; utxos.removeWhere( - (e) => - !e.isConfirmed( - currentHeight, - cryptoCurrency.minConfirms, - cryptoCurrency.minCoinbaseConfirms, - ), + (e) => !e.isConfirmed( + currentHeight, + cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, + ), ); if (utxos.isEmpty) { throw Exception("No confirmed UTXOs found"); @@ -174,10 +168,9 @@ class BitcoinFrostWallet extends Wallet } } - final int network = - cryptoCurrency.network == CryptoCurrencyNetwork.main - ? Network.Mainnet - : Network.Testnet; + final int network = cryptoCurrency.network == CryptoCurrencyNetwork.main + ? frostInterface.mainnet + : frostInterface.testnet; final List< ({ @@ -208,7 +201,7 @@ class BitcoinFrostWallet extends Wallet while (config == null) { try { - config = Frost.createSignConfig( + config = frostInterface.createSignConfig( network: network, inputs: inputs, outputs: txData.recipients!, @@ -216,9 +209,8 @@ class BitcoinFrostWallet extends Wallet feePerWeight: feePerWeight, serializedKeys: (await getSerializedKeys())!, ); - } on FrostdartException catch (e) { - if (e.errorCode == NOT_ENOUGH_FUNDS_ERROR && - utxosRemaining.isNotEmpty) { + } on FrostInsufficientFundsException catch (_) { + if (utxosRemaining.isNotEmpty) { // add extra utxo final utxo = utxosRemaining.take(1).first; final dData = await getDerivationData(utxo.address); @@ -281,17 +273,14 @@ class BitcoinFrostWallet extends Wallet } } - Future< - ({Pointer machinePtr, String preprocess}) - > + Future<({OpaqueWrapper machinePtr, String preprocess})> frostAttemptSignConfig({required String config}) async { - final int network = - cryptoCurrency.network == CryptoCurrencyNetwork.main - ? Network.Mainnet - : Network.Testnet; + final int network = cryptoCurrency.network == CryptoCurrencyNetwork.main + ? frostInterface.mainnet + : frostInterface.testnet; final serializedKeys = await getSerializedKeys(); - return Frost.attemptSignConfig( + return frostInterface.attemptSignConfig( network: network, config: config, serializedKeys: serializedKeys!, @@ -307,13 +296,15 @@ class BitcoinFrostWallet extends Wallet await _saveMultisigConfig(multisigConfig); await _updateThreshold( - frost.getThresholdFromKeys(serializedKeys: serializedKeys), + frostInterface.thresholdFromKeys(serializedKeys: serializedKeys), ); - final myNameIndex = frost.getParticipantIndexFromKeys( + final myNameIndex = frostInterface.participantIndexFromKeys( serializedKeys: serializedKeys, ); - final participants = Frost.getParticipants(multisigConfig: multisigConfig); + final participants = frostInterface.getParticipants( + multisigConfig: multisigConfig, + ); final myName = participants[myNameIndex]; await _updateParticipants(participants); @@ -409,16 +400,14 @@ class BitcoinFrostWallet extends Wallet await _fetchAddressesForElectrumXScan(); // Separate receiving and change addresses. - final Set receivingAddresses = - allAddressesOld - .where((e) => e.subType == AddressSubType.receiving) - .map((e) => e.value) - .toSet(); - final Set changeAddresses = - allAddressesOld - .where((e) => e.subType == AddressSubType.change) - .map((e) => e.value) - .toSet(); + final Set receivingAddresses = allAddressesOld + .where((e) => e.subType == AddressSubType.receiving) + .map((e) => e.value) + .toSet(); + final Set changeAddresses = allAddressesOld + .where((e) => e.subType == AddressSubType.change) + .map((e) => e.value) + .toSet(); // Remove duplicates. final allAddressesSet = {...receivingAddresses, ...changeAddresses}; @@ -433,13 +422,12 @@ class BitcoinFrostWallet extends Wallet final List> allTransactions = []; for (final txHash in allTxHashes) { - final storedTx = - await mainDB.isar.transactionV2s - .where() - .walletIdEqualTo(walletId) - .filter() - .txidEqualTo(txHash["tx_hash"] as String) - .findFirst(); + final storedTx = await mainDB.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) + .filter() + .txidEqualTo(txHash["tx_hash"] as String) + .findFirst(); if (storedTx == null || !storedTx.isConfirmed( @@ -766,21 +754,18 @@ class BitcoinFrostWallet extends Wallet numberOfBlocksFast: f, numberOfBlocksAverage: m, numberOfBlocksSlow: s, - fast: - Amount.fromDecimal( - fast, - fractionDigits: cryptoCurrency.fractionDigits, - ).raw, - medium: - Amount.fromDecimal( - medium, - fractionDigits: cryptoCurrency.fractionDigits, - ).raw, - slow: - Amount.fromDecimal( - slow, - fractionDigits: cryptoCurrency.fractionDigits, - ).raw, + fast: Amount.fromDecimal( + fast, + fractionDigits: cryptoCurrency.fractionDigits, + ).raw, + medium: Amount.fromDecimal( + medium, + fractionDigits: cryptoCurrency.fractionDigits, + ).raw, + slow: Amount.fromDecimal( + slow, + fractionDigits: cryptoCurrency.fractionDigits, + ).raw, ); Logging.instance.i("fetched fees: $feeObject"); @@ -827,8 +812,9 @@ class BitcoinFrostWallet extends Wallet try { await refreshMutex.protect(() async { if (!isRescan) { - final salt = - frost.multisigSalt(multisigConfig: multisigConfig!).toHex; + final salt = frostInterface + .getMultisigSalt(multisigConfig: multisigConfig!) + .toHex; final knownSalts = _getKnownSalts(); if (knownSalts.contains(salt)) { throw Exception("Known frost multisig salt found!"); @@ -1211,12 +1197,11 @@ class BitcoinFrostWallet extends Wallet // =================== DB ==================================================== - List _getKnownSalts() => - mainDB.isar.frostWalletInfo - .where() - .walletIdEqualTo(walletId) - .knownSaltsProperty() - .findFirstSync()!; + List _getKnownSalts() => mainDB.isar.frostWalletInfo + .where() + .walletIdEqualTo(walletId) + .knownSaltsProperty() + .findFirstSync()!; Future _updateKnownSalts(List knownSalts) async { final info = frostInfo; @@ -1229,12 +1214,11 @@ class BitcoinFrostWallet extends Wallet }); } - List _getParticipants() => - mainDB.isar.frostWalletInfo - .where() - .walletIdEqualTo(walletId) - .participantsProperty() - .findFirstSync()!; + List _getParticipants() => mainDB.isar.frostWalletInfo + .where() + .walletIdEqualTo(walletId) + .participantsProperty() + .findFirstSync()!; Future _updateParticipants(List participants) async { final info = frostInfo; @@ -1247,12 +1231,11 @@ class BitcoinFrostWallet extends Wallet }); } - int _getThreshold() => - mainDB.isar.frostWalletInfo - .where() - .walletIdEqualTo(walletId) - .thresholdProperty() - .findFirstSync()!; + int _getThreshold() => mainDB.isar.frostWalletInfo + .where() + .walletIdEqualTo(walletId) + .thresholdProperty() + .findFirstSync()!; Future _updateThreshold(int threshold) async { final info = frostInfo; @@ -1265,12 +1248,11 @@ class BitcoinFrostWallet extends Wallet }); } - String _getMyName() => - mainDB.isar.frostWalletInfo - .where() - .walletIdEqualTo(walletId) - .myNameProperty() - .findFirstSync()!; + String _getMyName() => mainDB.isar.frostWalletInfo + .where() + .walletIdEqualTo(walletId) + .myNameProperty() + .findFirstSync()!; Future _updateMyName(String myName) async { final info = frostInfo; @@ -1299,21 +1281,20 @@ class BitcoinFrostWallet extends Wallet // TODO [prio=low]: Use ElectrumXInterface method. Future _updateElectrumX() async { - final failovers = - nodeService - .failoverNodesFor(currency: cryptoCurrency) - .map( - (e) => ElectrumXNode( - address: e.host, - port: e.port, - name: e.name, - id: e.id, - useSSL: e.useSSL, - torEnabled: e.torEnabled, - clearnetEnabled: e.clearnetEnabled, - ), - ) - .toList(); + final failovers = nodeService + .failoverNodesFor(currency: cryptoCurrency) + .map( + (e) => ElectrumXNode( + address: e.host, + port: e.port, + name: e.name, + id: e.id, + useSSL: e.useSSL, + torEnabled: e.torEnabled, + clearnetEnabled: e.clearnetEnabled, + ), + ) + .toList(); final newNode = await _getCurrentElectrumXNode(); try { @@ -1477,10 +1458,9 @@ class BitcoinFrostWallet extends Wallet @override Future generateNewChangeAddress() async { final current = await getCurrentChangeAddress(); - int index = - current == null - ? kFrostSecureStartingIndex - : current.derivationIndex + 1; + int index = current == null + ? kFrostSecureStartingIndex + : current.derivationIndex + 1; const chain = 1; // change address final serializedKeys = (await getSerializedKeys())!; @@ -1494,14 +1474,10 @@ class BitcoinFrostWallet extends Wallet serializedKeys: serializedKeys, secure: true, ); - } on FrostdartException catch (e) { - if (e.errorCode == 72) { - // rust doesn't like the addressDerivationData - index++; - continue; - } else { - rethrow; - } + } on FrostBadIndexException catch (_) { + // rust doesn't like the addressDerivationData + index++; + continue; } } @@ -1511,10 +1487,9 @@ class BitcoinFrostWallet extends Wallet @override Future generateNewReceivingAddress() async { final current = await getCurrentReceivingAddress(); - int index = - current == null - ? kFrostSecureStartingIndex - : current.derivationIndex + 1; + int index = current == null + ? kFrostSecureStartingIndex + : current.derivationIndex + 1; const chain = 0; // receiving address final serializedKeys = (await getSerializedKeys())!; @@ -1528,14 +1503,10 @@ class BitcoinFrostWallet extends Wallet serializedKeys: serializedKeys, secure: true, ); - } on FrostdartException catch (e) { - if (e.errorCode == 72) { - // rust doesn't like the addressDerivationData - index++; - continue; - } else { - rethrow; - } + } on FrostBadIndexException catch (_) { + // rust doesn't like the addressDerivationData + index++; + continue; } } @@ -1627,14 +1598,10 @@ class BitcoinFrostWallet extends Wallet serializedKeys: serializedKeys, secure: true, ); - } on FrostdartException catch (e) { - if (e.errorCode == 72) { - // rust doesn't like the addressDerivationData - startingIndex++; - continue; - } else { - rethrow; - } + } on FrostBadIndexException catch (_) { + // rust doesn't like the addressDerivationData + startingIndex++; + continue; } } @@ -1657,13 +1624,12 @@ class BitcoinFrostWallet extends Wallet secure: secure, ); - final keys = frost.deserializeKeys(keys: serializedKeys); + final keys = frostInterface.getDeserializedKeys(keys: serializedKeys); - final addressString = frost.addressForKeys( - network: - cryptoCurrency.network == CryptoCurrencyNetwork.main - ? Network.Mainnet - : Network.Testnet, + final addressString = frostInterface.getAddressForKeys( + network: cryptoCurrency.network == CryptoCurrencyNetwork.main + ? frostInterface.mainnet + : frostInterface.testnet, keys: keys, addressDerivationData: addressDerivationData, secure: secure, @@ -1675,12 +1641,11 @@ class BitcoinFrostWallet extends Wallet publicKey: cryptoCurrency.addressToPubkey(address: addressString), derivationIndex: index, derivationPath: DerivationPath()..value = "$account/$change/$index", - subType: - change == 0 - ? AddressSubType.receiving - : change == 1 - ? AddressSubType.change - : AddressSubType.unknown, + subType: change == 0 + ? AddressSubType.receiving + : change == 1 + ? AddressSubType.change + : AddressSubType.unknown, type: AddressType.frostMS, zSafeFrost: secure && index >= kFrostSecureStartingIndex, ); @@ -1708,14 +1673,10 @@ class BitcoinFrostWallet extends Wallet serializedKeys: serializedKeys, secure: secure, ); - } on FrostdartException catch (e) { - if (e.errorCode == 72) { - // rust doesn't like the addressDerivationData - index++; - continue; - } else { - rethrow; - } + } on FrostBadIndexException catch (_) { + // rust doesn't like the addressDerivationData + index++; + continue; } } @@ -1746,18 +1707,17 @@ class BitcoinFrostWallet extends Wallet } Future> _fetchAddressesForElectrumXScan() async { - final allAddresses = - await mainDB - .getAddresses(walletId) - .filter() - .not() - .group( - (q) => q - .typeEqualTo(AddressType.nonWallet) - .or() - .subTypeEqualTo(AddressSubType.nonWallet), - ) - .findAll(); + final allAddresses = await mainDB + .getAddresses(walletId) + .filter() + .not() + .group( + (q) => q + .typeEqualTo(AddressType.nonWallet) + .or() + .subTypeEqualTo(AddressSubType.nonWallet), + ) + .findAll(); return allAddresses; } diff --git a/lib/wl_gen/interfaces/frost_interface.dart b/lib/wl_gen/interfaces/frost_interface.dart new file mode 100644 index 0000000000..affbda0d22 --- /dev/null +++ b/lib/wl_gen/interfaces/frost_interface.dart @@ -0,0 +1,237 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import '../../models/isar/models/blockchain_data/utxo.dart'; +import '../../utilities/amount/amount.dart'; +import '../../utilities/extensions/extensions.dart'; +import '../../wallets/crypto_currency/crypto_currency.dart'; +import '../../wallets/models/tx_recipient.dart'; + +export '../generated/frost_interface_impl.dart'; + +abstract class FrostInterface { + const FrostInterface(); + + //==================== utility =============================================== + List getParticipants({required String multisigConfig}); + + bool validateEncodedMultisigConfig({required String encodedConfig}); + + int getThreshold({required String multisigConfig}); + + ({ + List<({String address, Amount amount})> recipients, + String changeAddress, + int feePerWeight, + List inputs, + }) + extractDataFromSignConfig({ + required String serializedKeys, + required String signConfig, + required CryptoCurrency coin, + }); + + //==================== wallet creation ======================================= + + String createMultisigConfig({ + required String name, + required int threshold, + required List participants, + }); + + ({ + String seed, + String commitments, + // Pointer multisigConfigWithNamePtr, + OpaqueWrapper multisigConfigWithNamePtr, + // Pointer secretShareMachineWrapperPtr, + OpaqueWrapper secretShareMachineWrapperPtr, + }) + startKeyGeneration({required String multisigConfig, required String myName}); + + // ({String share, Pointer secretSharesResPtr}) + ({String share, OpaqueWrapper secretSharesResPtr}) generateSecretShares({ + // required Pointer multisigConfigWithNamePtr, + required OpaqueWrapper multisigConfigWithNamePtr, + required String mySeed, + // required Pointer secretShareMachineWrapperPtr, + required OpaqueWrapper secretShareMachineWrapperPtr, + required List commitments, + }); + + ({Uint8List multisigId, String recoveryString, String serializedKeys}) + completeKeyGeneration({ + // required Pointer multisigConfigWithNamePtr, + required OpaqueWrapper multisigConfigWithNamePtr, + // required Pointer secretSharesResPtr, + required OpaqueWrapper secretSharesResPtr, + required List shares, + }); + + //=================== transaction creation =================================== + + String createSignConfig({ + required String serializedKeys, + required int network, + required List< + ({ + UTXO utxo, + Uint8List scriptPubKey, + FrostAddressDerivationData addressDerivationData, + }) + > + inputs, + required List outputs, + required String changeAddress, + required int feePerWeight, + }); + + // ({Pointer machinePtr, String preprocess}) + ({OpaqueWrapper machinePtr, String preprocess}) attemptSignConfig({ + required int network, + required String config, + required String serializedKeys, + }); + + // ({Pointer machinePtr, String share}) + ({OpaqueWrapper machinePtr, String share}) continueSigning({ + // required Pointer machinePtr, + required OpaqueWrapper machinePtr, + required List preprocesses, + }); + + String completeSigning({ + // required Pointer machinePtr, + required OpaqueWrapper machinePtr, + required List shares, + }); + + // Pointer decodedSignConfig({ + OpaqueWrapper decodedSignConfig({ + required String serializedKeys, + required String encodedConfig, + required int network, + }); + + //========================== resharing ======================================= + + String createResharerConfig({ + required int newThreshold, + required List resharers, + required List newParticipants, + }); + + // ({String resharerStart, Pointer machine}) beginResharer({ + ({String resharerStart, OpaqueWrapper machine}) beginResharer({ + required String serializedKeys, + required String config, + }); + + /// expects [resharerStarts] of length equal to resharers. + // ({String resharedStart, Pointer prior}) beginReshared({ + ({String resharedStart, OpaqueWrapper prior}) beginReshared({ + required String myName, + required String resharerConfig, + required List resharerStarts, + }); + + /// expects [encryptionKeysOfResharedTo] of length equal to new participants + String finishResharer({ + // required StartResharerRes machine, + required OpaqueWrapper machine, + required List encryptionKeysOfResharedTo, + }); + + /// expects [resharerCompletes] of length equal to resharers + ({String multisigConfig, String serializedKeys, String resharedId}) + finishReshared({ + // required StartResharedRes prior, + required OpaqueWrapper prior, + required List resharerCompletes, + }); + + // Pointer decodedResharerConfig({ + OpaqueWrapper decodedResharerConfig({required String resharerConfig}); + + ({int newThreshold, Map resharers, List newParticipants}) + extractResharerConfigData({required String rConfig}); + + Uint8List getMultisigSalt({required String multisigConfig}); + int participantIndexFromKeys({required String serializedKeys}); + int getMultisigThreshold({required String multisigConfig}); + int thresholdFromKeys({required String serializedKeys}); + Uint8List getResharerSalt({required String resharerConfig}); + + OpaqueWrapper getDeserializedKeys({required String keys}); + String getAddressForKeys({ + required OpaqueWrapper keys, + required int network, + required FrostAddressDerivationData addressDerivationData, + required bool secure, + }); + + int get mainnet; + int get testnet; + + String encodeRConfig(String config, Map resharers) { + return base64Encode("$config@${jsonEncode(resharers)}".toUint8ListFromUtf8); + } + + String decodeRConfig(String rConfig) { + return base64Decode(rConfig).toUtf8String.split("@").first; + } +} + +final class FrostInsufficientFundsException implements Exception {} + +final class FrostBadIndexException implements Exception {} + +final class OpaqueWrapper { + final Object _value; + + const OpaqueWrapper(this._value); + + T getValue() { + if (_value is T) return _value as T; + throw Exception( + "OpaqueWrapper.getValue type of ${_value.runtimeType} is not ${T.runtimeType}", + ); + } + + @override + String toString() => "OpaqueWrapper(${_value.runtimeType})"; +} + +typedef FrostAddressDerivationData = ({ + int account, + bool change, + int index, + bool secure, +}); + +class FrostOutput { + final Uint8List hash; + final int vout; + final int value; + final Uint8List scriptPubKey; + + final FrostAddressDerivationData? addressDerivationData; + + FrostOutput({ + required this.hash, + required this.vout, + required this.value, + required this.scriptPubKey, + required this.addressDerivationData, + }); + + @override + String toString() => + 'FrostOutput{' + 'hash: $hash, ' + 'vout: $vout, ' + 'value: $value, ' + 'scriptPubKey: $scriptPubKey, ' + 'addressDerivationData: $addressDerivationData' + '}'; +} diff --git a/lib/services/frost.dart b/tool/wl_templates/FROST_frost_interface_impl.template.dart similarity index 64% rename from lib/services/frost.dart rename to tool/wl_templates/FROST_frost_interface_impl.template.dart index 33066de925..072af512d3 100644 --- a/lib/services/frost.dart +++ b/tool/wl_templates/FROST_frost_interface_impl.template.dart @@ -1,3 +1,4 @@ +//ON import 'dart:convert'; import 'dart:ffi'; import 'dart:typed_data'; @@ -7,238 +8,105 @@ import 'package:frostdart/frostdart_bindings_generated.dart'; import 'package:frostdart/output.dart'; import 'package:frostdart/util.dart'; -import '../models/isar/models/blockchain_data/utxo.dart'; -import '../utilities/amount/amount.dart'; -import '../utilities/extensions/extensions.dart'; -import '../utilities/logger.dart'; -import '../wallets/crypto_currency/crypto_currency.dart'; -import '../wallets/models/tx_recipient.dart'; +import '../../models/isar/models/blockchain_data/utxo.dart'; +import '../../utilities/amount/amount.dart'; +import '../../utilities/extensions/extensions.dart'; +import '../../utilities/logger.dart'; +import '../../wallets/crypto_currency/crypto_currency.dart'; +import '../../wallets/models/tx_recipient.dart'; +//END_ON +import '../interfaces/frost_interface.dart'; -abstract class Frost { - //==================== utility =============================================== - static List getParticipants({required String multisigConfig}) { - try { - final numberOfParticipants = multisigParticipants( - multisigConfig: multisigConfig, - ); - - final List participants = []; - for (int i = 0; i < numberOfParticipants; i++) { - participants.add( - multisigParticipant(multisigConfig: multisigConfig, index: i), - ); - } - - return participants; - } catch (e, s) { - Logging.instance.f("getParticipants failed: ", error: e, stackTrace: s); - rethrow; - } - } - - static bool validateEncodedMultisigConfig({required String encodedConfig}) { - try { - decodeMultisigConfig(multisigConfig: encodedConfig); - return true; - } catch (e, s) { - Logging.instance.f( - "validateEncodedMultisigConfig failed: ", - error: e, - stackTrace: s, - ); - return false; - } - } +FrostInterface get frostInterface => _getInterface(); - static int getThreshold({required String multisigConfig}) { - try { - final threshold = multisigThreshold(multisigConfig: multisigConfig); +//OFF +FrostInterface _getInterface() => throw Exception("FROST not enabled!"); - return threshold; - } catch (e, s) { - Logging.instance.f("getThreshold failed: ", error: e, stackTrace: s); - rethrow; - } - } +//END_OFF +//ON +FrostInterface _getInterface() => _FrostInterfaceImpl(); - static ({ - List<({String address, Amount amount})> recipients, - String changeAddress, - int feePerWeight, - List inputs, - }) - extractDataFromSignConfig({ +final class _FrostInterfaceImpl extends FrostInterface { + @override + ({OpaqueWrapper machinePtr, String preprocess}) attemptSignConfig({ + required int network, + required String config, required String serializedKeys, - required String signConfig, - required CryptoCurrency coin, }) { try { - final network = - coin.network.isTestNet ? Network.Testnet : Network.Mainnet; - final signConfigPointer = decodedSignConfig( - encodedConfig: signConfig, - network: network, - serializedKeys: serializedKeys, - ); + final keys = deserializeKeys(keys: serializedKeys); - // get various data from config - final feePerWeight = signFeePerWeight( - signConfigPointer: signConfigPointer, - ); - final changeAddress = signChange(signConfigPointer: signConfigPointer); - final recipientsCount = signPayments( - signConfigPointer: signConfigPointer, + final attemptSignRes = attemptSign( + thresholdKeysWrapperPointer: keys, + network: network, + signConfig: config, ); - // get tx recipient info - final List<({String address, Amount amount})> recipients = []; - for (int i = 0; i < recipientsCount; i++) { - final String address = signPaymentAddress( - signConfigPointer: signConfigPointer, - index: i, - ); - final int amount = signPaymentAmount( - signConfigPointer: signConfigPointer, - index: i, - ); - recipients.add(( - address: address, - amount: Amount( - rawValue: BigInt.from(amount), - fractionDigits: coin.fractionDigits, - ), - )); - } - - // get utxos - final count = signInputs(signConfigPointer: signConfigPointer); - final List outputs = []; - for (int i = 0; i < count; i++) { - final output = signInput( - thresholdKeysWrapperPointer: deserializeKeys(keys: serializedKeys), - signConfig: signConfig, - index: i, - network: network, - ); - - outputs.add(output); - } - return ( - recipients: recipients, - changeAddress: changeAddress, - feePerWeight: feePerWeight, - inputs: outputs, + preprocess: attemptSignRes.ref.preprocess.toDartString(), + machinePtr: OpaqueWrapper(attemptSignRes.ref.machine), ); } catch (e, s) { - Logging.instance.f( - "extractDataFromSignConfig failed: ", - error: e, - stackTrace: s, - ); + Logging.instance.f("attemptSignConfig failed: ", error: e, stackTrace: s); rethrow; } } - //==================== wallet creation ======================================= - - static String createMultisigConfig({ - required String name, - required int threshold, - required List participants, + @override + ({OpaqueWrapper prior, String resharedStart}) beginReshared({ + required String myName, + required String resharerConfig, + required List resharerStarts, }) { try { - final config = newMultisigConfig( - name: name, - threshold: threshold, - participants: participants, - ); - - return config; - } catch (e, s) { - Logging.instance.f( - "createMultisigConfig failed: ", - error: e, - stackTrace: s, - ); - rethrow; - } - } - - static ({ - String seed, - String commitments, - Pointer multisigConfigWithNamePtr, - Pointer secretShareMachineWrapperPtr, - }) - startKeyGeneration({required String multisigConfig, required String myName}) { - try { - final startKeyGenResPtr = startKeyGen( - multisigConfig: multisigConfig, + final result = startReshared( + newMultisigName: 'unused_property', myName: myName, - language: Language.english, + resharerConfig: resharerConfig, + resharerStarts: resharerStarts, ); - - final seed = startKeyGenResPtr.ref.seed.toDartString(); - final commitments = startKeyGenResPtr.ref.commitments.toDartString(); - final configWithNamePtr = startKeyGenResPtr.ref.config; - final machinePtr = startKeyGenResPtr.ref.machine; - return ( - seed: seed, - commitments: commitments, - multisigConfigWithNamePtr: configWithNamePtr, - secretShareMachineWrapperPtr: machinePtr, + resharedStart: result.encoded, + prior: OpaqueWrapper(result.machine), ); } catch (e, s) { - Logging.instance.f( - "startKeyGeneration failed: ", - error: e, - stackTrace: s, - ); + Logging.instance.f("beginReshared failed: ", error: e, stackTrace: s); rethrow; } } - static ({String share, Pointer secretSharesResPtr}) - generateSecretShares({ - required Pointer multisigConfigWithNamePtr, - required String mySeed, - required Pointer secretShareMachineWrapperPtr, - required List commitments, + @override + ({OpaqueWrapper machine, String resharerStart}) beginResharer({ + required String serializedKeys, + required String config, }) { try { - final secretSharesResPtr = getSecretShares( - multisigConfigWithName: multisigConfigWithNamePtr, - seed: mySeed, - language: Language.english, - machine: secretShareMachineWrapperPtr, - commitments: commitments, + final result = startResharer( + serializedKeys: serializedKeys, + config: config, ); - final share = secretSharesResPtr.ref.shares.toDartString(); - - return (share: share, secretSharesResPtr: secretSharesResPtr); - } catch (e, s) { - Logging.instance.f( - "generateSecretShares failed: ", - error: e, - stackTrace: s, + return ( + resharerStart: result.encoded, + machine: OpaqueWrapper(result.machine), ); + } catch (e, s) { + Logging.instance.f("beginResharer failed: ", error: e, stackTrace: s); rethrow; } } - static ({Uint8List multisigId, String recoveryString, String serializedKeys}) + @override + ({Uint8List multisigId, String recoveryString, String serializedKeys}) completeKeyGeneration({ - required Pointer multisigConfigWithNamePtr, - required Pointer secretSharesResPtr, + required OpaqueWrapper multisigConfigWithNamePtr, + required OpaqueWrapper secretSharesResPtr, required List shares, }) { try { final keyGenResPtr = completeKeyGen( - multisigConfigWithName: multisigConfigWithNamePtr, - machineAndCommitments: secretSharesResPtr, + multisigConfigWithName: multisigConfigWithNamePtr.getValue(), + machineAndCommitments: secretSharesResPtr.getValue(), shares: shares, ); @@ -268,97 +136,38 @@ abstract class Frost { } } - //=================== transaction creation =================================== - - static String createSignConfig({ - required String serializedKeys, - required int network, - required List< - ({ - UTXO utxo, - Uint8List scriptPubKey, - AddressDerivationData addressDerivationData, - }) - > - inputs, - required List outputs, - required String changeAddress, - required int feePerWeight, - }) { - try { - final signConfig = newSignConfig( - thresholdKeysWrapperPointer: deserializeKeys(keys: serializedKeys), - network: network, - outputs: - inputs - .map( - (e) => Output( - hash: e.utxo.txid.toUint8ListFromHex, - vout: e.utxo.vout, - value: e.utxo.value, - scriptPubKey: e.scriptPubKey, - addressDerivationData: e.addressDerivationData, - ), - ) - .toList(), - paymentAddresses: outputs.map((e) => e.address).toList(), - paymentAmounts: outputs.map((e) => e.amount.raw.toInt()).toList(), - change: changeAddress, - feePerWeight: feePerWeight, - ); - - return signConfig; - } catch (e, s) { - Logging.instance.f("createSignConfig failed: ", error: e, stackTrace: s); - rethrow; - } - } - - static ({ - Pointer machinePtr, - String preprocess, - }) - attemptSignConfig({ - required int network, - required String config, - required String serializedKeys, + @override + String completeSigning({ + required OpaqueWrapper machinePtr, + required List shares, }) { try { - final keys = deserializeKeys(keys: serializedKeys); - - final attemptSignRes = attemptSign( - thresholdKeysWrapperPointer: keys, - network: network, - signConfig: config, + final rawTransaction = completeSign( + machine: machinePtr.getValue(), + shares: shares, ); - return ( - preprocess: attemptSignRes.ref.preprocess.toDartString(), - machinePtr: attemptSignRes.ref.machine, - ); + return rawTransaction; } catch (e, s) { - Logging.instance.f("attemptSignConfig failed: ", error: e, stackTrace: s); + Logging.instance.f("completeSigning failed: ", error: e, stackTrace: s); rethrow; } } - static ({ - Pointer machinePtr, - String share, - }) - continueSigning({ - required Pointer machinePtr, + @override + ({OpaqueWrapper machinePtr, String share}) continueSigning({ + required OpaqueWrapper machinePtr, required List preprocesses, }) { try { final continueSignRes = continueSign( - machine: machinePtr, + machine: machinePtr.getValue(), preprocesses: preprocesses, ); return ( share: continueSignRes.ref.preprocess.toDartString(), - machinePtr: continueSignRes.ref.machine, + machinePtr: OpaqueWrapper(continueSignRes.ref.machine), ); } catch (e, s) { Logging.instance.f("continueSigning failed: ", error: e, stackTrace: s); @@ -366,41 +175,32 @@ abstract class Frost { } } - static String completeSigning({ - required Pointer machinePtr, - required List shares, + @override + String createMultisigConfig({ + required String name, + required int threshold, + required List participants, }) { try { - final rawTransaction = completeSign(machine: machinePtr, shares: shares); + final config = newMultisigConfig( + name: name, + threshold: threshold, + participants: participants, + ); - return rawTransaction; + return config; } catch (e, s) { - Logging.instance.f("completeSigning failed: ", error: e, stackTrace: s); - rethrow; - } - } - - static Pointer decodedSignConfig({ - required String serializedKeys, - required String encodedConfig, - required int network, - }) { - try { - final configPtr = decodeSignConfig( - thresholdKeysWrapperPointer: deserializeKeys(keys: serializedKeys), - encodedSignConfig: encodedConfig, - network: network, + Logging.instance.f( + "createMultisigConfig failed: ", + error: e, + stackTrace: s, ); - return configPtr; - } catch (e, s) { - Logging.instance.f("decodedSignConfig failed: ", error: e, stackTrace: s); rethrow; } } - //========================== resharing ======================================= - - static String createResharerConfig({ + @override + String createResharerConfig({ required int newThreshold, required List resharers, required List newParticipants, @@ -423,87 +223,176 @@ abstract class Frost { } } - static ({String resharerStart, Pointer machine}) - beginResharer({required String serializedKeys, required String config}) { + @override + String createSignConfig({ + required String serializedKeys, + required int network, + required List< + ({ + Uint8List scriptPubKey, + UTXO utxo, + FrostAddressDerivationData addressDerivationData, + }) + > + inputs, + required List outputs, + required String changeAddress, + required int feePerWeight, + }) { try { - final result = startResharer( - serializedKeys: serializedKeys, - config: config, + final signConfig = newSignConfig( + thresholdKeysWrapperPointer: deserializeKeys(keys: serializedKeys), + network: network, + outputs: inputs + .map( + (e) => Output( + hash: e.utxo.txid.toUint8ListFromHex, + vout: e.utxo.vout, + value: e.utxo.value, + scriptPubKey: e.scriptPubKey, + addressDerivationData: e.addressDerivationData, + ), + ) + .toList(), + paymentAddresses: outputs.map((e) => e.address).toList(), + paymentAmounts: outputs.map((e) => e.amount.raw.toInt()).toList(), + change: changeAddress, + feePerWeight: feePerWeight, ); - return (resharerStart: result.encoded, machine: result.machine); + return signConfig; } catch (e, s) { - Logging.instance.f("beginResharer failed: ", error: e, stackTrace: s); + Logging.instance.f("createSignConfig failed: ", error: e, stackTrace: s); + + if (e is FrostdartException && e.errorCode == NOT_ENOUGH_FUNDS_ERROR) { + throw FrostInsufficientFundsException(); + } + rethrow; } } - /// expects [resharerStarts] of length equal to resharers. - static ({String resharedStart, Pointer prior}) - beginReshared({ - required String myName, - required String resharerConfig, - required List resharerStarts, - }) { + @override + OpaqueWrapper decodedResharerConfig({required String resharerConfig}) { try { - final result = startReshared( - newMultisigName: 'unused_property', - myName: myName, - resharerConfig: resharerConfig, - resharerStarts: resharerStarts, - ); - return (resharedStart: result.encoded, prior: result.machine); + final config = decodeResharerConfig(resharerConfig: resharerConfig); + + return OpaqueWrapper(config); } catch (e, s) { - Logging.instance.f("beginReshared failed: ", error: e, stackTrace: s); + Logging.instance.f( + "decodedResharerConfig failed: ", + error: e, + stackTrace: s, + ); rethrow; } } - /// expects [encryptionKeysOfResharedTo] of length equal to new participants - static String finishResharer({ - required StartResharerRes machine, - required List encryptionKeysOfResharedTo, + @override + OpaqueWrapper decodedSignConfig({ + required String serializedKeys, + required String encodedConfig, + required int network, }) { try { - final result = completeResharer( - machine: machine, - encryptionKeysOfResharedTo: encryptionKeysOfResharedTo, + final configPtr = decodeSignConfig( + thresholdKeysWrapperPointer: deserializeKeys(keys: serializedKeys), + encodedSignConfig: encodedConfig, + network: network, ); - return result; + return OpaqueWrapper(configPtr); } catch (e, s) { - Logging.instance.f("finishResharer failed: ", error: e, stackTrace: s); + Logging.instance.f("decodedSignConfig failed: ", error: e, stackTrace: s); rethrow; } } - /// expects [resharerCompletes] of length equal to resharers - static ({String multisigConfig, String serializedKeys, String resharedId}) - finishReshared({ - required StartResharedRes prior, - required List resharerCompletes, + @override + ({ + String changeAddress, + int feePerWeight, + List inputs, + List<({String address, Amount amount})> recipients, + }) + extractDataFromSignConfig({ + required String serializedKeys, + required String signConfig, + required CryptoCurrency coin, }) { try { - final result = completeReshared( - prior: prior, - resharerCompletes: resharerCompletes, + final network = coin.network.isTestNet + ? Network.Testnet + : Network.Mainnet; + final signConfigPointer = decodedSignConfig( + encodedConfig: signConfig, + network: network, + serializedKeys: serializedKeys, ); - return result; - } catch (e, s) { - Logging.instance.f("finishReshared failed: ", error: e, stackTrace: s); - rethrow; - } - } - static Pointer decodedResharerConfig({ - required String resharerConfig, - }) { - try { - final config = decodeResharerConfig(resharerConfig: resharerConfig); + // get various data from config + final feePerWeight = signFeePerWeight( + signConfigPointer: signConfigPointer.getValue(), + ); + final changeAddress = signChange( + signConfigPointer: signConfigPointer.getValue(), + ); + final recipientsCount = signPayments( + signConfigPointer: signConfigPointer.getValue(), + ); - return config; + // get tx recipient info + final List<({String address, Amount amount})> recipients = []; + for (int i = 0; i < recipientsCount; i++) { + final String address = signPaymentAddress( + signConfigPointer: signConfigPointer.getValue(), + index: i, + ); + final int amount = signPaymentAmount( + signConfigPointer: signConfigPointer.getValue(), + index: i, + ); + recipients.add(( + address: address, + amount: Amount( + rawValue: BigInt.from(amount), + fractionDigits: coin.fractionDigits, + ), + )); + } + + // get utxos + final count = signInputs(signConfigPointer: signConfigPointer.getValue()); + final List outputs = []; + for (int i = 0; i < count; i++) { + final output = signInput( + thresholdKeysWrapperPointer: deserializeKeys(keys: serializedKeys), + signConfig: signConfig, + index: i, + network: network, + ); + + outputs.add(output); + } + + return ( + recipients: recipients, + changeAddress: changeAddress, + feePerWeight: feePerWeight, + inputs: outputs + .map( + (e) => FrostOutput( + hash: e.hash, + vout: e.vout, + value: e.value, + scriptPubKey: e.scriptPubKey, + addressDerivationData: e.addressDerivationData, + ), + ) + .toList(), + ); } catch (e, s) { Logging.instance.f( - "decodedResharerConfig failed: ", + "extractDataFromSignConfig failed: ", error: e, stackTrace: s, ); @@ -511,11 +400,8 @@ abstract class Frost { } } - static ({ - int newThreshold, - Map resharers, - List newParticipants, - }) + @override + ({List newParticipants, int newThreshold, Map resharers}) extractResharerConfigData({required String rConfig}) { final decoded = _decodeRConfigWithResharers(rConfig); final resharerConfig = decoded.config; @@ -524,13 +410,13 @@ abstract class Frost { final newThreshold = resharerNewThreshold( resharerConfigPointer: decodedResharerConfig( resharerConfig: resharerConfig, - ), + ).getValue(), ); final resharersCount = resharerResharers( resharerConfigPointer: decodedResharerConfig( resharerConfig: resharerConfig, - ), + ).getValue(), ); final List resharers = []; for (int i = 0; i < resharersCount; i++) { @@ -538,7 +424,7 @@ abstract class Frost { resharerResharer( resharerConfigPointer: decodedResharerConfig( resharerConfig: resharerConfig, - ), + ).getValue(), index: i, ), ); @@ -547,7 +433,7 @@ abstract class Frost { final newParticipantsCount = resharerNewParticipants( resharerConfigPointer: decodedResharerConfig( resharerConfig: resharerConfig, - ), + ).getValue(), ); final List newParticipants = []; for (int i = 0; i < newParticipantsCount; i++) { @@ -555,7 +441,7 @@ abstract class Frost { resharerNewParticipant( resharerConfigPointer: decodedResharerConfig( resharerConfig: resharerConfig, - ), + ).getValue(), index: i, ), ); @@ -585,16 +471,210 @@ abstract class Frost { } } - static String encodeRConfig(String config, Map resharers) { - return base64Encode("$config@${jsonEncode(resharers)}".toUint8ListFromUtf8); + @override + ({String multisigConfig, String resharedId, String serializedKeys}) + finishReshared({ + required OpaqueWrapper prior, + required List resharerCompletes, + }) { + try { + final result = completeReshared( + prior: prior.getValue(), + resharerCompletes: resharerCompletes, + ); + return result; + } catch (e, s) { + Logging.instance.f("finishReshared failed: ", error: e, stackTrace: s); + rethrow; + } + } + + @override + String finishResharer({ + required OpaqueWrapper machine, + required List encryptionKeysOfResharedTo, + }) { + try { + final result = completeResharer( + machine: machine.getValue(), + encryptionKeysOfResharedTo: encryptionKeysOfResharedTo, + ); + return result; + } catch (e, s) { + Logging.instance.f("finishResharer failed: ", error: e, stackTrace: s); + rethrow; + } + } + + @override + ({OpaqueWrapper secretSharesResPtr, String share}) generateSecretShares({ + required OpaqueWrapper multisigConfigWithNamePtr, + required String mySeed, + required OpaqueWrapper secretShareMachineWrapperPtr, + required List commitments, + }) { + try { + final secretSharesResPtr = getSecretShares( + multisigConfigWithName: multisigConfigWithNamePtr.getValue(), + seed: mySeed, + language: Language.english, + machine: secretShareMachineWrapperPtr.getValue(), + commitments: commitments, + ); + + final share = secretSharesResPtr.ref.shares.toDartString(); + + return ( + share: share, + secretSharesResPtr: OpaqueWrapper(secretSharesResPtr), + ); + } catch (e, s) { + Logging.instance.f( + "generateSecretShares failed: ", + error: e, + stackTrace: s, + ); + rethrow; + } + } + + @override + List getParticipants({required String multisigConfig}) { + try { + final numberOfParticipants = multisigParticipants( + multisigConfig: multisigConfig, + ); + + final List participants = []; + for (int i = 0; i < numberOfParticipants; i++) { + participants.add( + multisigParticipant(multisigConfig: multisigConfig, index: i), + ); + } + + return participants; + } catch (e, s) { + Logging.instance.f("getParticipants failed: ", error: e, stackTrace: s); + rethrow; + } + } + + @override + int getThreshold({required String multisigConfig}) { + try { + final threshold = multisigThreshold(multisigConfig: multisigConfig); + + return threshold; + } catch (e, s) { + Logging.instance.f("getThreshold failed: ", error: e, stackTrace: s); + rethrow; + } + } + + @override + ({ + String commitments, + OpaqueWrapper multisigConfigWithNamePtr, + OpaqueWrapper secretShareMachineWrapperPtr, + String seed, + }) + startKeyGeneration({required String multisigConfig, required String myName}) { + try { + final startKeyGenResPtr = startKeyGen( + multisigConfig: multisigConfig, + myName: myName, + language: Language.english, + ); + + final seed = startKeyGenResPtr.ref.seed.toDartString(); + final commitments = startKeyGenResPtr.ref.commitments.toDartString(); + final configWithNamePtr = startKeyGenResPtr.ref.config; + final machinePtr = startKeyGenResPtr.ref.machine; + + return ( + seed: seed, + commitments: commitments, + multisigConfigWithNamePtr: OpaqueWrapper(configWithNamePtr), + secretShareMachineWrapperPtr: OpaqueWrapper(machinePtr), + ); + } catch (e, s) { + Logging.instance.f( + "startKeyGeneration failed: ", + error: e, + stackTrace: s, + ); + rethrow; + } + } + + @override + bool validateEncodedMultisigConfig({required String encodedConfig}) { + try { + decodeMultisigConfig(multisigConfig: encodedConfig); + return true; + } catch (e, s) { + Logging.instance.f( + "validateEncodedMultisigConfig failed: ", + error: e, + stackTrace: s, + ); + return false; + } } - static String decodeRConfig(String rConfig) { - return base64Decode(rConfig).toUtf8String.split("@").first; + @override + Uint8List getMultisigSalt({required String multisigConfig}) => + multisigSalt(multisigConfig: multisigConfig); + + @override + int participantIndexFromKeys({required String serializedKeys}) => + getParticipantIndexFromKeys(serializedKeys: serializedKeys); + + @override + int getMultisigThreshold({required String multisigConfig}) => + multisigThreshold(multisigConfig: multisigConfig); + + @override + int thresholdFromKeys({required String serializedKeys}) => + getThresholdFromKeys(serializedKeys: serializedKeys); + + @override + Uint8List getResharerSalt({required String resharerConfig}) => + resharerSalt(resharerConfig: resharerConfig); + + @override + OpaqueWrapper getDeserializedKeys({required String keys}) => + OpaqueWrapper(deserializeKeys(keys: keys)); + + @override + String getAddressForKeys({ + required OpaqueWrapper keys, + required int network, + required FrostAddressDerivationData addressDerivationData, + required bool secure, + }) { + try { + return addressForKeys( + network: network, + keys: keys.getValue(), + addressDerivationData: addressDerivationData, + secure: secure, + ); + } on FrostdartException catch (e) { + if (e.errorCode == 72) throw FrostBadIndexException(); + rethrow; + } } - static ({Map resharers, String config}) - _decodeRConfigWithResharers(String rConfig) { + @override + int get mainnet => Network.Mainnet; + + @override + int get testnet => Network.Testnet; + + ({Map resharers, String config}) _decodeRConfigWithResharers( + String rConfig, + ) { final parts = base64Decode(rConfig).toUtf8String.split("@"); final config = parts[0]; @@ -603,3 +683,5 @@ abstract class Frost { return (resharers: resharers, config: config); } } + +//END_ON From 5f12db243881eaf298712516626d4bf95691c174 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 6 Oct 2025 15:56:00 -0600 Subject: [PATCH 14/30] implement optional import of flutter_libsparkmobile --- lib/db/sqlite/firo_cache.dart | 10 +- lib/db/sqlite/firo_cache_writer.dart | 7 +- .../spark_names/buy_spark_name_view.dart | 238 +++--- .../buy_spark_name_option_widget.dart | 138 ++-- lib/utilities/logger.dart | 30 +- .../spark_interface.dart | 687 ++++++++---------- .../interfaces/lib_spark_interface.dart | 252 +++++++ pubspec.lock | 4 +- ...IRO_lib_spark_interface_impl.template.dart | 290 ++++++++ 9 files changed, 1047 insertions(+), 609 deletions(-) create mode 100644 lib/wl_gen/interfaces/lib_spark_interface.dart create mode 100644 tool/wl_templates/FIRO_lib_spark_interface_impl.template.dart diff --git a/lib/db/sqlite/firo_cache.dart b/lib/db/sqlite/firo_cache.dart index 4478f421f4..9a0d83f6a2 100644 --- a/lib/db/sqlite/firo_cache.dart +++ b/lib/db/sqlite/firo_cache.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:io'; import 'dart:isolate'; -import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart'; import 'package:mutex/mutex.dart'; import 'package:sqlite3/sqlite3.dart'; import 'package:uuid/uuid.dart'; @@ -13,6 +12,7 @@ import '../../utilities/extensions/extensions.dart'; import '../../utilities/logger.dart'; import '../../utilities/stack_file_system.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; +import '../../wl_gen/interfaces/lib_spark_interface.dart'; part 'firo_cache_coordinator.dart'; part 'firo_cache_reader.dart'; @@ -30,12 +30,12 @@ abstract class _FiroCache { static String sparkSetCacheFileName(CryptoCurrencyNetwork network) => network == CryptoCurrencyNetwork.main - ? "spark_set_v$_setCacheVersion.sqlite3" - : "spark_set_v${_setCacheVersion}_${network.name}.sqlite3"; + ? "spark_set_v$_setCacheVersion.sqlite3" + : "spark_set_v${_setCacheVersion}_${network.name}.sqlite3"; static String sparkUsedTagsCacheFileName(CryptoCurrencyNetwork network) => network == CryptoCurrencyNetwork.main - ? "spark_tags_v$_tagsCacheVersion.sqlite3" - : "spark_tags_v${_tagsCacheVersion}_${network.name}.sqlite3"; + ? "spark_tags_v$_tagsCacheVersion.sqlite3" + : "spark_tags_v${_tagsCacheVersion}_${network.name}.sqlite3"; static final Map _setCacheDB = {}; static final Map _usedTagsCacheDB = {}; diff --git a/lib/db/sqlite/firo_cache_writer.dart b/lib/db/sqlite/firo_cache_writer.dart index 09383601ea..fadc3eb91c 100644 --- a/lib/db/sqlite/firo_cache_writer.dart +++ b/lib/db/sqlite/firo_cache_writer.dart @@ -15,10 +15,9 @@ class FCResult { /// returns true if successful, otherwise some exception FCResult _updateSparkUsedTagsWith(Database db, List> tags) { // hash the tags here since this function is called in a background isolate - final hashedTags = - LibSpark.hashTags( - base64Tags: tags.map((e) => e[0] as String).toList(), - ).toList(); + final hashedTags = hashTags( + base64Tags: tags.map((e) => e[0] as String).toList(), + ).toList(); if (hashedTags.isEmpty) { // nothing to add, return early return FCResult(success: true); diff --git a/lib/pages/spark_names/buy_spark_name_view.dart b/lib/pages/spark_names/buy_spark_name_view.dart index b05d6b2260..478893dc84 100644 --- a/lib/pages/spark_names/buy_spark_name_view.dart +++ b/lib/pages/spark_names/buy_spark_name_view.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:decimal/decimal.dart'; import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:isar_community/isar.dart'; @@ -32,6 +31,7 @@ import '../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../widgets/custom_buttons/blue_text_button.dart'; import '../../widgets/dialogs/s_dialog.dart'; import '../../widgets/rounded_white_container.dart'; +import '../../wl_gen/interfaces/lib_spark_interface.dart'; import 'confirm_spark_name_transaction_view.dart'; class BuySparkNameView extends ConsumerStatefulWidget { @@ -108,16 +108,15 @@ class _BuySparkNameViewState extends ConsumerState { throw Exception("Invalid Spark address selected"); } - final myAddresses = - await wallet.mainDB.isar.addresses - .where() - .walletIdEqualTo(widget.walletId) - .filter() - .typeEqualTo(AddressType.spark) - .and() - .subTypeEqualTo(AddressSubType.receiving) - .valueProperty() - .findAll(); + final myAddresses = await wallet.mainDB.isar.addresses + .where() + .walletIdEqualTo(widget.walletId) + .filter() + .typeEqualTo(AddressType.spark) + .and() + .subTypeEqualTo(AddressSubType.receiving) + .valueProperty() + .findAll(); if (!myAddresses.contains(chosenAddress)) { throw Exception("Selected Spark address does not belong to this wallet"); @@ -137,30 +136,28 @@ class _BuySparkNameViewState extends ConsumerState { if (_preRegLock) return; _preRegLock = true; try { - final txData = - (await showLoading( - whileFuture: _preRegFuture(), - context: context, - message: "Preparing transaction...", - onException: (e) { - throw e; - }, - ))!; + final txData = (await showLoading( + whileFuture: _preRegFuture(), + context: context, + message: "Preparing transaction...", + onException: (e) { + throw e; + }, + ))!; if (mounted) { if (Util.isDesktop) { await showDialog( context: context, - builder: - (context) => SDialog( - child: SizedBox( - width: 580, - child: ConfirmSparkNameTransactionView( - txData: txData, - walletId: widget.walletId, - ), - ), + builder: (context) => SDialog( + child: SizedBox( + width: 580, + child: ConfirmSparkNameTransactionView( + txData: txData, + walletId: widget.walletId, ), + ), + ), ); } else { await Navigator.of(context).pushNamed( @@ -180,13 +177,12 @@ class _BuySparkNameViewState extends ConsumerState { await showDialog( context: context, - builder: - (_) => StackOkDialog( - title: "Error", - message: err, - desktopPopRootNavigator: Util.isDesktop, - maxWidth: Util.isDesktop ? 600 : null, - ), + builder: (_) => StackOkDialog( + title: "Error", + message: err, + desktopPopRootNavigator: Util.isDesktop, + maxWidth: Util.isDesktop ? 600 : null, + ), ); } } finally { @@ -260,10 +256,9 @@ class _BuySparkNameViewState extends ConsumerState { ); }, child: Column( - crossAxisAlignment: - Util.isDesktop - ? CrossAxisAlignment.start - : CrossAxisAlignment.stretch, + crossAxisAlignment: Util.isDesktop + ? CrossAxisAlignment.start + : CrossAxisAlignment.stretch, children: [ RoundedWhiteContainer( padding: EdgeInsets.all(Util.isDesktop ? 0 : 12), @@ -272,27 +267,23 @@ class _BuySparkNameViewState extends ConsumerState { children: [ Text( "Name", - style: - Util.isDesktop - ? STextStyles.w500_14(context).copyWith( - color: - Theme.of( - context, - ).extension()!.infoItemLabel, - ) - : STextStyles.w500_12(context).copyWith( - color: - Theme.of( - context, - ).extension()!.infoItemLabel, - ), + style: Util.isDesktop + ? STextStyles.w500_14(context).copyWith( + color: Theme.of( + context, + ).extension()!.infoItemLabel, + ) + : STextStyles.w500_12(context).copyWith( + color: Theme.of( + context, + ).extension()!.infoItemLabel, + ), ), Text( widget.name, - style: - Util.isDesktop - ? STextStyles.w500_14(context) - : STextStyles.w500_12(context), + style: Util.isDesktop + ? STextStyles.w500_14(context) + : STextStyles.w500_12(context), ), ], ), @@ -307,20 +298,17 @@ class _BuySparkNameViewState extends ConsumerState { children: [ Text( "Spark address", - style: - Util.isDesktop - ? STextStyles.w500_14(context).copyWith( - color: - Theme.of( - context, - ).extension()!.infoItemLabel, - ) - : STextStyles.w500_12(context).copyWith( - color: - Theme.of( - context, - ).extension()!.infoItemLabel, - ), + style: Util.isDesktop + ? STextStyles.w500_14(context).copyWith( + color: Theme.of( + context, + ).extension()!.infoItemLabel, + ) + : STextStyles.w500_12(context).copyWith( + color: Theme.of( + context, + ).extension()!.infoItemLabel, + ), ), CustomTextButton( text: "Use current", @@ -361,20 +349,17 @@ class _BuySparkNameViewState extends ConsumerState { children: [ Text( "Additional info", - style: - Util.isDesktop - ? STextStyles.w500_14(context).copyWith( - color: - Theme.of( - context, - ).extension()!.infoItemLabel, - ) - : STextStyles.w500_12(context).copyWith( - color: - Theme.of( - context, - ).extension()!.infoItemLabel, - ), + style: Util.isDesktop + ? STextStyles.w500_14(context).copyWith( + color: Theme.of( + context, + ).extension()!.infoItemLabel, + ) + : STextStyles.w500_12(context).copyWith( + color: Theme.of( + context, + ).extension()!.infoItemLabel, + ), ), ], ), @@ -410,20 +395,17 @@ class _BuySparkNameViewState extends ConsumerState { children: [ Text( "${isRenewal ? "Renew" : "Register"} for", - style: - Util.isDesktop - ? STextStyles.w500_14(context).copyWith( - color: - Theme.of( - context, - ).extension()!.infoItemLabel, - ) - : STextStyles.w500_12(context).copyWith( - color: - Theme.of( - context, - ).extension()!.infoItemLabel, - ), + style: Util.isDesktop + ? STextStyles.w500_14(context).copyWith( + color: Theme.of( + context, + ).extension()!.infoItemLabel, + ) + : STextStyles.w500_12(context).copyWith( + color: Theme.of( + context, + ).extension()!.infoItemLabel, + ), ), SizedBox( width: Util.isDesktop ? 180 : 140, @@ -451,10 +433,9 @@ class _BuySparkNameViewState extends ConsumerState { isExpanded: true, buttonStyleData: ButtonStyleData( decoration: BoxDecoration( - color: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + color: Theme.of( + context, + ).extension()!.textFieldDefaultBG, borderRadius: BorderRadius.circular( Constants.size.circularBorderRadius, ), @@ -467,10 +448,9 @@ class _BuySparkNameViewState extends ConsumerState { Assets.svg.chevronDown, width: 12, height: 6, - color: - Theme.of(context) - .extension()! - .textFieldActiveSearchIconRight, + color: Theme.of(context) + .extension()! + .textFieldActiveSearchIconRight, ), ), ), @@ -479,10 +459,9 @@ class _BuySparkNameViewState extends ConsumerState { elevation: 0, maxHeight: 250, decoration: BoxDecoration( - color: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + color: Theme.of( + context, + ).extension()!.textFieldDefaultBG, borderRadius: BorderRadius.circular( Constants.size.circularBorderRadius, ), @@ -508,20 +487,17 @@ class _BuySparkNameViewState extends ConsumerState { children: [ Text( "Cost", - style: - Util.isDesktop - ? STextStyles.w500_14(context).copyWith( - color: - Theme.of( - context, - ).extension()!.infoItemLabel, - ) - : STextStyles.w500_12(context).copyWith( - color: - Theme.of( - context, - ).extension()!.infoItemLabel, - ), + style: Util.isDesktop + ? STextStyles.w500_14(context).copyWith( + color: Theme.of( + context, + ).extension()!.infoItemLabel, + ) + : STextStyles.w500_12(context).copyWith( + color: Theme.of( + context, + ).extension()!.infoItemLabel, + ), ), Text( ref @@ -529,15 +505,15 @@ class _BuySparkNameViewState extends ConsumerState { .format( Amount.fromDecimal( Decimal.fromInt( - kStandardSparkNamesFee[widget.name.length] * _years, + libSpark.standardSparkNamesFee[widget.name.length] * + _years, ), fractionDigits: coin.fractionDigits, ), ), - style: - Util.isDesktop - ? STextStyles.w500_14(context) - : STextStyles.w500_12(context), + style: Util.isDesktop + ? STextStyles.w500_14(context) + : STextStyles.w500_12(context), ), ], ), diff --git a/lib/pages/spark_names/sub_widgets/buy_spark_name_option_widget.dart b/lib/pages/spark_names/sub_widgets/buy_spark_name_option_widget.dart index cc7fcfa0e6..61c35fe937 100644 --- a/lib/pages/spark_names/sub_widgets/buy_spark_name_option_widget.dart +++ b/lib/pages/spark_names/sub_widgets/buy_spark_name_option_widget.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:drift/drift.dart' as drift; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; @@ -22,6 +21,7 @@ import '../../../widgets/desktop/secondary_button.dart'; import '../../../widgets/dialogs/s_dialog.dart'; import '../../../widgets/rounded_white_container.dart'; import '../../../widgets/stack_dialog.dart'; +import '../../../wl_gen/interfaces/lib_spark_interface.dart'; import '../buy_spark_name_view.dart'; class BuySparkNameOptionWidget extends ConsumerStatefulWidget { @@ -57,9 +57,9 @@ class _BuySparkNameWidgetState extends ConsumerState { rethrow; } final db = ref.read(pDrift(widget.walletId)); - final results = - await (db.select(db.sparkNames) - ..where((e) => e.name.lower().equals(name))).get(); + final results = await (db.select( + db.sparkNames, + )..where((e) => e.name.lower().equals(name))).get(); if (results.isNotEmpty) { return false; @@ -109,12 +109,11 @@ class _BuySparkNameWidgetState extends ConsumerState { if (mounted) { await showDialog( context: context, - builder: - (_) => StackOkDialog( - title: message, - desktopPopRootNavigator: Util.isDesktop, - maxWidth: Util.isDesktop ? 600 : null, - ), + builder: (_) => StackOkDialog( + title: message, + desktopPopRootNavigator: Util.isDesktop, + maxWidth: Util.isDesktop ? 600 : null, + ), ); } } finally { @@ -142,8 +141,9 @@ class _BuySparkNameWidgetState extends ConsumerState { @override Widget build(BuildContext context) { return Column( - crossAxisAlignment: - Util.isDesktop ? CrossAxisAlignment.start : CrossAxisAlignment.center, + crossAxisAlignment: Util.isDesktop + ? CrossAxisAlignment.start + : CrossAxisAlignment.center, children: [ SizedBox( height: 48, @@ -154,10 +154,9 @@ class _BuySparkNameWidgetState extends ConsumerState { height: 48, width: 100, decoration: BoxDecoration( - color: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + color: Theme.of( + context, + ).extension()!.textFieldDefaultBG, borderRadius: BorderRadius.all( Radius.circular(Constants.size.circularBorderRadius), ), @@ -168,7 +167,9 @@ class _BuySparkNameWidgetState extends ConsumerState { Expanded( child: TextField( inputFormatters: [ - LengthLimitingTextInputFormatter(kMaxNameLength), + LengthLimitingTextInputFormatter( + libSpark.maxNameLength, + ), ], textInputAction: TextInputAction.search, focusNode: _nameFieldFocus, @@ -183,10 +184,9 @@ class _BuySparkNameWidgetState extends ConsumerState { Assets.svg.search, width: 20, height: 20, - color: - Theme.of(context) - .extension()! - .textFieldDefaultSearchIconLeft, + color: Theme.of(context) + .extension()! + .textFieldDefaultSearchIconLeft, ), ), fillColor: Colors.transparent, @@ -206,7 +206,9 @@ class _BuySparkNameWidgetState extends ConsumerState { setState(() { _isInvalidCharacters = value.isNotEmpty && - !RegExp(kNameRegexString).hasMatch(value); + !RegExp( + libSpark.nameRegexString, + ).hasMatch(value); }); }, ), @@ -220,10 +222,9 @@ class _BuySparkNameWidgetState extends ConsumerState { ), const SizedBox(height: 4), Row( - mainAxisAlignment: - _isInvalidCharacters - ? MainAxisAlignment.spaceBetween - : MainAxisAlignment.end, + mainAxisAlignment: _isInvalidCharacters + ? MainAxisAlignment.spaceBetween + : MainAxisAlignment.end, children: [ if (_isInvalidCharacters) Text( @@ -238,12 +239,11 @@ class _BuySparkNameWidgetState extends ConsumerState { builder: (context) { final length = _nameController.text.length; return Text( - "$length/$kMaxNameLength", + "$length/${libSpark.maxNameLength}", style: STextStyles.w500_10(context).copyWith( - color: - Theme.of( - context, - ).extension()!.textSubtitle2, + color: Theme.of( + context, + ).extension()!.textSubtitle2, ), ); }, @@ -256,7 +256,7 @@ class _BuySparkNameWidgetState extends ConsumerState { label: "Lookup", enabled: _nameController.text.isNotEmpty && - RegExp(kNameRegexString).hasMatch(_nameController.text), + RegExp(libSpark.nameRegexString).hasMatch(_nameController.text), buttonHeight: Util.isDesktop ? ButtonHeight.l : null, onPressed: _lookup, ), @@ -287,15 +287,13 @@ class _NameCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final availability = isAvailable ? "Available" : "Unavailable"; - final color = - isAvailable - ? Theme.of(context).extension()!.accentColorGreen - : Theme.of(context).extension()!.accentColorRed; + final color = isAvailable + ? Theme.of(context).extension()!.accentColorGreen + : Theme.of(context).extension()!.accentColorRed; - final style = - (Util.isDesktop - ? STextStyles.w500_16(context) - : STextStyles.w500_12(context)); + final style = (Util.isDesktop + ? STextStyles.w500_16(context) + : STextStyles.w500_12(context)); return RoundedWhiteContainer( padding: EdgeInsets.all(Util.isDesktop ? 24 : 16), @@ -321,51 +319,49 @@ class _NameCard extends ConsumerWidget { PrimaryButton( label: "Buy name", enabled: isAvailable, - buttonHeight: - Util.isDesktop ? ButtonHeight.m : ButtonHeight.l, + buttonHeight: Util.isDesktop + ? ButtonHeight.m + : ButtonHeight.l, width: Util.isDesktop ? 140 : 120, onPressed: () async { if (context.mounted) { if (Util.isDesktop) { await showDialog( context: context, - builder: - (context) => SDialog( - child: SizedBox( - width: 580, - child: Column( + builder: (context) => SDialog( + child: SizedBox( + width: 580, + child: Column( + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, children: [ - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: const EdgeInsets.only( - left: 32, - ), - child: Text( - "Buy name", - style: STextStyles.desktopH3( - context, - ), - ), - ), - const DesktopDialogCloseButton(), - ], - ), Padding( - padding: const EdgeInsets.symmetric( - horizontal: 32, + padding: const EdgeInsets.only( + left: 32, ), - child: BuySparkNameView( - walletId: walletId, - name: name, + child: Text( + "Buy name", + style: STextStyles.desktopH3(context), ), ), + const DesktopDialogCloseButton(), ], ), - ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 32, + ), + child: BuySparkNameView( + walletId: walletId, + name: name, + ), + ), + ], ), + ), + ), ); } else { await Navigator.of(context).pushNamed( diff --git a/lib/utilities/logger.dart b/lib/utilities/logger.dart index d4cc03dcfa..b5ae5f00dc 100644 --- a/lib/utilities/logger.dart +++ b/lib/utilities/logger.dart @@ -14,7 +14,6 @@ import 'dart:core'; import 'dart:isolate'; import 'dart:ui'; -import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart' as spark; import 'package:logger/logger.dart'; import 'util.dart'; @@ -23,26 +22,6 @@ export 'enums/log_level_enum.dart'; const _kLoggerPortName = "logger_port"; -// convenience conversion for spark -extension LoggingLevelExt on spark.LoggingLevel { - Level getLoggerLevel() { - switch (this) { - case spark.LoggingLevel.info: - return Level.info; - case spark.LoggingLevel.warning: - return Level.warning; - case spark.LoggingLevel.error: - return Level.error; - case spark.LoggingLevel.fatal: - return Level.fatal; - case spark.LoggingLevel.debug: - return Level.debug; - case spark.LoggingLevel.trace: - return Level.trace; - } - } -} - class Logging { Logging._(); static final Logging _instance = Logging._(); @@ -82,8 +61,9 @@ class Logging { PrettyPrinter prettyPrinter(bool toFile) => PrettyPrinter( printEmojis: false, methodCount: 0, - dateTimeFormat: - toFile ? DateTimeFormat.none : DateTimeFormat.dateAndTime, + dateTimeFormat: toFile + ? DateTimeFormat.none + : DateTimeFormat.dateAndTime, colors: !toFile, noBoxingByDefault: toFile, ); @@ -132,8 +112,8 @@ class Logging { String _stringifyMessage(dynamic message) => !(message is Map || message is Iterable) - ? message.toString() - : JsonEncoder.withIndent(' ', (o) => o.toString()).convert(message); + ? message.toString() + : JsonEncoder.withIndent(' ', (o) => o.toString()).convert(message); void log( Level level, diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart index a008d1b24a..d9059a5bb1 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart @@ -5,10 +5,6 @@ import 'dart:math'; import 'package:bitcoindart/bitcoindart.dart' as btc; import 'package:decimal/decimal.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart' - as spark - show Log; -import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart'; import 'package:isar_community/isar.dart'; import 'package:logger/logger.dart'; @@ -28,6 +24,7 @@ import '../../../utilities/enums/derive_path_type_enum.dart'; import '../../../utilities/extensions/extensions.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/prefs.dart'; +import '../../../wl_gen/interfaces/lib_spark_interface.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../crypto_currency/interfaces/electrumx_currency_interface.dart'; import '../../isar/models/spark_coin.dart'; @@ -55,25 +52,11 @@ String _hashTag(String tag) { final x = components[0].substring(1); final y = components[1].substring(0, components[1].length - 1); - final hash = LibSpark.hashTag(x, y); + final hash = libSpark.hashTag(x, y); return hash; } -void initSparkLogging(Level level) { - final levels = Level.values.where((e) => e >= level).map((e) => e.name); - spark.Log.levels.addAll( - LoggingLevel.values.where((e) => levels.contains(e.name)), - ); - spark.Log.onLog = (level, value, {error, stackTrace, required time}) { - Logging.instance.log( - level.getLoggerLevel(), - value, - error: error, - stackTrace: stackTrace, - time: time, - ); - }; -} +void initSparkLogging(Level level) => libSpark.initSparkLogging(level); abstract class _SparkIsolate { static Isolate? _isolate; @@ -135,7 +118,7 @@ mixin SparkInterface static bool validateSparkAddress({ required String address, required bool isTestNet, - }) => LibSpark.validateAddress(address: address, isTestNet: isTestNet); + }) => libSpark.validateAddress(address: address, isTestNet: isTestNet); Future hashTag(String tag) async { try { @@ -174,16 +157,17 @@ mixin SparkInterface final String derivationPath; if (cryptoCurrency.network.isTestNet) { derivationPath = - "$kSparkBaseDerivationPathTestnet$kDefaultSparkIndex"; + "${libSpark.sparkBaseDerivationPathTestnet}$kDefaultSparkIndex"; } else { - derivationPath = "$kSparkBaseDerivationPath$kDefaultSparkIndex"; + derivationPath = + "${libSpark.sparkBaseDerivationPath}$kDefaultSparkIndex"; } final keys = root.derivePath(derivationPath); - _sparkChangeAddressCached = await LibSpark.getAddress( + _sparkChangeAddressCached = await libSpark.getAddress( privateKey: keys.privateKey.data, index: kDefaultSparkIndex, - diversifier: kSparkChange, + diversifier: libSpark.sparkChange, isTestNet: cryptoCurrency.network.isTestNet, ); } @@ -202,20 +186,19 @@ mixin SparkInterface @override Future> fetchAddressesForElectrumXScan() async { - final allAddresses = - await mainDB - .getAddresses(walletId) - .filter() - .not() - .group( - (q) => q - .typeEqualTo(AddressType.spark) - .or() - .typeEqualTo(AddressType.nonWallet) - .or() - .subTypeEqualTo(AddressSubType.nonWallet), - ) - .findAll(); + final allAddresses = await mainDB + .getAddresses(walletId) + .filter() + .not() + .group( + (q) => q + .typeEqualTo(AddressType.spark) + .or() + .typeEqualTo(AddressType.nonWallet) + .or() + .subTypeEqualTo(AddressSubType.nonWallet), + ) + .findAll(); return allAddresses; } @@ -236,20 +219,21 @@ mixin SparkInterface // default to starting at 1 if none found int diversifier = (highestStoredDiversifier ?? 0) + 1; // change address check - if (diversifier == kSparkChange) { + if (diversifier == libSpark.sparkChange) { diversifier++; } final root = await getRootHDNode(); final String derivationPath; if (cryptoCurrency.network.isTestNet) { - derivationPath = "$kSparkBaseDerivationPathTestnet$kDefaultSparkIndex"; + derivationPath = + "${libSpark.sparkBaseDerivationPathTestnet}$kDefaultSparkIndex"; } else { - derivationPath = "$kSparkBaseDerivationPath$kDefaultSparkIndex"; + derivationPath = "${libSpark.sparkBaseDerivationPath}$kDefaultSparkIndex"; } final keys = root.derivePath(derivationPath); - final String addressString = await LibSpark.getAddress( + final String addressString = await libSpark.getAddress( privateKey: keys.privateKey.data, index: kDefaultSparkIndex, diversifier: diversifier, @@ -276,18 +260,17 @@ mixin SparkInterface ); } else { // fetch spendable spark coins - final coins = - await mainDB.isar.sparkCoins - .where() - .walletIdEqualToAnyLTagHash(walletId) - .filter() - .isUsedEqualTo(false) - .and() - .heightIsNotNull() - .and() - .not() - .valueIntStringEqualTo("0") - .findAll(); + final coins = await mainDB.isar.sparkCoins + .where() + .walletIdEqualToAnyLTagHash(walletId) + .filter() + .isUsedEqualTo(false) + .and() + .heightIsNotNull() + .and() + .not() + .valueIntStringEqualTo("0") + .findAll(); final available = coins .map((e) => e.value) @@ -301,24 +284,25 @@ mixin SparkInterface } // prepare coin data for ffi - final serializedCoins = - coins - .map( - (e) => ( - serializedCoin: e.serializedCoinB64!, - serializedCoinContext: e.contextB64!, - groupId: e.groupId, - height: e.height!, - ), - ) - .toList(); + final serializedCoins = coins + .map( + (e) => ( + serializedCoin: e.serializedCoinB64!, + serializedCoinContext: e.contextB64!, + groupId: e.groupId, + height: e.height!, + ), + ) + .toList(); final root = await getRootHDNode(); final String derivationPath; if (cryptoCurrency.network.isTestNet) { - derivationPath = "$kSparkBaseDerivationPathTestnet$kDefaultSparkIndex"; + derivationPath = + "${libSpark.sparkBaseDerivationPathTestnet}$kDefaultSparkIndex"; } else { - derivationPath = "$kSparkBaseDerivationPath$kDefaultSparkIndex"; + derivationPath = + "${libSpark.sparkBaseDerivationPath}$kDefaultSparkIndex"; } final privateKey = root.derivePath(derivationPath).privateKey.data; int estimate = await _asyncSparkFeesWrapper( @@ -393,18 +377,17 @@ mixin SparkInterface final txAmount = transparentSumOut + sparkSumOut; // fetch spendable spark coins - final coins = - await mainDB.isar.sparkCoins - .where() - .walletIdEqualToAnyLTagHash(walletId) - .filter() - .isUsedEqualTo(false) - .and() - .heightIsNotNull() - .and() - .not() - .valueIntStringEqualTo("0") - .findAll(); + final coins = await mainDB.isar.sparkCoins + .where() + .walletIdEqualToAnyLTagHash(walletId) + .filter() + .isUsedEqualTo(false) + .and() + .heightIsNotNull() + .and() + .not() + .valueIntStringEqualTo("0") + .findAll(); final available = info.cachedBalanceTertiary.spendable; @@ -415,17 +398,16 @@ mixin SparkInterface final bool isSendAll = available == txAmount; // prepare coin data for ffi - final serializedCoins = - coins - .map( - (e) => ( - serializedCoin: e.serializedCoinB64!, - serializedCoinContext: e.contextB64!, - groupId: e.groupId, - height: e.height!, - ), - ) - .toList(); + final serializedCoins = coins + .map( + (e) => ( + serializedCoin: e.serializedCoinB64!, + serializedCoinContext: e.contextB64!, + groupId: e.groupId, + height: e.height!, + ), + ) + .toList(); final currentId = await electrumXClient.getSparkLatestCoinId(); final List> setMaps = []; @@ -451,8 +433,9 @@ mixin SparkInterface "blockHash": info.blockHash, "setHash": info.setHash, "coinGroupID": i, - "coins": - resultSet.map((e) => [e.serialized, e.txHash, e.context]).toList(), + "coins": resultSet + .map((e) => [e.serialized, e.txHash, e.context]) + .toList(), }; setData["coinGroupID"] = i; @@ -463,31 +446,28 @@ mixin SparkInterface )); } - final allAnonymitySets = - setMaps - .map( - (e) => ( - setId: e["coinGroupID"] as int, - setHash: e["setHash"] as String, - set: - (e["coins"] as List) - .map( - (e) => ( - serializedCoin: e[0] as String, - txHash: e[1] as String, - ), - ) - .toList(), - ), - ) - .toList(); + final allAnonymitySets = setMaps + .map( + (e) => ( + setId: e["coinGroupID"] as int, + setHash: e["setHash"] as String, + set: (e["coins"] as List) + .map( + (e) => + (serializedCoin: e[0] as String, txHash: e[1] as String), + ) + .toList(), + ), + ) + .toList(); final root = await getRootHDNode(); final String derivationPath; if (cryptoCurrency.network.isTestNet) { - derivationPath = "$kSparkBaseDerivationPathTestnet$kDefaultSparkIndex"; + derivationPath = + "${libSpark.sparkBaseDerivationPathTestnet}$kDefaultSparkIndex"; } else { - derivationPath = "$kSparkBaseDerivationPath$kDefaultSparkIndex"; + derivationPath = "${libSpark.sparkBaseDerivationPath}$kDefaultSparkIndex"; } final privateKey = root.derivePath(derivationPath).privateKey.data; @@ -500,7 +480,7 @@ mixin SparkInterface sparkRecipientsWithFeeSubtracted; final recipientCount = (txData.recipients?.where((e) => e.amount.raw > BigInt.zero).length ?? - 0); + 0); final totalRecipientCount = recipientCount + (txData.sparkRecipients?.length ?? 0); final BigInt estimatedFee; @@ -620,7 +600,7 @@ mixin SparkInterface ({Uint8List script, int size})? noProofNameTxData; if (txData.sparkNameInfo != null) { - noProofNameTxData = LibSpark.createSparkNameScript( + noProofNameTxData = libSpark.createSparkNameScript( sparkNameValidityBlocks: txData.sparkNameInfo!.validBlocks, name: txData.sparkNameInfo!.name, additionalInfo: txData.sparkNameInfo!.additionalInfo, @@ -662,15 +642,13 @@ mixin SparkInterface [], serializedCoins: serializedCoins, allAnonymitySets: allAnonymitySets, - idAndBlockHashes: - idAndBlockHashes - .map( - (e) => (setId: e.groupId, blockHash: base64Decode(e.blockHash)), - ) - .toList(), + idAndBlockHashes: idAndBlockHashes + .map((e) => (setId: e.groupId, blockHash: base64Decode(e.blockHash))) + .toList(), txHash: extractedTx.getHash(), - additionalTxSize: - txData.sparkNameInfo == null ? 0 : noProofNameTxData!.size, + additionalTxSize: txData.sparkNameInfo == null + ? 0 + : noProofNameTxData!.size, )); for (final outputScript in spend.outputScripts) { @@ -695,7 +673,7 @@ mixin SparkInterface int hashFailSafe = 0; while (nameScriptData == null) { try { - nameScriptData = LibSpark.createSparkNameScript( + nameScriptData = libSpark.createSparkNameScript( sparkNameValidityBlocks: txData.sparkNameInfo!.validBlocks, name: txData.sparkNameInfo!.name, additionalInfo: txData.sparkNameInfo!.additionalInfo, @@ -745,11 +723,10 @@ mixin SparkInterface sequence: 0xffffffff, outpoint: null, addresses: [], - valueStringSats: - tempOutputs - .map((e) => e.value) - .fold(fee.raw, (p, e) => p + e) - .toString(), + valueStringSats: tempOutputs + .map((e) => e.value) + .fold(fee.raw, (p, e) => p + e) + .toString(), witness: null, innerRedeemScriptAsm: null, coinbase: null, @@ -792,10 +769,9 @@ mixin SparkInterface timestamp: DateTime.timestamp().millisecondsSinceEpoch ~/ 1000, inputs: List.unmodifiable(tempInputs), outputs: List.unmodifiable(tempOutputs), - type: - tempOutputs.map((e) => e.walletOwns).fold(true, (p, e) => p &= e) - ? TransactionType.sentToSelf - : TransactionType.outgoing, + type: tempOutputs.map((e) => e.walletOwns).fold(true, (p, e) => p &= e) + ? TransactionType.sentToSelf + : TransactionType.outgoing, subType: TransactionSubType.sparkSpend, otherData: jsonEncode({"overrideFee": fee.toJsonString()}), height: null, @@ -962,10 +938,9 @@ mixin SparkInterface + 1; // update balance - final percentIncrement = - refreshProgressRange == null - ? null - : (refreshProgressRange.$2 - refreshProgressRange.$1) / steps; + final percentIncrement = refreshProgressRange == null + ? null + : (refreshProgressRange.$2 - refreshProgressRange.$1) / steps; double currentPercent = refreshProgressRange?.$1 ?? 0; // fetch and update process for each set groupId as required @@ -1025,10 +1000,9 @@ mixin SparkInterface afterBlockHash: lastCheckedHash, network: cryptoCurrency.network, ); - final coinsRaw = - anonymitySetResult - .map((e) => [e.serialized, e.txHash, e.context]) - .toList(); + final coinsRaw = anonymitySetResult + .map((e) => [e.serialized, e.txHash, e.context]) + .toList(); if (coinsRaw.isNotEmpty) { rawCoinsBySetId[i] = coinsRaw; @@ -1044,25 +1018,19 @@ mixin SparkInterface // get address(es) to get the private key hex strings required for // identifying spark coins - final sparkAddresses = - await mainDB.isar.addresses - .where() - .walletIdEqualTo(walletId) - .filter() - .typeEqualTo(AddressType.spark) - .findAll(); + final sparkAddresses = await mainDB.isar.addresses + .where() + .walletIdEqualTo(walletId) + .filter() + .typeEqualTo(AddressType.spark) + .findAll(); final root = await getRootHDNode(); - final Set privateKeyHexSet = - sparkAddresses - .map( - (e) => - root - .derivePath(e.derivationPath!.value) - .privateKey - .data - .toHex, - ) - .toSet(); + final Set privateKeyHexSet = sparkAddresses + .map( + (e) => + root.derivePath(e.derivationPath!.value).privateKey.data.toHex, + ) + .toSet(); // try to identify any coins in the unchecked set data final List newlyIdCoins = []; @@ -1108,15 +1076,14 @@ mixin SparkInterface } // get unused and or unconfirmed coins from db - final coinsToCheck = - await mainDB.isar.sparkCoins - .where() - .walletIdEqualToAnyLTagHash(walletId) - .filter() - .heightIsNull() - .or() - .isUsedEqualTo(false) - .findAll(); + final coinsToCheck = await mainDB.isar.sparkCoins + .where() + .walletIdEqualToAnyLTagHash(walletId) + .filter() + .heightIsNull() + .or() + .isUsedEqualTo(false) + .findAll(); List? spentCoinTags; // only fetch tags from db if we need them to compare against any items @@ -1147,10 +1114,9 @@ mixin SparkInterface checked = coin; } } else { - checked = - spentCoinTags!.contains(coin.lTagHash) - ? coin.copyWith(isUsed: true) - : coin; + checked = spentCoinTags!.contains(coin.lTagHash) + ? coin.copyWith(isUsed: true) + : coin; } checkedCoins.add(checked); @@ -1170,13 +1136,12 @@ mixin SparkInterface final currentHeight = await chainHeight; // get all unused coins to update wallet spark balance - final unusedCoins = - await mainDB.isar.sparkCoins - .where() - .walletIdEqualToAnyLTagHash(walletId) - .filter() - .isUsedEqualTo(false) - .findAll(); + final unusedCoins = await mainDB.isar.sparkCoins + .where() + .walletIdEqualToAnyLTagHash(walletId) + .filter() + .isUsedEqualTo(false) + .findAll(); final sparkNamesUpdateFuture = refreshSparkNames(); @@ -1229,14 +1194,13 @@ mixin SparkInterface } Future> getSparkSpendTransactionIds() async { - final tags = - await mainDB.isar.sparkCoins - .where() - .walletIdEqualToAnyLTagHash(walletId) - .filter() - .isUsedEqualTo(true) - .lTagHashProperty() - .findAll(); + final tags = await mainDB.isar.sparkCoins + .where() + .walletIdEqualToAnyLTagHash(walletId) + .filter() + .isUsedEqualTo(true) + .lTagHashProperty() + .findAll(); final pairs = await FiroCacheCoordinator.getUsedCoinTxidsFor( tags: tags, @@ -1272,8 +1236,9 @@ mixin SparkInterface Logging.instance.i("Refreshing spark names for $walletId ${info.name}"); final db = Drift.get(walletId); - final myNameStrings = - await db.managers.sparkNames.map((e) => e.name).get(); + final myNameStrings = await db.managers.sparkNames + .map((e) => e.name) + .get(); final names = await electrumXClient.getSparkNames(); // start update shared cache of all names @@ -1304,9 +1269,11 @@ mixin SparkInterface final root = await getRootHDNode(); final String derivationPath; if (cryptoCurrency.network.isTestNet) { - derivationPath = "$kSparkBaseDerivationPathTestnet$kDefaultSparkIndex"; + derivationPath = + "${libSpark.sparkBaseDerivationPathTestnet}$kDefaultSparkIndex"; } else { - derivationPath = "$kSparkBaseDerivationPath$kDefaultSparkIndex"; + derivationPath = + "${libSpark.sparkBaseDerivationPath}$kDefaultSparkIndex"; } final keys = root.derivePath(derivationPath); @@ -1317,10 +1284,10 @@ mixin SparkInterface while (diversifier < maxDiversifier) { // change address check - if (diversifier == kSparkChange) { + if (diversifier == libSpark.sparkChange) { diversifier++; } - final addressString = await LibSpark.getAddress( + final addressString = await libSpark.getAddress( privateKey: keys.privateKey.data, index: kDefaultSparkIndex, diversifier: diversifier, @@ -1415,10 +1382,9 @@ mixin SparkInterface // setup some vars int nChangePosInOut = -1; final int nChangePosRequest = nChangePosInOut; - List outputs_ = - outputs - .map((e) => MutableSparkRecipient(e.address, e.value, e.memo)) - .toList(); // deep copy + List outputs_ = outputs + .map((e) => MutableSparkRecipient(e.address, e.value, e.memo)) + .toList(); // deep copy final feesObject = await fees; final currentHeight = await chainHeight; final random = Random.secure(); @@ -1427,10 +1393,9 @@ mixin SparkInterface valueAndUTXOs.shuffle(random); while (valueAndUTXOs.isNotEmpty) { - final lockTime = - random.nextInt(10) == 0 - ? max(0, currentHeight - random.nextInt(100)) - : currentHeight; + final lockTime = random.nextInt(10) == 0 + ? max(0, currentHeight - random.nextInt(100)) + : currentHeight; const txVersion = 1; final List vin = []; final List<(dynamic, int, String?)> vout = []; @@ -1477,10 +1442,9 @@ mixin SparkInterface setCoins.clear(); // deep copy - final remainingOutputs = - outputs_ - .map((e) => MutableSparkRecipient(e.address, e.value, e.memo)) - .toList(); + final remainingOutputs = outputs_ + .map((e) => MutableSparkRecipient(e.address, e.value, e.memo)) + .toList(); final List singleTxOutputs = []; if (autoMintAll) { @@ -1540,17 +1504,13 @@ mixin SparkInterface } // Generate dummy mint coins to save time - final dummyRecipients = LibSpark.createSparkMintRecipients( - outputs: - singleTxOutputs - .map( - (e) => ( - sparkAddress: e.address, - value: e.value.toInt(), - memo: "", - ), - ) - .toList(), + final dummyRecipients = libSpark.createSparkMintRecipients( + outputs: singleTxOutputs + .map( + (e) => + (sparkAddress: e.address, value: e.value.toInt(), memo: ""), + ) + .toList(), serialContext: Uint8List(0), generate: false, ); @@ -1623,40 +1583,36 @@ mixin SparkInterface switch (sd.derivePathType) { case DerivePathType.bip44: - data = - btc - .P2PKH( - data: btc.PaymentData(pubkey: pubKey), - network: _bitcoinDartNetwork, - ) - .data; + data = btc + .P2PKH( + data: btc.PaymentData(pubkey: pubKey), + network: _bitcoinDartNetwork, + ) + .data; break; case DerivePathType.bip49: - final p2wpkh = - btc - .P2WPKH( - data: btc.PaymentData(pubkey: pubKey), - network: _bitcoinDartNetwork, - ) - .data; - data = - btc - .P2SH( - data: btc.PaymentData(redeem: p2wpkh), - network: _bitcoinDartNetwork, - ) - .data; + final p2wpkh = btc + .P2WPKH( + data: btc.PaymentData(pubkey: pubKey), + network: _bitcoinDartNetwork, + ) + .data; + data = btc + .P2SH( + data: btc.PaymentData(redeem: p2wpkh), + network: _bitcoinDartNetwork, + ) + .data; break; case DerivePathType.bip84: - data = - btc - .P2WPKH( - data: btc.PaymentData(pubkey: pubKey), - network: _bitcoinDartNetwork, - ) - .data; + data = btc + .P2WPKH( + data: btc.PaymentData(pubkey: pubKey), + network: _bitcoinDartNetwork, + ) + .data; break; case DerivePathType.bip86: @@ -1715,20 +1671,19 @@ mixin SparkInterface } // Generate real mint coins - final serialContext = LibSpark.serializeMintContext( + final serialContext = libSpark.serializeMintContext( inputs: setCoins.map((e) => (e.utxo.txid, e.utxo.vout)).toList(), ); - final recipients = LibSpark.createSparkMintRecipients( - outputs: - singleTxOutputs - .map( - (e) => ( - sparkAddress: e.address, - memo: e.memo, - value: e.value.toInt(), - ), - ) - .toList(), + final recipients = libSpark.createSparkMintRecipients( + outputs: singleTxOutputs + .map( + (e) => ( + sparkAddress: e.address, + memo: e.memo, + value: e.value.toInt(), + ), + ) + .toList(), serialContext: serialContext, generate: true, ); @@ -1753,10 +1708,9 @@ mixin SparkInterface } // deep copy - outputs_ = - remainingOutputs - .map((e) => MutableSparkRecipient(e.address, e.value, e.memo)) - .toList(); + outputs_ = remainingOutputs + .map((e) => MutableSparkRecipient(e.address, e.value, e.memo)) + .toList(); break; // Done, enough fee included. } @@ -1784,40 +1738,36 @@ mixin SparkInterface switch (input.derivePathType) { case DerivePathType.bip44: - data = - btc - .P2PKH( - data: btc.PaymentData(pubkey: pubKey), - network: _bitcoinDartNetwork, - ) - .data; + data = btc + .P2PKH( + data: btc.PaymentData(pubkey: pubKey), + network: _bitcoinDartNetwork, + ) + .data; break; case DerivePathType.bip49: - final p2wpkh = - btc - .P2WPKH( - data: btc.PaymentData(pubkey: pubKey), - network: _bitcoinDartNetwork, - ) - .data; - data = - btc - .P2SH( - data: btc.PaymentData(redeem: p2wpkh), - network: _bitcoinDartNetwork, - ) - .data; + final p2wpkh = btc + .P2WPKH( + data: btc.PaymentData(pubkey: pubKey), + network: _bitcoinDartNetwork, + ) + .data; + data = btc + .P2SH( + data: btc.PaymentData(redeem: p2wpkh), + network: _bitcoinDartNetwork, + ) + .data; break; case DerivePathType.bip84: - data = - btc - .P2WPKH( - data: btc.PaymentData(pubkey: pubKey), - network: _bitcoinDartNetwork, - ) - .data; + data = btc + .P2WPKH( + data: btc.PaymentData(pubkey: pubKey), + network: _bitcoinDartNetwork, + ) + .data; break; case DerivePathType.bip86: @@ -1862,8 +1812,9 @@ mixin SparkInterface tempOutputs.add( OutputV2.isarCantDoRequiredInDefaultConstructor( - scriptPubKeyHex: - addressOrScript is Uint8List ? addressOrScript.toHex : "000000", + scriptPubKeyHex: addressOrScript is Uint8List + ? addressOrScript.toHex + : "000000", valueStringSats: value.toString(), addresses: [ if (addressOrScript is String) addressOrScript.toString(), @@ -1914,24 +1865,22 @@ mixin SparkInterface assert(outputs.length == 1); final data = TxData( - sparkRecipients: - vout - .where((e) => e.$1 is Uint8List) // ignore change - .map( - (e) => ( - address: - outputs - .first - .address, // for display purposes on confirm tx screen. See todos above - memo: "", - amount: Amount( - rawValue: BigInt.from(e.$2), - fractionDigits: cryptoCurrency.fractionDigits, - ), - isChange: false, // ok? - ), - ) - .toList(), + sparkRecipients: vout + .where((e) => e.$1 is Uint8List) // ignore change + .map( + (e) => ( + address: outputs + .first + .address, // for display purposes on confirm tx screen. See todos above + memo: "", + amount: Amount( + rawValue: BigInt.from(e.$2), + fractionDigits: cryptoCurrency.fractionDigits, + ), + isChange: false, // ok? + ), + ) + .toList(), vSize: builtTx.virtualSize(), txid: builtTx.getId(), raw: builtTx.toHex(), @@ -1950,8 +1899,8 @@ mixin SparkInterface outputs: List.unmodifiable(tempOutputs), type: tempOutputs.map((e) => e.walletOwns).fold(true, (p, e) => p &= e) - ? TransactionType.sentToSelf - : TransactionType.outgoing, + ? TransactionType.sentToSelf + : TransactionType.outgoing, subType: TransactionSubType.sparkMint, otherData: null, height: null, @@ -2018,25 +1967,23 @@ mixin SparkInterface const subtractFeeFromAmount = true; // must be true for mint all final currentHeight = await chainHeight; - final spendableUtxos = - await mainDB.isar.utxos - .where() - .walletIdEqualTo(walletId) - .filter() - .isBlockedEqualTo(false) - .and() - .group((q) => q.usedEqualTo(false).or().usedIsNull()) - .and() - .valueGreaterThan(0) - .findAll(); + final spendableUtxos = await mainDB.isar.utxos + .where() + .walletIdEqualTo(walletId) + .filter() + .isBlockedEqualTo(false) + .and() + .group((q) => q.usedEqualTo(false).or().usedIsNull()) + .and() + .valueGreaterThan(0) + .findAll(); spendableUtxos.removeWhere( - (e) => - !e.isConfirmed( - currentHeight, - cryptoCurrency.minConfirms, - cryptoCurrency.minCoinbaseConfirms, - ), + (e) => !e.isConfirmed( + currentHeight, + cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, + ), ); if (spendableUtxos.isEmpty) { @@ -2077,12 +2024,9 @@ mixin SparkInterface if (txData.sparkRecipients?.isNotEmpty != true) { throw Exception("Missing spark recipients."); } - final recipients = - txData.sparkRecipients! - .map( - (e) => MutableSparkRecipient(e.address, e.amount.raw, e.memo), - ) - .toList(); + final recipients = txData.sparkRecipients! + .map((e) => MutableSparkRecipient(e.address, e.amount.raw, e.memo)) + .toList(); final total = recipients .map((e) => e.value) @@ -2094,16 +2038,17 @@ mixin SparkInterface throw Exception("Attempted send of zero amount"); } - final utxos = - txData.utxos?.whereType().map((e) => e.utxo).toList(); + final utxos = txData.utxos + ?.whereType() + .map((e) => e.utxo) + .toList(); final bool coinControl = utxos != null; - final utxosTotal = - coinControl - ? utxos - .map((e) => e.value) - .fold(BigInt.zero, (p, e) => p + BigInt.from(e)) - : null; + final utxosTotal = coinControl + ? utxos + .map((e) => e.value) + .fold(BigInt.zero, (p, e) => p + BigInt.from(e)) + : null; if (coinControl && utxosTotal! < total) { throw Exception("Insufficient selected UTXOs!"); @@ -2128,18 +2073,17 @@ mixin SparkInterface final canCPFP = this is CpfpInterface && coinControl; - final spendableUtxos = - availableOutputs - .where( - (e) => - canCPFP || - e.isConfirmed( - currentHeight, - cryptoCurrency.minConfirms, - cryptoCurrency.minCoinbaseConfirms, - ), - ) - .toList(); + final spendableUtxos = availableOutputs + .where( + (e) => + canCPFP || + e.isConfirmed( + currentHeight, + cryptoCurrency.minConfirms, + cryptoCurrency.minCoinbaseConfirms, + ), + ) + .toList(); if (spendableUtxos.isEmpty) { throw Exception("No available UTXOs found to anonymize"); @@ -2197,21 +2141,21 @@ mixin SparkInterface } } - if (years < 1 || years > kMaxNameRegistrationLengthYears) { + if (years < 1 || years > libSpark.maxNameRegistrationLengthYears) { throw Exception("Invalid spark name registration period years: $years"); } - if (name.isEmpty || name.length > kMaxNameLength) { + if (name.isEmpty || name.length > libSpark.maxNameLength) { throw Exception("Invalid spark name length: ${name.length}"); } - if (!RegExp(kNameRegexString).hasMatch(name)) { + if (!RegExp(libSpark.nameRegexString).hasMatch(name)) { throw Exception("Invalid symbols found in spark name: $name"); } if (additionalInfo.toUint8ListFromUtf8.length > - kMaxAdditionalInfoLengthBytes) { + libSpark.maxAdditionalInfoLengthBytes) { throw Exception( - "Additional info exceeds $kMaxAdditionalInfoLengthBytes bytes.", + "Additional info exceeds ${libSpark.maxAdditionalInfoLengthBytes} bytes.", ); } @@ -2233,11 +2177,11 @@ mixin SparkInterface final String destinationAddress; switch (cryptoCurrency.network) { case CryptoCurrencyNetwork.main: - destinationAddress = kStage3DevelopmentFundAddressMainNet; + destinationAddress = libSpark.stage3DevelopmentFundAddressMainNet; break; case CryptoCurrencyNetwork.test: - destinationAddress = kStage3DevelopmentFundAddressTestNet; + destinationAddress = libSpark.stage3DevelopmentFundAddressTestNet; break; default: @@ -2253,7 +2197,9 @@ mixin SparkInterface TxRecipient( address: destinationAddress, amount: Amount.fromDecimal( - Decimal.fromInt(kStandardSparkNamesFee[name.length] * years), + Decimal.fromInt( + libSpark.standardSparkNamesFee[name.length] * years, + ), fractionDigits: cryptoCurrency.fractionDigits, ), isChange: false, @@ -2347,7 +2293,7 @@ _createSparkSend( }) args, ) async { - final spend = LibSpark.createSparkSendTransaction( + final spend = libSpark.createSparkSendTransaction( privateKeyHex: args.privateKeyHex, index: args.index, recipients: args.recipients, @@ -2387,7 +2333,7 @@ Future> _identifyCoins( final txHash = data[1].toHexReversedFromBase64; final contextB64 = data[2]; - final coin = LibSpark.identifyAndRecoverCoin( + final coin = libSpark.identifyAndRecoverCoin( serializedCoinB64, privateKeyHex: privateKeyHex, index: kDefaultSparkIndex, @@ -2460,13 +2406,12 @@ class MutableSparkRecipient { } } -typedef SerializedCoinData = - ({ - int groupId, - int height, - String serializedCoin, - String serializedCoinContext, - }); +typedef SerializedCoinData = ({ + int groupId, + int height, + String serializedCoin, + String serializedCoinContext, +}); Future _asyncSparkFeesWrapper({ required String privateKeyHex, @@ -2503,7 +2448,7 @@ int _estSparkFeeComputeFunc( }) args, ) { - final est = LibSpark.estimateSparkFee( + final est = libSpark.estimateSparkFee( privateKeyHex: args.privateKeyHex, index: args.index, sendAmount: args.sendAmount, diff --git a/lib/wl_gen/interfaces/lib_spark_interface.dart b/lib/wl_gen/interfaces/lib_spark_interface.dart new file mode 100644 index 0000000000..5f24277857 --- /dev/null +++ b/lib/wl_gen/interfaces/lib_spark_interface.dart @@ -0,0 +1,252 @@ +import 'dart:typed_data'; + +import 'package:logger/logger.dart'; + +export '../generated/lib_spark_interface_impl.dart'; + +abstract class LibSparkInterface { + const LibSparkInterface(); + + String get sparkBaseDerivationPath; + String get sparkBaseDerivationPathTestnet; + int get sparkChange; + int get maxNameRegistrationLengthYears; + int get maxNameLength; + int get maxAdditionalInfoLengthBytes; + String get nameRegexString; + String get stage3DevelopmentFundAddressMainNet; + String get stage3DevelopmentFundAddressTestNet; + List get standardSparkNamesFee; + + void initSparkLogging(Level level); + + String hashTag(String x, String y); + + bool validateAddress({required String address, required bool isTestNet}); + + Future getAddress({ + required Uint8List privateKey, + required int index, + required int diversifier, + bool isTestNet = false, + }); + + ({Uint8List script, int size}) createSparkNameScript({ + required int sparkNameValidityBlocks, + required String name, + required String additionalInfo, + required String scalarHex, + required String privateKeyHex, + required int spendKeyIndex, + required int diversifier, + required bool isTestNet, + required int hashFailSafe, + required bool ignoreProof, + }); + + List<({Uint8List scriptPubKey, int amount, bool subtractFeeFromAmount})> + createSparkMintRecipients({ + required List<({String sparkAddress, int value, String memo})> outputs, + required Uint8List serialContext, + bool generate = false, + }); + + Uint8List serializeMintContext({required List<(String, int)> inputs}); + + WrappedLibSparkCoin? identifyAndRecoverCoin( + final String serializedCoin, { + required final String privateKeyHex, + required final int index, + required final Uint8List context, + final bool isTestNet = false, + }); + + ({ + Uint8List serializedSpendPayload, + List outputScripts, + int fee, + List< + ({ + String serializedCoin, + String serializedCoinContext, + int groupId, + int height, + }) + > + usedCoins, + }) + createSparkSendTransaction({ + required String privateKeyHex, + int index = 1, + required List<({String address, int amount, bool subtractFeeFromAmount})> + recipients, + required List< + ({ + String sparkAddress, + int amount, + bool subtractFeeFromAmount, + String memo, + }) + > + privateRecipients, + required List< + ({ + String serializedCoin, + String serializedCoinContext, + int groupId, + int height, + }) + > + serializedCoins, + required List< + ({ + int setId, + String setHash, + List<({String serializedCoin, String txHash})> set, + }) + > + allAnonymitySets, + required List<({int setId, Uint8List blockHash})> idAndBlockHashes, + required Uint8List txHash, + required int additionalTxSize, + }); + + int estimateSparkFee({ + required String privateKeyHex, + int index = 1, + required int sendAmount, + required bool subtractFeeFromAmount, + required List< + ({ + String serializedCoin, + String serializedCoinContext, + int groupId, + int height, + }) + > + serializedCoins, + required int privateRecipientsCount, + required int utxoNum, + required int additionalTxSize, + }); +} + +// stupid +enum WrappedLibSparkCoinType { + mint(0), + spend(1); + + const WrappedLibSparkCoinType(this.value); + final int value; +} + +// stupid +final class WrappedLibSparkCoin { + final WrappedLibSparkCoinType type; + + final int? id; + final int? height; + + final bool? isUsed; + + final String? nonceHex; + + final String? address; + + final BigInt? value; + + final String? memo; + + final Uint8List? txHash; + + final Uint8List? serialContext; + + final BigInt? diversifier; + final Uint8List? encryptedDiversifier; + + final Uint8List? serial; + final Uint8List? tag; + + final String? lTagHash; + + final String? serializedCoin; + + WrappedLibSparkCoin({ + required this.type, + this.id, + this.height, + this.isUsed, + this.nonceHex, + this.address, + this.value, + this.memo, + this.txHash, + this.serialContext, + this.diversifier, + this.encryptedDiversifier, + this.serial, + this.tag, + this.lTagHash, + this.serializedCoin, + }); + + WrappedLibSparkCoin copyWith({ + WrappedLibSparkCoinType? type, + int? id, + int? height, + bool? isUsed, + String? nonceHex, + String? address, + BigInt? value, + String? memo, + Uint8List? txHash, + Uint8List? serialContext, + BigInt? diversifier, + Uint8List? encryptedDiversifier, + Uint8List? serial, + Uint8List? tag, + String? lTagHash, + String? serializedCoin, + }) { + return WrappedLibSparkCoin( + type: type ?? this.type, + id: id ?? this.id, + height: height ?? this.height, + isUsed: isUsed ?? this.isUsed, + nonceHex: nonceHex ?? this.nonceHex, + address: address ?? this.address, + value: value ?? this.value, + memo: memo ?? this.memo, + txHash: txHash ?? this.txHash, + serialContext: serialContext ?? this.serialContext, + diversifier: diversifier ?? this.diversifier, + encryptedDiversifier: encryptedDiversifier ?? this.encryptedDiversifier, + serial: serial ?? this.serial, + tag: tag ?? this.tag, + lTagHash: lTagHash ?? this.lTagHash, + serializedCoin: serializedCoin ?? this.serializedCoin, + ); + } + + @override + String toString() { + return 'WrappedLibSparkCoin(' + ', type: $type' + ', id: $id' + ', height: $height' + ', isUsed: $isUsed' + ', k: $nonceHex' + ', address: $address' + ', value: $value' + ', memo: $memo' + ', txHash: $txHash' + ', serialContext: $serialContext' + ', diversifier: $diversifier' + ', encryptedDiversifier: $encryptedDiversifier' + ', serial: $serial' + ', tag: $tag' + ', lTagHash: $lTagHash' + ', serializedCoin: $serializedCoin' + ')'; + } +} diff --git a/pubspec.lock b/pubspec.lock index a1b9af3fea..6318fed7b0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -178,10 +178,10 @@ packages: dependency: transitive description: name: build_cli_annotations - sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172 + sha256: e563c2e01de8974566a1998410d3f6f03521788160a02503b0b1f1a46c7b3d95 url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" build_config: dependency: transitive description: diff --git a/tool/wl_templates/FIRO_lib_spark_interface_impl.template.dart b/tool/wl_templates/FIRO_lib_spark_interface_impl.template.dart new file mode 100644 index 0000000000..2403a81c42 --- /dev/null +++ b/tool/wl_templates/FIRO_lib_spark_interface_impl.template.dart @@ -0,0 +1,290 @@ +//ON +import 'dart:typed_data'; + +import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart'; +import 'package:logger/logger.dart'; + +import '../../utilities/logger.dart'; +//END_ON +import '../interfaces/lib_spark_interface.dart'; + +LibSparkInterface get libSpark => _getInterface(); + +//OFF +LibSparkInterface _getInterface() => throw Exception("FIRO not enabled!"); + +List hashTags({required List base64Tags}) => + throw Exception("FIRO not enabled!"); + +//END_OFF +//ON +LibSparkInterface _getInterface() => _LibSparkInterfaceImpl(); + +List hashTags({required List base64Tags}) => + LibSpark.hashTags(base64Tags: base64Tags); + +class _LibSparkInterfaceImpl extends LibSparkInterface { + @override + String get sparkBaseDerivationPath => kSparkBaseDerivationPath; + + @override + String get sparkBaseDerivationPathTestnet => kSparkBaseDerivationPathTestnet; + + @override + int get sparkChange => kSparkChange; + + @override + int get maxAdditionalInfoLengthBytes => kMaxAdditionalInfoLengthBytes; + + @override + int get maxNameLength => kMaxNameLength; + + @override + int get maxNameRegistrationLengthYears => kMaxNameRegistrationLengthYears; + + @override + String get nameRegexString => kNameRegexString; + + @override + String get stage3DevelopmentFundAddressMainNet => + kStage3DevelopmentFundAddressMainNet; + + @override + String get stage3DevelopmentFundAddressTestNet => + kStage3DevelopmentFundAddressTestNet; + + @override + List get standardSparkNamesFee => + List.unmodifiable(kStandardSparkNamesFee); + + @override + void initSparkLogging(Level level) { + final levels = Level.values.where((e) => e >= level).map((e) => e.name); + Log.levels.addAll( + LoggingLevel.values.where((e) => levels.contains(e.name)), + ); + Log.onLog = (level, value, {error, stackTrace, required time}) { + Logging.instance.log( + level.getLoggerLevel(), + value, + error: error, + stackTrace: stackTrace, + time: time, + ); + }; + } + + @override + String hashTag(String x, String y) => LibSpark.hashTag(x, y); + + @override + bool validateAddress({required String address, required bool isTestNet}) => + LibSpark.validateAddress(address: address, isTestNet: isTestNet); + + @override + Future getAddress({ + required Uint8List privateKey, + required int index, + required int diversifier, + bool isTestNet = false, + }) => LibSpark.getAddress( + privateKey: privateKey, + index: index, + diversifier: diversifier, + isTestNet: isTestNet, + ); + + @override + ({Uint8List script, int size}) createSparkNameScript({ + required int sparkNameValidityBlocks, + required String name, + required String additionalInfo, + required String scalarHex, + required String privateKeyHex, + required int spendKeyIndex, + required int diversifier, + required bool isTestNet, + required int hashFailSafe, + required bool ignoreProof, + }) => LibSpark.createSparkNameScript( + sparkNameValidityBlocks: sparkNameValidityBlocks, + name: name, + additionalInfo: additionalInfo, + scalarHex: scalarHex, + privateKeyHex: privateKeyHex, + spendKeyIndex: spendKeyIndex, + diversifier: diversifier, + isTestNet: isTestNet, + hashFailSafe: hashFailSafe, + ignoreProof: ignoreProof, + ); + + @override + List<({int amount, Uint8List scriptPubKey, bool subtractFeeFromAmount})> + createSparkMintRecipients({ + required List<({String memo, String sparkAddress, int value})> outputs, + required Uint8List serialContext, + bool generate = false, + }) => LibSpark.createSparkMintRecipients( + outputs: outputs, + serialContext: serialContext, + generate: generate, + ); + + @override + Uint8List serializeMintContext({required List<(String, int)> inputs}) => + LibSpark.serializeMintContext(inputs: inputs); + + @override + WrappedLibSparkCoin? identifyAndRecoverCoin( + String serializedCoin, { + required String privateKeyHex, + required int index, + required Uint8List context, + bool isTestNet = false, + }) { + final coin = LibSpark.identifyAndRecoverCoin( + serializedCoin, + privateKeyHex: privateKeyHex, + index: index, + context: context, + isTestNet: isTestNet, + ); + + if (coin == null) return null; + + return WrappedLibSparkCoin( + type: WrappedLibSparkCoinType.values.firstWhere( + (e) => e.value == coin.type.value, + ), + + id: coin.id, + height: coin.height, + isUsed: coin.isUsed, + nonceHex: coin.nonceHex, + address: coin.address, + value: coin.value, + serial: coin.serial, + memo: coin.memo, + txHash: coin.txHash, + serialContext: coin.serialContext, + diversifier: coin.diversifier, + encryptedDiversifier: coin.encryptedDiversifier, + tag: coin.tag, + lTagHash: coin.lTagHash, + serializedCoin: coin.serializedCoin, + ); + } + + @override + ({ + int fee, + List outputScripts, + Uint8List serializedSpendPayload, + List< + ({ + int groupId, + int height, + String serializedCoin, + String serializedCoinContext, + }) + > + usedCoins, + }) + createSparkSendTransaction({ + required String privateKeyHex, + int index = 1, + required List<({String address, int amount, bool subtractFeeFromAmount})> + recipients, + required List< + ({ + int amount, + String memo, + String sparkAddress, + bool subtractFeeFromAmount, + }) + > + privateRecipients, + required List< + ({ + int groupId, + int height, + String serializedCoin, + String serializedCoinContext, + }) + > + serializedCoins, + required List< + ({ + List<({String serializedCoin, String txHash})> set, + String setHash, + int setId, + }) + > + allAnonymitySets, + required List<({Uint8List blockHash, int setId})> idAndBlockHashes, + required Uint8List txHash, + required int additionalTxSize, + }) => LibSpark.createSparkSendTransaction( + index: index, + privateKeyHex: privateKeyHex, + recipients: recipients, + privateRecipients: privateRecipients, + serializedCoins: serializedCoins, + allAnonymitySets: allAnonymitySets, + idAndBlockHashes: idAndBlockHashes, + txHash: txHash, + additionalTxSize: additionalTxSize, + ); + + @override + int estimateSparkFee({ + required String privateKeyHex, + int index = 1, + required int sendAmount, + required bool subtractFeeFromAmount, + required List< + ({ + int groupId, + int height, + String serializedCoin, + String serializedCoinContext, + }) + > + serializedCoins, + required int privateRecipientsCount, + required int utxoNum, + required int additionalTxSize, + }) => LibSpark.estimateSparkFee( + privateKeyHex: privateKeyHex, + sendAmount: sendAmount, + subtractFeeFromAmount: subtractFeeFromAmount, + serializedCoins: serializedCoins, + privateRecipientsCount: privateRecipientsCount, + utxoNum: utxoNum, + additionalTxSize: additionalTxSize, + index: index, + ); +} + +// convenience conversion for spark +extension _LoggingLevelExt on LoggingLevel { + Level getLoggerLevel() { + switch (this) { + case LoggingLevel.info: + return Level.info; + case LoggingLevel.warning: + return Level.warning; + case LoggingLevel.error: + return Level.error; + case LoggingLevel.fatal: + return Level.fatal; + case LoggingLevel.debug: + return Level.debug; + case LoggingLevel.trace: + return Level.trace; + } + } +} + +//END_ON From f7de74223c5aa82278b60fe1f8a81995018eb050 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 7 Oct 2025 08:10:03 -0600 Subject: [PATCH 15/30] implement optional import of xelis ffi package --- lib/main.dart | 38 +- .../restore_wallet_view.dart | 11 +- .../xelis_table_progress_provider.dart | 36 +- lib/wallets/crypto_currency/coins/xelis.dart | 5 +- lib/wallets/wallet/impl/xelis_wallet.dart | 220 +++++----- .../wallet/intermediate/lib_xelis_wallet.dart | 211 +-------- .../interfaces/lib_xelis_interface.dart | 317 ++++++++++++++ ...XEL_lib_xelis_interface_impl.template.dart | 411 ++++++++++++++++++ 8 files changed, 855 insertions(+), 394 deletions(-) create mode 100644 lib/wl_gen/interfaces/lib_xelis_interface.dart create mode 100644 tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart diff --git a/lib/main.dart b/lib/main.dart index c75941ecc2..481e398ca8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -25,9 +25,6 @@ import 'package:keyboard_dismisser/keyboard_dismisser.dart'; import 'package:logger/logger.dart'; import 'package:path_provider/path_provider.dart'; import 'package:window_size/window_size.dart'; -import 'package:xelis_flutter/src/api/api.dart' as xelis_api; -import 'package:xelis_flutter/src/api/logger.dart' as xelis_logging; -import 'package:xelis_flutter/src/frb_generated.dart' as xelis_rust; import 'app_config.dart'; import 'db/db_version_migration.dart'; @@ -79,45 +76,18 @@ import 'wallets/isar/providers/all_wallets_info_provider.dart'; import 'wallets/wallet/wallet_mixin_interfaces/spark_interface.dart'; import 'widgets/crypto_notifications.dart'; import 'wl_gen/interfaces/cs_monero_interface.dart'; +import 'wl_gen/interfaces/lib_xelis_interface.dart'; final openedFromSWBFileStringStateProvider = StateProvider( (ref) => null, ); -void startListeningToRustLogs() { - xelis_api.createLogStream().listen( - (logEntry) { - final Level level; - switch (logEntry.level) { - case xelis_logging.Level.error: - level = Level.error; - case xelis_logging.Level.warn: - level = Level.warning; - case xelis_logging.Level.info: - level = Level.info; - case xelis_logging.Level.debug: - level = Level.debug; - case xelis_logging.Level.trace: - level = Level.trace; - } - - Logging.instance.log( - level, - "[Xelis Rust Log] ${logEntry.tag}: ${logEntry.msg}", - ); - }, - onError: (dynamic e) { - Logging.instance.e("Error receiving Xelis Rust logs: $e"); - }, - ); -} - // main() is the entry point to the app. It initializes Hive (local database), // runs the MyApp widget and checks for new users, caching the value in the // miscellaneous box for later use void main(List args) async { // talker.info('initializing Rust lib ...'); - await xelis_rust.RustLib.init(); + await libXelis.initRustLib(); WidgetsFlutterBinding.ensureInitialized(); if (Util.isDesktop && args.length == 2 && args.first == "-d") { @@ -205,8 +175,8 @@ void main(List args) async { debugConsoleLevel: kDebugMode ? Level.trace : null, ); - await xelis_api.setUpRustLogger(); - startListeningToRustLogs(); + await libXelis.setupRustLogger(); + libXelis.startListeningToRustLogs(); // setup lib spark logging initSparkLogging(Prefs.instance.logLevel); diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart index f1bf78b8ca..50df152020 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart @@ -21,7 +21,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; -import 'package:xelis_flutter/src/api/seed_search_engine.dart' as x_seed; import '../../../notifications/show_flush_bar.dart'; import '../../../pages_desktop_specific/desktop_home_view.dart'; @@ -62,6 +61,7 @@ import '../../../widgets/table_view/table_view.dart'; import '../../../widgets/table_view/table_view_cell.dart'; import '../../../widgets/table_view/table_view_row.dart'; import '../../../wl_gen/interfaces/cs_monero_interface.dart'; +import '../../../wl_gen/interfaces/lib_xelis_interface.dart'; import '../../home_view/home_view.dart'; import '../add_token_view/edit_wallet_tokens_view.dart'; import '../select_wallet_for_token_view.dart'; @@ -101,7 +101,6 @@ class _RestoreWalletViewState extends ConsumerState { late final int _seedWordCount; late final bool isDesktop; - x_seed.SearchEngine? _xelisSeedSearch; final HashSet _wordListHashSet = HashSet.from(bip39wordlist.WORDLIST); final ScrollController controller = ScrollController(); @@ -163,12 +162,6 @@ class _RestoreWalletViewState extends ConsumerState { // _focusNodes.add(FocusNode()); } - if (widget.coin is Xelis) { - _xelisSeedSearch = x_seed.SearchEngine.init( - languageIndex: BigInt.from(0), - ); - } - super.initState(); } @@ -203,7 +196,7 @@ class _RestoreWalletViewState extends ConsumerState { return wowneroWordList.contains(word); } if (widget.coin is Xelis) { - return _xelisSeedSearch!.search(query: word).length > 0; + return libXelis.validateSeedWord(word); } return _wordListHashSet.contains(word); } diff --git a/lib/providers/progress_report/xelis_table_progress_provider.dart b/lib/providers/progress_report/xelis_table_progress_provider.dart index cbebe82243..82a6b353ef 100644 --- a/lib/providers/progress_report/xelis_table_progress_provider.dart +++ b/lib/providers/progress_report/xelis_table_progress_provider.dart @@ -1,8 +1,6 @@ -import 'package:xelis_flutter/src/api/api.dart' as xelis_api; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter/foundation.dart'; -import 'dart:math' as math; +import '../../wl_gen/interfaces/lib_xelis_interface.dart'; enum XelisTableGenerationStep { t1PointsGeneration, @@ -47,32 +45,6 @@ class XelisTableProgressState { } } -final xelisTableProgressProvider = StreamProvider((ref) { - double lastPrintedProgress = 0.0; - return xelis_api.createProgressReportStream().map((report) { - return report.when( - tableGeneration: (progress, step, _) { - final currentStep = XelisTableGenerationStep.fromString(step); - final stepIndex = switch(currentStep) { - XelisTableGenerationStep.t1PointsGeneration => 0, - XelisTableGenerationStep.t1CuckooSetup => 1, - XelisTableGenerationStep.t2Table => 2, - XelisTableGenerationStep.unknown => 0, - }; - - if ((progress - lastPrintedProgress).abs() >= 0.05 || - currentStep != XelisTableGenerationStep.fromString(step) || - progress >= 0.99) { - debugPrint("Xelis Table Generation: $step - ${progress*100.0}%"); - lastPrintedProgress = progress; - } - - return XelisTableProgressState( - tableProgress: progress, - currentStep: currentStep, - ); - }, - misc: (_) => const XelisTableProgressState(), - ); - }); -}); \ No newline at end of file +final xelisTableProgressProvider = StreamProvider( + (ref) => libXelis.createProgressReportStream(), +); diff --git a/lib/wallets/crypto_currency/coins/xelis.dart b/lib/wallets/crypto_currency/coins/xelis.dart index 62b33326b2..e036c24427 100644 --- a/lib/wallets/crypto_currency/coins/xelis.dart +++ b/lib/wallets/crypto_currency/coins/xelis.dart @@ -1,9 +1,8 @@ -import 'package:xelis_flutter/src/api/utils.dart' as x_utils; - import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/node_model.dart'; import '../../../utilities/default_nodes.dart'; import '../../../utilities/enums/derive_path_type_enum.dart'; +import '../../../wl_gen/interfaces/lib_xelis_interface.dart'; import '../crypto_currency.dart'; import '../intermediate/electrum_currency.dart'; @@ -94,7 +93,7 @@ class Xelis extends ElectrumCurrency { @override bool validateAddress(String address) { try { - return x_utils.isAddressValid(strAddress: address); + return libXelis.isAddressValid(address: address); } catch (_) { return false; } diff --git a/lib/wallets/wallet/impl/xelis_wallet.dart b/lib/wallets/wallet/impl/xelis_wallet.dart index 71f5f38949..d36b8310c0 100644 --- a/lib/wallets/wallet/impl/xelis_wallet.dart +++ b/lib/wallets/wallet/impl/xelis_wallet.dart @@ -5,8 +5,6 @@ import 'dart:math'; import 'package:isar_community/isar.dart'; import 'package:mutex/mutex.dart'; import 'package:stack_wallet_backup/generate_password.dart'; -import 'package:xelis_dart_sdk/xelis_dart_sdk.dart' as xelis_sdk; -import 'package:xelis_flutter/src/api/wallet.dart' as x_wallet; import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; @@ -20,6 +18,7 @@ import '../../../services/event_bus/global_event_bus.dart'; import '../../../utilities/amount/amount.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/stack_file_system.dart'; +import '../../../wl_gen/interfaces/lib_xelis_interface.dart'; import '../../crypto_currency/crypto_currency.dart'; import '../../models/tx_data.dart'; import '../intermediate/lib_xelis_wallet.dart'; @@ -44,29 +43,28 @@ class XelisWallet extends LibXelisWallet { key: Wallet.mnemonicPassphraseKey(walletId: info.walletId), ); - final mnemonic = await getMnemonic(); - final seedLength = mnemonic.trim().split(" ").length; + final mnemonic = (await getMnemonic()).trim(); + final seedLength = mnemonic.split(" ").length; invalidSeedLengthCheck(seedLength); Logging.instance.i("Xelis: recovering wallet"); - final wallet = await x_wallet.createXelisWallet( + await libXelis.createXelisWallet( + walletId, name: name, directory: directory, password: password!, - seed: mnemonic.trim(), - network: cryptoCurrency.network.xelisNetwork, + seed: mnemonic, + network: cryptoCurrency.network, precomputedTablesPath: tablePath, l1Low: tableState.currentSize.isLow, ); await secureStorageInterface.write( key: Wallet.mnemonicKey(walletId: walletId), - value: mnemonic.trim(), + value: mnemonic, ); - libXelisWallet = wallet; - await _finishInit(); } @@ -84,23 +82,22 @@ class XelisWallet extends LibXelisWallet { value: password, ); - final wallet = await x_wallet.createXelisWallet( + await libXelis.createXelisWallet( + walletId, name: name, directory: directory, password: password, - network: cryptoCurrency.network.xelisNetwork, + network: cryptoCurrency.network, precomputedTablesPath: tablePath, l1Low: tableState.currentSize.isLow, ); - final mnemonic = await wallet.getSeed(); + final mnemonic = await libXelis.getSeed(walletId); await secureStorageInterface.write( key: Wallet.mnemonicKey(walletId: walletId), value: mnemonic.trim(), ); - libXelisWallet = wallet; - await _finishInit(); } @@ -115,11 +112,12 @@ class XelisWallet extends LibXelisWallet { key: Wallet.mnemonicPassphraseKey(walletId: info.walletId), ); - libXelisWallet = await x_wallet.openXelisWallet( + await libXelis.openXelisWallet( + walletId, name: name, directory: directory, password: password!, - network: cryptoCurrency.network.xelisNetwork, + network: cryptoCurrency.network, precomputedTablesPath: tablePath, l1Low: tableState.currentSize.isLow, ); @@ -138,7 +136,7 @@ class XelisWallet extends LibXelisWallet { walletId: walletId, derivationIndex: 0, derivationPath: null, - value: libXelisWallet!.getAddressStr(), + value: libXelis.getAddress(walletId), publicKey: [], type: AddressType.xelis, subType: AddressSubType.receiving, @@ -170,7 +168,7 @@ class XelisWallet extends LibXelisWallet { walletId, ); - if (libXelisWallet == null) { + if (!libXelis.walletInstanceExists(walletId)) { if (isRestore == true) { await _restoreWallet(); } else { @@ -226,7 +224,7 @@ class XelisWallet extends LibXelisWallet { @override Future pingCheck() async { try { - await libXelisWallet!.getDaemonInfo(); + await libXelis.getDaemonInfo(walletId); await handleOnline(); return true; } catch (_) { @@ -241,12 +239,12 @@ class XelisWallet extends LibXelisWallet { Future updateBalance({int? newBalance}) async { await _balanceUpdateMutex.protect(() async { try { - if (await libXelisWallet!.hasXelisBalance()) { - final BigInt xelBalance = - newBalance != null - ? BigInt.from(newBalance) - : await libXelisWallet! - .getXelisBalanceRaw(); // in the future, use getAssetBalances and handle each + if (await libXelis.hasXelisBalance(walletId)) { + final BigInt xelBalance = newBalance != null + ? BigInt.from(newBalance) + : await libXelis.getXelisBalanceRaw( + walletId, + ); // in the future, use getAssetBalances and handle each final balance = Balance( total: Amount( rawValue: xelBalance, @@ -276,9 +274,9 @@ class XelisWallet extends LibXelisWallet { } Future _fetchChainHeight() async { - final infoString = await libXelisWallet!.getDaemonInfo(); - final Map nodeInfo = - (json.decode(infoString) as Map).cast(); + final infoString = await libXelis.getDaemonInfo(walletId); + final Map nodeInfo = (json.decode(infoString) as Map) + .cast(); pruningHeight = int.tryParse(nodeInfo['pruned_topoheight']?.toString() ?? '0') ?? 0; @@ -306,9 +304,9 @@ class XelisWallet extends LibXelisWallet { @override Future updateNode() async { try { - final bool online = await libXelisWallet!.isOnline(); + final bool online = await libXelis.isOnline(walletId); if (online == true) { - await libXelisWallet!.offlineMode(); + await libXelis.offlineMode(walletId); } await super.connect(); } catch (e, s) { @@ -324,7 +322,7 @@ class XelisWallet extends LibXelisWallet { @override Future> updateTransactions({ bool isRescan = false, - List? objTransactions, + List? objTransactions, int? topoheight, }) async { checkInitialized(); @@ -335,7 +333,7 @@ class XelisWallet extends LibXelisWallet { walletId: walletId, derivationIndex: 0, derivationPath: null, - value: libXelisWallet!.getAddressStr(), + value: libXelis.getAddress(walletId), publicKey: [], type: AddressType.xelis, subType: AddressSubType.receiving, @@ -358,36 +356,20 @@ class XelisWallet extends LibXelisWallet { firstBlock -= 10; } } else { - await libXelisWallet!.rescan(topoheight: BigInt.from(pruningHeight)); - } - - xelis_sdk.TransactionEntry _checkDecodeJsonStringTxEntry( - String jsonString, - ) { - final json = jsonDecode(jsonString); - if (json is Map) { - return xelis_sdk.TransactionEntry.fromJson(json.cast()); - } - - throw Exception("Not a Map on jsonDecode($jsonString)"); + await libXelis.rescan(walletId, topoheight: BigInt.from(pruningHeight)); } - final txList = - objTransactions ?? - (await libXelisWallet!.allHistory()) - .map(_checkDecodeJsonStringTxEntry) - .toList(); + final txList = objTransactions ?? (await libXelis.allHistory(walletId)); final List txns = []; for (final transactionEntry in txList) { try { // Check for duplicates - final storedTx = - await mainDB.isar.transactionV2s - .where() - .txidWalletIdEqualTo(transactionEntry.hash, walletId) - .findFirst(); + final storedTx = await mainDB.isar.transactionV2s + .where() + .txidWalletIdEqualTo(transactionEntry.hash, walletId) + .findFirst(); if (storedTx != null && storedTx.height != null && @@ -406,14 +388,15 @@ class XelisWallet extends LibXelisWallet { ); final Map otherData = {}; - final entryType = transactionEntry.txEntryType; + final entryType = transactionEntry.entryType; - if (entryType is xelis_sdk.CoinbaseEntry) { + if (entryType is CoinbaseEntryWrapper) { final coinbase = entryType; txType = TransactionType.incoming; - final int decimals = await libXelisWallet!.getAssetDecimals( - asset: xelis_sdk.xelisAsset, + final int decimals = await libXelis.getAssetDecimals( + walletId, + asset: libXelis.xelisAsset, ); fee = Amount( @@ -430,11 +413,12 @@ class XelisWallet extends LibXelisWallet { ), ); otherData['overrideFee'] = fee.toJsonString(); - } else if (entryType is xelis_sdk.BurnEntry) { + } else if (entryType is BurnEntryWrapper) { final burn = entryType; txType = TransactionType.outgoing; - final int decimals = await libXelisWallet!.getAssetDecimals( + final int decimals = await libXelis.getAssetDecimals( + walletId, asset: burn.asset, ); @@ -468,15 +452,15 @@ class XelisWallet extends LibXelisWallet { ); otherData['burnAsset'] = burn.asset; - } else if (entryType is xelis_sdk.IncomingEntry) { + } else if (entryType is IncomingEntryWrapper) { final incoming = entryType; - txType = - incoming.from == thisAddress - ? TransactionType.sentToSelf - : TransactionType.incoming; + txType = incoming.from == thisAddress + ? TransactionType.sentToSelf + : TransactionType.incoming; for (final transfer in incoming.transfers) { - final int decimals = await libXelisWallet!.getAssetDecimals( + final int decimals = await libXelis.getAssetDecimals( + walletId, asset: transfer.asset, ); @@ -496,12 +480,11 @@ class XelisWallet extends LibXelisWallet { otherData['asset_${transfer.asset}'] = transfer.amount.toString(); if (transfer.extraData != null) { - otherData['extraData_${transfer.asset}'] = - transfer.extraData!.toJson(); + otherData['extraData_${transfer.asset}'] = transfer.extraData!; } otherData['overrideFee'] = fee.toJsonString(); } - } else if (entryType is xelis_sdk.OutgoingEntry) { + } else if (entryType is OutgoingEntryWrapper) { final outgoing = entryType; txType = TransactionType.outgoing; nonce = outgoing.nonce; @@ -551,11 +534,10 @@ class XelisWallet extends LibXelisWallet { ), ); - otherData['asset_${transfer.asset}_amount'] = - transfer.amount.toString(); + otherData['asset_${transfer.asset}_amount'] = transfer.amount + .toString(); if (transfer.extraData != null) { - otherData['extraData_${transfer.asset}'] = - transfer.extraData!.toJson(); + otherData['extraData_${transfer.asset}'] = transfer.extraData!; } } } else { @@ -639,14 +621,13 @@ class XelisWallet extends LibXelisWallet { try { checkInitialized(); - final recipients = - txData.recipients?.isNotEmpty == true - ? txData.recipients! - : throw ArgumentError( - 'Address cannot be empty.', - ); // in the future, support for multiple recipients will work. + final recipients = txData.recipients?.isNotEmpty == true + ? txData.recipients! + : throw ArgumentError( + 'Address cannot be empty.', + ); // in the future, support for multiple recipients will work. - final asset = assetId ?? xelis_sdk.xelisAsset; + final asset = assetId ?? libXelis.xelisAsset; // Calculate total send amount final totalSendAmount = recipients.fold( @@ -658,7 +639,7 @@ class XelisWallet extends LibXelisWallet { ); // Check balance using raw method - final xelBalance = await libXelisWallet!.getXelisBalanceRaw(); + final xelBalance = await libXelis.getXelisBalanceRaw(walletId); final balance = Amount( rawValue: xelBalance, fractionDigits: cryptoCurrency.fractionDigits, @@ -675,12 +656,14 @@ class XelisWallet extends LibXelisWallet { // Check if we have enough for both transfers and fee if (totalSendAmount + boostedFee > balance) { - final requiredAmt = await libXelisWallet!.formatCoin( + final requiredAmt = await libXelis.formatCoin( + walletId, atomicAmount: (totalSendAmount + boostedFee).raw, assetHash: asset, ); - final availableAmt = await libXelisWallet!.formatCoin( + final availableAmt = await libXelis.formatCoin( + walletId, atomicAmount: xelBalance, assetHash: asset, ); @@ -714,37 +697,37 @@ class XelisWallet extends LibXelisWallet { }) async { try { checkInitialized(); - final asset = assetId ?? xelis_sdk.xelisAsset; + final asset = assetId ?? libXelis.xelisAsset; // Default values for a new wallet or when estimation fails final defaultDecimals = cryptoCurrency.fractionDigits; final defaultFee = BigInt.from(0); // Use default address if recipients list is empty to ensure basic fee estimates are readily available - final effectiveRecipients = - recipients.isNotEmpty - ? recipients - : [ - TxRecipient( - address: - 'xel:xz9574c80c4xegnvurazpmxhw5dlg2n0g9qm60uwgt75uqyx3pcsqzzra9m', - amount: amount, - isChange: false, - addressType: AddressType.xelis, - ), - ]; + final effectiveRecipients = recipients.isNotEmpty + ? recipients + : [ + TxRecipient( + address: + 'xel:xz9574c80c4xegnvurazpmxhw5dlg2n0g9qm60uwgt75uqyx3pcsqzzra9m', + amount: amount, + isChange: false, + addressType: AddressType.xelis, + ), + ]; try { final transfers = await Future.wait( effectiveRecipients.map((recipient) async { try { final amt = double.parse( - await libXelisWallet!.formatCoin( + await libXelis.formatCoin( + walletId, atomicAmount: recipient.amount.raw, assetHash: asset, ), ); - return x_wallet.Transfer( + return WrappedTransfer( floatAmount: amt, strAddress: recipient.address, assetHash: asset, @@ -760,7 +743,7 @@ class XelisWallet extends LibXelisWallet { final rawAmount = recipient.amount.raw; final floatAmount = rawAmount / BigInt.from(10).pow(defaultDecimals); - return x_wallet.Transfer( + return WrappedTransfer( floatAmount: floatAmount.toDouble(), strAddress: recipient.address, assetHash: asset, @@ -770,9 +753,12 @@ class XelisWallet extends LibXelisWallet { }), ); - final decimals = await libXelisWallet!.getAssetDecimals(asset: asset); + final decimals = await libXelis.getAssetDecimals( + walletId, + asset: asset, + ); final estimatedFee = double.parse( - await libXelisWallet!.estimateFees(transfers: transfers), + await libXelis.estimateFees(walletId, transfers: transfers), ); final rawFee = (estimatedFee * pow(10, decimals)).round(); return Amount( @@ -817,19 +803,21 @@ class XelisWallet extends LibXelisWallet { ? jsonDecode(txData.otherData!) : null)?['asset'] as String? ?? - xelis_sdk.xelisAsset; + libXelis.xelisAsset; final amt = double.parse( - await libXelisWallet!.formatCoin( + await libXelis.formatCoin( + walletId, atomicAmount: sendAmount.raw, assetHash: asset, ), ); // Create a transfer transaction - final txJson = await libXelisWallet!.createTransfersTransaction( + final txJson = await libXelis.createTransfersTransaction( + walletId, transfers: [ - x_wallet.Transfer( + WrappedTransfer( floatAmount: amt, strAddress: recipient.address, assetHash: asset, @@ -842,7 +830,7 @@ class XelisWallet extends LibXelisWallet { final txHash = txMap['hash'] as String; // Broadcast the transaction - await libXelisWallet!.broadcastTransaction(txHash: txHash); + await libXelis.broadcastTransaction(walletId, txHash: txHash); return await updateSentCachedTxData( txData: txData.copyWith(txid: txHash), @@ -862,11 +850,11 @@ class XelisWallet extends LibXelisWallet { switch (event) { case NewTopoheight(:final height): await handleNewTopoHeight(height); - case NewAsset(:final asset): - await handleNewAsset(asset); + case NewAsset(): + await handleNewAsset(event); case NewTransaction(:final transaction): await handleNewTransaction(transaction); - case BalanceChanged(:final event): + case BalanceChanged(): await handleBalanceChanged(event); case Rescan(:final startTopoheight): await handleRescan(startTopoheight); @@ -892,7 +880,7 @@ class XelisWallet extends LibXelisWallet { } @override - Future handleNewTransaction(xelis_sdk.TransactionEntry tx) async { + Future handleNewTransaction(TransactionEntryWrapper tx) async { try { final txList = [tx]; final newTxIds = await updateTransactions( @@ -916,10 +904,10 @@ class XelisWallet extends LibXelisWallet { } @override - Future handleBalanceChanged(xelis_sdk.BalanceChangedEvent event) async { + Future handleBalanceChanged(BalanceChanged event) async { try { - final asset = event.assetHash; - if (asset == xelis_sdk.xelisAsset) { + final asset = event.asset; + if (asset == libXelis.xelisAsset) { await updateBalance(newBalance: event.balance); } @@ -983,7 +971,7 @@ class XelisWallet extends LibXelisWallet { } @override - Future handleNewAsset(xelis_sdk.AssetData asset) async { + Future handleNewAsset(NewAsset asset) async { // TODO: Store asset information if needed // TODO: Update UI/state for new asset Logging.instance.d("New xelis asset detected: $asset"); @@ -993,7 +981,7 @@ class XelisWallet extends LibXelisWallet { Future refresh({int? topoheight}) async { await refreshMutex.protect(() async { try { - final bool online = await libXelisWallet!.isOnline(); + final bool online = await libXelis.isOnline(walletId); if (online == true) { await updateChainHeight(topoheight: topoheight); await updateBalance(); diff --git a/lib/wallets/wallet/intermediate/lib_xelis_wallet.dart b/lib/wallets/wallet/intermediate/lib_xelis_wallet.dart index e9604eafd6..4cec5e1f4e 100644 --- a/lib/wallets/wallet/intermediate/lib_xelis_wallet.dart +++ b/lib/wallets/wallet/intermediate/lib_xelis_wallet.dart @@ -1,149 +1,18 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:isar_community/isar.dart'; import 'package:mutex/mutex.dart'; -import 'package:xelis_dart_sdk/xelis_dart_sdk.dart' as xelis_sdk; -import 'package:xelis_flutter/src/api/network.dart' as x_network; -import 'package:xelis_flutter/src/api/wallet.dart' as x_wallet; import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/stack_file_system.dart'; -import '../../crypto_currency/crypto_currency.dart'; +import '../../../wl_gen/interfaces/lib_xelis_interface.dart'; import '../../crypto_currency/intermediate/electrum_currency.dart'; import '../wallet_mixin_interfaces/mnemonic_interface.dart'; import 'external_wallet.dart'; -enum XelisTableSize { - low, - full; - - bool get isLow => this == XelisTableSize.low; - - static XelisTableSize get platformDefault { - if (kIsWeb) { - return XelisTableSize.low; - } - return XelisTableSize.full; - } -} - -class XelisTableState { - final bool isGenerating; - final XelisTableSize currentSize; - final XelisTableSize _desiredSize; - - XelisTableSize get desiredSize { - if (kIsWeb) { - return XelisTableSize.low; - } - return _desiredSize; - } - - const XelisTableState({ - this.isGenerating = false, - this.currentSize = XelisTableSize.low, - XelisTableSize desiredSize = XelisTableSize.full, - }) : _desiredSize = desiredSize; - - XelisTableState copyWith({ - bool? isGenerating, - XelisTableSize? currentSize, - XelisTableSize? desiredSize, - }) { - return XelisTableState( - isGenerating: isGenerating ?? this.isGenerating, - currentSize: currentSize ?? this.currentSize, - desiredSize: kIsWeb ? XelisTableSize.low : (desiredSize ?? _desiredSize), - ); - } - - factory XelisTableState.fromJson(Map json) { - return XelisTableState( - isGenerating: json['isGenerating'] as bool, - currentSize: XelisTableSize.values[json['currentSize'] as int], - desiredSize: XelisTableSize.values[json['desiredSize'] as int], - ); - } - - Map toJson() => { - 'isGenerating': isGenerating, - 'currentSize': currentSize.index, - 'desiredSize': _desiredSize.index, - }; -} - -extension XelisNetworkConversion on CryptoCurrencyNetwork { - x_network.Network get xelisNetwork { - switch (this) { - case CryptoCurrencyNetwork.main: - return x_network.Network.mainnet; - case CryptoCurrencyNetwork.test: - return x_network.Network.testnet; - default: - throw ArgumentError('Unsupported network type for Xelis: $this'); - } - } -} - -extension CryptoCurrencyNetworkConversion on x_network.Network { - CryptoCurrencyNetwork get cryptoCurrencyNetwork { - switch (this) { - case x_network.Network.mainnet: - return CryptoCurrencyNetwork.main; - case x_network.Network.testnet: - return CryptoCurrencyNetwork.test; - default: - throw ArgumentError('Unsupported Xelis network type: $this'); - } - } -} - -sealed class Event { - const Event(); -} - -final class NewTopoheight extends Event { - final int height; - const NewTopoheight(this.height); -} - -final class NewAsset extends Event { - final xelis_sdk.AssetData asset; - const NewAsset(this.asset); -} - -final class NewTransaction extends Event { - final xelis_sdk.TransactionEntry transaction; - const NewTransaction(this.transaction); -} - -final class BalanceChanged extends Event { - final xelis_sdk.BalanceChangedEvent event; - const BalanceChanged(this.event); -} - -final class Rescan extends Event { - final int startTopoheight; - const Rescan(this.startTopoheight); -} - -final class Online extends Event { - const Online(); -} - -final class Offline extends Event { - const Offline(); -} - -final class HistorySynced extends Event { - final int topoheight; - const HistorySynced(this.topoheight); -} - abstract class LibXelisWallet extends ExternalWallet with MnemonicInterface { @@ -155,19 +24,10 @@ abstract class LibXelisWallet static final _tableGenerationMutex = Mutex(); static Completer? _tableGenerationCompleter; - x_wallet.XelisWallet? libXelisWallet; int pruningHeight = 0; - x_wallet.XelisWallet? get wallet => libXelisWallet; - set wallet(x_wallet.XelisWallet? newWallet) { - if (newWallet == null && libXelisWallet != null) { - throw StateError('Cannot set wallet to null after initialization'); - } - libXelisWallet = newWallet; - } - void checkInitialized() { - if (libXelisWallet == null) { + if (!libXelis.walletInstanceExists(walletId)) { throw StateError('libXelisWallet not initialized'); } } @@ -216,66 +76,15 @@ abstract class LibXelisWallet ); } - Stream convertRawEvents() async* { - checkInitialized(); - final rawEventStream = libXelisWallet!.eventsStream(); - - await for (final rawData in rawEventStream) { - final json = jsonDecode(rawData); - try { - final eventType = xelis_sdk.WalletEvent.fromStr( - json['event'] as String, - ); - switch (eventType) { - case xelis_sdk.WalletEvent.newTopoHeight: - yield NewTopoheight(json['data']['topoheight'] as int); - case xelis_sdk.WalletEvent.newAsset: - yield NewAsset( - xelis_sdk.AssetData.fromJson( - json['data'] as Map, - ), - ); - case xelis_sdk.WalletEvent.newTransaction: - yield NewTransaction( - xelis_sdk.TransactionEntry.fromJson( - json['data'] as Map, - ), - ); - case xelis_sdk.WalletEvent.balanceChanged: - yield BalanceChanged( - xelis_sdk.BalanceChangedEvent.fromJson( - json['data'] as Map, - ), - ); - case xelis_sdk.WalletEvent.rescan: - yield Rescan(json['data']['start_topoheight'] as int); - case xelis_sdk.WalletEvent.online: - yield const Online(); - case xelis_sdk.WalletEvent.offline: - yield const Offline(); - case xelis_sdk.WalletEvent.historySynced: - yield HistorySynced(json['data']['topoheight'] as int); - } - } catch (e, s) { - Logging.instance.e( - "Error processing xelis wallet event: $rawData", - error: e, - stackTrace: s, - ); - continue; - } - } - } - Future handleEvent(Event event) async {} Future handleNewTopoHeight(int height); - Future handleNewTransaction(xelis_sdk.TransactionEntry tx); - Future handleBalanceChanged(xelis_sdk.BalanceChangedEvent event); + Future handleNewTransaction(TransactionEntryWrapper tx); + Future handleBalanceChanged(BalanceChanged event); Future handleRescan(int startTopoheight) async {} Future handleOnline() async {} Future handleOffline() async {} Future handleHistorySynced(int topoheight) async {} - Future handleNewAsset(xelis_sdk.AssetData asset) async {} + Future handleNewAsset(NewAsset asset) async {} @override Future refresh({int? topoheight}); @@ -283,10 +92,12 @@ abstract class LibXelisWallet Future connect() async { final node = getCurrentNode(); try { - _eventSubscription = convertRawEvents().listen(handleEvent); + checkInitialized(); + _eventSubscription = libXelis.eventsStream(walletId).listen(handleEvent); Logging.instance.i("Connecting to node: ${node.host}:${node.port}"); - await libXelisWallet!.onlineMode( + await libXelis.onlineMode( + walletId, daemonAddress: "${node.host}:${node.port}", ); await super.refresh(); @@ -356,7 +167,7 @@ abstract class LibXelisWallet await _eventSubscription?.cancel(); _eventSubscription = null; - await libXelisWallet?.offlineMode(); + await libXelis.offlineMode(walletId); await super.exit(); }); } finally { @@ -413,7 +224,7 @@ extension XelisTableManagement on LibXelisWallet { Logging.instance.i("Xelis: Generating large tables in background"); final tablePath = await getPrecomputedTablesPath(); - await x_wallet.updateTables( + await libXelis.updateTables( precomputedTablesPath: tablePath, l1Low: state.desiredSize.isLow, ); diff --git a/lib/wl_gen/interfaces/lib_xelis_interface.dart b/lib/wl_gen/interfaces/lib_xelis_interface.dart new file mode 100644 index 0000000000..533b90f901 --- /dev/null +++ b/lib/wl_gen/interfaces/lib_xelis_interface.dart @@ -0,0 +1,317 @@ +import 'package:flutter/foundation.dart'; + +import '../../providers/progress_report/xelis_table_progress_provider.dart'; +import '../../wallets/crypto_currency/crypto_currency.dart'; + +export '../generated/lib_xelis_interface_impl.dart'; + +abstract class LibXelisInterface { + String get xelisAsset; + + bool walletInstanceExists(String walletId); + + Future initRustLib(); + + Future setupRustLogger(); + + void startListeningToRustLogs(); + + Stream createProgressReportStream(); + + bool isAddressValid({required String address}); + + bool validateSeedWord(String word); + + Stream eventsStream(String walletId); + + Future onlineMode(String walletId, {required String daemonAddress}); + Future offlineMode(String walletId); + + Future updateTables({ + required String precomputedTablesPath, + required bool l1Low, + }); + + Future getSeed(String walletId); + + Future createXelisWallet( + String walletId, { + required String name, + required String directory, + required String password, + required CryptoCurrencyNetwork network, + String? seed, + String? privateKey, + String? precomputedTablesPath, + bool? l1Low, + }); + + Future openXelisWallet( + String walletId, { + required String name, + required String directory, + required String password, + required CryptoCurrencyNetwork network, + String? precomputedTablesPath, + bool? l1Low, + }); + + String getAddress(String walletId); + + Future getDaemonInfo(String walletId); + + Future isOnline(String walletId); + + Future rescan(String walletId, {required BigInt topoheight}); + + Future> allHistory(String walletId); + + Future broadcastTransaction(String walletId, {required String txHash}); + + Future estimateFees( + String walletId, { + required List transfers, + }); + + Future createTransfersTransaction( + String walletId, { + required List transfers, + }); + + Future formatCoin( + String walletId, { + required BigInt atomicAmount, + String? assetHash, + }); + + Future getAssetDecimals(String walletId, {required String asset}); + + Future getXelisBalanceRaw(String walletId); + + Future hasXelisBalance(String walletId); +} + +// ============================================================================= +// ============== stupid ======================================================= +class WrappedTransfer { + final double floatAmount; + final String strAddress; + final String assetHash; + final String? extraData; + + const WrappedTransfer({ + required this.floatAmount, + required this.strAddress, + required this.assetHash, + this.extraData, + }); + + @override + int get hashCode => + floatAmount.hashCode ^ + strAddress.hashCode ^ + assetHash.hashCode ^ + extraData.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is WrappedTransfer && + runtimeType == other.runtimeType && + floatAmount == other.floatAmount && + strAddress == other.strAddress && + assetHash == other.assetHash && + extraData == other.extraData; +} + +class TransactionEntryWrapper { + final Object _value; + + final EntryWrapper entryType; + + final String hash; + final DateTime? timestamp; + final int topoheight; + + TransactionEntryWrapper( + this._value, { + required this.entryType, + required this.hash, + required this.timestamp, + required this.topoheight, + }); + + T getValue() => _value is T + ? _value as T + : throw Exception( + "Type mismatch: ${_value.runtimeType} is not ${T.runtimeType}", + ); +} + +sealed class EntryWrapper { + const EntryWrapper(); +} + +class CoinbaseEntryWrapper extends EntryWrapper { + final int reward; + const CoinbaseEntryWrapper({required this.reward}); +} + +class BurnEntryWrapper extends EntryWrapper { + final int amount; + final int fee; + final String asset; + + const BurnEntryWrapper({ + required this.amount, + required this.fee, + required this.asset, + }); +} + +class IncomingEntryWrapper extends EntryWrapper { + final String from; + final List<({int amount, String asset, Map? extraData})> + transfers; + + const IncomingEntryWrapper({required this.from, required this.transfers}); +} + +class OutgoingEntryWrapper extends EntryWrapper { + final int nonce; + final int fee; + final List< + ({ + String destination, + int amount, + String asset, + Map? extraData, + }) + > + transfers; + + const OutgoingEntryWrapper({ + required this.nonce, + required this.fee, + required this.transfers, + }); +} + +class UnknownEntryWrapper extends EntryWrapper {} + +// ============================================================================= + +// ============================================================================= +// ============== moved from lib_xelis_wallet.dart ============================= +enum XelisTableSize { + low, + full; + + bool get isLow => this == XelisTableSize.low; + + static XelisTableSize get platformDefault { + if (kIsWeb) { + return XelisTableSize.low; + } + return XelisTableSize.full; + } +} + +class XelisTableState { + final bool isGenerating; + final XelisTableSize currentSize; + final XelisTableSize _desiredSize; + + XelisTableSize get desiredSize { + if (kIsWeb) { + return XelisTableSize.low; + } + return _desiredSize; + } + + const XelisTableState({ + this.isGenerating = false, + this.currentSize = XelisTableSize.low, + XelisTableSize desiredSize = XelisTableSize.full, + }) : _desiredSize = desiredSize; + + XelisTableState copyWith({ + bool? isGenerating, + XelisTableSize? currentSize, + XelisTableSize? desiredSize, + }) { + return XelisTableState( + isGenerating: isGenerating ?? this.isGenerating, + currentSize: currentSize ?? this.currentSize, + desiredSize: kIsWeb ? XelisTableSize.low : (desiredSize ?? _desiredSize), + ); + } + + factory XelisTableState.fromJson(Map json) { + return XelisTableState( + isGenerating: json['isGenerating'] as bool, + currentSize: XelisTableSize.values[json['currentSize'] as int], + desiredSize: XelisTableSize.values[json['desiredSize'] as int], + ); + } + + Map toJson() => { + 'isGenerating': isGenerating, + 'currentSize': currentSize.index, + 'desiredSize': _desiredSize.index, + }; +} + +sealed class Event { + const Event(); +} + +final class NewTopoheight extends Event { + final int height; + + const NewTopoheight(this.height); +} + +final class NewAsset extends Event { + // final xelis_sdk.AssetData asset; + final String name; + final int decimals; + final int? maxSupply; + + NewAsset(this.name, this.decimals, this.maxSupply); +} + +final class NewTransaction extends Event { + // final xelis_sdk.TransactionEntry transaction; + final TransactionEntryWrapper transaction; + const NewTransaction(this.transaction); +} + +final class BalanceChanged extends Event { + // final xelis_sdk.BalanceChangedEvent event; + final String asset; + final int balance; + + const BalanceChanged(this.asset, this.balance); +} + +final class Rescan extends Event { + final int startTopoheight; + + const Rescan(this.startTopoheight); +} + +final class Online extends Event { + const Online(); +} + +final class Offline extends Event { + const Offline(); +} + +final class HistorySynced extends Event { + final int topoheight; + const HistorySynced(this.topoheight); +} + +// ============================================================================= diff --git a/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart b/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart new file mode 100644 index 0000000000..08f1f9b4aa --- /dev/null +++ b/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart @@ -0,0 +1,411 @@ +//ON +import 'dart:convert'; + +import 'package:logger/logger.dart'; +import 'package:xelis_dart_sdk/xelis_dart_sdk.dart' as xelis_sdk; +import 'package:xelis_flutter/src/api/api.dart' as xelis_api; +import 'package:xelis_flutter/src/api/logger.dart' as xelis_logging; +import 'package:xelis_flutter/src/api/network.dart' as x_network; +import 'package:xelis_flutter/src/api/seed_search_engine.dart' as x_seed; +import 'package:xelis_flutter/src/api/utils.dart' as x_utils; +import 'package:xelis_flutter/src/api/wallet.dart' as x_wallet; +import 'package:xelis_flutter/src/frb_generated.dart' as xelis_rust; + +import '../../providers/progress_report/xelis_table_progress_provider.dart'; +import '../../utilities/logger.dart'; +import '../../wallets/crypto_currency/crypto_currency.dart'; +//END_ON +import '../interfaces/lib_xelis_interface.dart'; + +LibXelisInterface get libXelis => _getInterface(); + +//OFF +LibXelisInterface _getInterface() => throw Exception("XEL not enabled!"); + +//END_OFF +//ON +LibXelisInterface _getInterface() => _MwebdServerInterfaceImpl(); + +final class _MwebdServerInterfaceImpl extends LibXelisInterface { + final Map _wallets = {}; + + @override + String get xelisAsset => xelis_sdk.xelisAsset; + + @override + bool walletInstanceExists(String walletId) => _wallets[walletId] != null; + + @override + Future initRustLib() => xelis_rust.RustLib.init(); + + @override + Future setupRustLogger() => xelis_api.setUpRustLogger(); + + @override + void startListeningToRustLogs() => xelis_api.createLogStream().listen( + (logEntry) { + final Level level; + switch (logEntry.level) { + case xelis_logging.Level.error: + level = Level.error; + case xelis_logging.Level.warn: + level = Level.warning; + case xelis_logging.Level.info: + level = Level.info; + case xelis_logging.Level.debug: + level = Level.debug; + case xelis_logging.Level.trace: + level = Level.trace; + } + + Logging.instance.log( + level, + "[Xelis Rust Log] ${logEntry.tag}: ${logEntry.msg}", + ); + }, + onError: (dynamic e) { + Logging.instance.e("Error receiving Xelis Rust logs: $e"); + }, + ); + + @override + Stream createProgressReportStream() { + double lastPrintedProgress = 0.0; + return xelis_api.createProgressReportStream().map((report) { + return report.when( + tableGeneration: (progress, step, _) { + final currentStep = XelisTableGenerationStep.fromString(step); + if ((progress - lastPrintedProgress).abs() >= 0.05 || + currentStep != XelisTableGenerationStep.fromString(step) || + progress >= 0.99) { + Logging.instance.d( + "Xelis Table Generation: $step - ${progress * 100.0}%", + ); + lastPrintedProgress = progress; + } + + return XelisTableProgressState( + tableProgress: progress, + currentStep: currentStep, + ); + }, + misc: (_) => const XelisTableProgressState(), + ); + }); + } + + @override + bool isAddressValid({required String address}) => + x_utils.isAddressValid(strAddress: address); + + x_seed.SearchEngine? _xelisSeedSearch; + @override + bool validateSeedWord(String word) { + _xelisSeedSearch ??= x_seed.SearchEngine.init( + languageIndex: BigInt.from(0), + ); + + return _xelisSeedSearch!.search(query: word).isNotEmpty; + } + + @override + Stream eventsStream(String walletId) async* { + final rawEventStream = _wallets[walletId]!.eventsStream(); + + await for (final rawData in rawEventStream) { + final json = jsonDecode(rawData); + try { + final eventType = xelis_sdk.WalletEvent.fromStr( + json['event'] as String, + ); + switch (eventType) { + case xelis_sdk.WalletEvent.newTopoHeight: + yield NewTopoheight(json['data']['topoheight'] as int); + case xelis_sdk.WalletEvent.newAsset: + final data = xelis_sdk.AssetData.fromJson( + json['data'] as Map, + ); + + yield NewAsset(data.name, data.decimals, data.maxSupply); + case xelis_sdk.WalletEvent.newTransaction: + final tx = xelis_sdk.TransactionEntry.fromJson( + json['data'] as Map, + ); + yield NewTransaction( + TransactionEntryWrapper( + tx, + entryType: _entryTypeConversion(tx.txEntryType), + hash: tx.hash, + timestamp: tx.timestamp, + topoheight: tx.topoheight, + ), + ); + case xelis_sdk.WalletEvent.balanceChanged: + final data = xelis_sdk.BalanceChangedEvent.fromJson( + json['data'] as Map, + ); + yield BalanceChanged(data.assetHash, data.balance); + case xelis_sdk.WalletEvent.rescan: + yield Rescan(json['data']['start_topoheight'] as int); + case xelis_sdk.WalletEvent.online: + yield const Online(); + case xelis_sdk.WalletEvent.offline: + yield const Offline(); + case xelis_sdk.WalletEvent.historySynced: + yield HistorySynced(json['data']['topoheight'] as int); + } + } catch (e, s) { + Logging.instance.e( + "Error processing xelis wallet event: $rawData", + error: e, + stackTrace: s, + ); + continue; + } + } + } + + @override + Future onlineMode(String walletId, {required String daemonAddress}) => + _wallets[walletId]!.onlineMode(daemonAddress: daemonAddress); + + @override + Future offlineMode(String walletId) => + _wallets[walletId]?.offlineMode() ?? Future.value(); + + @override + Future updateTables({ + required String precomputedTablesPath, + required bool l1Low, + }) => x_wallet.updateTables( + precomputedTablesPath: precomputedTablesPath, + l1Low: l1Low, + ); + + @override + Future getSeed(String walletId) => _wallets[walletId]!.getSeed(); + + @override + Future createXelisWallet( + String walletId, { + required String name, + required String directory, + required String password, + required CryptoCurrencyNetwork network, + String? seed, + String? privateKey, + String? precomputedTablesPath, + bool? l1Low, + }) async { + if (walletInstanceExists(walletId)) { + throw Exception( + "Attempted overwrite of existing wallet in cache on create", + ); + } + + _wallets[walletId] = await x_wallet.createXelisWallet( + name: name, + directory: directory, + password: password, + privateKey: privateKey, + seed: seed, + network: network.xelisNetwork, + precomputedTablesPath: precomputedTablesPath, + l1Low: l1Low, + ); + } + + @override + Future openXelisWallet( + String walletId, { + required String name, + required String directory, + required String password, + required CryptoCurrencyNetwork network, + String? precomputedTablesPath, + bool? l1Low, + }) async { + if (walletInstanceExists(walletId)) { + throw Exception( + "Attempted overwrite of existing wallet in cache on open", + ); + } + + _wallets[walletId] = await x_wallet.openXelisWallet( + name: name, + directory: directory, + password: password, + network: network.xelisNetwork, + precomputedTablesPath: precomputedTablesPath, + l1Low: l1Low, + ); + } + + @override + String getAddress(String walletId) => _wallets[walletId]!.getAddressStr(); + + @override + Future getDaemonInfo(String walletId) => + _wallets[walletId]!.getDaemonInfo(); + + @override + Future isOnline(String walletId) => _wallets[walletId]!.isOnline(); + + @override + Future rescan(String walletId, {required BigInt topoheight}) => + _wallets[walletId]!.rescan(topoheight: topoheight); + + @override + Future> allHistory(String walletId) async => + (await _wallets[walletId]!.allHistory()).map((e) { + final tx = _checkDecodeJsonStringTxEntry(e); + return TransactionEntryWrapper( + tx, + entryType: _entryTypeConversion(tx.txEntryType), + hash: tx.hash, + timestamp: tx.timestamp, + topoheight: tx.topoheight, + ); + }).toList(); + + @override + Future broadcastTransaction( + String walletId, { + required String txHash, + }) => _wallets[walletId]!.broadcastTransaction(txHash: txHash); + + @override + Future createTransfersTransaction( + String walletId, { + required List transfers, + }) => _wallets[walletId]!.createTransfersTransaction( + transfers: transfers + .map( + (e) => x_wallet.Transfer( + floatAmount: e.floatAmount, + strAddress: e.strAddress, + assetHash: e.assetHash, + extraData: e.extraData, + ), + ) + .toList(), + ); + + @override + Future estimateFees( + String walletId, { + required List transfers, + }) => _wallets[walletId]!.estimateFees( + transfers: transfers + .map( + (e) => x_wallet.Transfer( + floatAmount: e.floatAmount, + strAddress: e.strAddress, + assetHash: e.assetHash, + extraData: e.extraData, + ), + ) + .toList(), + ); + + @override + Future formatCoin( + String walletId, { + required BigInt atomicAmount, + String? assetHash, + }) => _wallets[walletId]!.formatCoin( + atomicAmount: atomicAmount, + assetHash: assetHash, + ); + + @override + Future getAssetDecimals(String walletId, {required String asset}) => + _wallets[walletId]!.getAssetDecimals(asset: asset); + + @override + Future getXelisBalanceRaw(String walletId) => + _wallets[walletId]!.getXelisBalanceRaw(); + + @override + Future hasXelisBalance(String walletId) => + _wallets[walletId]!.hasXelisBalance(); +} + +extension _XelisNetworkConversion on CryptoCurrencyNetwork { + x_network.Network get xelisNetwork { + switch (this) { + case CryptoCurrencyNetwork.main: + return x_network.Network.mainnet; + case CryptoCurrencyNetwork.test: + return x_network.Network.testnet; + default: + throw ArgumentError('Unsupported network type for Xelis: $this'); + } + } +} + +extension _CryptoCurrencyNetworkConversion on x_network.Network { + CryptoCurrencyNetwork get cryptoCurrencyNetwork { + switch (this) { + case x_network.Network.mainnet: + return CryptoCurrencyNetwork.main; + case x_network.Network.testnet: + return CryptoCurrencyNetwork.test; + default: + throw ArgumentError('Unsupported Xelis network type: $this'); + } + } +} + +EntryWrapper _entryTypeConversion(xelis_sdk.TransactionEntryType entryType) { + if (entryType is xelis_sdk.CoinbaseEntry) { + return CoinbaseEntryWrapper(reward: entryType.reward); + } else if (entryType is xelis_sdk.BurnEntry) { + return BurnEntryWrapper( + amount: entryType.amount, + fee: entryType.fee, + asset: entryType.asset, + ); + } else if (entryType is xelis_sdk.IncomingEntry) { + return IncomingEntryWrapper( + from: entryType.from, + transfers: entryType.transfers + .map( + (e) => ( + amount: e.amount, + asset: e.asset, + extraData: e.extraData?.toJson(), + ), + ) + .toList(), + ); + } else if (entryType is xelis_sdk.OutgoingEntry) { + return OutgoingEntryWrapper( + nonce: entryType.nonce, + fee: entryType.fee, + transfers: entryType.transfers + .map( + (e) => ( + destination: e.destination, + amount: e.amount, + asset: e.asset, + extraData: e.extraData?.toJson(), + ), + ) + .toList(), + ); + } else { + return UnknownEntryWrapper(); + } +} + +xelis_sdk.TransactionEntry _checkDecodeJsonStringTxEntry(String jsonString) { + final json = jsonDecode(jsonString); + if (json is Map) { + return xelis_sdk.TransactionEntry.fromJson(json.cast()); + } + + throw Exception("Not a Map on jsonDecode($jsonString)"); +} + +//END_ON From b127e8e7ae99470d7056dbfacd4fe160a3128576 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 7 Oct 2025 09:13:35 -0600 Subject: [PATCH 16/30] fix xmr rpc import --- scripts/app_config/templates/pubspec.template.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/app_config/templates/pubspec.template.yaml b/scripts/app_config/templates/pubspec.template.yaml index 71610671b9..0374094789 100644 --- a/scripts/app_config/templates/pubspec.template.yaml +++ b/scripts/app_config/templates/pubspec.template.yaml @@ -63,7 +63,6 @@ dependencies: # %%ENABLE_XMR%% # cs_monero: 1.0.0-pre.3 # cs_monero_flutter_libs: 1.0.0-pre.0 -# monero_rpc: ^2.0.0 # %%END_ENABLE_XMR%% # %%ENABLE_SAL%% @@ -75,6 +74,8 @@ dependencies: # flutter_mwebd: ^0.0.1-pre.8 # %%END_ENABLE_MWEBD%% + monero_rpc: ^2.0.0 + # cs_monero compat (unpublished) compat: git: From ce22845a51f2c7f8feb26be43da6e9cdd8c01e6c Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 7 Oct 2025 09:39:05 -0600 Subject: [PATCH 17/30] add tor optino to app config --- lib/app_config.dart | 9 ++++----- scripts/app_config/configure_campfire.sh | 1 + scripts/app_config/configure_stack_duo.sh | 1 + scripts/app_config/configure_stack_wallet.sh | 1 + 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/app_config.dart b/lib/app_config.dart index 5f9b37ab77..3004413d2a 100644 --- a/lib/app_config.dart +++ b/lib/app_config.dart @@ -6,7 +6,7 @@ import 'wallets/crypto_currency/intermediate/frost_currency.dart'; part 'app_config.g.dart'; -enum AppFeature { themeSelection, buy, swap } +enum AppFeature { themeSelection, buy, swap, tor } abstract class AppConfig { static const appName = _prefix + _separator + suffix; @@ -77,10 +77,9 @@ abstract class AppConfig { static CryptoCurrency getCryptoCurrencyByPrettyName(final String prettyName) { // trocador hack const hackSplitter = " (Mainnet"; - final name = - prettyName.contains(hackSplitter) - ? prettyName.split(hackSplitter).first.toLowerCase() - : prettyName.replaceAll(" ", "").toLowerCase(); + final name = prettyName.contains(hackSplitter) + ? prettyName.split(hackSplitter).first.toLowerCase() + : prettyName.replaceAll(" ", "").toLowerCase(); try { return coins.firstWhere( diff --git a/scripts/app_config/configure_campfire.sh b/scripts/app_config/configure_campfire.sh index 8f25938488..c5e49296ac 100755 --- a/scripts/app_config/configure_campfire.sh +++ b/scripts/app_config/configure_campfire.sh @@ -61,6 +61,7 @@ const _shortDescriptionText = "Your privacy. Your wallet. Your Firo."; const _commitHash = "$BUILT_COMMIT_HASH"; const Set _features = { + AppFeature.tor, AppFeature.swap }; diff --git a/scripts/app_config/configure_stack_duo.sh b/scripts/app_config/configure_stack_duo.sh index 17de39bd3a..7a839ae3c0 100755 --- a/scripts/app_config/configure_stack_duo.sh +++ b/scripts/app_config/configure_stack_duo.sh @@ -59,6 +59,7 @@ const _commitHash = "$BUILT_COMMIT_HASH"; const Set _features = { AppFeature.themeSelection, AppFeature.buy, + AppFeature.tor, AppFeature.swap }; diff --git a/scripts/app_config/configure_stack_wallet.sh b/scripts/app_config/configure_stack_wallet.sh index b4f503d346..e976821c8e 100755 --- a/scripts/app_config/configure_stack_wallet.sh +++ b/scripts/app_config/configure_stack_wallet.sh @@ -71,6 +71,7 @@ const _commitHash = "$BUILT_COMMIT_HASH"; const Set _features = { AppFeature.themeSelection, AppFeature.buy, + AppFeature.tor, AppFeature.swap }; From 0e17f1e4107af9561079c1ebebdf6339901db7fc Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 7 Oct 2025 09:42:11 -0600 Subject: [PATCH 18/30] only build imported/used packages --- scripts/android/build_all_campfire.sh | 6 ------ scripts/android/build_all_duo.sh | 5 ----- scripts/ios/build_all_campfire.sh | 6 ------ scripts/ios/build_all_duo.sh | 4 ---- scripts/linux/build_all_campfire.sh | 6 ------ scripts/linux/build_all_duo.sh | 4 ---- scripts/macos/build_all_campfire.sh | 6 ------ scripts/macos/build_all_duo.sh | 4 ---- scripts/windows/build_all_campfire.sh | 6 ------ scripts/windows/build_all_duo.sh | 4 ---- 10 files changed, 51 deletions(-) diff --git a/scripts/android/build_all_campfire.sh b/scripts/android/build_all_campfire.sh index dc904e95dd..fd10e418fa 100755 --- a/scripts/android/build_all_campfire.sh +++ b/scripts/android/build_all_campfire.sh @@ -9,13 +9,7 @@ PLUGINS_DIR=../../crypto_plugins # libepiccash requires old rust source ../rust_version.sh -set_rust_version_for_libepiccash -(cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./build_all.sh ) -(cd "${PLUGINS_DIR}"/flutter_libmwc/scripts/android && ./build_all.sh ) -# set rust (back) to a more recent stable release after building epiccash set_rust_to_everything_else -(cd "${PLUGINS_DIR}"/frostdart/scripts/android && ./build_all.sh ) - wait echo "Done building" diff --git a/scripts/android/build_all_duo.sh b/scripts/android/build_all_duo.sh index 1a0d6058f9..dcfc24427a 100755 --- a/scripts/android/build_all_duo.sh +++ b/scripts/android/build_all_duo.sh @@ -9,12 +9,7 @@ mkdir -p build PLUGINS_DIR=../../crypto_plugins -# libepiccash requires old rust source ../rust_version.sh -set_rust_version_for_libepiccash -(cd "${PLUGINS_DIR}"/flutter_libepiccash/scripts/android && ./build_all.sh ) -(cd "${PLUGINS_DIR}"/flutter_libmwc/scripts/android && ./build_all.sh ) -# set rust (back) to a more recent stable release after building epiccash set_rust_to_everything_else (cd "${PLUGINS_DIR}"/frostdart/scripts/android && ./build_all.sh ) diff --git a/scripts/ios/build_all_campfire.sh b/scripts/ios/build_all_campfire.sh index 83177db5c2..994b682446 100755 --- a/scripts/ios/build_all_campfire.sh +++ b/scripts/ios/build_all_campfire.sh @@ -12,14 +12,8 @@ rustup target add x86_64-apple-ios # libepiccash requires old rust source ../rust_version.sh -set_rust_version_for_libepiccash -(cd ../../crypto_plugins/flutter_libepiccash/scripts/ios && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmwc/scripts/ios/ && ./build_all.sh ) -# set rust (back) to a more recent stable release after building epiccash set_rust_to_everything_else -(cd ../../crypto_plugins/frostdart/scripts/ios && ./build_all.sh ) - wait echo "Done building" diff --git a/scripts/ios/build_all_duo.sh b/scripts/ios/build_all_duo.sh index 0b560202b6..c09b3528fa 100755 --- a/scripts/ios/build_all_duo.sh +++ b/scripts/ios/build_all_duo.sh @@ -14,10 +14,6 @@ rustup target add x86_64-apple-ios # libepiccash requires old rust source ../rust_version.sh -set_rust_version_for_libepiccash -(cd ../../crypto_plugins/flutter_libepiccash/scripts/ios && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmwc/scripts/ios/ && ./build_all.sh ) -# set rust (back) to a more recent stable release after building epiccash set_rust_to_everything_else (cd ../../crypto_plugins/frostdart/scripts/ios && ./build_all.sh ) diff --git a/scripts/linux/build_all_campfire.sh b/scripts/linux/build_all_campfire.sh index 50490b1979..d1e1de71a1 100755 --- a/scripts/linux/build_all_campfire.sh +++ b/scripts/linux/build_all_campfire.sh @@ -11,14 +11,8 @@ mkdir -p build # libepiccash requires old rust source ../rust_version.sh -set_rust_version_for_libepiccash -(cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmwc/scripts/linux && ./build_all.sh ) -# set rust (back) to a more recent stable release after building epiccash set_rust_to_everything_else -(cd ../../crypto_plugins/frostdart/scripts/linux && ./build_all.sh ) - ./build_secp256k1.sh wait diff --git a/scripts/linux/build_all_duo.sh b/scripts/linux/build_all_duo.sh index a29947f4f6..3e2ee5b5b1 100755 --- a/scripts/linux/build_all_duo.sh +++ b/scripts/linux/build_all_duo.sh @@ -14,10 +14,6 @@ mkdir -p build # libepiccash requires old rust source ../rust_version.sh -set_rust_version_for_libepiccash -(cd ../../crypto_plugins/flutter_libepiccash/scripts/linux && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmwc/scripts/linux && ./build_all.sh ) -# set rust (back) to a more recent stable release after building epiccash set_rust_to_everything_else (cd ../../crypto_plugins/frostdart/scripts/linux && ./build_all.sh ) diff --git a/scripts/macos/build_all_campfire.sh b/scripts/macos/build_all_campfire.sh index 2bc4aaf4bb..e1b4216bc0 100755 --- a/scripts/macos/build_all_campfire.sh +++ b/scripts/macos/build_all_campfire.sh @@ -5,13 +5,7 @@ set -x -e # libepiccash requires old rust source ../rust_version.sh -set_rust_version_for_libepiccash -(cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmwc/scripts/macos && ./build_all.sh ) -# set rust (back) to a more recent stable release after building epiccash set_rust_to_everything_else -(cd ../../crypto_plugins/frostdart/scripts/macos && ./build_all.sh ) - wait echo "Done building" diff --git a/scripts/macos/build_all_duo.sh b/scripts/macos/build_all_duo.sh index 29a0d82420..a618eeebb7 100755 --- a/scripts/macos/build_all_duo.sh +++ b/scripts/macos/build_all_duo.sh @@ -7,10 +7,6 @@ set -x -e # libepiccash requires old rust source ../rust_version.sh -set_rust_version_for_libepiccash -(cd ../../crypto_plugins/flutter_libepiccash/scripts/macos && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmwc/scripts/macos && ./build_all.sh ) -# set rust (back) to a more recent stable release after building epiccash set_rust_to_everything_else (cd ../../crypto_plugins/frostdart/scripts/macos && ./build_all.sh ) diff --git a/scripts/windows/build_all_campfire.sh b/scripts/windows/build_all_campfire.sh index 6d7395bbf3..e74572b457 100755 --- a/scripts/windows/build_all_campfire.sh +++ b/scripts/windows/build_all_campfire.sh @@ -6,14 +6,8 @@ mkdir -p build # libepiccash requires old rust source ../rust_version.sh -set_rust_version_for_libepiccash -(cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmwc/scripts/windows && ./build_all.sh ) -# set rust (back) to a more recent stable release after building epiccash set_rust_to_everything_else -(cd ../../crypto_plugins/frostdart/scripts/windows && ./build_all.sh ) - ./build_secp256k1_wsl.sh wait diff --git a/scripts/windows/build_all_duo.sh b/scripts/windows/build_all_duo.sh index 6a19b94f52..42ff340c37 100755 --- a/scripts/windows/build_all_duo.sh +++ b/scripts/windows/build_all_duo.sh @@ -8,10 +8,6 @@ mkdir -p build # libepiccash requires old rust source ../rust_version.sh -set_rust_version_for_libepiccash -(cd ../../crypto_plugins/flutter_libepiccash/scripts/windows && ./build_all.sh ) -(cd ../../crypto_plugins/flutter_libmwc/scripts/windows && ./build_all.sh ) -# set rust (back) to a more recent stable release after building epiccash set_rust_to_everything_else (cd ../../crypto_plugins/frostdart/scripts/windows && ./build_all.sh ) From 3664871ddf1be60e0de04164b0ac2b4cefea5871 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 7 Oct 2025 12:03:14 -0600 Subject: [PATCH 19/30] tor ffi package optional import config --- lib/electrumx_rpc/electrumx_client.dart | 110 ++-- lib/main.dart | 23 +- lib/pages/buy_view/buy_view.dart | 33 +- lib/pages/home_view/home_view.dart | 184 +++--- lib/pages/ordinals/ordinal_details_view.dart | 62 +- lib/pages/paynym/subwidgets/paynym_bot.dart | 10 +- .../tor_settings/tor_settings_view.dart | 53 +- .../wallet_network_settings_view.dart | 585 ++++++++--------- lib/pages/wallet_view/wallet_view.dart | 426 ++++++------- .../desktop_buy/desktop_buy_view.dart | 46 +- lib/pages_desktop_specific/desktop_menu.dart | 92 ++- .../sub_widgets/desktop_wallet_features.dart | 226 ++++--- .../desktop_ordinal_details_view.dart | 72 +-- lib/route_generator.dart | 601 ++++++++---------- lib/services/buy/simplex/simplex_api.dart | 122 ++-- lib/services/coins/tezos/api/tezos_api.dart | 41 +- .../coins/tezos/api/tezos_rpc_api.dart | 14 +- lib/services/ethereum/ethereum_api.dart | 73 ++- .../exchange/change_now/change_now_api.dart | 26 +- .../exchange/nanswap/nanswap_api.dart | 19 +- .../exchange/simpleswap/simpleswap_api.dart | 153 ++--- .../exchange/trocador/trocador_api.dart | 15 +- lib/services/fusion_tor_service.dart | 55 +- lib/services/litescribe_api.dart | 5 +- lib/services/monkey_service.dart | 15 +- lib/services/nano_api.dart | 22 +- lib/services/price.dart | 25 +- lib/services/tor_service.dart | 115 +--- lib/themes/theme_service.dart | 54 +- .../electrum_connection_check.dart | 39 +- lib/utilities/paynym_is_api.dart | 37 +- lib/utilities/test_epic_box_connection.dart | 9 +- lib/utilities/test_mwcmqs_connection.dart | 10 +- lib/utilities/test_node_connection.dart | 22 +- .../test_stellar_node_connection.dart | 5 +- lib/wallets/api/tezos/tezos_api.dart | 41 +- lib/wallets/api/tezos/tezos_rpc_api.dart | 14 +- lib/wallets/wallet/impl/cardano_wallet.dart | 86 ++- lib/wallets/wallet/impl/solana_wallet.dart | 35 +- lib/wallets/wallet/impl/stellar_wallet.dart | 3 +- lib/wallets/wallet/impl/tezos_wallet.dart | 26 +- .../intermediate/lib_monero_wallet.dart | 5 +- .../intermediate/lib_salvium_wallet.dart | 5 +- .../cash_fusion_interface.dart | 127 ++-- .../nano_interface.dart | 99 +-- .../TOR_tor_service_impl.template.dart | 185 ++++++ 46 files changed, 1961 insertions(+), 2064 deletions(-) create mode 100644 tool/wl_templates/TOR_tor_service_impl.template.dart diff --git a/lib/electrumx_rpc/electrumx_client.dart b/lib/electrumx_rpc/electrumx_client.dart index c0d497a9e2..2ef2791cbe 100644 --- a/lib/electrumx_rpc/electrumx_client.dart +++ b/lib/electrumx_rpc/electrumx_client.dart @@ -20,6 +20,7 @@ import 'package:event_bus/event_bus.dart'; import 'package:mutex/mutex.dart'; import 'package:stream_channel/stream_channel.dart'; +import '../app_config.dart'; import '../exceptions/electrumx/no_such_transaction.dart'; import '../models/electrumx_response/spark_models.dart'; import '../services/event_bus/events/global/tor_connection_status_changed_event.dart'; @@ -220,46 +221,47 @@ class ElectrumXClient { Future checkElectrumAdapter() async { ({InternetAddress host, int port})? proxyInfo; - // If we're supposed to use Tor... - if (_prefs.useTor) { - // But Tor isn't running... - if (_torService.status != TorConnectionStatus.connected) { - // And the killswitch isn't set... - if (!_prefs.torKillSwitch) { - // Then we'll just proceed and connect to ElectrumX through - // clearnet at the bottom of this function. - Logging.instance.w( - "Tor preference set but Tor is not enabled, killswitch not set," - " connecting to Electrum adapter through clearnet", - ); + if (AppConfig.hasFeature(AppFeature.tor)) { + // If we're supposed to use Tor... + if (_prefs.useTor) { + // But Tor isn't running... + if (_torService.status != TorConnectionStatus.connected) { + // And the killswitch isn't set... + if (!_prefs.torKillSwitch) { + // Then we'll just proceed and connect to ElectrumX through + // clearnet at the bottom of this function. + Logging.instance.w( + "Tor preference set but Tor is not enabled, killswitch not set," + " connecting to Electrum adapter through clearnet", + ); + } else { + // ... But if the killswitch is set, then we throw an exception. + throw Exception( + "Tor preference and killswitch set but Tor is not enabled, " + "not connecting to Electrum adapter", + ); + // TODO [prio=low]: Try to start Tor. + } } else { - // ... But if the killswitch is set, then we throw an exception. - throw Exception( - "Tor preference and killswitch set but Tor is not enabled, " - "not connecting to Electrum adapter", + // Get the proxy info from the TorService. + proxyInfo = _torService.getProxyInfo(); + } + + if (netType == TorPlainNetworkOption.clear) { + _electrumAdapterChannel = null; + await ClientManager.sharedInstance.remove( + cryptoCurrency: cryptoCurrency, ); - // TODO [prio=low]: Try to start Tor. } } else { - // Get the proxy info from the TorService. - proxyInfo = _torService.getProxyInfo(); - } - - if (netType == TorPlainNetworkOption.clear) { - _electrumAdapterChannel = null; - await ClientManager.sharedInstance.remove( - cryptoCurrency: cryptoCurrency, - ); - } - } else { - if (netType == TorPlainNetworkOption.tor) { - _electrumAdapterChannel = null; - await ClientManager.sharedInstance.remove( - cryptoCurrency: cryptoCurrency, - ); + if (netType == TorPlainNetworkOption.tor) { + _electrumAdapterChannel = null; + await ClientManager.sharedInstance.remove( + cryptoCurrency: cryptoCurrency, + ); + } } } - // If the current ElectrumAdapterClient is closed, create a new one. if (getElectrumAdapter() != null && getElectrumAdapter()!.peer.isClosed) { _electrumAdapterChannel = null; @@ -847,9 +849,9 @@ class ElectrumXClient { }) async { Logging.instance.d("attempting to fetch lelantus.getanonymityset..."); await checkElectrumAdapter(); - final Map response = await (getElectrumAdapter() - as FiroElectrumClient) - .getLelantusAnonymitySet(groupId: groupId, blockHash: blockhash); + final Map response = + await (getElectrumAdapter() as FiroElectrumClient) + .getLelantusAnonymitySet(groupId: groupId, blockHash: blockhash); Logging.instance.d("Fetching lelantus.getanonymityset finished"); return response; } @@ -900,8 +902,8 @@ class ElectrumXClient { Future getLelantusLatestCoinId({String? requestID}) async { Logging.instance.d("attempting to fetch lelantus.getlatestcoinid..."); await checkElectrumAdapter(); - final int response = - await (getElectrumAdapter() as FiroElectrumClient).getLatestCoinId(); + final int response = await (getElectrumAdapter() as FiroElectrumClient) + .getLatestCoinId(); Logging.instance.d("Fetching lelantus.getlatestcoinid finished"); return response; } @@ -929,12 +931,12 @@ class ElectrumXClient { try { final start = DateTime.now(); await checkElectrumAdapter(); - final Map response = await (getElectrumAdapter() - as FiroElectrumClient) - .getSparkAnonymitySet( - coinGroupId: coinGroupId, - startBlockHash: startBlockHash, - ); + final Map response = + await (getElectrumAdapter() as FiroElectrumClient) + .getSparkAnonymitySet( + coinGroupId: coinGroupId, + startBlockHash: startBlockHash, + ); Logging.instance.d( "Finished ElectrumXClient.getSparkAnonymitySet(coinGroupId" "=$coinGroupId, startBlockHash=$startBlockHash). " @@ -1025,9 +1027,8 @@ class ElectrumXClient { try { Logging.instance.d("attempting to fetch spark.getsparklatestcoinid..."); await checkElectrumAdapter(); - final int response = - await (getElectrumAdapter() as FiroElectrumClient) - .getSparkLatestCoinId(); + final int response = await (getElectrumAdapter() as FiroElectrumClient) + .getSparkLatestCoinId(); Logging.instance.d("Fetching spark.getsparklatestcoinid finished"); return response; } catch (e, s) { @@ -1045,10 +1046,9 @@ class ElectrumXClient { command: "spark.getmempoolsparktxids", ); - final txids = - List.from( - response as List, - ).map((e) => e.toHexReversedFromBase64).toSet(); + final txids = List.from( + response as List, + ).map((e) => e.toHexReversedFromBase64).toSet(); Logging.instance.d( "Finished ElectrumXClient.getMempoolTxids(). " @@ -1158,10 +1158,8 @@ class ElectrumXClient { return response .map( - (e) => ( - name: e["name"] as String, - address: e["address"] as String, - ), + (e) => + (name: e["name"] as String, address: e["address"] as String), ) .toList(); } else if (response["error"] != null) { diff --git a/lib/main.dart b/lib/main.dart index 481e398ca8..4de72171e0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -206,18 +206,19 @@ void main(List args) async { ); } - // TODO: - // This should be moved to happen during the loading animation instead of - // showing a blank screen for 4-10 seconds. - // Some refactoring will need to be done here to make sure we don't make any - // network calls before starting up tor - if (Prefs.instance.useTor) { - TorService.sharedInstance.init( - torDataDirPath: (await StackFileSystem.applicationTorDirectory()).path, - ); - await TorService.sharedInstance.start(); + if (AppConfig.hasFeature(AppFeature.tor)) { + // TODO: + // This should be moved to happen during the loading animation instead of + // showing a blank screen for 4-10 seconds. + // Some refactoring will need to be done here to make sure we don't make any + // network calls before starting up tor + if (Prefs.instance.useTor) { + TorService.sharedInstance.init( + torDataDirPath: (await StackFileSystem.applicationTorDirectory()).path, + ); + await TorService.sharedInstance.start(); + } } - await StackFileSystem.initThemesDir(); await FiroCacheCoordinator.init(); diff --git a/lib/pages/buy_view/buy_view.dart b/lib/pages/buy_view/buy_view.dart index 9589621cc9..552a8457ed 100644 --- a/lib/pages/buy_view/buy_view.dart +++ b/lib/pages/buy_view/buy_view.dart @@ -10,21 +10,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../app_config.dart'; import '../../models/isar/models/ethereum/eth_contract.dart'; -import 'buy_form.dart'; import '../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; import '../../services/tor_service.dart'; import '../../themes/stack_colors.dart'; import '../../wallets/crypto_currency/crypto_currency.dart'; import '../../widgets/stack_dialog.dart'; import '../../widgets/tor_subscription.dart'; +import 'buy_form.dart'; class BuyView extends ConsumerStatefulWidget { - const BuyView({ - super.key, - this.coin, - this.tokenContract, - }); + const BuyView({super.key, this.coin, this.tokenContract}); final CryptoCurrency? coin; final EthContract? tokenContract; @@ -46,8 +44,9 @@ class _BuyViewState extends ConsumerState { coin = widget.coin; tokenContract = widget.tokenContract; - torEnabled = - ref.read(pTorService).status != TorConnectionStatus.disconnected; + torEnabled = AppConfig.hasFeature(AppFeature.tor) + ? ref.read(pTorService).status != TorConnectionStatus.disconnected + : false; super.initState(); } @@ -66,23 +65,15 @@ class _BuyViewState extends ConsumerState { children: [ SafeArea( child: Padding( - padding: const EdgeInsets.only( - left: 16, - right: 16, - top: 16, - ), - child: BuyForm( - coin: coin, - tokenContract: tokenContract, - ), + padding: const EdgeInsets.only(left: 16, right: 16, top: 16), + child: BuyForm(coin: coin, tokenContract: tokenContract), ), ), if (torEnabled) Container( - color: Theme.of(context) - .extension()! - .overlay - .withOpacity(0.7), + color: Theme.of( + context, + ).extension()!.overlay.withOpacity(0.7), height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, child: const StackDialog( diff --git a/lib/pages/home_view/home_view.dart b/lib/pages/home_view/home_view.dart index 223c3d9c14..9c5af137cf 100644 --- a/lib/pages/home_view/home_view.dart +++ b/lib/pages/home_view/home_view.dart @@ -80,17 +80,16 @@ class _HomeViewState extends ConsumerState { context, RouteGenerator.getRoute( shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute, - builder: - (_) => const LockscreenView( - showBackButton: false, - popOnSuccess: true, - routeOnSuccessArguments: true, - routeOnSuccess: "", - biometricsCancelButtonString: "CANCEL", - biometricsLocalizedReason: - "Authenticate to unlock ${AppConfig.appName}", - biometricsAuthenticationTitle: "Unlock ${AppConfig.appName}", - ), + builder: (_) => const LockscreenView( + showBackButton: false, + popOnSuccess: true, + routeOnSuccessArguments: true, + routeOnSuccess: "", + biometricsCancelButtonString: "CANCEL", + biometricsLocalizedReason: + "Authenticate to unlock ${AppConfig.appName}", + biometricsAuthenticationTitle: "Unlock ${AppConfig.appName}", + ), settings: const RouteSettings(name: "/unlockTimedOutAppScreen"), ), ); @@ -136,14 +135,13 @@ class _HomeViewState extends ConsumerState { await showDialog( context: context, barrierDismissible: false, - builder: - (_) => WillPopScope( - onWillPop: () async { - _exitEnabled = true; - return true; - }, - child: const StackDialog(title: "Tap back again to exit"), - ), + builder: (_) => WillPopScope( + onWillPop: () async { + _exitEnabled = true; + return true; + }, + child: const StackDialog(title: "Tap back again to exit"), + ), ).timeout( timeout, onTimeout: () { @@ -300,8 +298,9 @@ class _HomeViewState extends ConsumerState { key: _key, appBar: AppBar( automaticallyImplyLeading: false, - backgroundColor: - Theme.of(context).extension()!.backgroundAppBar, + backgroundColor: Theme.of( + context, + ).extension()!.backgroundAppBar, title: Row( children: [ GestureDetector( @@ -321,10 +320,11 @@ class _HomeViewState extends ConsumerState { ], ), actions: [ - const Padding( - padding: EdgeInsets.only(top: 10, bottom: 10, right: 10), - child: AspectRatio(aspectRatio: 1, child: SmallTorIcon()), - ), + if (AppConfig.hasFeature(AppFeature.tor)) + const Padding( + padding: EdgeInsets.only(top: 10, bottom: 10, right: 10), + child: AspectRatio(aspectRatio: 1, child: SmallTorIcon()), + ), Padding( padding: const EdgeInsets.only(top: 10, bottom: 10, right: 10), child: AspectRatio( @@ -335,54 +335,51 @@ class _HomeViewState extends ConsumerState { key: const Key("walletsViewAlertsButton"), size: 36, shadows: const [], - color: - Theme.of( - context, - ).extension()!.backgroundAppBar, + color: Theme.of( + context, + ).extension()!.backgroundAppBar, icon: ref.watch( - notificationsProvider.select( - (value) => value.hasUnreadNotifications, - ), - ) - ? SvgPicture.file( - File( - ref.watch( - themeProvider.select( - (value) => value.assets.bellNew, - ), + notificationsProvider.select( + (value) => value.hasUnreadNotifications, + ), + ) + ? SvgPicture.file( + File( + ref.watch( + themeProvider.select( + (value) => value.assets.bellNew, ), ), - width: 20, - height: 20, - color: - ref.watch( - notificationsProvider.select( - (value) => - value.hasUnreadNotifications, - ), - ) - ? null - : Theme.of(context) - .extension()! - .topNavIconPrimary, - ) - : SvgPicture.asset( - Assets.svg.bell, - width: 20, - height: 20, - color: - ref.watch( - notificationsProvider.select( - (value) => - value.hasUnreadNotifications, - ), - ) - ? null - : Theme.of(context) - .extension()! - .topNavIconPrimary, ), + width: 20, + height: 20, + color: + ref.watch( + notificationsProvider.select( + (value) => value.hasUnreadNotifications, + ), + ) + ? null + : Theme.of( + context, + ).extension()!.topNavIconPrimary, + ) + : SvgPicture.asset( + Assets.svg.bell, + width: 20, + height: 20, + color: + ref.watch( + notificationsProvider.select( + (value) => value.hasUnreadNotifications, + ), + ) + ? null + : Theme.of( + context, + ).extension()!.topNavIconPrimary, + ), onPressed: () { // reset unread state ref.refresh(unreadNotificationsStateProvider); @@ -390,10 +387,9 @@ class _HomeViewState extends ConsumerState { Navigator.of( context, ).pushNamed(NotificationsView.routeName).then((_) { - final Set unreadNotificationIds = - ref - .read(unreadNotificationsStateProvider.state) - .state; + final Set unreadNotificationIds = ref + .read(unreadNotificationsStateProvider.state) + .state; if (unreadNotificationIds.isEmpty) return; final List> futures = []; @@ -433,16 +429,14 @@ class _HomeViewState extends ConsumerState { key: const Key("walletsViewSettingsButton"), size: 36, shadows: const [], - color: - Theme.of( - context, - ).extension()!.backgroundAppBar, + color: Theme.of( + context, + ).extension()!.backgroundAppBar, icon: SvgPicture.asset( Assets.svg.gear, - color: - Theme.of( - context, - ).extension()!.topNavIconPrimary, + color: Theme.of( + context, + ).extension()!.topNavIconPrimary, width: 20, height: 20, ), @@ -465,21 +459,20 @@ class _HomeViewState extends ConsumerState { ref.watch(prefsChangeNotifierProvider).enableExchange) Container( decoration: BoxDecoration( - color: - Theme.of( - context, - ).extension()!.backgroundAppBar, + color: Theme.of( + context, + ).extension()!.backgroundAppBar, boxShadow: Theme.of(context) - .extension()! - .homeViewButtonBarBoxShadow != - null - ? [ - Theme.of(context) - .extension()! - .homeViewButtonBarBoxShadow!, - ] - : null, + .extension()! + .homeViewButtonBarBoxShadow != + null + ? [ + Theme.of(context) + .extension()! + .homeViewButtonBarBoxShadow!, + ] + : null, ), child: const Padding( padding: EdgeInsets.only( @@ -516,8 +509,9 @@ class _HomeViewState extends ConsumerState { onPageChanged: (pageIndex) { if (!_lock) { ref - .read(homeViewPageIndexStateProvider.state) - .state = pageIndex; + .read(homeViewPageIndexStateProvider.state) + .state = + pageIndex; } }, ); diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart index 05d7eb227c..996f20db67 100644 --- a/lib/pages/ordinals/ordinal_details_view.dart +++ b/lib/pages/ordinals/ordinal_details_view.dart @@ -8,6 +8,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; +import '../../app_config.dart'; import '../../models/isar/models/blockchain_data/utxo.dart'; import '../../models/isar/ordinal.dart'; import '../../networking/http.dart'; @@ -64,8 +65,9 @@ class _OrdinalDetailsViewState extends ConsumerState { child: Scaffold( backgroundColor: Theme.of(context).extension()!.background, appBar: AppBar( - backgroundColor: - Theme.of(context).extension()!.background, + backgroundColor: Theme.of( + context, + ).extension()!.background, leading: const AppBarBackButton(), title: Text( "Ordinal details", @@ -104,17 +106,16 @@ class _OrdinalDetailsViewState extends ConsumerState { const SizedBox(height: _spacing), _DetailsItemWCopy( title: "Amount", - data: - utxo == null - ? "ERROR" - : ref - .watch(pAmountFormatter(coin)) - .format( - Amount( - rawValue: BigInt.from(utxo!.value), - fractionDigits: coin.fractionDigits, - ), + data: utxo == null + ? "ERROR" + : ref + .watch(pAmountFormatter(coin)) + .format( + Amount( + rawValue: BigInt.from(utxo!.value), + fractionDigits: coin.fractionDigits, ), + ), ), const SizedBox(height: _spacing), _DetailsItemWCopy( @@ -170,20 +171,18 @@ class _DetailsItemWCopy extends StatelessWidget { children: [ SvgPicture.asset( Assets.svg.copy, - color: - Theme.of( - context, - ).extension()!.infoItemIcons, + color: Theme.of( + context, + ).extension()!.infoItemIcons, width: 12, ), const SizedBox(width: 6), Text( "Copy", style: STextStyles.infoSmall(context).copyWith( - color: - Theme.of( - context, - ).extension()!.infoItemIcons, + color: Theme.of( + context, + ).extension()!.infoItemIcons, ), ), ], @@ -216,10 +215,11 @@ class _OrdinalImageGroup extends ConsumerWidget { final response = await client.get( url: Uri.parse(ordinal.content), - proxyInfo: - ref.read(prefsChangeNotifierProvider).useTor - ? ref.read(pTorService).getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : ref.read(prefsChangeNotifierProvider).useTor + ? ref.read(pTorService).getProxyInfo() + : null, ); if (response.code != 200) { @@ -230,10 +230,9 @@ class _OrdinalImageGroup extends ConsumerWidget { final bytes = response.bodyBytes; - final dir = - Platform.isAndroid - ? await StackFileSystem.wtfAndroidDocumentsPath() - : await getApplicationDocumentsDirectory(); + final dir = Platform.isAndroid + ? await StackFileSystem.wtfAndroidDocumentsPath() + : await getApplicationDocumentsDirectory(); final filePath = path.join( dir.path, "ordinal_${ordinal.inscriptionNumber}.png", @@ -289,10 +288,9 @@ class _OrdinalImageGroup extends ConsumerWidget { Assets.svg.arrowDown, width: 10, height: 12, - color: - Theme.of( - context, - ).extension()!.buttonTextSecondary, + color: Theme.of( + context, + ).extension()!.buttonTextSecondary, ), buttonHeight: ButtonHeight.l, iconSpacing: 4, diff --git a/lib/pages/paynym/subwidgets/paynym_bot.dart b/lib/pages/paynym/subwidgets/paynym_bot.dart index 384e471e53..3307665a40 100644 --- a/lib/pages/paynym/subwidgets/paynym_bot.dart +++ b/lib/pages/paynym/subwidgets/paynym_bot.dart @@ -12,6 +12,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; +import '../../../app_config.dart'; import '../../../networking/http.dart'; import '../../../services/tor_service.dart'; import '../../../utilities/paynym_is_api.dart'; @@ -52,12 +53,15 @@ class PayNymBot extends StatelessWidget { Future _fetchImage() async { final HTTP client = HTTP(); - final Uri uri = - Uri.parse("${PaynymIsApi.baseURL}/$paymentCodeString/avatar"); + final Uri uri = Uri.parse( + "${PaynymIsApi.baseURL}/$paymentCodeString/avatar", + ); final response = await client.get( url: uri, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); diff --git a/lib/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart b/lib/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart index 24800936d0..ea8461b0fc 100644 --- a/lib/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart +++ b/lib/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart @@ -50,8 +50,9 @@ class _TorSettingsViewState extends ConsumerState { backgroundColor: Colors.transparent, appBar: AppBar( automaticallyImplyLeading: false, - backgroundColor: - Theme.of(context).extension()!.backgroundAppBar, + backgroundColor: Theme.of( + context, + ).extension()!.backgroundAppBar, leading: AppBarBackButton( onPressed: () { Navigator.of(context).pop(); @@ -98,12 +99,12 @@ class _TorSettingsViewState extends ConsumerState { children: [ Padding( padding: EdgeInsets.all(10.0), - child: TorAnimatedButton(), + child: _TorAnimatedButton(), ), ], ), const SizedBox(height: 30), - const TorButton(), + const _TorButton(), const SizedBox(height: 8), RoundedWhiteContainer( child: Consumer( @@ -144,8 +145,9 @@ class _TorSettingsViewState extends ConsumerState { " connection is disrupted or compromised.", rightButton: SecondaryButton( label: "Close", - onPressed: - Navigator.of(context).pop, + onPressed: Navigator.of( + context, + ).pop, ), ); }, @@ -155,10 +157,9 @@ class _TorSettingsViewState extends ConsumerState { Assets.svg.circleInfo, height: 16, width: 16, - color: - Theme.of(context) - .extension()! - .infoItemLabel, + color: Theme.of( + context, + ).extension()!.infoItemLabel, ), ), ], @@ -174,8 +175,9 @@ class _TorSettingsViewState extends ConsumerState { ), onValueChanged: (newValue) { ref - .read(prefsChangeNotifierProvider) - .torKillSwitch = newValue; + .read(prefsChangeNotifierProvider) + .torKillSwitch = + newValue; }, ), ), @@ -195,14 +197,14 @@ class _TorSettingsViewState extends ConsumerState { } } -class TorAnimatedButton extends ConsumerStatefulWidget { - const TorAnimatedButton({super.key}); +class _TorAnimatedButton extends ConsumerStatefulWidget { + const _TorAnimatedButton({super.key}); @override - ConsumerState createState() => _TorAnimatedButtonState(); + ConsumerState<_TorAnimatedButton> createState() => _TorAnimatedButtonState(); } -class _TorAnimatedButtonState extends ConsumerState +class _TorAnimatedButtonState extends ConsumerState<_TorAnimatedButton> with SingleTickerProviderStateMixin { late final AnimationController controller1; @@ -224,7 +226,6 @@ class _TorAnimatedButtonState extends ConsumerState case TorConnectionStatus.connected: await disconnectTor(ref, context); - break; case TorConnectionStatus.connecting: @@ -365,7 +366,7 @@ class _TorAnimatedButtonState extends ConsumerState }, ), ), - const UpperCaseTorText(), + const _UpperCaseTorText(), ], ), ), @@ -373,14 +374,14 @@ class _TorAnimatedButtonState extends ConsumerState } } -class TorButton extends ConsumerStatefulWidget { - const TorButton({super.key}); +class _TorButton extends ConsumerStatefulWidget { + const _TorButton({super.key}); @override - ConsumerState createState() => _TorButtonState(); + ConsumerState<_TorButton> createState() => _TorButtonState(); } -class _TorButtonState extends ConsumerState { +class _TorButtonState extends ConsumerState<_TorButton> { late TorConnectionStatus _status; Color _color(TorConnectionStatus status, StackColors colors) { @@ -483,14 +484,14 @@ class _TorButtonState extends ConsumerState { } } -class UpperCaseTorText extends ConsumerStatefulWidget { - const UpperCaseTorText({super.key}); +class _UpperCaseTorText extends ConsumerStatefulWidget { + const _UpperCaseTorText({super.key}); @override - ConsumerState createState() => _UpperCaseTorTextState(); + ConsumerState<_UpperCaseTorText> createState() => _UpperCaseTorTextState(); } -class _UpperCaseTorTextState extends ConsumerState { +class _UpperCaseTorTextState extends ConsumerState<_UpperCaseTorText> { late TorConnectionStatus _status; Color _color(TorConnectionStatus status, StackColors colors) { diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart index 75b121ff36..2e3ffbb5af 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart @@ -18,6 +18,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:tuple/tuple.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; +import '../../../../app_config.dart'; import '../../../../providers/providers.dart'; import '../../../../route_generator.dart'; import '../../../../services/event_bus/events/global/blocks_remaining_event.dart'; @@ -158,31 +159,26 @@ class _WalletNetworkSettingsViewState context: context, useSafeArea: false, barrierDismissible: true, - builder: - (context) => ConditionalParent( - condition: isDesktop, - builder: - (child) => DesktopDialog( - maxHeight: 150, - maxWidth: 500, - child: child, - ), - child: StackDialog( - title: "Rescan completed", - rightButton: TextButton( - style: Theme.of(context) - .extension()! - .getSecondaryEnabledButtonStyle(context), - child: Text( - "Ok", - style: STextStyles.itemSubtitle12(context), - ), - onPressed: () { - Navigator.of(context, rootNavigator: isDesktop).pop(); - }, - ), + builder: (context) => ConditionalParent( + condition: isDesktop, + builder: (child) => + DesktopDialog(maxHeight: 150, maxWidth: 500, child: child), + child: StackDialog( + title: "Rescan completed", + rightButton: TextButton( + style: Theme.of(context) + .extension()! + .getSecondaryEnabledButtonStyle(context), + child: Text( + "Ok", + style: STextStyles.itemSubtitle12(context), ), + onPressed: () { + Navigator.of(context, rootNavigator: isDesktop).pop(); + }, ), + ), + ), ); } } catch (e) { @@ -197,23 +193,19 @@ class _WalletNetworkSettingsViewState context: context, useSafeArea: false, barrierDismissible: true, - builder: - (context) => StackDialog( - title: "Rescan failed", - message: e.toString(), - rightButton: TextButton( - style: Theme.of(context) - .extension()! - .getSecondaryEnabledButtonStyle(context), - child: Text( - "Ok", - style: STextStyles.itemSubtitle12(context), - ), - onPressed: () { - Navigator.of(context, rootNavigator: isDesktop).pop(); - }, - ), - ), + builder: (context) => StackDialog( + title: "Rescan failed", + message: e.toString(), + rightButton: TextButton( + style: Theme.of(context) + .extension()! + .getSecondaryEnabledButtonStyle(context), + child: Text("Ok", style: STextStyles.itemSubtitle12(context)), + onPressed: () { + Navigator.of(context, rootNavigator: isDesktop).pop(); + }, + ), + ), ); } } @@ -249,8 +241,9 @@ class _WalletNetworkSettingsViewState _blocksRemaining = -1; } - eventBus = - widget.eventBus != null ? widget.eventBus! : GlobalEventBus.instance; + eventBus = widget.eventBus != null + ? widget.eventBus! + : GlobalEventBus.instance; _syncStatusSubscription = eventBus .on() @@ -334,10 +327,9 @@ class _WalletNetworkSettingsViewState Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; - final progressLength = - isDesktop - ? 430.0 - : screenWidth - (_padding * 2) - (_boxPadding * 3) - _iconSize; + final progressLength = isDesktop + ? 430.0 + : screenWidth - (_padding * 2) - (_boxPadding * 3) - _iconSize; final coin = ref.watch(pWalletCoin(widget.walletId)); @@ -384,8 +376,9 @@ class _WalletNetworkSettingsViewState builder: (child) { return Background( child: Scaffold( - backgroundColor: - Theme.of(context).extension()!.background, + backgroundColor: Theme.of( + context, + ).extension()!.background, appBar: AppBar( leading: AppBarBackButton( onPressed: () { @@ -413,16 +406,14 @@ class _WalletNetworkSettingsViewState ), size: 36, shadows: const [], - color: - Theme.of( - context, - ).extension()!.background, + color: Theme.of( + context, + ).extension()!.background, icon: SvgPicture.asset( Assets.svg.verticalEllipsis, - color: - Theme.of( - context, - ).extension()!.accentColorDark, + color: Theme.of( + context, + ).extension()!.accentColorDark, width: 20, height: 20, ), @@ -439,10 +430,9 @@ class _WalletNetworkSettingsViewState right: 10, child: Container( decoration: BoxDecoration( - color: - Theme.of( - context, - ).extension()!.popupBG, + color: Theme.of( + context, + ).extension()!.popupBG, borderRadius: BorderRadius.circular( Constants.size.circularBorderRadius, ), @@ -520,10 +510,9 @@ class _WalletNetworkSettingsViewState Text( "Blockchain status", textAlign: TextAlign.left, - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall(context) - : STextStyles.smallMed12(context), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context) + : STextStyles.smallMed12(context), ), CustomTextButton( text: "Resync", @@ -536,14 +525,12 @@ class _WalletNetworkSettingsViewState SizedBox(height: isDesktop ? 12 : 9), if (_currentSyncStatus == WalletSyncStatus.synced) RoundedWhiteContainer( - borderColor: - isDesktop - ? Theme.of(context).extension()!.background - : null, - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + borderColor: isDesktop + ? Theme.of(context).extension()!.background + : null, + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Row( children: [ Container( @@ -561,10 +548,9 @@ class _WalletNetworkSettingsViewState Assets.svg.radio, height: isDesktop ? 19 : 14, width: isDesktop ? 19 : 14, - color: - Theme.of( - context, - ).extension()!.accentColorGreen, + color: Theme.of( + context, + ).extension()!.accentColorGreen, ), ), ), @@ -587,14 +573,12 @@ class _WalletNetworkSettingsViewState ProgressBar( width: progressLength, height: 5, - fillColor: - Theme.of( - context, - ).extension()!.accentColorGreen, - backgroundColor: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + fillColor: Theme.of( + context, + ).extension()!.accentColorGreen, + backgroundColor: Theme.of( + context, + ).extension()!.textFieldDefaultBG, percent: 1, ), ], @@ -604,14 +588,12 @@ class _WalletNetworkSettingsViewState ), if (_currentSyncStatus == WalletSyncStatus.syncing) RoundedWhiteContainer( - borderColor: - isDesktop - ? Theme.of(context).extension()!.background - : null, - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + borderColor: isDesktop + ? Theme.of(context).extension()!.background + : null, + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Row( children: [ Container( @@ -629,10 +611,9 @@ class _WalletNetworkSettingsViewState Assets.svg.radioSyncing, height: 14, width: 14, - color: - Theme.of( - context, - ).extension()!.accentColorYellow, + color: Theme.of( + context, + ).extension()!.accentColorYellow, ), ), ), @@ -657,14 +638,12 @@ class _WalletNetworkSettingsViewState children: [ Text( _percentString(_percent), - style: STextStyles.syncPercent( - context, - ).copyWith( - color: - Theme.of(context) + style: STextStyles.syncPercent(context) + .copyWith( + color: Theme.of(context) .extension()! .accentColorYellow, - ), + ), ), if (coin is Monero || coin is Wownero || @@ -679,14 +658,12 @@ class _WalletNetworkSettingsViewState ))) Text( " (Blocks to go: ${_blocksRemaining == -1 ? "?" : _blocksRemaining})", - style: STextStyles.syncPercent( - context, - ).copyWith( - color: - Theme.of(context) + style: STextStyles.syncPercent(context) + .copyWith( + color: Theme.of(context) .extension()! .accentColorYellow, - ), + ), ), ], ), @@ -697,14 +674,12 @@ class _WalletNetworkSettingsViewState ProgressBar( width: progressLength, height: 5, - fillColor: - Theme.of( - context, - ).extension()!.accentColorYellow, - backgroundColor: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + fillColor: Theme.of( + context, + ).extension()!.accentColorYellow, + backgroundColor: Theme.of( + context, + ).extension()!.textFieldDefaultBG, percent: _percent, ), ], @@ -714,14 +689,12 @@ class _WalletNetworkSettingsViewState ), if (_currentSyncStatus == WalletSyncStatus.unableToSync) RoundedWhiteContainer( - borderColor: - isDesktop - ? Theme.of(context).extension()!.background - : null, - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + borderColor: isDesktop + ? Theme.of(context).extension()!.background + : null, + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Row( children: [ Container( @@ -739,10 +712,9 @@ class _WalletNetworkSettingsViewState Assets.svg.radioProblem, height: 14, width: 14, - color: - Theme.of( - context, - ).extension()!.accentColorRed, + color: Theme.of( + context, + ).extension()!.accentColorRed, ), ), ), @@ -757,19 +729,17 @@ class _WalletNetworkSettingsViewState Text( "Unable to synchronize", style: STextStyles.w600_12(context).copyWith( - color: - Theme.of( - context, - ).extension()!.accentColorRed, + color: Theme.of( + context, + ).extension()!.accentColorRed, ), ), Text( "0%", style: STextStyles.syncPercent(context).copyWith( - color: - Theme.of( - context, - ).extension()!.accentColorRed, + color: Theme.of( + context, + ).extension()!.accentColorRed, ), ), ], @@ -779,14 +749,12 @@ class _WalletNetworkSettingsViewState ProgressBar( width: progressLength, height: 5, - fillColor: - Theme.of( - context, - ).extension()!.accentColorRed, - backgroundColor: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + fillColor: Theme.of( + context, + ).extension()!.accentColorRed, + backgroundColor: Theme.of( + context, + ).extension()!.textFieldDefaultBG, percent: 0, ), ], @@ -798,39 +766,36 @@ class _WalletNetworkSettingsViewState Padding( padding: const EdgeInsets.only(top: 12), child: RoundedContainer( - color: - Theme.of( - context, - ).extension()!.warningBackground, + color: Theme.of( + context, + ).extension()!.warningBackground, child: Text( "Please check your internet connection and make sure your current node is not having issues.", style: STextStyles.baseXS(context).copyWith( - color: - Theme.of( - context, - ).extension()!.warningForeground, + color: Theme.of( + context, + ).extension()!.warningForeground, ), ), ), ), SizedBox(height: isDesktop ? 12 : 9), RoundedWhiteContainer( - borderColor: - isDesktop - ? Theme.of(context).extension()!.background - : null, - padding: - isDesktop ? const EdgeInsets.all(16) : const EdgeInsets.all(12), + borderColor: isDesktop + ? Theme.of(context).extension()!.background + : null, + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Current height", textAlign: TextAlign.left, - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall(context) - : STextStyles.smallMed12(context), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context) + : STextStyles.smallMed12(context), ), Text( ref.watch(pWalletChainHeight(widget.walletId)).toString(), @@ -839,140 +804,140 @@ class _WalletNetworkSettingsViewState ], ), ), - SizedBox(height: isDesktop ? 32 : 20), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Tor status", - textAlign: TextAlign.left, - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall(context) - : STextStyles.smallMed12(context), - ), - CustomTextButton( - text: - ref.watch( - prefsChangeNotifierProvider.select( - (value) => value.useTor, - ), - ) - ? "Disconnect" - : "Connect", - onTap: onTorTapped, - ), - ], - ), - SizedBox(height: isDesktop ? 12 : 9), - RoundedWhiteContainer( - borderColor: - isDesktop - ? Theme.of(context).extension()!.background - : null, - padding: - isDesktop ? const EdgeInsets.all(16) : const EdgeInsets.all(12), - child: Row( + if (AppConfig.hasFeature(AppFeature.tor)) + SizedBox(height: isDesktop ? 32 : 20), + if (AppConfig.hasFeature(AppFeature.tor)) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - if (ref.watch( - prefsChangeNotifierProvider.select((value) => value.useTor), - )) - Container( - width: _iconSize, - height: _iconSize, - decoration: BoxDecoration( - color: Theme.of(context) - .extension()! - .accentColorGreen - .withOpacity(0.2), - borderRadius: BorderRadius.circular(_iconSize), - ), - child: Center( - child: SvgPicture.asset( - Assets.svg.tor, - height: isDesktop ? 19 : 14, - width: isDesktop ? 19 : 14, - color: - Theme.of( - context, - ).extension()!.accentColorGreen, + Text( + "Tor status", + textAlign: TextAlign.left, + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context) + : STextStyles.smallMed12(context), + ), + CustomTextButton( + text: + ref.watch( + prefsChangeNotifierProvider.select( + (value) => value.useTor, + ), + ) + ? "Disconnect" + : "Connect", + onTap: onTorTapped, + ), + ], + ), + if (AppConfig.hasFeature(AppFeature.tor)) + SizedBox(height: isDesktop ? 12 : 9), + if (AppConfig.hasFeature(AppFeature.tor)) + RoundedWhiteContainer( + borderColor: isDesktop + ? Theme.of(context).extension()!.background + : null, + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), + child: Row( + children: [ + if (ref.watch( + prefsChangeNotifierProvider.select((value) => value.useTor), + )) + Container( + width: _iconSize, + height: _iconSize, + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .accentColorGreen + .withOpacity(0.2), + borderRadius: BorderRadius.circular(_iconSize), ), - ), - ), - if (!ref.watch( - prefsChangeNotifierProvider.select((value) => value.useTor), - )) - Container( - width: _iconSize, - height: _iconSize, - decoration: BoxDecoration( - color: Theme.of( - context, - ).extension()!.textDark.withOpacity(0.08), - borderRadius: BorderRadius.circular(_iconSize), - ), - child: Center( - child: SvgPicture.asset( - Assets.svg.tor, - height: isDesktop ? 19 : 14, - width: isDesktop ? 19 : 14, - color: - Theme.of( - context, - ).extension()!.textDark, + child: Center( + child: SvgPicture.asset( + Assets.svg.tor, + height: isDesktop ? 19 : 14, + width: isDesktop ? 19 : 14, + color: Theme.of( + context, + ).extension()!.accentColorGreen, + ), ), ), - ), - SizedBox(width: _boxPadding), - TorSubscription( - onTorStatusChanged: (status) { - setState(() { - _torConnectionStatus = status; - }); - }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Tor status", - style: STextStyles.desktopTextExtraExtraSmall( + if (!ref.watch( + prefsChangeNotifierProvider.select((value) => value.useTor), + )) + Container( + width: _iconSize, + height: _iconSize, + decoration: BoxDecoration( + color: Theme.of( context, - ).copyWith( - color: - Theme.of( - context, - ).extension()!.textDark, - ), + ).extension()!.textDark.withOpacity(0.08), + borderRadius: BorderRadius.circular(_iconSize), ), - if (_torConnectionStatus == TorConnectionStatus.connected) - Text( - "Connected", - style: STextStyles.desktopTextExtraExtraSmall( + child: Center( + child: SvgPicture.asset( + Assets.svg.tor, + height: isDesktop ? 19 : 14, + width: isDesktop ? 19 : 14, + color: Theme.of( context, - ), + ).extension()!.textDark, ), - if (_torConnectionStatus == - TorConnectionStatus.connecting) + ), + ), + SizedBox(width: _boxPadding), + TorSubscription( + onTorStatusChanged: (status) { + setState(() { + _torConnectionStatus = status; + }); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ Text( - "Connecting...", - style: STextStyles.desktopTextExtraExtraSmall( - context, - ), + "Tor status", + style: STextStyles.desktopTextExtraExtraSmall(context) + .copyWith( + color: Theme.of( + context, + ).extension()!.textDark, + ), ), - if (_torConnectionStatus == - TorConnectionStatus.disconnected) - Text( - "Disconnected", - style: STextStyles.desktopTextExtraExtraSmall( - context, + if (_torConnectionStatus == + TorConnectionStatus.connected) + Text( + "Connected", + style: STextStyles.desktopTextExtraExtraSmall( + context, + ), ), - ), - ], + if (_torConnectionStatus == + TorConnectionStatus.connecting) + Text( + "Connecting...", + style: STextStyles.desktopTextExtraExtraSmall( + context, + ), + ), + if (_torConnectionStatus == + TorConnectionStatus.disconnected) + Text( + "Disconnected", + style: STextStyles.desktopTextExtraExtraSmall( + context, + ), + ), + ], + ), ), - ), - ], + ], + ), ), - ), SizedBox(height: isDesktop ? 32 : 20), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -980,10 +945,9 @@ class _WalletNetworkSettingsViewState Text( "${ref.watch(pWalletCoin(widget.walletId)).prettyName} nodes", textAlign: TextAlign.left, - style: - isDesktop - ? STextStyles.desktopTextExtraExtraSmall(context) - : STextStyles.smallMed12(context), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context) + : STextStyles.smallMed12(context), ), CustomTextButton( text: "Add new node", @@ -1030,14 +994,12 @@ class _WalletNetworkSettingsViewState ref.watch(pWalletCoin(widget.walletId)) is! Epiccash && ref.watch(pWalletCoin(widget.walletId)) is! Mimblewimblecoin) RoundedWhiteContainer( - borderColor: - isDesktop - ? Theme.of(context).extension()!.background - : null, - padding: - isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), + borderColor: isDesktop + ? Theme.of(context).extension()!.background + : null, + padding: isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), child: Expandable( onExpandChanged: (state) { setState(() { @@ -1053,20 +1015,18 @@ class _WalletNetworkSettingsViewState width: _iconSize, height: _iconSize, decoration: BoxDecoration( - color: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + color: Theme.of( + context, + ).extension()!.textFieldDefaultBG, borderRadius: BorderRadius.circular(_iconSize), ), child: Center( child: SvgPicture.asset( Assets.svg.networkWired, width: 24, - color: - Theme.of( - context, - ).extension()!.textDark, + color: Theme.of( + context, + ).extension()!.textDark, ), ), ), @@ -1077,14 +1037,14 @@ class _WalletNetworkSettingsViewState children: [ Text( "Advanced", - style: STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of( + style: + STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of( context, ).extension()!.textDark, - ), + ), ), Text( "Rescan blockchain", @@ -1102,10 +1062,9 @@ class _WalletNetworkSettingsViewState : Assets.svg.chevronDown, width: 12, height: 6, - color: - Theme.of( - context, - ).extension()!.textSubtitle1, + color: Theme.of( + context, + ).extension()!.textSubtitle1, ), ], ), diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 32c565e3df..c8971e6067 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -183,8 +183,9 @@ class _WalletViewState extends ConsumerState { } } - eventBus = - widget.eventBus != null ? widget.eventBus! : GlobalEventBus.instance; + eventBus = widget.eventBus != null + ? widget.eventBus! + : GlobalEventBus.instance; _syncStatusSubscription = eventBus .on() @@ -354,27 +355,24 @@ class _WalletViewState extends ConsumerState { if (coin.network.isTestNet) { await showDialog( context: context, - builder: - (_) => const StackOkDialog( - title: "Exchange not available for test net coins", - ), + builder: (_) => const StackOkDialog( + title: "Exchange not available for test net coins", + ), ); } else { Future _future; final isar = await ExchangeDataLoadingService.instance.isar; try { - _future = - isar.currencies - .where() - .tickerEqualToAnyExchangeNameName(coin.ticker) - .findFirst(); + _future = isar.currencies + .where() + .tickerEqualToAnyExchangeNameName(coin.ticker) + .findFirst(); } catch (_) { _future = ExchangeDataLoadingService.instance.loadAll().then( - (_) => - isar.currencies - .where() - .tickerEqualToAnyExchangeNameName(coin.ticker) - .findFirst(), + (_) => isar.currencies + .where() + .tickerEqualToAnyExchangeNameName(coin.ticker) + .findFirst(), ); } @@ -404,18 +402,17 @@ class _WalletViewState extends ConsumerState { if (coin.network.isTestNet) { await showDialog( context: context, - builder: - (_) => const StackOkDialog( - title: "Buy not available for test net coins", - ), + builder: (_) => + const StackOkDialog(title: "Buy not available for test net coins"), ); } else { if (mounted) { unawaited( Navigator.of(context).pushNamed( BuyInWalletView.routeName, - arguments: - coin.hasBuySupport ? coin : Bitcoin(CryptoCurrencyNetwork.main), + arguments: coin.hasBuySupport + ? coin + : Bitcoin(CryptoCurrencyNetwork.main), ), ); } @@ -427,14 +424,13 @@ class _WalletViewState extends ConsumerState { unawaited( showDialog( context: context, - builder: - (context) => WillPopScope( - child: const CustomLoadingOverlay( - message: "Anonymizing balance", - eventBus: null, - ), - onWillPop: () async => shouldPop, - ), + builder: (context) => WillPopScope( + child: const CustomLoadingOverlay( + message: "Anonymizing balance", + eventBus: null, + ), + onWillPop: () async => shouldPop, + ), ), ); final wallet = ref.read(pWallets).getWallet(walletId); @@ -484,11 +480,10 @@ class _WalletViewState extends ConsumerState { ).popUntil(ModalRoute.withName(WalletView.routeName)); await showDialog( context: context, - builder: - (_) => StackOkDialog( - title: "Privatize all failed", - message: "Reason: $e", - ), + builder: (_) => StackOkDialog( + title: "Privatize all failed", + message: "Reason: $e", + ), ); } } @@ -521,40 +516,36 @@ class _WalletViewState extends ConsumerState { "Migration in progress\nThis could take a while\nPlease don't leave this screen", subMessage: "This only needs to run once per wallet", eventBus: null, - textColor: - Theme.of(context).extension()!.textDark, + textColor: Theme.of( + context, + ).extension()!.textDark, actionButton: SecondaryButton( label: "Cancel", onPressed: () async { await showDialog( context: context, - builder: - (context) => StackDialog( - title: "Warning!", - message: - "Skipping this process can completely" - " break your wallet. It is only meant to be done in" - " emergency situations where the migration fails" - " and will not let you continue. Still skip?", - leftButton: SecondaryButton( - label: "Cancel", - onPressed: - Navigator.of( - context, - rootNavigator: true, - ).pop, - ), - rightButton: SecondaryButton( - label: "Ok", - onPressed: () { - Navigator.of( - context, - rootNavigator: true, - ).pop(); - setState(() => _rescanningOnOpen = false); - }, - ), - ), + builder: (context) => StackDialog( + title: "Warning!", + message: + "Skipping this process can completely" + " break your wallet. It is only meant to be done in" + " emergency situations where the migration fails" + " and will not let you continue. Still skip?", + leftButton: SecondaryButton( + label: "Cancel", + onPressed: Navigator.of( + context, + rootNavigator: true, + ).pop, + ), + rightButton: SecondaryButton( + label: "Ok", + onPressed: () { + Navigator.of(context, rootNavigator: true).pop(); + setState(() => _rescanningOnOpen = false); + }, + ), + ), ); }, ), @@ -570,8 +561,9 @@ class _WalletViewState extends ConsumerState { child: Stack( children: [ Scaffold( - backgroundColor: - Theme.of(context).extension()!.background, + backgroundColor: Theme.of( + context, + ).extension()!.background, appBar: AppBar( leading: AppBarBackButton( onPressed: () { @@ -598,10 +590,18 @@ class _WalletViewState extends ConsumerState { ], ), actions: [ - const Padding( - padding: EdgeInsets.only(top: 10, bottom: 10, right: 10), - child: AspectRatio(aspectRatio: 1, child: SmallTorIcon()), - ), + if (AppConfig.hasFeature(AppFeature.tor)) + const Padding( + padding: EdgeInsets.only( + top: 10, + bottom: 10, + right: 10, + ), + child: AspectRatio( + aspectRatio: 1, + child: SmallTorIcon(), + ), + ), Padding( padding: const EdgeInsets.only( top: 10, @@ -616,10 +616,9 @@ class _WalletViewState extends ConsumerState { key: const Key("walletViewRadioButton"), size: 36, shadows: const [], - color: - Theme.of( - context, - ).extension()!.background, + color: Theme.of( + context, + ).extension()!.background, icon: _buildNetworkIcon(_currentSyncStatus), onPressed: () { Navigator.of(context).pushNamed( @@ -648,59 +647,58 @@ class _WalletViewState extends ConsumerState { key: const Key("walletViewAlertsButton"), size: 36, shadows: const [], - color: - Theme.of( - context, - ).extension()!.background, + color: Theme.of( + context, + ).extension()!.background, icon: ref.watch( - notificationsProvider.select( - (value) => value - .hasUnreadNotificationsFor(walletId), - ), - ) - ? SvgPicture.file( - File( - ref.watch( - themeProvider.select( - (value) => value.assets.bellNew, - ), + notificationsProvider.select( + (value) => + value.hasUnreadNotificationsFor(walletId), + ), + ) + ? SvgPicture.file( + File( + ref.watch( + themeProvider.select( + (value) => value.assets.bellNew, ), ), - width: 20, - height: 20, - color: - ref.watch( - notificationsProvider.select( - (value) => value - .hasUnreadNotificationsFor( - walletId, - ), + ), + width: 20, + height: 20, + color: + ref.watch( + notificationsProvider.select( + (value) => + value.hasUnreadNotificationsFor( + walletId, ), - ) - ? null - : Theme.of(context) - .extension()! - .topNavIconPrimary, - ) - : SvgPicture.asset( - Assets.svg.bell, - width: 20, - height: 20, - color: - ref.watch( - notificationsProvider.select( - (value) => value - .hasUnreadNotificationsFor( - walletId, - ), + ), + ) + ? null + : Theme.of(context) + .extension()! + .topNavIconPrimary, + ) + : SvgPicture.asset( + Assets.svg.bell, + width: 20, + height: 20, + color: + ref.watch( + notificationsProvider.select( + (value) => + value.hasUnreadNotificationsFor( + walletId, ), - ) - ? null - : Theme.of(context) - .extension()! - .topNavIconPrimary, - ), + ), + ) + ? null + : Theme.of(context) + .extension()! + .topNavIconPrimary, + ), onPressed: () { // reset unread state ref.refresh(unreadNotificationsStateProvider); @@ -711,13 +709,11 @@ class _WalletViewState extends ConsumerState { arguments: walletId, ) .then((_) { - final Set unreadNotificationIds = - ref - .read( - unreadNotificationsStateProvider - .state, - ) - .state; + final Set unreadNotificationIds = ref + .read( + unreadNotificationsStateProvider.state, + ) + .state; if (unreadNotificationIds.isEmpty) return; final List> futures = []; @@ -765,16 +761,14 @@ class _WalletViewState extends ConsumerState { key: const Key("walletViewSettingsButton"), size: 36, shadows: const [], - color: - Theme.of( - context, - ).extension()!.background, + color: Theme.of( + context, + ).extension()!.background, icon: SvgPicture.asset( Assets.svg.bars, - color: - Theme.of( - context, - ).extension()!.accentColorDark, + color: Theme.of( + context, + ).extension()!.accentColorDark, width: 20, height: 20, ), @@ -798,8 +792,9 @@ class _WalletViewState extends ConsumerState { ), body: SafeArea( child: Container( - color: - Theme.of(context).extension()!.background, + color: Theme.of( + context, + ).extension()!.background, child: Column( children: [ const SizedBox(height: 10), @@ -811,12 +806,12 @@ class _WalletViewState extends ConsumerState { aspectRatio: 1.75, initialSyncStatus: ref - .watch(pWallets) - .getWallet(walletId) - .refreshMutex - .isLocked - ? WalletSyncStatus.syncing - : WalletSyncStatus.synced, + .watch(pWallets) + .getWallet(walletId) + .refreshMutex + .isLocked + ? WalletSyncStatus.syncing + : WalletSyncStatus.synced, ), ), ), @@ -839,60 +834,55 @@ class _WalletViewState extends ConsumerState { onPressed: () async { await showDialog( context: context, - builder: - (context) => StackDialog( - title: "Attention!", - message: - "You're about to privatize all of your public funds.", - leftButton: TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: Text( - "Cancel", - style: STextStyles.button( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .accentColorDark, + builder: (context) => StackDialog( + title: "Attention!", + message: + "You're about to privatize all of your public funds.", + leftButton: TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text( + "Cancel", + style: STextStyles.button(context) + .copyWith( + color: Theme.of(context) + .extension< + StackColors + >()! + .accentColorDark, ), - ), - ), - rightButton: TextButton( - onPressed: () async { - Navigator.of(context).pop(); + ), + ), + rightButton: TextButton( + onPressed: () async { + Navigator.of(context).pop(); - unawaited(attemptAnonymize()); - }, - style: Theme.of(context) - .extension()! - .getPrimaryEnabledButtonStyle( - context, - ), - child: Text( - "Continue", - style: STextStyles.button( - context, - ), + unawaited(attemptAnonymize()); + }, + style: Theme.of(context) + .extension()! + .getPrimaryEnabledButtonStyle( + context, ), + child: Text( + "Continue", + style: STextStyles.button( + context, ), ), + ), + ), ); }, child: Text( "Privatize funds", - style: STextStyles.button( - context, - ).copyWith( - color: - Theme.of(context) + style: STextStyles.button(context) + .copyWith( + color: Theme.of(context) .extension()! .buttonTextSecondary, - ), + ), ), ), ), @@ -907,14 +897,12 @@ class _WalletViewState extends ConsumerState { children: [ Text( "Transactions", - style: STextStyles.itemSubtitle( - context, - ).copyWith( - color: - Theme.of( + style: STextStyles.itemSubtitle(context) + .copyWith( + color: Theme.of( context, ).extension()!.textDark3, - ), + ), ), CustomTextButton( text: "See all", @@ -976,18 +964,16 @@ class _WalletViewState extends ConsumerState { Expanded( child: ref - .read(pWallets) - .getWallet( - widget.walletId, - ) - .isarTransactionVersion == - 2 - ? TransactionsV2List( - walletId: widget.walletId, - ) - : TransactionsList( - walletId: walletId, - ), + .read(pWallets) + .getWallet(widget.walletId) + .isarTransactionVersion == + 2 + ? TransactionsV2List( + walletId: widget.walletId, + ) + : TransactionsList( + walletId: walletId, + ), ), ], ), @@ -1102,11 +1088,10 @@ class _WalletViewState extends ConsumerState { moreItems: [ if (ref.watch( pWallets.select( - (value) => - value - .getWallet(widget.walletId) - .cryptoCurrency - .hasTokenSupport, + (value) => value + .getWallet(widget.walletId) + .cryptoCurrency + .hasTokenSupport, ), )) WalletNavigationBarItemData( @@ -1125,10 +1110,9 @@ class _WalletViewState extends ConsumerState { Assets.svg.monkey, height: 20, width: 20, - color: - Theme.of( - context, - ).extension()!.bottomNavIconIcon, + color: Theme.of( + context, + ).extension()!.bottomNavIconIcon, ), label: "MonKey", onTap: () { @@ -1192,9 +1176,8 @@ class _WalletViewState extends ConsumerState { unawaited( showDialog( context: context, - builder: - (context) => - const LoadingIndicator(width: 100), + builder: (context) => + const LoadingIndicator(width: 100), ), ); @@ -1224,8 +1207,9 @@ class _WalletViewState extends ConsumerState { // account.value!.segwit ) { ref - .read(myPaynymAccountStateProvider.state) - .state = account.value!; + .read(myPaynymAccountStateProvider.state) + .state = + account.value!; await Navigator.of(context).pushNamed( PaynymHomeView.routeName, @@ -1257,7 +1241,9 @@ class _WalletViewState extends ConsumerState { ); }, ), - if (wallet is CashFusionInterface && !viewOnly) + if (AppConfig.hasFeature(AppFeature.tor) && + wallet is CashFusionInterface && + !viewOnly) WalletNavigationBarItemData( label: "Fusion", icon: const FusionNavIcon(), diff --git a/lib/pages_desktop_specific/desktop_buy/desktop_buy_view.dart b/lib/pages_desktop_specific/desktop_buy/desktop_buy_view.dart index 6c5d2dc2db..77781396a0 100644 --- a/lib/pages_desktop_specific/desktop_buy/desktop_buy_view.dart +++ b/lib/pages_desktop_specific/desktop_buy/desktop_buy_view.dart @@ -10,6 +10,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../app_config.dart'; import '../../pages/buy_view/buy_form.dart'; import '../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; import '../../services/tor_service.dart'; @@ -35,8 +37,9 @@ class _DesktopBuyViewState extends ConsumerState { @override void initState() { - torEnabled = - ref.read(pTorService).status != TorConnectionStatus.disconnected; + torEnabled = AppConfig.hasFeature(AppFeature.tor) + ? ref.read(pTorService).status != TorConnectionStatus.disconnected + : false; super.initState(); } @@ -54,9 +57,7 @@ class _DesktopBuyViewState extends ConsumerState { appBar: DesktopAppBar( isCompactHeight: true, leading: Padding( - padding: const EdgeInsets.only( - left: 24, - ), + padding: const EdgeInsets.only(left: 24), child: Text( "Buy crypto", style: STextStyles.desktopH3(context), @@ -64,11 +65,7 @@ class _DesktopBuyViewState extends ConsumerState { ), ), body: const Padding( - padding: EdgeInsets.only( - left: 24, - right: 24, - bottom: 24, - ), + padding: EdgeInsets.only(left: 24, right: 24, bottom: 24), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -77,9 +74,7 @@ class _DesktopBuyViewState extends ConsumerState { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( - height: 16, - ), + SizedBox(height: 16), RoundedWhiteContainer( padding: EdgeInsets.all(24), child: BuyForm(), @@ -87,9 +82,7 @@ class _DesktopBuyViewState extends ConsumerState { ], ), ), - SizedBox( - width: 16, - ), + SizedBox(width: 16), // Expanded( // child: Row( // children: const [ @@ -105,19 +98,16 @@ class _DesktopBuyViewState extends ConsumerState { ), if (torEnabled) Container( - color: Theme.of(context) - .extension()! - .overlay - .withOpacity(0.7), + color: Theme.of( + context, + ).extension()!.overlay.withOpacity(0.7), height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, child: DesktopDialog( maxHeight: 200, maxWidth: 350, child: Padding( - padding: const EdgeInsets.all( - 15.0, - ), + padding: const EdgeInsets.all(15.0), child: Column( // crossAxisAlignment: CrossAxisAlignment.center, // mainAxisAlignment: MainAxisAlignment.center, @@ -127,16 +117,14 @@ class _DesktopBuyViewState extends ConsumerState { textAlign: TextAlign.center, style: STextStyles.pageTitleH1(context), ), - const SizedBox( - height: 30, - ), + const SizedBox(height: 30), Text( "Purchasing not available while Tor is enabled", textAlign: TextAlign.center, style: STextStyles.desktopTextMedium(context).copyWith( - color: Theme.of(context) - .extension()! - .infoItemLabel, + color: Theme.of( + context, + ).extension()!.infoItemLabel, ), ), ], diff --git a/lib/pages_desktop_specific/desktop_menu.dart b/lib/pages_desktop_specific/desktop_menu.dart index 9c9b9cc1d4..c0cbf107f5 100644 --- a/lib/pages_desktop_specific/desktop_menu.dart +++ b/lib/pages_desktop_specific/desktop_menu.dart @@ -126,19 +126,13 @@ class _DesktopMenuState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - const SizedBox( - height: 25, - ), + const SizedBox(height: 25), AnimatedContainer( duration: duration, width: _width == expandedWidth ? 70 : 32, - child: LivingStackIcon( - onPressed: toggleMinimize, - ), - ), - const SizedBox( - height: 10, + child: LivingStackIcon(onPressed: toggleMinimize), ), + const SizedBox(height: 10), AnimatedOpacity( duration: duration, opacity: _width == expandedWidth ? 1 : 0, @@ -146,40 +140,40 @@ class _DesktopMenuState extends ConsumerState { height: 28, child: Text( AppConfig.appName, - style: STextStyles.desktopH2(context).copyWith( - fontSize: 18, - height: 23.4 / 18, - ), + style: STextStyles.desktopH2( + context, + ).copyWith(fontSize: 18, height: 23.4 / 18), ), ), ), - const SizedBox( - height: 5, - ), - AnimatedContainer( - duration: duration, - width: _width == expandedWidth - ? _width - 32 // 16 padding on either side - : _width - 16, // 8 padding on either side - child: DesktopTorStatusButton( - transitionDuration: duration, - controller: torButtonController, - onPressed: () { - ref.read(currentDesktopMenuItemProvider.state).state = - DesktopMenuItemId.settings; - ref.read(selectedSettingsMenuItemStateProvider.state).state = - 4; - }, + if (AppConfig.hasFeature(AppFeature.tor)) const SizedBox(height: 5), + if (AppConfig.hasFeature(AppFeature.tor)) + AnimatedContainer( + duration: duration, + width: _width == expandedWidth + ? _width - + 32 // 16 padding on either side + : _width - 16, // 8 padding on either side + child: DesktopTorStatusButton( + transitionDuration: duration, + controller: torButtonController, + onPressed: () { + ref.read(currentDesktopMenuItemProvider.state).state = + DesktopMenuItemId.settings; + ref + .read(selectedSettingsMenuItemStateProvider.state) + .state = + 4; + }, + ), ), - ), - const SizedBox( - height: 40, - ), + const SizedBox(height: 40), Expanded( child: AnimatedContainer( duration: duration, width: _width == expandedWidth - ? _width - 32 // 16 padding on either side + ? _width - + 32 // 16 padding on either side : _width - 16, // 8 padding on either side child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, @@ -196,9 +190,7 @@ class _DesktopMenuState extends ConsumerState { ), if (AppConfig.hasFeature(AppFeature.swap) && showExchange) ...[ - const SizedBox( - height: 2, - ), + const SizedBox(height: 2), DesktopMenuItem( key: const ValueKey('swap'), duration: duration, @@ -212,9 +204,7 @@ class _DesktopMenuState extends ConsumerState { ], if (AppConfig.hasFeature(AppFeature.buy) && showExchange) ...[ - const SizedBox( - height: 2, - ), + const SizedBox(height: 2), DesktopMenuItem( key: const ValueKey('buy'), duration: duration, @@ -226,9 +216,7 @@ class _DesktopMenuState extends ConsumerState { isExpandedInitially: !_isMinimized, ), ], - const SizedBox( - height: 2, - ), + const SizedBox(height: 2), DesktopMenuItem( key: const ValueKey('notifications'), duration: duration, @@ -239,9 +227,7 @@ class _DesktopMenuState extends ConsumerState { controller: controllers[3], isExpandedInitially: !_isMinimized, ), - const SizedBox( - height: 2, - ), + const SizedBox(height: 2), DesktopMenuItem( key: const ValueKey('addressBook'), duration: duration, @@ -252,9 +238,7 @@ class _DesktopMenuState extends ConsumerState { controller: controllers[4], isExpandedInitially: !_isMinimized, ), - const SizedBox( - height: 2, - ), + const SizedBox(height: 2), DesktopMenuItem( key: const ValueKey('settings'), duration: duration, @@ -265,9 +249,7 @@ class _DesktopMenuState extends ConsumerState { controller: controllers[5], isExpandedInitially: !_isMinimized, ), - const SizedBox( - height: 2, - ), + const SizedBox(height: 2), DesktopMenuItem( key: const ValueKey('support'), duration: duration, @@ -278,9 +260,7 @@ class _DesktopMenuState extends ConsumerState { controller: controllers[6], isExpandedInitially: !_isMinimized, ), - const SizedBox( - height: 2, - ), + const SizedBox(height: 2), DesktopMenuItem( key: const ValueKey('about'), duration: duration, diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart index e5b05270f9..c5e5428f45 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart @@ -126,9 +126,8 @@ class _DesktopWalletFeaturesState extends ConsumerState { ) async { await showDialog( context: context, - builder: - (_) => - MoreFeaturesDialog(walletId: widget.walletId, options: options), + builder: (_) => + MoreFeaturesDialog(walletId: widget.walletId, options: options), ); } @@ -154,49 +153,48 @@ class _DesktopWalletFeaturesState extends ConsumerState { await showDialog( context: context, barrierDismissible: false, - builder: - (context) => DesktopDialog( - maxWidth: 500, - maxHeight: double.infinity, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 20), - child: Column( + builder: (context) => DesktopDialog( + maxWidth: 500, + maxHeight: double.infinity, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 20), + child: Column( + children: [ + Text("Attention!", style: STextStyles.desktopH2(context)), + const SizedBox(height: 16), + Text( + "You're about to privatize all of your public funds.", + style: STextStyles.desktopTextSmall(context), + ), + const SizedBox(height: 32), + Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ - Text("Attention!", style: STextStyles.desktopH2(context)), - const SizedBox(height: 16), - Text( - "You're about to privatize all of your public funds.", - style: STextStyles.desktopTextSmall(context), + SecondaryButton( + width: 200, + buttonHeight: ButtonHeight.l, + label: "Cancel", + onPressed: () { + Navigator.of(context).pop(); + }, ), - const SizedBox(height: 32), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SecondaryButton( - width: 200, - buttonHeight: ButtonHeight.l, - label: "Cancel", - onPressed: () { - Navigator.of(context).pop(); - }, - ), - const SizedBox(width: 20), - PrimaryButton( - width: 200, - buttonHeight: ButtonHeight.l, - label: "Continue", - onPressed: () { - Navigator.of(context).pop(); - - unawaited(_attemptAnonymize()); - }, - ), - ], + const SizedBox(width: 20), + PrimaryButton( + width: 200, + buttonHeight: ButtonHeight.l, + label: "Continue", + onPressed: () { + Navigator.of(context).pop(); + + unawaited(_attemptAnonymize()); + }, ), ], ), - ), + ], ), + ), + ), ); } @@ -205,14 +203,13 @@ class _DesktopWalletFeaturesState extends ConsumerState { unawaited( showDialog( context: context, - builder: - (context) => WillPopScope( - child: const CustomLoadingOverlay( - message: "Privatizing balance", - eventBus: null, - ), - onWillPop: () async => shouldPop, - ), + builder: (context) => WillPopScope( + child: const CustomLoadingOverlay( + message: "Privatizing balance", + eventBus: null, + ), + onWillPop: () async => shouldPop, + ), ), ); @@ -265,46 +262,44 @@ class _DesktopWalletFeaturesState extends ConsumerState { ).popUntil(ModalRoute.withName(DesktopWalletView.routeName)); await showDialog( context: context, - builder: - (_) => DesktopDialog( - maxWidth: 400, - maxHeight: 300, - child: Padding( - padding: const EdgeInsets.all(24), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + builder: (_) => DesktopDialog( + maxWidth: 400, + maxHeight: 300, + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Privatize all failed", + style: STextStyles.desktopH3(context), + ), + const Spacer(flex: 1), + Text( + "Reason: $e", + style: STextStyles.desktopTextSmall(context), + ), + const Spacer(flex: 2), + Row( children: [ - Text( - "Privatize all failed", - style: STextStyles.desktopH3(context), - ), - const Spacer(flex: 1), - Text( - "Reason: $e", - style: STextStyles.desktopTextSmall(context), - ), - const Spacer(flex: 2), - Row( - children: [ - const Spacer(), - const SizedBox(width: 16), - Expanded( - child: PrimaryButton( - label: "Ok", - buttonHeight: ButtonHeight.l, - onPressed: - Navigator.of( - context, - rootNavigator: true, - ).pop, - ), - ), - ], + const Spacer(), + const SizedBox(width: 16), + Expanded( + child: PrimaryButton( + label: "Ok", + buttonHeight: ButtonHeight.l, + onPressed: Navigator.of( + context, + rootNavigator: true, + ).pop, + ), ), ], ), - ), + ], ), + ), + ), ); } } @@ -447,7 +442,9 @@ class _DesktopWalletFeaturesState extends ConsumerState { if (wallet.info.coin is Banano) (WalletFeature.monkey, Assets.svg.monkey, _onMonkeyPressed), - if (!isViewOnly && wallet is CashFusionInterface) + if (AppConfig.hasFeature(AppFeature.tor) && + !isViewOnly && + wallet is CashFusionInterface) (WalletFeature.fusion, Assets.svg.cashFusion, _onFusionPressed), if (!isViewOnly && @@ -493,8 +490,8 @@ class _DesktopWalletFeaturesState extends ConsumerState { canGen = (wallet is MultiAddressInterface || - wallet is SparkInterface || - supportsMweb); + wallet is SparkInterface || + supportsMweb); } final showMwebOption = wallet is MwebInterface && !wallet.isViewOnly; @@ -523,16 +520,14 @@ class _DesktopWalletFeaturesState extends ConsumerState { Assets.svg.bars, height: 20, width: 20, - color: - Theme.of( - context, - ).extension()!.buttonTextSecondary, + color: Theme.of( + context, + ).extension()!.buttonTextSecondary, ), - onPressed: - () => _onMorePressed([ - ...options.sublist(options.length - count), - ...extraOptions, - ]), + onPressed: () => _onMorePressed([ + ...options.sublist(options.length - count), + ...extraOptions, + ]), ), ); }, @@ -545,30 +540,27 @@ class _DesktopWalletFeaturesState extends ConsumerState { label: option.$1.label, padding: const EdgeInsets.symmetric(horizontal: 16), buttonHeight: ButtonHeight.l, - icon: - option.$1 == WalletFeature.buy - ? SvgPicture.file( - File( - ref.watch( - themeProvider.select((value) => value.assets.buy), - ), + icon: option.$1 == WalletFeature.buy + ? SvgPicture.file( + File( + ref.watch( + themeProvider.select((value) => value.assets.buy), ), - height: 20, - width: 20, - color: - Theme.of( - context, - ).extension()!.buttonTextSecondary, - ) - : SvgPicture.asset( - option.$2, - height: 20, - width: 20, - color: - Theme.of( - context, - ).extension()!.buttonTextSecondary, ), + height: 20, + width: 20, + color: Theme.of( + context, + ).extension()!.buttonTextSecondary, + ) + : SvgPicture.asset( + option.$2, + height: 20, + width: 20, + color: Theme.of( + context, + ).extension()!.buttonTextSecondary, + ), onPressed: () => option.$3(), ), ), diff --git a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart index aea4a8f021..6c2a12e1d3 100644 --- a/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart +++ b/lib/pages_desktop_specific/ordinals/desktop_ordinal_details_view.dart @@ -6,6 +6,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; +import '../../app_config.dart'; import '../../models/isar/models/blockchain_data/utxo.dart'; import '../../models/isar/ordinal.dart'; import '../../networking/http.dart'; @@ -57,10 +58,11 @@ class _DesktopOrdinalDetailsViewState final response = await client.get( url: Uri.parse(widget.ordinal.content), - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); if (response.code != 200) { @@ -71,10 +73,9 @@ class _DesktopOrdinalDetailsViewState final bytes = response.bodyBytes; - final dir = - Platform.isAndroid - ? await StackFileSystem.wtfAndroidDocumentsPath() - : await getApplicationDocumentsDirectory(); + final dir = Platform.isAndroid + ? await StackFileSystem.wtfAndroidDocumentsPath() + : await getApplicationDocumentsDirectory(); final filePath = path.join( dir.path, @@ -109,19 +110,17 @@ class _DesktopOrdinalDetailsViewState const SizedBox(width: 32), AppBarIconButton( size: 32, - color: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + color: Theme.of( + context, + ).extension()!.textFieldDefaultBG, shadows: const [], icon: SvgPicture.asset( Assets.svg.arrowLeft, width: 18, height: 18, - color: - Theme.of( - context, - ).extension()!.topNavIconPrimary, + color: Theme.of( + context, + ).extension()!.topNavIconPrimary, ), onPressed: Navigator.of(context).pop, ), @@ -213,10 +212,9 @@ class _DesktopOrdinalDetailsViewState Assets.svg.arrowDown, width: 13, height: 18, - color: - Theme.of(context) - .extension()! - .buttonTextSecondary, + color: Theme.of( + context, + ).extension()!.buttonTextSecondary, ), buttonHeight: ButtonHeight.l, iconSpacing: 8, @@ -276,28 +274,26 @@ class _DesktopOrdinalDetailsViewState const _Divider(), Consumer( builder: (context, ref, _) { - final coin = - ref - .watch(pWallets) - .getWallet(widget.walletId) - .info - .coin; + final coin = ref + .watch(pWallets) + .getWallet(widget.walletId) + .info + .coin; return _DetailsItemWCopy( title: "Amount", - data: - utxo == null - ? "ERROR" - : ref - .watch(pAmountFormatter(coin)) - .format( - Amount( - rawValue: BigInt.from( - utxo!.value, - ), - fractionDigits: - coin.fractionDigits, + data: utxo == null + ? "ERROR" + : ref + .watch(pAmountFormatter(coin)) + .format( + Amount( + rawValue: BigInt.from( + utxo!.value, ), + fractionDigits: + coin.fractionDigits, ), + ), ); }, ), diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 79c7ec4f5a..4775dadaab 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -13,6 +13,7 @@ import 'package:flutter/material.dart'; import 'package:isar_community/isar.dart'; import 'package:tuple/tuple.dart'; +import 'app_config.dart'; import 'db/drift/database.dart'; import 'models/add_wallet_list_entity/add_wallet_list_entity.dart'; import 'models/add_wallet_list_entity/sub_classes/eth_token_entity.dart'; @@ -289,12 +290,11 @@ class RouteGenerator { if (args is Tuple3) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => ChooseCoinView( - title: args.item1, - coinAdditional: args.item2, - nextRouteName: args.item3, - ), + builder: (_) => ChooseCoinView( + title: args.item1, + coinAdditional: args.item2, + nextRouteName: args.item3, + ), settings: RouteSettings(name: settings.name), ); } @@ -344,11 +344,10 @@ class RouteGenerator { } else if (args is Tuple2>) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => EditWalletTokensView( - walletId: args.item1, - contractsToMarkSelected: args.item2, - ), + builder: (_) => EditWalletTokensView( + walletId: args.item1, + contractsToMarkSelected: args.item2, + ), settings: RouteSettings(name: settings.name), ); } @@ -395,11 +394,10 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => TokenContractDetailsView( - contractAddress: args.item1, - walletId: args.item2, - ), + builder: (_) => TokenContractDetailsView( + contractAddress: args.item1, + walletId: args.item2, + ), settings: RouteSettings(name: settings.name), ); } @@ -409,11 +407,10 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => SingleFieldEditView( - initialValue: args.item1, - label: args.item2, - ), + builder: (_) => SingleFieldEditView( + initialValue: args.item1, + label: args.item2, + ), settings: RouteSettings(name: settings.name), ); } @@ -433,11 +430,10 @@ class RouteGenerator { if (args is ({String walletName, FrostCurrency frostCurrency})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => CreateNewFrostMsWalletView( - walletName: args.walletName, - frostCurrency: args.frostCurrency, - ), + builder: (_) => CreateNewFrostMsWalletView( + walletName: args.walletName, + frostCurrency: args.frostCurrency, + ), settings: RouteSettings(name: settings.name), ); } @@ -447,11 +443,10 @@ class RouteGenerator { if (args is ({String walletName, FrostCurrency frostCurrency})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => RestoreFrostMsWalletView( - walletName: args.walletName, - frostCurrency: args.frostCurrency, - ), + builder: (_) => RestoreFrostMsWalletView( + walletName: args.walletName, + frostCurrency: args.frostCurrency, + ), settings: RouteSettings(name: settings.name), ); } @@ -461,11 +456,10 @@ class RouteGenerator { if (args is ({String walletName, FrostCurrency frostCurrency})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => SelectNewFrostImportTypeView( - walletName: args.walletName, - frostCurrency: args.frostCurrency, - ), + builder: (_) => SelectNewFrostImportTypeView( + walletName: args.walletName, + frostCurrency: args.frostCurrency, + ), settings: RouteSettings(name: settings.name), ); } @@ -512,11 +506,10 @@ class RouteGenerator { if (args is ({String walletId, Map resharers})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => CompleteReshareConfigView( - walletId: args.walletId, - resharers: args.resharers, - ), + builder: (_) => CompleteReshareConfigView( + walletId: args.walletId, + resharers: args.resharers, + ), settings: RouteSettings(name: settings.name), ); } @@ -526,8 +519,8 @@ class RouteGenerator { if (args is ({String walletId, CryptoCurrency coin})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => FrostSendView(walletId: args.walletId, coin: args.coin), + builder: (_) => + FrostSendView(walletId: args.walletId, coin: args.coin), settings: RouteSettings(name: settings.name), ); } @@ -552,21 +545,20 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => CoinControlView(walletId: args.item1, type: args.item2), + builder: (_) => + CoinControlView(walletId: args.item1, type: args.item2), settings: RouteSettings(name: settings.name), ); } else if (args is Tuple4?>) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => CoinControlView( - walletId: args.item1, - type: args.item2, - requestedTotal: args.item3, - selectedUTXOs: args.item4, - ), + builder: (_) => CoinControlView( + walletId: args.item1, + type: args.item2, + requestedTotal: args.item3, + selectedUTXOs: args.item4, + ), settings: RouteSettings(name: settings.name), ); } @@ -596,11 +588,10 @@ class RouteGenerator { if (args is ({Ordinal ordinal, String walletId})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => OrdinalDetailsView( - walletId: args.walletId, - ordinal: args.ordinal, - ), + builder: (_) => OrdinalDetailsView( + walletId: args.walletId, + ordinal: args.ordinal, + ), settings: RouteSettings(name: settings.name), ); } @@ -610,11 +601,10 @@ class RouteGenerator { if (args is ({Ordinal ordinal, String walletId})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => DesktopOrdinalDetailsView( - walletId: args.walletId, - ordinal: args.ordinal, - ), + builder: (_) => DesktopOrdinalDetailsView( + walletId: args.walletId, + ordinal: args.ordinal, + ), settings: RouteSettings(name: settings.name), ); } @@ -631,9 +621,8 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => - UtxoDetailsView(walletId: args.item2, utxoId: args.item1), + builder: (_) => + UtxoDetailsView(walletId: args.item2, utxoId: args.item1), settings: RouteSettings(name: settings.name), ); } @@ -683,7 +672,9 @@ class RouteGenerator { if (args is String) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: (_) => CashFusionView(walletId: args), + builder: (_) => AppConfig.hasFeature(AppFeature.tor) + ? CashFusionView(walletId: args) + : throw Exception("Tor not configured in prebuild setup"), settings: RouteSettings(name: settings.name), ); } @@ -703,9 +694,8 @@ class RouteGenerator { if (args is ({String walletId, UTXO utxo})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => - ManageDomainView(walletId: args.walletId, utxo: args.utxo), + builder: (_) => + ManageDomainView(walletId: args.walletId, utxo: args.utxo), settings: RouteSettings(name: settings.name), ); } @@ -825,21 +815,19 @@ class RouteGenerator { if (args is ({String walletId, String name})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => - BuySparkNameView(walletId: args.walletId, name: args.name), + builder: (_) => + BuySparkNameView(walletId: args.walletId, name: args.name), settings: RouteSettings(name: settings.name), ); } else if (args is ({String walletId, String name, SparkName? nameToRenew})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => BuySparkNameView( - walletId: args.walletId, - name: args.name, - nameToRenew: args.nameToRenew, - ), + builder: (_) => BuySparkNameView( + walletId: args.walletId, + name: args.name, + nameToRenew: args.nameToRenew, + ), settings: RouteSettings(name: settings.name), ); } @@ -849,11 +837,10 @@ class RouteGenerator { if (args is ({String walletId, TxData txData})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => ConfirmSparkNameTransactionView( - walletId: args.walletId, - txData: args.txData, - ), + builder: (_) => ConfirmSparkNameTransactionView( + walletId: args.walletId, + txData: args.txData, + ), settings: RouteSettings(name: settings.name), ); } @@ -863,11 +850,8 @@ class RouteGenerator { if (args is ({String walletId, SparkName name})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => SparkNameDetailsView( - walletId: args.walletId, - name: args.name, - ), + builder: (_) => + SparkNameDetailsView(walletId: args.walletId, name: args.name), settings: RouteSettings(name: settings.name), ); } @@ -877,7 +861,9 @@ class RouteGenerator { if (args is String) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: (_) => FusionProgressView(walletId: args), + builder: (_) => AppConfig.hasFeature(AppFeature.tor) + ? FusionProgressView(walletId: args) + : throw Exception("Tor not configured in prebuild setup"), settings: RouteSettings(name: settings.name), ); } @@ -1024,14 +1010,18 @@ class RouteGenerator { case TorSettingsView.routeName: return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: (_) => const TorSettingsView(), + builder: (_) => AppConfig.hasFeature(AppFeature.tor) + ? const TorSettingsView() + : throw Exception("Tor not configured in prebuild setup"), settings: RouteSettings(name: settings.name), ); case TorSettings.routeName: return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: (_) => const TorSettings(), + builder: (_) => AppConfig.hasFeature(AppFeature.tor) + ? const TorSettings() + : throw Exception("Tor not configured in prebuild setup"), settings: RouteSettings(name: settings.name), ); @@ -1208,12 +1198,11 @@ class RouteGenerator { if (args is Tuple3) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => NodeDetailsView( - coin: args.item1, - nodeId: args.item2, - popRouteName: args.item3, - ), + builder: (_) => NodeDetailsView( + coin: args.item1, + nodeId: args.item2, + popRouteName: args.item3, + ), settings: RouteSettings(name: settings.name), ); } @@ -1223,8 +1212,8 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => EditNoteView(txid: args.item1, walletId: args.item2), + builder: (_) => + EditNoteView(txid: args.item1, walletId: args.item2), settings: RouteSettings(name: settings.name), ); } @@ -1244,8 +1233,8 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => EditTradeNoteView(tradeId: args.item1, note: args.item2), + builder: (_) => + EditTradeNoteView(tradeId: args.item1, note: args.item2), settings: RouteSettings(name: settings.name), ); } @@ -1256,13 +1245,12 @@ class RouteGenerator { is Tuple4) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => AddEditNodeView( - viewType: args.item1, - coin: args.item2, - nodeId: args.item3, - routeOnSuccessOrDelete: args.item4, - ), + builder: (_) => AddEditNodeView( + viewType: args.item1, + coin: args.item2, + nodeId: args.item3, + routeOnSuccessOrDelete: args.item4, + ), settings: RouteSettings(name: settings.name), ); } @@ -1302,11 +1290,10 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => EditContactAddressView( - contactId: args.item1, - addressEntry: args.item2, - ), + builder: (_) => EditContactAddressView( + contactId: args.item1, + addressEntry: args.item2, + ), settings: RouteSettings(name: settings.name), ); } @@ -1323,12 +1310,11 @@ class RouteGenerator { if (args is Tuple3) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => WalletNetworkSettingsView( - walletId: args.item1, - initialSyncStatus: args.item2, - initialNodeStatus: args.item3, - ), + builder: (_) => WalletNetworkSettingsView( + walletId: args.item1, + initialSyncStatus: args.item2, + initialNodeStatus: args.item3, + ), settings: RouteSettings(name: settings.name), ); } @@ -1338,11 +1324,10 @@ class RouteGenerator { if (args is ({String walletId, List mnemonic})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => WalletBackupView( - walletId: args.walletId, - mnemonic: args.mnemonic, - ), + builder: (_) => WalletBackupView( + walletId: args.walletId, + mnemonic: args.mnemonic, + ), settings: RouteSettings(name: settings.name), ); } else if (args @@ -1359,12 +1344,11 @@ class RouteGenerator { })) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => WalletBackupView( - walletId: args.walletId, - mnemonic: args.mnemonic, - frostWalletData: args.frostWalletData, - ), + builder: (_) => WalletBackupView( + walletId: args.walletId, + mnemonic: args.mnemonic, + frostWalletData: args.frostWalletData, + ), settings: RouteSettings(name: settings.name), ); } else if (args @@ -1375,12 +1359,11 @@ class RouteGenerator { })) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => WalletBackupView( - walletId: args.walletId, - mnemonic: args.mnemonic, - keyData: args.keyData, - ), + builder: (_) => WalletBackupView( + walletId: args.walletId, + mnemonic: args.mnemonic, + keyData: args.keyData, + ), settings: RouteSettings(name: settings.name), ); } else if (args @@ -1398,13 +1381,12 @@ class RouteGenerator { })) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => WalletBackupView( - walletId: args.walletId, - mnemonic: args.mnemonic, - frostWalletData: args.frostWalletData, - keyData: args.keyData, - ), + builder: (_) => WalletBackupView( + walletId: args.walletId, + mnemonic: args.mnemonic, + frostWalletData: args.frostWalletData, + keyData: args.keyData, + ), settings: RouteSettings(name: settings.name), ); } @@ -1414,11 +1396,10 @@ class RouteGenerator { if (args is ({String walletId, KeyDataInterface keyData})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => MobileKeyDataView( - walletId: args.walletId, - keyData: args.keyData, - ), + builder: (_) => MobileKeyDataView( + walletId: args.walletId, + keyData: args.keyData, + ), settings: RouteSettings(name: settings.name), ); } @@ -1468,11 +1449,8 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => NameYourWalletView( - addWalletType: args.item1, - coin: args.item2, - ), + builder: (_) => + NameYourWalletView(addWalletType: args.item1, coin: args.item2), settings: RouteSettings(name: settings.name), ); } @@ -1482,11 +1460,10 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => NewWalletRecoveryPhraseWarningView( - walletName: args.item1, - coin: args.item2, - ), + builder: (_) => NewWalletRecoveryPhraseWarningView( + walletName: args.item1, + coin: args.item2, + ), settings: RouteSettings(name: settings.name), ); } @@ -1496,11 +1473,8 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => RestoreOptionsView( - walletName: args.item1, - coin: args.item2, - ), + builder: (_) => + RestoreOptionsView(walletName: args.item1, coin: args.item2), settings: RouteSettings(name: settings.name), ); } @@ -1510,11 +1484,8 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => NewWalletOptionsView( - walletName: args.item1, - coin: args.item2, - ), + builder: (_) => + NewWalletOptionsView(walletName: args.item1, coin: args.item2), settings: RouteSettings(name: settings.name), ); } @@ -1524,14 +1495,13 @@ class RouteGenerator { if (args is Tuple5) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => RestoreWalletView( - walletName: args.item1, - coin: args.item2, - seedWordsLength: args.item3, - restoreBlockHeight: args.item4, - mnemonicPassphrase: args.item5, - ), + builder: (_) => RestoreWalletView( + walletName: args.item1, + coin: args.item2, + seedWordsLength: args.item3, + restoreBlockHeight: args.item4, + mnemonicPassphrase: args.item5, + ), settings: RouteSettings(name: settings.name), ); } @@ -1546,12 +1516,11 @@ class RouteGenerator { })) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => RestoreViewOnlyWalletView( - walletName: args.walletName, - coin: args.coin, - restoreBlockHeight: args.restoreBlockHeight, - ), + builder: (_) => RestoreViewOnlyWalletView( + walletName: args.walletName, + coin: args.coin, + restoreBlockHeight: args.restoreBlockHeight, + ), settings: RouteSettings(name: settings.name), ); } @@ -1561,11 +1530,10 @@ class RouteGenerator { if (args is Tuple2>) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => NewWalletRecoveryPhraseView( - wallet: args.item1, - mnemonic: args.item2, - ), + builder: (_) => NewWalletRecoveryPhraseView( + wallet: args.item1, + mnemonic: args.item2, + ), settings: RouteSettings(name: settings.name), ); } @@ -1575,11 +1543,10 @@ class RouteGenerator { if (args is Tuple2>) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => VerifyRecoveryPhraseView( - wallet: args.item1, - mnemonic: args.item2, - ), + builder: (_) => VerifyRecoveryPhraseView( + wallet: args.item1, + mnemonic: args.item2, + ), settings: RouteSettings(name: settings.name), ); } @@ -1605,12 +1572,11 @@ class RouteGenerator { if (args is Tuple3) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => TransactionDetailsView( - transaction: args.item1, - coin: args.item2, - walletId: args.item3, - ), + builder: (_) => TransactionDetailsView( + transaction: args.item1, + coin: args.item2, + walletId: args.item3, + ), settings: RouteSettings(name: settings.name), ); } @@ -1621,12 +1587,11 @@ class RouteGenerator { is ({TransactionV2 tx, CryptoCurrency coin, String walletId})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => tvd.TransactionV2DetailsView( - transaction: args.tx, - coin: args.coin, - walletId: args.walletId, - ), + builder: (_) => tvd.TransactionV2DetailsView( + transaction: args.tx, + coin: args.coin, + walletId: args.walletId, + ), settings: RouteSettings(name: settings.name), ); } @@ -1641,12 +1606,11 @@ class RouteGenerator { })) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => FusionGroupDetailsView( - transactions: args.transactions, - coin: args.coin, - walletId: args.walletId, - ), + builder: (_) => FusionGroupDetailsView( + transactions: args.transactions, + coin: args.coin, + walletId: args.walletId, + ), settings: RouteSettings(name: settings.name), ); } @@ -1673,11 +1637,10 @@ class RouteGenerator { if (args is ({String walletId, String contractAddress})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => AllTransactionsV2View( - walletId: args.walletId, - contractAddress: args.contractAddress, - ), + builder: (_) => AllTransactionsV2View( + walletId: args.walletId, + contractAddress: args.contractAddress, + ), settings: RouteSettings(name: settings.name), ); } @@ -1703,11 +1666,8 @@ class RouteGenerator { } else if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => ReceiveView( - walletId: args.item1, - tokenContract: args.item2, - ), + builder: (_) => + ReceiveView(walletId: args.item1, tokenContract: args.item2), settings: RouteSettings(name: settings.name), ); } @@ -1737,11 +1697,8 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => AddressDetailsView( - walletId: args.item2, - addressId: args.item1, - ), + builder: (_) => + AddressDetailsView(walletId: args.item2, addressId: args.item1), settings: RouteSettings(name: settings.name), ); } @@ -1758,23 +1715,21 @@ class RouteGenerator { is Tuple3) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => SendView( - walletId: args.item1, - coin: args.item2, - autoFillData: args.item3, - ), + builder: (_) => SendView( + walletId: args.item1, + coin: args.item2, + autoFillData: args.item3, + ), settings: RouteSettings(name: settings.name), ); } else if (args is Tuple3) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => SendView( - walletId: args.item1, - coin: args.item2, - accountLite: args.item3, - ), + builder: (_) => SendView( + walletId: args.item1, + coin: args.item2, + accountLite: args.item3, + ), settings: RouteSettings(name: settings.name), ); } else if (args is ({CryptoCurrency coin, String walletId})) { @@ -1791,12 +1746,11 @@ class RouteGenerator { if (args is Tuple3) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => TokenSendView( - walletId: args.item1, - coin: args.item2, - tokenContract: args.item3, - ), + builder: (_) => TokenSendView( + walletId: args.item1, + coin: args.item2, + tokenContract: args.item3, + ), settings: RouteSettings(name: settings.name), ); } @@ -1806,12 +1760,11 @@ class RouteGenerator { if (args is (TxData, String, VoidCallback)) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => ConfirmTransactionView( - txData: args.$1, - walletId: args.$2, - onSuccess: args.$3, - ), + builder: (_) => ConfirmTransactionView( + txData: args.$1, + walletId: args.$2, + onSuccess: args.$3, + ), settings: RouteSettings(name: settings.name), ); } @@ -1821,11 +1774,8 @@ class RouteGenerator { if (args is (TxData, String)) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => ConfirmNameTransactionView( - txData: args.$1, - walletId: args.$2, - ), + builder: (_) => + ConfirmNameTransactionView(txData: args.$1, walletId: args.$2), settings: RouteSettings(name: settings.name), ); } @@ -1835,37 +1785,35 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => Stack( - children: [ - WalletInitiatedExchangeView( - walletId: args.item1, - coin: args.item2, - ), - // ExchangeLoadingOverlayView( - // unawaitedLoad: args.item3, - // ), - ], + builder: (_) => Stack( + children: [ + WalletInitiatedExchangeView( + walletId: args.item1, + coin: args.item2, ), + // ExchangeLoadingOverlayView( + // unawaitedLoad: args.item3, + // ), + ], + ), settings: RouteSettings(name: settings.name), ); } if (args is Tuple3) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => Stack( - children: [ - WalletInitiatedExchangeView( - walletId: args.item1, - coin: args.item2, - contract: args.item3, - ), - // ExchangeLoadingOverlayView( - // unawaitedLoad: args.item3, - // ), - ], + builder: (_) => Stack( + children: [ + WalletInitiatedExchangeView( + walletId: args.item1, + coin: args.item2, + contract: args.item3, ), + // ExchangeLoadingOverlayView( + // unawaitedLoad: args.item3, + // ), + ], + ), settings: RouteSettings(name: settings.name), ); } @@ -1891,13 +1839,12 @@ class RouteGenerator { >) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => WalletSettingsView( - walletId: args.item1, - coin: args.item2, - initialSyncStatus: args.item3, - initialNodeStatus: args.item4, - ), + builder: (_) => WalletSettingsView( + walletId: args.item1, + coin: args.item2, + initialSyncStatus: args.item3, + initialNodeStatus: args.item4, + ), settings: RouteSettings(name: settings.name), ); } @@ -1907,11 +1854,10 @@ class RouteGenerator { if (args is ({String walletId, List mnemonicWords})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => DeleteWalletRecoveryPhraseView( - mnemonic: args.mnemonicWords, - walletId: args.walletId, - ), + builder: (_) => DeleteWalletRecoveryPhraseView( + mnemonic: args.mnemonicWords, + walletId: args.walletId, + ), settings: RouteSettings(name: settings.name), ); } else if (args @@ -1928,12 +1874,11 @@ class RouteGenerator { })) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => DeleteWalletRecoveryPhraseView( - mnemonic: args.mnemonicWords, - walletId: args.walletId, - frostWalletData: args.frostWalletData, - ), + builder: (_) => DeleteWalletRecoveryPhraseView( + mnemonic: args.mnemonicWords, + walletId: args.walletId, + frostWalletData: args.frostWalletData, + ), settings: RouteSettings(name: settings.name), ); } @@ -1943,11 +1888,10 @@ class RouteGenerator { if (args is ({String walletId, ViewOnlyWalletData data})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => DeleteViewOnlyWalletKeysView( - data: args.data, - walletId: args.walletId, - ), + builder: (_) => DeleteViewOnlyWalletKeysView( + data: args.data, + walletId: args.walletId, + ), settings: RouteSettings(name: settings.name), ); } @@ -1999,13 +1943,12 @@ class RouteGenerator { if (args is Tuple4) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => TradeDetailsView( - tradeId: args.item1, - transactionIfSentFromStack: args.item2, - walletId: args.item3, - walletName: args.item4, - ), + builder: (_) => TradeDetailsView( + tradeId: args.item1, + transactionIfSentFromStack: args.item2, + walletId: args.item3, + walletName: args.item4, + ), settings: RouteSettings(name: settings.name), ); } @@ -2025,13 +1968,12 @@ class RouteGenerator { if (args is Tuple4) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => SendFromView( - coin: args.item1, - amount: args.item2, - trade: args.item4, - address: args.item3, - ), + builder: (_) => SendFromView( + coin: args.item1, + amount: args.item2, + trade: args.item4, + address: args.item3, + ), settings: RouteSettings(name: settings.name), ); } @@ -2041,11 +1983,10 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => GenerateUriQrCodeView( - coin: args.item1, - receivingAddress: args.item2, - ), + builder: (_) => GenerateUriQrCodeView( + coin: args.item1, + receivingAddress: args.item2, + ), settings: RouteSettings(name: settings.name), ); } @@ -2095,11 +2036,10 @@ class RouteGenerator { if (args is ({String walletId, String domainName})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => BuyDomainView( - walletId: args.walletId, - domainName: args.domainName, - ), + builder: (_) => BuyDomainView( + walletId: args.walletId, + domainName: args.domainName, + ), settings: RouteSettings(name: settings.name), ); } @@ -2183,8 +2123,8 @@ class RouteGenerator { if (args is Tuple2) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => BuyInWalletView(coin: args.item1, contract: args.item2), + builder: (_) => + BuyInWalletView(coin: args.item1, contract: args.item2), settings: RouteSettings(name: settings.name), ); } @@ -2546,11 +2486,10 @@ class RouteGenerator { } else if (args is ({String walletId, bool popPrevious})) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: - (_) => TokenView( - walletId: args.walletId, - popPrevious: args.popPrevious, - ), + builder: (_) => TokenView( + walletId: args.walletId, + popPrevious: args.popPrevious, + ), settings: RouteSettings(name: settings.name), ); } diff --git a/lib/services/buy/simplex/simplex_api.dart b/lib/services/buy/simplex/simplex_api.dart index eed33ea1b0..8fff4972cb 100644 --- a/lib/services/buy/simplex/simplex_api.dart +++ b/lib/services/buy/simplex/simplex_api.dart @@ -51,15 +51,15 @@ class SimplexAPI { final Map headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; - final Map data = { - 'ROUTE': 'supported_cryptos', - }; + final Map data = {'ROUTE': 'supported_cryptos'}; final Uri url = _buildUri('api.php', data); final res = await client.post( url: url, headers: headers, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -78,10 +78,7 @@ class SimplexAPI { stackTrace: s, ); return BuyResponse( - exception: BuyException( - e.toString(), - BuyExceptionType.generic, - ), + exception: BuyException(e.toString(), BuyExceptionType.generic), ); } } @@ -107,16 +104,9 @@ class SimplexAPI { return BuyResponse(value: cryptos); } catch (e, s) { - Logging.instance.e( - "_parseSupported exception", - error: e, - stackTrace: s, - ); + Logging.instance.e("_parseSupported exception", error: e, stackTrace: s); return BuyResponse( - exception: BuyException( - e.toString(), - BuyExceptionType.generic, - ), + exception: BuyException(e.toString(), BuyExceptionType.generic), ); } } @@ -126,15 +116,15 @@ class SimplexAPI { final Map headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; - final Map data = { - 'ROUTE': 'supported_fiats', - }; + final Map data = {'ROUTE': 'supported_fiats'}; final Uri url = _buildUri('api.php', data); final res = await client.post( url: url, headers: headers, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -147,13 +137,13 @@ class SimplexAPI { return _parseSupportedFiats(jsonArray); } catch (e, s) { - Logging.instance - .e("getAvailableCurrencies exception: ", error: e, stackTrace: s); + Logging.instance.e( + "getAvailableCurrencies exception: ", + error: e, + stackTrace: s, + ); return BuyResponse( - exception: BuyException( - e.toString(), - BuyExceptionType.generic, - ), + exception: BuyException(e.toString(), BuyExceptionType.generic), ); } } @@ -169,8 +159,9 @@ class SimplexAPI { fiats.add( Fiat.fromJson({ 'ticker': "${fiat['ticker_symbol']}", - 'name': fiatFromTickerCaseInsensitive("${fiat['ticker_symbol']}") - .prettyName, + 'name': fiatFromTickerCaseInsensitive( + "${fiat['ticker_symbol']}", + ).prettyName, 'minAmount': "${fiat['min_amount']}", 'maxAmount': "${fiat['max_amount']}", 'image': "", @@ -181,16 +172,9 @@ class SimplexAPI { return BuyResponse(value: fiats); } catch (e, s) { - Logging.instance.e( - "_parseSupported exception", - error: e, - stackTrace: s, - ); + Logging.instance.e("_parseSupported exception", error: e, stackTrace: s); return BuyResponse( - exception: BuyException( - e.toString(), - BuyExceptionType.generic, - ), + exception: BuyException(e.toString(), BuyExceptionType.generic), ); } } @@ -222,7 +206,9 @@ class SimplexAPI { final res = await client.get( url: url, headers: headers, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -241,16 +227,9 @@ class SimplexAPI { return _parseQuote(jsonArray); } catch (e, s) { - Logging.instance.e( - "getQuote exception", - error: e, - stackTrace: s, - ); + Logging.instance.e("getQuote exception", error: e, stackTrace: s); return BuyResponse( - exception: BuyException( - e.toString(), - BuyExceptionType.generic, - ), + exception: BuyException(e.toString(), BuyExceptionType.generic), ); } } @@ -280,8 +259,9 @@ class SimplexAPI { youPayFiatPrice: quote.buyWithFiat ? quote.youPayFiatPrice : Decimal.parse("${jsonArray['fiat_money']['base_amount']}"), - youReceiveCryptoAmount: - Decimal.parse("${jsonArray['digital_money']['amount']}"), + youReceiveCryptoAmount: Decimal.parse( + "${jsonArray['digital_money']['amount']}", + ), id: jsonArray['quote_id'] as String, receivingAddress: quote.receivingAddress, buyWithFiat: quote.buyWithFiat, @@ -289,16 +269,9 @@ class SimplexAPI { return BuyResponse(value: _quote); } catch (e, s) { - Logging.instance.e( - "_parseQuote exception", - error: e, - stackTrace: s, - ); + Logging.instance.e("_parseQuote exception", error: e, stackTrace: s); return BuyResponse( - exception: BuyException( - e.toString(), - BuyExceptionType.generic, - ), + exception: BuyException(e.toString(), BuyExceptionType.generic), ); } } @@ -329,8 +302,9 @@ class SimplexAPI { data['USER_ID'] = userID; } if (signupEpoch != null && signupEpoch != 0) { - final DateTime date = - DateTime.fromMillisecondsSinceEpoch(signupEpoch * 1000); + final DateTime date = DateTime.fromMillisecondsSinceEpoch( + signupEpoch * 1000, + ); data['SIGNUP_TIMESTAMP'] = date.toIso8601String() + timeZoneFormatter(date.timeZoneOffset); } @@ -339,7 +313,9 @@ class SimplexAPI { final res = await client.get( url: url, headers: headers, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -362,16 +338,9 @@ class SimplexAPI { return BuyResponse(value: _order); } catch (e, s) { - Logging.instance.e( - "newOrder exception", - error: e, - stackTrace: s, - ); + Logging.instance.e("newOrder exception", error: e, stackTrace: s); return BuyResponse( - exception: BuyException( - e.toString(), - BuyExceptionType.generic, - ), + exception: BuyException(e.toString(), BuyExceptionType.generic), ); } } @@ -394,16 +363,9 @@ class SimplexAPI { return BuyResponse(value: status); } catch (e, s) { - Logging.instance.e( - "newOrder exception", - error: e, - stackTrace: s, - ); + Logging.instance.e("newOrder exception", error: e, stackTrace: s); return BuyResponse( - exception: BuyException( - e.toString(), - BuyExceptionType.generic, - ), + exception: BuyException(e.toString(), BuyExceptionType.generic), ); } } diff --git a/lib/services/coins/tezos/api/tezos_api.dart b/lib/services/coins/tezos/api/tezos_api.dart index 6c21935775..a85e6b25d1 100644 --- a/lib/services/coins/tezos/api/tezos_api.dart +++ b/lib/services/coins/tezos/api/tezos_api.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import '../../../../app_config.dart'; import '../../../../networking/http.dart'; import '../../../../utilities/logger.dart'; import '../../../../utilities/prefs.dart'; @@ -17,7 +18,9 @@ abstract final class TezosAPI { final response = await _client.get( url: Uri.parse(uriString), headers: {'Content-Type': 'application/json'}, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -25,7 +28,11 @@ abstract final class TezosAPI { final result = jsonDecode(response.body); return result as int; } catch (e, s) { - Logging.instance.e("Error occurred in TezosAPI while getting counter for $address: ", error: e, stackTrace: s); + Logging.instance.e( + "Error occurred in TezosAPI while getting counter for $address: ", + error: e, + stackTrace: s, + ); rethrow; } } @@ -39,7 +46,9 @@ abstract final class TezosAPI { final response = await _client.get( url: Uri.parse(uriString), headers: {'Content-Type': 'application/json'}, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -50,7 +59,11 @@ abstract final class TezosAPI { return account; } catch (e, s) { - Logging.instance.e("Error occurred in TezosAPI while getting account for $address: ", error: e, stackTrace: s); + Logging.instance.e( + "Error occurred in TezosAPI while getting account for $address: ", + error: e, + stackTrace: s, + ); rethrow; } } @@ -63,7 +76,9 @@ abstract final class TezosAPI { final response = await _client.get( url: Uri.parse(transactionsCall), headers: {'Content-Type': 'application/json'}, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -78,9 +93,10 @@ abstract final class TezosAPI { hash: tx["hash"] as String, type: tx["type"] as String, height: tx["level"] as int, - timestamp: DateTime.parse(tx["timestamp"].toString()) - .toUtc() - .millisecondsSinceEpoch ~/ + timestamp: + DateTime.parse( + tx["timestamp"].toString(), + ).toUtc().millisecondsSinceEpoch ~/ 1000, cycle: tx["cycle"] as int?, counter: tx["counter"] as int, @@ -91,7 +107,8 @@ abstract final class TezosAPI { gasUsed: tx["gasUsed"] as int, storageLimit: tx["storageLimit"] as int?, amountInMicroTez: tx["amount"] as int, - feeInMicroTez: (tx["bakerFee"] as int? ?? 0) + + feeInMicroTez: + (tx["bakerFee"] as int? ?? 0) + (tx["storageFee"] as int? ?? 0) + (tx["allocationFee"] as int? ?? 0), burnedAmountInMicroTez: tx["burned"] as int?, @@ -103,7 +120,11 @@ abstract final class TezosAPI { } return txs; } catch (e, s) { - Logging.instance.e("Error occurred in TezosAPI while getting transactions for $address: ", error: e, stackTrace: s); + Logging.instance.e( + "Error occurred in TezosAPI while getting transactions for $address: ", + error: e, + stackTrace: s, + ); rethrow; } } diff --git a/lib/services/coins/tezos/api/tezos_rpc_api.dart b/lib/services/coins/tezos/api/tezos_rpc_api.dart index 688d9f68e5..67316c20ac 100644 --- a/lib/services/coins/tezos/api/tezos_rpc_api.dart +++ b/lib/services/coins/tezos/api/tezos_rpc_api.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import '../../../../app_config.dart'; import '../../../../networking/http.dart'; import '../../../../utilities/logger.dart'; import '../../../../utilities/prefs.dart'; @@ -19,13 +20,16 @@ abstract final class TezosRpcAPI { final response = await _client.get( url: Uri.parse(balanceCall), headers: {'Content-Type': 'application/json'}, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); - final balance = - BigInt.parse(response.body.substring(1, response.body.length - 2)); + final balance = BigInt.parse( + response.body.substring(1, response.body.length - 2), + ); return balance; } catch (e, s) { Logging.instance.e( @@ -47,7 +51,9 @@ abstract final class TezosRpcAPI { final response = await _client.get( url: Uri.parse(api), headers: {'Content-Type': 'application/json'}, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); diff --git a/lib/services/ethereum/ethereum_api.dart b/lib/services/ethereum/ethereum_api.dart index fd644944a0..efffc5105b 100644 --- a/lib/services/ethereum/ethereum_api.dart +++ b/lib/services/ethereum/ethereum_api.dart @@ -10,6 +10,7 @@ import 'dart:convert'; +import '../../app_config.dart'; import '../../dto/ethereum/eth_token_tx_dto.dart'; import '../../dto/ethereum/eth_tx_dto.dart'; import '../../models/isar/models/ethereum/eth_contract.dart'; @@ -57,10 +58,11 @@ abstract class EthereumAPI { url: Uri.parse( "$stackBaseServer/export?addrs=$address&firstBlock=$firstBlock&unripe=true", ), - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); if (response.code == 200) { @@ -110,10 +112,11 @@ abstract class EthereumAPI { url: Uri.parse( "$stackBaseServer/export?addrs=$address&emitter=$tokenContractAddress&logs=true", ), - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); if (response.code == 200) { @@ -164,10 +167,11 @@ abstract class EthereumAPI { ); final response = await client.get( url: uri, - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); if (response.code == 200) { @@ -203,10 +207,11 @@ abstract class EthereumAPI { try { final response = await client.get( url: Uri.parse("$stackBaseServer/gas-prices"), - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); if (response.code == 200) { @@ -267,10 +272,11 @@ abstract class EthereumAPI { url: Uri.parse( "$stackBaseServer/names?terms=$contractAddress&autoname=$contractAddress&all", ), - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); } @@ -284,10 +290,11 @@ abstract class EthereumAPI { // "$stackBaseServer/tokens?addrs=$contractAddress&parts=all", "$stackBaseServer/names?terms=$contractAddress&all", ), - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); if (response.code == 200) { @@ -371,10 +378,11 @@ abstract class EthereumAPI { url: Uri.parse( "$stackBaseServer/abis?addrs=$contractAddress&verbose=true", ), - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); if (response.code == 200) { @@ -408,10 +416,11 @@ abstract class EthereumAPI { url: Uri.parse( "$stackBaseServer/state?addrs=$contractAddress&parts=proxy", ), - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); if (response.code == 200) { final json = jsonDecode(response.body); diff --git a/lib/services/exchange/change_now/change_now_api.dart b/lib/services/exchange/change_now/change_now_api.dart index 7b5cd07375..870287448c 100644 --- a/lib/services/exchange/change_now/change_now_api.dart +++ b/lib/services/exchange/change_now/change_now_api.dart @@ -13,6 +13,7 @@ import 'dart:convert'; import 'package:decimal/decimal.dart'; import 'package:flutter/foundation.dart'; +import '../../../app_config.dart'; import '../../../exceptions/exchange/exchange_exception.dart'; import '../../../exceptions/exchange/pair_unavailable_exception.dart'; import '../../../external_api_keys.dart'; @@ -73,10 +74,11 @@ class ChangeNowAPI { "Content-Type": "application/json", "x-changenow-api-key": apiKey, }, - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); final data = response.body; @@ -103,10 +105,11 @@ class ChangeNowAPI { "x-changenow-api-key": apiKey, }, body: jsonEncode(body), - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); String? data; @@ -209,10 +212,9 @@ class ChangeNowAPI { currencies.add( Currency.fromJson( map, - rateType: - (map["supportsFixedRate"] as bool) - ? SupportedRateType.both - : SupportedRateType.estimated, + rateType: (map["supportsFixedRate"] as bool) + ? SupportedRateType.both + : SupportedRateType.estimated, exchangeName: ChangeNowExchange.exchangeName, ), ); diff --git a/lib/services/exchange/nanswap/nanswap_api.dart b/lib/services/exchange/nanswap/nanswap_api.dart index 6e2848586d..00aea8d212 100644 --- a/lib/services/exchange/nanswap/nanswap_api.dart +++ b/lib/services/exchange/nanswap/nanswap_api.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; +import '../../../app_config.dart'; import '../../../exceptions/exchange/exchange_exception.dart'; import '../../../external_api_keys.dart'; import '../../../networking/http.dart'; @@ -34,10 +35,11 @@ class NanswapAPI { final response = await _client.get( url: uri, headers: {'Accept': 'application/json'}, - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); code = response.code; @@ -66,10 +68,11 @@ class NanswapAPI { 'Accept': 'application/json', }, body: jsonEncode(body), - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); code = response.code; diff --git a/lib/services/exchange/simpleswap/simpleswap_api.dart b/lib/services/exchange/simpleswap/simpleswap_api.dart index 0a532822d5..d7440121fe 100644 --- a/lib/services/exchange/simpleswap/simpleswap_api.dart +++ b/lib/services/exchange/simpleswap/simpleswap_api.dart @@ -15,6 +15,7 @@ import 'package:flutter/foundation.dart'; import 'package:tuple/tuple.dart'; import 'package:uuid/uuid.dart'; +import '../../../app_config.dart'; import '../../../exceptions/exchange/exchange_exception.dart'; import '../../../external_api_keys.dart'; import '../../../models/exchange/response_objects/fixed_rate_market.dart'; @@ -48,7 +49,9 @@ class SimpleSwapAPI { try { final response = await client.get( url: uri, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -59,22 +62,24 @@ class SimpleSwapAPI { return parsed; } catch (e, s) { - Logging.instance - .e("_makeRequest($uri) HTTP:$code threw: ", error: e, stackTrace: s); + Logging.instance.e( + "_makeRequest($uri) HTTP:$code threw: ", + error: e, + stackTrace: s, + ); rethrow; } } - Future _makePostRequest( - Uri uri, - Map body, - ) async { + Future _makePostRequest(Uri uri, Map body) async { try { final response = await client.post( url: uri, headers: {'Content-Type': 'application/json'}, body: jsonEncode(body), - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -86,11 +91,7 @@ class SimpleSwapAPI { throw Exception("response: ${response.body}"); } catch (e, s) { - Logging.instance.e( - "_makeRequest($uri) threw", - error: e, - stackTrace: s, - ); + Logging.instance.e("_makeRequest($uri) threw", error: e, stackTrace: s); rethrow; } } @@ -117,8 +118,9 @@ class SimpleSwapAPI { "extraIdTo": extraIdTo, }; - final uri = - _buildUri("/create_exchange", {"api_key": apiKey ?? kSimpleSwapApiKey}); + final uri = _buildUri("/create_exchange", { + "api_key": apiKey ?? kSimpleSwapApiKey, + }); try { final jsonObject = await _makePostRequest(uri, body); @@ -150,8 +152,11 @@ class SimpleSwapAPI { ); return ExchangeResponse(value: trade, exception: null); } catch (e, s) { - Logging.instance - .e("getAvailableCurrencies exception: ", error: e, stackTrace: s); + Logging.instance.e( + "getAvailableCurrencies exception: ", + error: e, + stackTrace: s, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -166,18 +171,20 @@ class SimpleSwapAPI { String? apiKey, required bool fixedRate, }) async { - final uri = _buildUri( - "/get_all_currencies", - {"api_key": apiKey ?? kSimpleSwapApiKey}, - ); + final uri = _buildUri("/get_all_currencies", { + "api_key": apiKey ?? kSimpleSwapApiKey, + }); try { final jsonArray = await _makeGetRequest(uri); return await compute(_parseAvailableCurrenciesJson, jsonArray as List); } catch (e, s) { - Logging.instance - .e("getAvailableCurrencies exception: ", error: e, stackTrace: s); + Logging.instance.e( + "getAvailableCurrencies exception: ", + error: e, + stackTrace: s, + ); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -195,8 +202,9 @@ class SimpleSwapAPI { for (final json in jsonArray) { try { - currencies - .add(SPCurrency.fromJson(Map.from(json as Map))); + currencies.add( + SPCurrency.fromJson(Map.from(json as Map)), + ); } catch (_) { return ExchangeResponse( exception: ExchangeException( @@ -227,13 +235,10 @@ class SimpleSwapAPI { required String symbol, String? apiKey, }) async { - final uri = _buildUri( - "/get_currency", - { - "api_key": apiKey ?? kSimpleSwapApiKey, - "symbol": symbol, - }, - ); + final uri = _buildUri("/get_currency", { + "api_key": apiKey ?? kSimpleSwapApiKey, + "symbol": symbol, + }); try { final jsonObject = await _makeGetRequest(uri); @@ -244,11 +249,7 @@ class SimpleSwapAPI { ), ); } catch (e, s) { - Logging.instance.e( - "getCurrency exception", - error: e, - stackTrace: s, - ); + Logging.instance.e("getCurrency exception", error: e, stackTrace: s); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -264,13 +265,10 @@ class SimpleSwapAPI { required bool isFixedRate, String? apiKey, }) async { - final uri = _buildUri( - "/get_all_pairs", - { - "api_key": apiKey ?? kSimpleSwapApiKey, - "fixed": isFixedRate.toString(), - }, - ); + final uri = _buildUri("/get_all_pairs", { + "api_key": apiKey ?? kSimpleSwapApiKey, + "fixed": isFixedRate.toString(), + }); try { final jsonObject = await _makeGetRequest(uri); @@ -280,11 +278,7 @@ class SimpleSwapAPI { ); return result; } catch (e, s) { - Logging.instance.e( - "getAllPairs exception", - error: e, - stackTrace: s, - ); + Logging.instance.e("getAllPairs exception", error: e, stackTrace: s); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -347,27 +341,20 @@ class SimpleSwapAPI { required String amount, String? apiKey, }) async { - final uri = _buildUri( - "/get_estimated", - { - "api_key": apiKey ?? kSimpleSwapApiKey, - "fixed": isFixedRate.toString(), - "currency_from": currencyFrom, - "currency_to": currencyTo, - "amount": amount, - }, - ); + final uri = _buildUri("/get_estimated", { + "api_key": apiKey ?? kSimpleSwapApiKey, + "fixed": isFixedRate.toString(), + "currency_from": currencyFrom, + "currency_to": currencyTo, + "amount": amount, + }); try { final jsonObject = await _makeGetRequest(uri); return ExchangeResponse(value: jsonObject as String); } catch (e, s) { - Logging.instance.e( - "getEstimated exception", - error: e, - stackTrace: s, - ); + Logging.instance.e("getEstimated exception", error: e, stackTrace: s); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -383,13 +370,10 @@ class SimpleSwapAPI { String? apiKey, Trade? oldTrade, }) async { - final uri = _buildUri( - "/get_exchange", - { - "api_key": apiKey ?? kSimpleSwapApiKey, - "id": exchangeId, - }, - ); + final uri = _buildUri("/get_exchange", { + "api_key": apiKey ?? kSimpleSwapApiKey, + "id": exchangeId, + }); try { final jsonObject = await _makeGetRequest(uri); @@ -423,11 +407,7 @@ class SimpleSwapAPI { return ExchangeResponse(value: trade); } catch (e, s) { - Logging.instance.e( - "getExchange exception", - error: e, - stackTrace: s, - ); + Logging.instance.e("getExchange exception", error: e, stackTrace: s); return ExchangeResponse( exception: ExchangeException( e.toString(), @@ -444,15 +424,12 @@ class SimpleSwapAPI { required String currencyTo, String? apiKey, }) async { - final uri = _buildUri( - "/get_ranges", - { - "api_key": apiKey ?? kSimpleSwapApiKey, - "fixed": isFixedRate.toString(), - "currency_from": currencyFrom, - "currency_to": currencyTo, - }, - ); + final uri = _buildUri("/get_ranges", { + "api_key": apiKey ?? kSimpleSwapApiKey, + "fixed": isFixedRate.toString(), + "currency_from": currencyFrom, + "currency_to": currencyTo, + }); try { final jsonObject = await _makeGetRequest(uri); @@ -465,11 +442,7 @@ class SimpleSwapAPI { ), ); } catch (e, s) { - Logging.instance.e( - "getRange exception", - error: e, - stackTrace: s, - ); + Logging.instance.e("getRange exception", error: e, stackTrace: s); return ExchangeResponse( exception: ExchangeException( e.toString(), diff --git a/lib/services/exchange/trocador/trocador_api.dart b/lib/services/exchange/trocador/trocador_api.dart index 917fee3f3c..2869cef7a3 100644 --- a/lib/services/exchange/trocador/trocador_api.dart +++ b/lib/services/exchange/trocador/trocador_api.dart @@ -12,6 +12,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; +import '../../../app_config.dart'; import '../../../exceptions/exchange/exchange_exception.dart'; import '../../../networking/http.dart'; import '../../../utilities/extensions/extensions.dart'; @@ -56,10 +57,11 @@ abstract class TrocadorAPI { "Content-Type": "application/json", "API-KEY": kTrocadorApiKey, }, - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); code = response.code; @@ -99,8 +101,9 @@ abstract class TrocadorAPI { if (json is List) { final list = List>.from(json); - final List coins = - list.map((e) => TrocadorCoin.fromMap(e)).toList(); + final List coins = list + .map((e) => TrocadorCoin.fromMap(e)) + .toList(); return ExchangeResponse(value: coins); } else { diff --git a/lib/services/fusion_tor_service.dart b/lib/services/fusion_tor_service.dart index caa0ec2ee6..1ad9beb4fa 100644 --- a/lib/services/fusion_tor_service.dart +++ b/lib/services/fusion_tor_service.dart @@ -1,49 +1,18 @@ import 'dart:io'; -import 'package:tor_ffi_plugin/tor_ffi_plugin.dart'; +import '../wl_gen/generated/tor_service_impl.dart'; -import '../utilities/logger.dart'; - -class FusionTorService { - Tor? _tor; - String? _torDataDirPath; - - TorStatus get status => _tor!.status; - - /// Singleton instance of the TorService. - /// - /// Use this to access the TorService and its properties. - static final sharedInstance = FusionTorService._(); - - // private constructor for singleton - FusionTorService._(); +abstract class FusionTorService { + static FusionTorService get sharedInstance => fusionTorService; /// Getter for the proxyInfo. /// /// Throws if Tor is not connected. - ({ - InternetAddress host, - int port, - }) getProxyInfo() { - try { - return ( - host: InternetAddress.loopbackIPv4, - port: _tor!.port, - ); - } catch (_) { - throw Exception("Tor proxy info fetched while not connected!"); - } - } + ({InternetAddress host, int port}) getProxyInfo(); /// Initialize the tor ffi lib instance if it hasn't already been set. Nothing /// changes if _tor is already been set. - void init({ - required String torDataDirPath, - Tor? mockableOverride, - }) { - _tor ??= mockableOverride ?? Tor.instance; - _torDataDirPath ??= torDataDirPath; - } + void init({required String torDataDirPath}); /// Start the Tor service. /// @@ -53,17 +22,5 @@ class FusionTorService { /// service fails to start. /// /// Returns a Future that completes when the Tor service has started. - Future start() async { - if (_tor == null || _torDataDirPath == null) { - throw Exception("FusionTorService.init has not been called!"); - } - - // Start the Tor service. - try { - await _tor!.start(torDataDirPath: _torDataDirPath!); - } catch (e, s) { - Logging.instance.w("FusionTorService.start failed: ", error: e, stackTrace: s); - rethrow; - } - } + Future start(); } diff --git a/lib/services/litescribe_api.dart b/lib/services/litescribe_api.dart index 9a41f4f2b9..244043f9af 100644 --- a/lib/services/litescribe_api.dart +++ b/lib/services/litescribe_api.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import '../app_config.dart'; import '../dto/ordinals/inscription_data.dart'; import '../dto/ordinals/litescribe_response.dart'; import '../networking/http.dart'; @@ -22,7 +23,9 @@ class LitescribeAPI { Future _getResponse(String endpoint) async { final response = await client.get( url: Uri.parse('$baseUrl$endpoint'), - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); diff --git a/lib/services/monkey_service.dart b/lib/services/monkey_service.dart index 0ae745d349..bbdd49ac36 100644 --- a/lib/services/monkey_service.dart +++ b/lib/services/monkey_service.dart @@ -2,6 +2,7 @@ import 'dart:typed_data'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../app_config.dart'; import '../networking/http.dart'; import '../utilities/logger.dart'; import '../utilities/prefs.dart'; @@ -26,7 +27,9 @@ class MonKeyService { final response = await client.get( url: Uri.parse(url), - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -34,12 +37,14 @@ class MonKeyService { if (response.code == 200) { return Uint8List.fromList(response.bodyBytes); } else { - throw Exception( - "statusCode=${response.code} body=${response.body}", - ); + throw Exception("statusCode=${response.code} body=${response.body}"); } } catch (e, s) { - Logging.instance.e("Failed fetchMonKey($address): ", error: e, stackTrace: s); + Logging.instance.e( + "Failed fetchMonKey($address): ", + error: e, + stackTrace: s, + ); rethrow; } } diff --git a/lib/services/nano_api.dart b/lib/services/nano_api.dart index 8627b67b3f..9e425dd14b 100644 --- a/lib/services/nano_api.dart +++ b/lib/services/nano_api.dart @@ -2,16 +2,14 @@ import 'dart:convert'; import 'package:nanodart/nanodart.dart'; +import '../app_config.dart'; import '../networking/http.dart'; import '../utilities/prefs.dart'; import 'tor_service.dart'; class NanoAPI { - static Future< - ({ - NAccountInfo? accountInfo, - Exception? exception, - })> getAccountInfo({ + static Future<({NAccountInfo? accountInfo, Exception? exception})> + getAccountInfo({ required Uri server, required bool representative, required String account, @@ -31,7 +29,9 @@ class NanoAPI { "representative": "true", "account": account, }), - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -99,11 +99,7 @@ class NanoAPI { block["signature"] = signature; - final map = await postBlock( - server: server, - block: block, - headers: headers, - ); + final map = await postBlock(server: server, block: block, headers: headers); if (map is Map && map["error"] != null) { throw Exception(map["error"].toString()); @@ -129,7 +125,9 @@ class NanoAPI { "subtype": "change", "block": block, }), - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); diff --git a/lib/services/price.dart b/lib/services/price.dart index 7748271281..ee08952eeb 100644 --- a/lib/services/price.dart +++ b/lib/services/price.dart @@ -148,10 +148,11 @@ class PriceAPI { final coinGeckoResponse = await client.get( url: uri, headers: {'Content-Type': 'application/json'}, - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); final coinGeckoData = jsonDecode(coinGeckoResponse.body) as List; @@ -162,10 +163,9 @@ class PriceAPI { try { final price = Decimal.parse(map["current_price"].toString()); - final change24h = - map["price_change_percentage_24h"] != null - ? double.parse(map["price_change_percentage_24h"].toString()) - : 0.0; + final change24h = map["price_change_percentage_24h"] != null + ? double.parse(map["price_change_percentage_24h"].toString()) + : 0.0; result[coin] = (value: price, change24h: change24h); } catch (_) { @@ -204,10 +204,11 @@ class PriceAPI { final response = await client.get( url: uri, headers: {'Content-Type': 'application/json'}, - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); final json = jsonDecode(response.body) as List; diff --git a/lib/services/tor_service.dart b/lib/services/tor_service.dart index 542032df74..a4d43c1cf0 100644 --- a/lib/services/tor_service.dart +++ b/lib/services/tor_service.dart @@ -1,56 +1,26 @@ import 'dart:io'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:tor_ffi_plugin/tor_ffi_plugin.dart'; -import '../utilities/logger.dart'; +import '../wl_gen/generated/tor_service_impl.dart'; import 'event_bus/events/global/tor_connection_status_changed_event.dart'; -import 'event_bus/global_event_bus.dart'; -final pTorService = Provider((_) => TorService.sharedInstance); +final pTorService = Provider((_) => torService); -class TorService { - Tor? _tor; - String? _torDataDirPath; +abstract class TorService { + static TorService get sharedInstance => torService; /// Current status. Same as that fired on the event bus. - TorConnectionStatus get status => _status; - TorConnectionStatus _status = TorConnectionStatus.disconnected; - - /// Singleton instance of the TorService. - /// - /// Use this to access the TorService and its properties. - static final sharedInstance = TorService._(); - - // private constructor for singleton - TorService._(); + TorConnectionStatus get status; /// Getter for the proxyInfo. /// /// Throws if Tor is not connected. - ({ - InternetAddress host, - int port, - }) getProxyInfo() { - if (status == TorConnectionStatus.connected) { - return ( - host: InternetAddress.loopbackIPv4, - port: _tor!.port, - ); - } else { - throw Exception("Tor proxy info fetched while not connected!"); - } - } + ({InternetAddress host, int port}) getProxyInfo(); /// Initialize the tor ffi lib instance if it hasn't already been set. Nothing /// changes if _tor is already been set. - void init({ - required String torDataDirPath, - Tor? mockableOverride, - }) { - _tor ??= mockableOverride ?? Tor.instance; - _torDataDirPath ??= torDataDirPath; - } + void init({required String torDataDirPath}); /// Start the Tor service. /// @@ -60,75 +30,8 @@ class TorService { /// service fails to start. /// /// Returns a Future that completes when the Tor service has started. - Future start() async { - if (_tor == null || _torDataDirPath == null) { - throw Exception("TorService.init has not been called!"); - } - - // Start the Tor service. - try { - _updateStatusAndFireEvent( - status: TorConnectionStatus.connecting, - message: "TorService.start call in progress", - ); - - await _tor!.start(torDataDirPath: _torDataDirPath!); - - // no exception or error so we can (probably?) assume tor - // has started successfully - // Fire a TorConnectionStatusChangedEvent on the event bus. - _updateStatusAndFireEvent( - status: TorConnectionStatus.connected, - message: "TorService.start call success", - ); - - // Complete the future. - return; - } catch (e, s) { - Logging.instance.w("TorService.start failed: ", error: e, stackTrace: s); - // _enabled should already be false - - // Fire a TorConnectionStatusChangedEvent on the event bus. - _updateStatusAndFireEvent( - status: TorConnectionStatus.disconnected, - message: "TorService.start call failed", - ); - rethrow; - } - } + Future start(); /// Disable Tor. - Future disable() async { - if (_tor == null) { - throw Exception("TorService.init has not been called!"); - } - - // No need to update status and fire event if status won't change. - if (_status == TorConnectionStatus.disconnected) { - return; - } - - _tor!.disable(); - await _tor?.stop(); - - _updateStatusAndFireEvent( - status: TorConnectionStatus.disconnected, - message: "TorService.disable call success", - ); - - return; - } - - void _updateStatusAndFireEvent({ - required TorConnectionStatus status, - required String message, - }) { - _status = status; - GlobalEventBus.instance.fire( - TorConnectionStatusChangedEvent( - _status, - message, - ), - ); - } + Future disable(); } diff --git a/lib/themes/theme_service.dart b/lib/themes/theme_service.dart index 47d25a0227..04e32f6de2 100644 --- a/lib/themes/theme_service.dart +++ b/lib/themes/theme_service.dart @@ -92,12 +92,11 @@ class ThemeService { Future remove({required String themeId}) async { final themesDir = StackFileSystem.themesDir!; - final isarId = - await db.isar.stackThemes - .where() - .themeIdEqualTo(themeId) - .idProperty() - .findFirst(); + final isarId = await db.isar.stackThemes + .where() + .themeIdEqualTo(themeId) + .idProperty() + .findFirst(); if (isarId != null) { await db.isar.writeTxn(() async { await db.isar.stackThemes.delete(isarId); @@ -149,8 +148,10 @@ class ThemeService { // TODO more thorough check/verification of theme Future verifyInstalled({required String themeId}) async { - final theme = - await db.isar.stackThemes.where().themeIdEqualTo(themeId).findFirst(); + final theme = await db.isar.stackThemes + .where() + .themeIdEqualTo(themeId) + .findFirst(); if (theme != null) { try { theme.assets; @@ -159,10 +160,12 @@ class ThemeService { } final themesDir = StackFileSystem.themesDir!; - final jsonFileExists = - await File("${themesDir.path}/$themeId/theme.json").exists(); - final assetsDirExists = - await Directory("${themesDir.path}/$themeId/assets").exists(); + final jsonFileExists = await File( + "${themesDir.path}/$themeId/theme.json", + ).exists(); + final assetsDirExists = await Directory( + "${themesDir.path}/$themeId/assets", + ).exists(); if (!jsonFileExists || !assetsDirExists) { Logging.instance.w( @@ -181,19 +184,19 @@ class ThemeService { try { final response = await client.get( url: Uri.parse("$baseServerUrl/themes"), - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); final jsonList = jsonDecode(response.body) as List; - final result = - List>.from(jsonList) - .map((e) => StackThemeMetaData.fromMap(e)) - .where((e) => e.id != "light" && e.id != "dark") - .toList(); + final result = List>.from(jsonList) + .map((e) => StackThemeMetaData.fromMap(e)) + .where((e) => e.id != "light" && e.id != "dark") + .toList(); return result; } catch (e, s) { @@ -212,10 +215,11 @@ class ThemeService { try { final response = await client.get( url: Uri.parse("$baseServerUrl/theme/${themeMetaData.id}"), - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); final bytes = Uint8List.fromList(response.bodyBytes); diff --git a/lib/utilities/connection_check/electrum_connection_check.dart b/lib/utilities/connection_check/electrum_connection_check.dart index 325cfa059e..478d5e5b3a 100644 --- a/lib/utilities/connection_check/electrum_connection_check.dart +++ b/lib/utilities/connection_check/electrum_connection_check.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:electrum_adapter/electrum_adapter.dart'; +import '../../app_config.dart'; import '../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; import '../../services/tor_service.dart'; import '../logger.dart'; @@ -15,13 +16,12 @@ Future checkElectrumServer({ TorService? overrideTorService, }) async { final _prefs = overridePrefs ?? Prefs.instance; - final _torService = overrideTorService ?? TorService.sharedInstance; ({InternetAddress host, int port})? proxyInfo; try { - // If we're supposed to use Tor... - if (_prefs.useTor) { + if (AppConfig.hasFeature(AppFeature.tor) && _prefs.useTor) { + final _torService = overrideTorService ?? TorService.sharedInstance; // But Tor isn't running... if (_torService.status != TorConnectionStatus.connected) { // And the killswitch isn't set... @@ -43,29 +43,26 @@ Future checkElectrumServer({ } } - final client = await ElectrumClient.connect( - host: host, - port: port, - useSSL: useSSL && !host.endsWith('.onion'), - proxyInfo: proxyInfo, - ).timeout( + final client = + await ElectrumClient.connect( + host: host, + port: port, + useSSL: useSSL && !host.endsWith('.onion'), + proxyInfo: proxyInfo, + ).timeout( + Duration(seconds: (proxyInfo == null ? 5 : 30)), + onTimeout: () => throw Exception( + "The checkElectrumServer connect() call timed out.", + ), + ); + + await client.ping().timeout( Duration(seconds: (proxyInfo == null ? 5 : 30)), - onTimeout: () => throw Exception( - "The checkElectrumServer connect() call timed out.", - ), ); - await client - .ping() - .timeout(Duration(seconds: (proxyInfo == null ? 5 : 30))); - return true; } catch (e, s) { - Logging.instance.e( - "$e\n$s", - error: e, - stackTrace: s, - ); + Logging.instance.e("$e\n$s", error: e, stackTrace: s); return false; } } diff --git a/lib/utilities/paynym_is_api.dart b/lib/utilities/paynym_is_api.dart index f81fc855a6..9285fef7f6 100644 --- a/lib/utilities/paynym_is_api.dart +++ b/lib/utilities/paynym_is_api.dart @@ -12,6 +12,7 @@ import 'dart:convert'; import 'package:tuple/tuple.dart'; +import '../app_config.dart'; import '../models/paynym/created_paynym.dart'; import '../models/paynym/paynym_account.dart'; import '../models/paynym/paynym_claim.dart'; @@ -44,14 +45,16 @@ class PaynymIsApi { final headers = { 'Content-Type': 'application/json; charset=UTF-8', - 'content-length': - contentLength.toString(), // Set the Content-Length header. + 'content-length': contentLength + .toString(), // Set the Content-Length header. }..addAll(additionalHeaders); final response = await client.post( url: uri, headers: headers, body: jsonEncode(body), - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -414,13 +417,8 @@ class PaynymIsApi { ) async { final result = await _post( "/follow", - { - "target": target, - "signature": signature, - }, - { - "auth-token": token, - }, + {"target": target, "signature": signature}, + {"auth-token": token}, ); String message; @@ -495,13 +493,8 @@ class PaynymIsApi { ) async { final result = await _post( "/unfollow", - { - "target": target, - "signature": signature, - }, - { - "auth-token": token, - }, + {"target": target, "signature": signature}, + {"auth-token": token}, ); String message; @@ -579,14 +572,8 @@ class PaynymIsApi { ) async { final result = await _post( "/nym/add", - { - "nym": nym, - "code": code, - "signature": signature, - }, - { - "auth-token": token, - }, + {"nym": nym, "code": code, "signature": signature}, + {"auth-token": token}, ); String message; diff --git a/lib/utilities/test_epic_box_connection.dart b/lib/utilities/test_epic_box_connection.dart index 1c39390e25..89eefd4421 100644 --- a/lib/utilities/test_epic_box_connection.dart +++ b/lib/utilities/test_epic_box_connection.dart @@ -10,6 +10,7 @@ import 'dart:convert'; +import '../app_config.dart'; import '../networking/http.dart'; import '../pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart'; import '../services/tor_service.dart'; @@ -24,7 +25,9 @@ Future _testEpicBoxNodeConnection(Uri uri) async { .get( url: uri, headers: {'Content-Type': 'application/json'}, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ) @@ -41,7 +44,7 @@ Future _testEpicBoxNodeConnection(Uri uri) async { return false; } } catch (e, s) { - Logging.instance.w("$e\n$s", error: e, stackTrace: s,); + Logging.instance.w("$e\n$s", error: e, stackTrace: s); return false; } } @@ -87,7 +90,7 @@ Future testEpicNodeConnection(NodeFormData data) async { return null; } } catch (e, s) { - Logging.instance.w("$e\n$s", error: e, stackTrace: s,); + Logging.instance.w("$e\n$s", error: e, stackTrace: s); return null; } } diff --git a/lib/utilities/test_mwcmqs_connection.dart b/lib/utilities/test_mwcmqs_connection.dart index c0615b25da..c199afcbf3 100644 --- a/lib/utilities/test_mwcmqs_connection.dart +++ b/lib/utilities/test_mwcmqs_connection.dart @@ -10,6 +10,7 @@ import 'dart:convert'; +import '../app_config.dart'; import '../networking/http.dart'; import '../pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart'; import '../services/tor_service.dart'; @@ -31,10 +32,11 @@ Future _testMwcMqsNodeConnection(Uri uri) async { .get( url: uri, headers: headers, - proxyInfo: - Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ) .timeout( const Duration(milliseconds: 2000), diff --git a/lib/utilities/test_node_connection.dart b/lib/utilities/test_node_connection.dart index 8491ca3db5..b849d2ddf2 100644 --- a/lib/utilities/test_node_connection.dart +++ b/lib/utilities/test_node_connection.dart @@ -8,6 +8,7 @@ import 'package:on_chain/ada/ada.dart'; import 'package:socks5_proxy/socks.dart'; import 'package:xelis_dart_sdk/xelis_dart_sdk.dart' as xelis_sdk; +import '../app_config.dart'; import '../networking/http.dart'; import '../pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart'; import '../providers/global/prefs_provider.dart'; @@ -144,10 +145,11 @@ Future testNodeConnection({ case CryptonoteCurrency(): try { - final proxyInfo = - ref.read(prefsChangeNotifierProvider).useTor - ? ref.read(pTorService).getProxyInfo() - : null; + final proxyInfo = !AppConfig.hasFeature(AppFeature.tor) + ? null + : ref.read(prefsChangeNotifierProvider).useTor + ? ref.read(pTorService).getProxyInfo() + : null; final url = formData.host!; final uri = Uri.tryParse(url); @@ -232,10 +234,11 @@ Future testNodeConnection({ url: uri, headers: {"Content-Type": "application/json"}, body: jsonEncode({"action": "version"}), - proxyInfo: - ref.read(prefsChangeNotifierProvider).useTor - ? ref.read(pTorService).getProxyInfo() - : null, + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : ref.read(prefsChangeNotifierProvider).useTor + ? ref.read(pTorService).getProxyInfo() + : null, ); testPassed = response.code == 200; @@ -271,7 +274,8 @@ Future testNodeConnection({ case Cardano(): try { final client = HttpClient(); - if (ref.read(prefsChangeNotifierProvider).useTor) { + if (AppConfig.hasFeature(AppFeature.tor) && + ref.read(prefsChangeNotifierProvider).useTor) { final proxyInfo = TorService.sharedInstance.getProxyInfo(); final proxySettings = ProxySettings(proxyInfo.host, proxyInfo.port); SocksTCPClient.assignToHttpClient(client, [proxySettings]); diff --git a/lib/utilities/test_stellar_node_connection.dart b/lib/utilities/test_stellar_node_connection.dart index 2fd2f9fcc0..3af380f633 100644 --- a/lib/utilities/test_stellar_node_connection.dart +++ b/lib/utilities/test_stellar_node_connection.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart'; +import '../app_config.dart'; import '../networking/http.dart' as http; import '../services/tor_service.dart'; import 'prefs.dart'; @@ -14,7 +15,9 @@ Future testStellarNodeConnection(String host, int port) async { .get( url: uri, headers: {'Content-Type': 'application/json'}, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ) diff --git a/lib/wallets/api/tezos/tezos_api.dart b/lib/wallets/api/tezos/tezos_api.dart index 49065433e5..6bd7496cdd 100644 --- a/lib/wallets/api/tezos/tezos_api.dart +++ b/lib/wallets/api/tezos/tezos_api.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import '../../../app_config.dart'; import '../../../networking/http.dart'; import '../../../services/tor_service.dart'; import '../../../utilities/logger.dart'; @@ -17,7 +18,9 @@ abstract final class TezosAPI { final response = await _client.get( url: Uri.parse(uriString), headers: {'Content-Type': 'application/json'}, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -25,7 +28,11 @@ abstract final class TezosAPI { final result = jsonDecode(response.body); return result as int; } catch (e, s) { - Logging.instance.e("Error occurred in TezosAPI while getting counter for $address: ", error: e, stackTrace: s); + Logging.instance.e( + "Error occurred in TezosAPI while getting counter for $address: ", + error: e, + stackTrace: s, + ); rethrow; } } @@ -39,7 +46,9 @@ abstract final class TezosAPI { final response = await _client.get( url: Uri.parse(uriString), headers: {'Content-Type': 'application/json'}, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -50,7 +59,11 @@ abstract final class TezosAPI { return account; } catch (e, s) { - Logging.instance.e("Error occurred in TezosAPI while getting account for $address: ", error: e, stackTrace: s); + Logging.instance.e( + "Error occurred in TezosAPI while getting account for $address: ", + error: e, + stackTrace: s, + ); rethrow; } } @@ -63,7 +76,9 @@ abstract final class TezosAPI { final response = await _client.get( url: Uri.parse(transactionsCall), headers: {'Content-Type': 'application/json'}, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -78,9 +93,10 @@ abstract final class TezosAPI { hash: tx["hash"] as String, type: tx["type"] as String, height: tx["level"] as int, - timestamp: DateTime.parse(tx["timestamp"].toString()) - .toUtc() - .millisecondsSinceEpoch ~/ + timestamp: + DateTime.parse( + tx["timestamp"].toString(), + ).toUtc().millisecondsSinceEpoch ~/ 1000, cycle: tx["cycle"] as int?, counter: tx["counter"] as int, @@ -91,7 +107,8 @@ abstract final class TezosAPI { gasUsed: tx["gasUsed"] as int, storageLimit: tx["storageLimit"] as int?, amountInMicroTez: tx["amount"] as int, - feeInMicroTez: (tx["bakerFee"] as int? ?? 0) + + feeInMicroTez: + (tx["bakerFee"] as int? ?? 0) + (tx["storageFee"] as int? ?? 0) + (tx["allocationFee"] as int? ?? 0), burnedAmountInMicroTez: tx["burned"] as int?, @@ -103,7 +120,11 @@ abstract final class TezosAPI { } return txs; } catch (e, s) { - Logging.instance.e("Error occurred in TezosAPI while getting transactions for $address: ", error: e, stackTrace: s); + Logging.instance.e( + "Error occurred in TezosAPI while getting transactions for $address: ", + error: e, + stackTrace: s, + ); rethrow; } } diff --git a/lib/wallets/api/tezos/tezos_rpc_api.dart b/lib/wallets/api/tezos/tezos_rpc_api.dart index 4f9c71fe85..721d8b0fa6 100644 --- a/lib/wallets/api/tezos/tezos_rpc_api.dart +++ b/lib/wallets/api/tezos/tezos_rpc_api.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import '../../../app_config.dart'; import '../../../networking/http.dart'; import '../../../services/tor_service.dart'; import '../../../utilities/logger.dart'; @@ -19,13 +20,16 @@ abstract final class TezosRpcAPI { final response = await _client.get( url: Uri.parse(balanceCall), headers: {'Content-Type': 'application/json'}, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); - final balance = - BigInt.parse(response.body.substring(1, response.body.length - 2)); + final balance = BigInt.parse( + response.body.substring(1, response.body.length - 2), + ); return balance; } catch (e, s) { Logging.instance.e( @@ -47,7 +51,9 @@ abstract final class TezosRpcAPI { final response = await _client.get( url: Uri.parse(api), headers: {'Content-Type': 'application/json'}, - proxyInfo: Prefs.instance.useTor + proxyInfo: !AppConfig.hasFeature(AppFeature.tor) + ? null + : Prefs.instance.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); diff --git a/lib/wallets/wallet/impl/cardano_wallet.dart b/lib/wallets/wallet/impl/cardano_wallet.dart index 93430b77bf..1583123a2b 100644 --- a/lib/wallets/wallet/impl/cardano_wallet.dart +++ b/lib/wallets/wallet/impl/cardano_wallet.dart @@ -11,6 +11,7 @@ import 'package:on_chain/ada/ada.dart'; import 'package:socks5_proxy/socks.dart'; import 'package:tuple/tuple.dart'; +import '../../../app_config.dart'; import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart'; import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; @@ -50,11 +51,10 @@ class CardanoWallet extends Bip39Wallet { ).change(Bip44Changes.chainExt).addressIndex(0); final paymentPublicKey = shelley.bip44.publicKey.compressed; final stakePublicKey = shelley.bip44Sk.publicKey.compressed; - final addressStr = - ADABaseAddress.fromPublicKey( - basePubkeyBytes: paymentPublicKey, - stakePubkeyBytes: stakePublicKey, - ).address; + final addressStr = ADABaseAddress.fromPublicKey( + basePubkeyBytes: paymentPublicKey, + stakePubkeyBytes: stakePublicKey, + ).address; return Address( walletId: walletId, value: addressStr, @@ -214,15 +214,14 @@ class CardanoWallet extends Bip39Wallet { } final body = TransactionBody( - inputs: - listOfUtxosToBeUsed - .map( - (e) => TransactionInput( - transactionId: TransactionHash.fromHex(e.txHash), - index: e.outputIndex, - ), - ) - .toList(), + inputs: listOfUtxosToBeUsed + .map( + (e) => TransactionInput( + transactionId: TransactionHash.fromHex(e.txHash), + index: e.outputIndex, + ), + ) + .toList(), outputs: [ change, TransactionOutput( @@ -363,15 +362,14 @@ class CardanoWallet extends Bip39Wallet { ]; } final body = TransactionBody( - inputs: - listOfUtxosToBeUsed - .map( - (e) => TransactionInput( - transactionId: TransactionHash.fromHex(e.txHash), - index: e.outputIndex, - ), - ) - .toList(), + inputs: listOfUtxosToBeUsed + .map( + (e) => TransactionInput( + transactionId: TransactionHash.fromHex(e.txHash), + index: e.outputIndex, + ), + ) + .toList(), outputs: outputs, fee: txData.fee!.raw, ); @@ -582,11 +580,10 @@ class CardanoWallet extends Bip39Wallet { type: txType, subType: isar.TransactionSubType.none, amount: amount, - amountString: - Amount( - rawValue: BigInt.from(amount), - fractionDigits: cryptoCurrency.fractionDigits, - ).toJsonString(), + amountString: Amount( + rawValue: BigInt.from(amount), + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), fee: int.parse(txInfo.fees), height: txInfo.blockHeight, isCancelled: false, @@ -606,10 +603,9 @@ class CardanoWallet extends Bip39Wallet { derivationIndex: 0, derivationPath: DerivationPath()..value = _addressDerivationPath, type: AddressType.cardanoShelley, - subType: - txType == isar.TransactionType.outgoing - ? AddressSubType.unknown - : AddressSubType.receiving, + subType: txType == isar.TransactionType.outgoing + ? AddressSubType.unknown + : AddressSubType.receiving, ); parsedTxsList.add(Tuple2(transaction, txAddress)); @@ -637,7 +633,7 @@ class CardanoWallet extends Bip39Wallet { final currentNode = getCurrentNode(); final client = HttpClient(); - if (prefs.useTor) { + if (AppConfig.hasFeature(AppFeature.tor) && prefs.useTor) { final proxyInfo = TorService.sharedInstance.getProxyInfo(); final proxySettings = ProxySettings(proxyInfo.host, proxyInfo.port); SocksTCPClient.assignToHttpClient(client, [proxySettings]); @@ -667,17 +663,19 @@ class CustomBlockForestProvider extends BlockforestProvider { BlockforestRequestParam request, [ Duration? timeout, ]) async { - if (prefs.useTor) { - if (netOption == TorPlainNetworkOption.clear) { - throw NodeTorMismatchConfigException( - message: "TOR enabled but node set to clearnet only", - ); - } - } else { - if (netOption == TorPlainNetworkOption.tor) { - throw NodeTorMismatchConfigException( - message: "TOR off but node set to TOR only", - ); + if (AppConfig.hasFeature(AppFeature.tor)) { + if (prefs.useTor) { + if (netOption == TorPlainNetworkOption.clear) { + throw NodeTorMismatchConfigException( + message: "TOR enabled but node set to clearnet only", + ); + } + } else { + if (netOption == TorPlainNetworkOption.tor) { + throw NodeTorMismatchConfigException( + message: "TOR off but node set to TOR only", + ); + } } } diff --git a/lib/wallets/wallet/impl/solana_wallet.dart b/lib/wallets/wallet/impl/solana_wallet.dart index ad88d57b90..c88d995624 100644 --- a/lib/wallets/wallet/impl/solana_wallet.dart +++ b/lib/wallets/wallet/impl/solana_wallet.dart @@ -9,6 +9,7 @@ import 'package:solana/dto.dart'; import 'package:solana/solana.dart'; import 'package:tuple/tuple.dart'; +import '../../../app_config.dart'; import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart'; import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart' as isar; @@ -66,18 +67,19 @@ class SolanaWallet extends Bip39Wallet { final latestBlockhash = await _rpcClient?.getLatestBlockhash(); final pubKey = (await _getKeyPair()).publicKey; - final compiledMessage = Message( - instructions: [ - SystemInstruction.transfer( - fundingAccount: pubKey, - recipientAccount: pubKey, - lamports: transferAmount.raw.toInt(), - ), - ], - ).compile( - recentBlockhash: latestBlockhash!.value.blockhash, - feePayer: pubKey, - ); + final compiledMessage = + Message( + instructions: [ + SystemInstruction.transfer( + fundingAccount: pubKey, + recipientAccount: pubKey, + lamports: transferAmount.raw.toInt(), + ), + ], + ).compile( + recentBlockhash: latestBlockhash!.value.blockhash, + feePayer: pubKey, + ); final estimate = await _rpcClient?.getFeeForMessage( base64Encode(compiledMessage.toByteArray().toList()), @@ -456,10 +458,9 @@ class SolanaWallet extends Bip39Wallet { derivationIndex: 0, derivationPath: DerivationPath()..value = _addressDerivationPath, type: AddressType.solana, - subType: - txType == isar.TransactionType.outgoing - ? AddressSubType.unknown - : AddressSubType.receiving, + subType: txType == isar.TransactionType.outgoing + ? AddressSubType.unknown + : AddressSubType.receiving, ); txsList.add(Tuple2(transaction, txAddress)); @@ -526,7 +527,7 @@ class SolanaWallet extends Bip39Wallet { ) { HttpClient? httpClient; - if (prefs.useTor) { + if (AppConfig.hasFeature(AppFeature.tor) && prefs.useTor) { // Make proxied HttpClient. final proxyInfo = torService.getProxyInfo(); diff --git a/lib/wallets/wallet/impl/stellar_wallet.dart b/lib/wallets/wallet/impl/stellar_wallet.dart index dbfda5ee9e..86cc1aa026 100644 --- a/lib/wallets/wallet/impl/stellar_wallet.dart +++ b/lib/wallets/wallet/impl/stellar_wallet.dart @@ -7,6 +7,7 @@ import 'package:mutex/mutex.dart'; import 'package:socks5_proxy/socks.dart'; import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart' as stellar; +import '../../../app_config.dart'; import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart'; import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; @@ -138,7 +139,7 @@ class StellarWallet extends Bip39Wallet { final currentNode = getCurrentNode(); HttpClient? _httpClient; - if (prefs.useTor) { + if (AppConfig.hasFeature(AppFeature.tor) && prefs.useTor) { final ({InternetAddress host, int port}) proxyInfo = TorService.sharedInstance.getProxyInfo(); diff --git a/lib/wallets/wallet/impl/tezos_wallet.dart b/lib/wallets/wallet/impl/tezos_wallet.dart index 129ade324a..5ce5bef625 100644 --- a/lib/wallets/wallet/impl/tezos_wallet.dart +++ b/lib/wallets/wallet/impl/tezos_wallet.dart @@ -5,6 +5,7 @@ import 'package:isar_community/isar.dart'; import 'package:tezart/tezart.dart' as tezart; import 'package:tuple/tuple.dart'; +import '../../../app_config.dart'; import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart'; import '../../../models/balance.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; @@ -51,8 +52,9 @@ class TezosWallet extends Bip39Wallet { // TODO: some kind of better check to see if the address has been used - final hasHistory = - (await TezosAPI.getTransactions(ks.address)).isNotEmpty; + final hasHistory = (await TezosAPI.getTransactions( + ks.address, + )).isNotEmpty; if (hasHistory) { return path; @@ -113,13 +115,14 @@ class TezosWallet extends Bip39Wallet { // print("customFee: $customFee"); // } final ({InternetAddress host, int port})? proxyInfo = - prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null; + AppConfig.hasFeature(AppFeature.tor) && prefs.useTor + ? TorService.sharedInstance.getProxyInfo() + : null; final tezartClient = tezart.TezartClient( server, - proxy: - proxyInfo != null - ? "socks5://${proxyInfo.host}:${proxyInfo.port};" - : null, + proxy: proxyInfo != null + ? "socks5://${proxyInfo.host}:${proxyInfo.port};" + : null, ); final opList = await tezartClient.transferOperation( @@ -595,11 +598,10 @@ class TezosWallet extends Bip39Wallet { type: txType, subType: TransactionSubType.none, amount: theTx.amountInMicroTez, - amountString: - Amount( - rawValue: BigInt.from(theTx.amountInMicroTez), - fractionDigits: cryptoCurrency.fractionDigits, - ).toJsonString(), + amountString: Amount( + rawValue: BigInt.from(theTx.amountInMicroTez), + fractionDigits: cryptoCurrency.fractionDigits, + ).toJsonString(), fee: theTx.feeInMicroTez, height: theTx.height, isCancelled: false, diff --git a/lib/wallets/wallet/intermediate/lib_monero_wallet.dart b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart index 1d8a5da7bd..e0f17630e9 100644 --- a/lib/wallets/wallet/intermediate/lib_monero_wallet.dart +++ b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart @@ -8,6 +8,7 @@ import 'package:isar_community/isar.dart'; import 'package:mutex/mutex.dart'; import 'package:stack_wallet_backup/generate_password.dart'; +import '../../../app_config.dart'; import '../../../db/hive/db.dart'; import '../../../models/balance.dart'; import '../../../models/input.dart'; @@ -511,8 +512,8 @@ abstract class LibMoneroWallet final host = node.host.endsWith(".onion") ? node.host : Uri.parse(node.host).host; - ({InternetAddress host, int port})? proxy; - proxy = prefs.useTor && !node.forceNoTor + final ({InternetAddress host, int port})? proxy = + AppConfig.hasFeature(AppFeature.tor) && prefs.useTor && !node.forceNoTor ? TorService.sharedInstance.getProxyInfo() : null; diff --git a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart index 784e12c51d..4aa05989e6 100644 --- a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart +++ b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart @@ -7,6 +7,7 @@ import 'package:isar_community/isar.dart'; import 'package:mutex/mutex.dart'; import 'package:stack_wallet_backup/generate_password.dart'; +import '../../../app_config.dart'; import '../../../models/balance.dart'; import '../../../models/input.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; @@ -488,8 +489,8 @@ abstract class LibSalviumWallet final host = node.host.endsWith(".onion") ? node.host : Uri.parse(node.host).host; - ({InternetAddress host, int port})? proxy; - proxy = prefs.useTor && !node.forceNoTor + final ({InternetAddress host, int port})? proxy = + AppConfig.hasFeature(AppFeature.tor) && prefs.useTor && !node.forceNoTor ? TorService.sharedInstance.getProxyInfo() : null; diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart index 618c2b1c79..7fe44bf81e 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart'; import 'package:fusiondart/fusiondart.dart' as fusion; import 'package:isar_community/isar.dart'; +import '../../../app_config.dart'; import '../../../models/fusion_progress_ui_state.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/isar/models/blockchain_data/transaction.dart'; @@ -340,8 +341,10 @@ mixin CashFusionInterface Future>> _getTransactionsByAddress( String address, ) async { - final txidList = - await mainDB.getTransactions(walletId).txidProperty().findAll(); + final txidList = await mainDB + .getTransactions(walletId) + .txidProperty() + .findAll(); final futures = txidList.map( (e) => electrumXCachedClient.getTransaction( @@ -391,10 +394,9 @@ mixin CashFusionInterface return []; } - final updatedAddresses = - addresses - .map((e) => e.copyWith(otherData: kReservedFusionAddress)) - .toList(); + final updatedAddresses = addresses + .map((e) => e.copyWith(otherData: kReservedFusionAddress)) + .toList(); await mainDB.isar.writeTxn(() async { for (final newAddress in updatedAddresses) { @@ -441,10 +443,9 @@ mixin CashFusionInterface ); // Initialize a list of unused reserved change addresses. - final List
unusedReservedAddresses = - unusedChangeAddresses - .where((e) => e.otherData == kReservedFusionAddress) - .toList(); + final List
unusedReservedAddresses = unusedChangeAddresses + .where((e) => e.otherData == kReservedFusionAddress) + .toList(); unusedReservedAddresses.addAll( await _reserveAddresses( @@ -476,24 +477,17 @@ mixin CashFusionInterface ); } - final changeAddresses = - await mainDB.isar.addresses - .buildQuery
( - whereClauses: [ - IndexWhereClause.equalTo( - indexName: r"walletId", - value: [walletId], - ), - ], - filter: changeAddressFilterOperation, - sortBy: [ - const SortProperty( - property: r"derivationIndex", - sort: Sort.desc, - ), - ], - ) - .findAll(); + final changeAddresses = await mainDB.isar.addresses + .buildQuery
( + whereClauses: [ + IndexWhereClause.equalTo(indexName: r"walletId", value: [walletId]), + ], + filter: changeAddressFilterOperation, + sortBy: [ + const SortProperty(property: r"derivationIndex", sort: Sort.desc), + ], + ) + .findAll(); final List
unused = []; @@ -527,12 +521,11 @@ mixin CashFusionInterface } Future _isUnused(String address) async { - final txCountInDB = - await mainDB - .getTransactions(walletId) - .filter() - .address((q) => q.valueEqualTo(address)) - .count(); + final txCountInDB = await mainDB + .getTransactions(walletId) + .filter() + .address((q) => q.valueEqualTo(address)) + .count(); if (txCountInDB == 0) { // double check via electrumx // _getTxCountForAddress can throw! @@ -603,6 +596,10 @@ mixin CashFusionInterface Future fuse({required FusionInfo fusionInfo}) async { // Initial attempt for CashFusion integration goes here. + if (!AppConfig.hasFeature(AppFeature.tor)) { + throw Exception("AppConfig has no Tor"); + } + try { _updateStatus(status: fusion.FusionStatus.reset); _updateStatus( @@ -632,15 +629,11 @@ mixin CashFusionInterface getChainHeight: fetchChainHeight, updateStatusCallback: _updateStatus, checkUtxoExists: _checkUtxoExists, - getTransactionJson: - (String txid) async => await electrumXCachedClient.getTransaction( - cryptoCurrency: info.coin, - txHash: txid, - ), + getTransactionJson: (String txid) async => await electrumXCachedClient + .getTransaction(cryptoCurrency: info.coin, txHash: txid), getPrivateKeyForPubKey: _getPrivateKeyForPubKey, - broadcastTransaction: - (String txHex) => - electrumXClient.broadcastTransaction(rawTx: txHex), + broadcastTransaction: (String txHex) => + electrumXClient.broadcastTransaction(rawTx: txHex), unReserveAddresses: (List addresses) async { final List> futures = []; for (final addr in addresses) { @@ -692,14 +685,13 @@ mixin CashFusionInterface await updateUTXOs(); // Add unfrozen stack UTXOs. - final List walletUtxos = - await mainDB - .getUTXOs(walletId) - .filter() - .isBlockedEqualTo(false) - .and() - .addressIsNotNull() - .findAll(); + final List walletUtxos = await mainDB + .getUTXOs(walletId) + .filter() + .isBlockedEqualTo(false) + .and() + .addressIsNotNull() + .findAll(); final List coinList = []; // Loop through UTXOs, checking and adding valid ones. @@ -719,24 +711,23 @@ mixin CashFusionInterface } // Fetch address to get pubkey - final addr = - await mainDB - .getAddresses(walletId) - .filter() - .anyOf< - String, - QueryBuilder - >(possibleAddresses, (q, e) => q.valueEqualTo(e)) - .and() - .group( - (q) => q - .subTypeEqualTo(AddressSubType.change) - .or() - .subTypeEqualTo(AddressSubType.receiving), - ) - .and() - .typeEqualTo(AddressType.p2pkh) - .findFirst(); + final addr = await mainDB + .getAddresses(walletId) + .filter() + .anyOf< + String, + QueryBuilder + >(possibleAddresses, (q, e) => q.valueEqualTo(e)) + .and() + .group( + (q) => q + .subTypeEqualTo(AddressSubType.change) + .or() + .subTypeEqualTo(AddressSubType.receiving), + ) + .and() + .typeEqualTo(AddressType.p2pkh) + .findFirst(); // depending on the address type in the query above this can be null if (addr == null) { diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart index f5215ed95a..b08b6bb426 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart @@ -5,6 +5,7 @@ import 'package:isar_community/isar.dart'; import 'package:nanodart/nanodart.dart'; import 'package:tuple/tuple.dart'; +import '../../../app_config.dart'; import '../../../exceptions/wallet/node_tor_mismatch_config_exception.dart'; import '../../../external_api_keys.dart'; import '../../../models/balance.dart'; @@ -53,8 +54,9 @@ mixin NanoInterface on Bip39Wallet { url: Uri.parse(_kWorkServer), // this should be a headers: _buildHeaders(_kWorkServer), body: json.encode({"action": "work_generate", "hash": hash}), - proxyInfo: - prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, + proxyInfo: AppConfig.hasFeature(AppFeature.tor) && prefs.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ) .then((_httpClient) { if (_httpClient.code == 200) { @@ -119,7 +121,9 @@ mixin NanoInterface on Bip39Wallet { url: Uri.parse(node.host), headers: _buildHeaders(node.host), body: infoBody, - proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, + proxyInfo: AppConfig.hasFeature(AppFeature.tor) && prefs.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); final infoData = jsonDecode(infoResponse.body); @@ -138,7 +142,9 @@ mixin NanoInterface on Bip39Wallet { url: Uri.parse(node.host), headers: _buildHeaders(node.host), body: balanceBody, - proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, + proxyInfo: AppConfig.hasFeature(AppFeature.tor) && prefs.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); final balanceData = jsonDecode(balanceResponse.body); @@ -168,10 +174,9 @@ mixin NanoInterface on Bip39Wallet { final Map receiveBlock = { "type": "state", "account": publicAddress, - "previous": - openBlock - ? "0000000000000000000000000000000000000000000000000000000000000000" - : frontier, + "previous": openBlock + ? "0000000000000000000000000000000000000000000000000000000000000000" + : frontier, "representative": representative, "balance": balanceAfterTx.toString(), "link": link, @@ -216,7 +221,9 @@ mixin NanoInterface on Bip39Wallet { url: Uri.parse(node.host), headers: _buildHeaders(node.host), body: processBody, - proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, + proxyInfo: AppConfig.hasFeature(AppFeature.tor) && prefs.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); final Map decoded = @@ -237,7 +244,9 @@ mixin NanoInterface on Bip39Wallet { "source": "true", "account": accountAddress, }), - proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, + proxyInfo: AppConfig.hasFeature(AppFeature.tor) && prefs.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); final receivableData = await jsonDecode(receivableResponse.body); @@ -369,7 +378,9 @@ mixin NanoInterface on Bip39Wallet { url: uri, headers: _buildHeaders(node.host), body: jsonEncode({"action": "version"}), - proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, + proxyInfo: AppConfig.hasFeature(AppFeature.tor) && prefs.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); return response.code == 200; @@ -419,14 +430,17 @@ mixin NanoInterface on Bip39Wallet { url: Uri.parse(node.host), headers: _buildHeaders(node.host), body: infoBody, - proxyInfo: - prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, + proxyInfo: AppConfig.hasFeature(AppFeature.tor) && prefs.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); - final String frontier = - jsonDecode(infoResponse.body)["frontier"].toString(); - final String representative = - jsonDecode(infoResponse.body)["representative"].toString(); + final String frontier = jsonDecode( + infoResponse.body, + )["frontier"].toString(); + final String representative = jsonDecode( + infoResponse.body, + )["representative"].toString(); // link = destination address: final String linkAsAccount = txData.recipients!.first.address; final String link = NanoAccounts.extractPublicKey(linkAsAccount); @@ -473,8 +487,9 @@ mixin NanoInterface on Bip39Wallet { url: Uri.parse(node.host), headers: _buildHeaders(node.host), body: processBody, - proxyInfo: - prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, + proxyInfo: AppConfig.hasFeature(AppFeature.tor) && prefs.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); final Map decoded = @@ -533,7 +548,9 @@ mixin NanoInterface on Bip39Wallet { url: Uri.parse(node.host), headers: _buildHeaders(node.host), body: jsonEncode(body), - proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, + proxyInfo: AppConfig.hasFeature(AppFeature.tor) && prefs.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); // this should really have proper type checking and error propagation but I'm out of time @@ -566,10 +583,9 @@ mixin NanoInterface on Bip39Wallet { final data = await _fetchAll(publicAddress, null, null); - final transactions = - data["history"] is List - ? data["history"] as List - : []; + final transactions = data["history"] is List + ? data["history"] as List + : []; if (transactions.isEmpty) { return; } else { @@ -607,18 +623,17 @@ mixin NanoInterface on Bip39Wallet { numberOfMessages: null, ); - final Address address = - transactionType == TransactionType.incoming - ? receivingAddress - : Address( - walletId: walletId, - publicKey: [], - value: tx["account"].toString(), - derivationIndex: 0, - derivationPath: null, - type: info.mainAddressType, - subType: AddressSubType.nonWallet, - ); + final Address address = transactionType == TransactionType.incoming + ? receivingAddress + : Address( + walletId: walletId, + publicKey: [], + value: tx["account"].toString(), + derivationIndex: 0, + derivationPath: null, + type: info.mainAddressType, + subType: AddressSubType.nonWallet, + ); final Tuple2 tuple = Tuple2(transaction, address); transactionList.add(tuple); } @@ -643,15 +658,16 @@ mixin NanoInterface on Bip39Wallet { url: Uri.parse(node.host), headers: _buildHeaders(node.host), body: body, - proxyInfo: - prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, + proxyInfo: AppConfig.hasFeature(AppFeature.tor) && prefs.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); final data = jsonDecode(response.body); final balance = Balance( total: Amount( rawValue: (BigInt.parse(data["balance"].toString()) + - BigInt.parse(data["receivable"].toString())), + BigInt.parse(data["receivable"].toString())), fractionDigits: cryptoCurrency.fractionDigits, ), spendable: Amount( @@ -695,8 +711,9 @@ mixin NanoInterface on Bip39Wallet { url: Uri.parse(node.host), headers: _buildHeaders(node.host), body: infoBody, - proxyInfo: - prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, + proxyInfo: AppConfig.hasFeature(AppFeature.tor) && prefs.useTor + ? TorService.sharedInstance.getProxyInfo() + : null, ); final infoData = jsonDecode(infoResponse.body); diff --git a/tool/wl_templates/TOR_tor_service_impl.template.dart b/tool/wl_templates/TOR_tor_service_impl.template.dart new file mode 100644 index 0000000000..3e26cafa2c --- /dev/null +++ b/tool/wl_templates/TOR_tor_service_impl.template.dart @@ -0,0 +1,185 @@ +//ON +import 'dart:io'; + +import 'package:tor_ffi_plugin/tor_ffi_plugin.dart'; + +import '../../services/event_bus/events/global/tor_connection_status_changed_event.dart'; +import '../../services/event_bus/global_event_bus.dart'; +//END_ON +import '../../services/fusion_tor_service.dart'; +import '../../services/tor_service.dart'; +//ON +import '../../utilities/logger.dart'; +//END_ON + +TorService get torService => _getInterface(); +FusionTorService get fusionTorService => _getFusionInterface(); + +//OFF +TorService _getInterface() => throw Exception("TOR not enabled!"); +FusionTorService _getFusionInterface() => throw Exception("TOR not enabled!"); + +//END_OFF +//ON +TorService _getInterface() => _TorServiceImpl(); +FusionTorService _getFusionInterface() => _FusionTorServiceImpl(); + +class _TorServiceImpl extends TorService { + Tor? _tor; + String? _torDataDirPath; + TorConnectionStatus _status = TorConnectionStatus.disconnected; + + @override + TorConnectionStatus get status => _status; + + /// Getter for the proxyInfo. + /// + /// Throws if Tor is not connected. + @override + ({InternetAddress host, int port}) getProxyInfo() { + if (status == TorConnectionStatus.connected) { + return (host: InternetAddress.loopbackIPv4, port: _tor!.port); + } else { + throw Exception("Tor proxy info fetched while not connected!"); + } + } + + /// Initialize the tor ffi lib instance if it hasn't already been set. Nothing + /// changes if _tor is already been set. + @override + void init({required String torDataDirPath, Tor? mockableOverride}) { + _tor ??= mockableOverride ?? Tor.instance; + _torDataDirPath ??= torDataDirPath; + } + + /// Start the Tor service. + /// + /// This will start the Tor service and establish a Tor circuit. + /// + /// Throws an exception if the Tor library was not inited or if the Tor + /// service fails to start. + /// + /// Returns a Future that completes when the Tor service has started. + @override + Future start() async { + if (_tor == null || _torDataDirPath == null) { + throw Exception("TorService.init has not been called!"); + } + + // Start the Tor service. + try { + _updateStatusAndFireEvent( + status: TorConnectionStatus.connecting, + message: "TorService.start call in progress", + ); + + await _tor!.start(torDataDirPath: _torDataDirPath!); + + // no exception or error so we can (probably?) assume tor + // has started successfully + // Fire a TorConnectionStatusChangedEvent on the event bus. + _updateStatusAndFireEvent( + status: TorConnectionStatus.connected, + message: "TorService.start call success", + ); + + // Complete the future. + return; + } catch (e, s) { + Logging.instance.w("TorService.start failed: ", error: e, stackTrace: s); + // _enabled should already be false + + // Fire a TorConnectionStatusChangedEvent on the event bus. + _updateStatusAndFireEvent( + status: TorConnectionStatus.disconnected, + message: "TorService.start call failed", + ); + rethrow; + } + } + + /// Disable Tor. + @override + Future disable() async { + if (_tor == null) { + throw Exception("TorService.init has not been called!"); + } + + // No need to update status and fire event if status won't change. + if (_status == TorConnectionStatus.disconnected) { + return; + } + + _tor!.disable(); + await _tor?.stop(); + + _updateStatusAndFireEvent( + status: TorConnectionStatus.disconnected, + message: "TorService.disable call success", + ); + } + + void _updateStatusAndFireEvent({ + required TorConnectionStatus status, + required String message, + }) { + _status = status; + GlobalEventBus.instance.fire( + TorConnectionStatusChangedEvent(_status, message), + ); + } +} + +class _FusionTorServiceImpl extends FusionTorService { + Tor? _tor; + String? _torDataDirPath; + + /// Getter for the proxyInfo. + /// + /// Throws if Tor is not connected. + @override + ({InternetAddress host, int port}) getProxyInfo() { + try { + return (host: InternetAddress.loopbackIPv4, port: _tor!.port); + } catch (_) { + throw Exception("Tor proxy info fetched while not connected!"); + } + } + + /// Initialize the tor ffi lib instance if it hasn't already been set. Nothing + /// changes if _tor is already been set. + @override + void init({required String torDataDirPath, Tor? mockableOverride}) { + _tor ??= mockableOverride ?? Tor.instance; + _torDataDirPath ??= torDataDirPath; + } + + /// Start the Tor service. + /// + /// This will start the Tor service and establish a Tor circuit. + /// + /// Throws an exception if the Tor library was not inited or if the Tor + /// service fails to start. + /// + /// Returns a Future that completes when the Tor service has started. + @override + Future start() async { + if (_tor == null || _torDataDirPath == null) { + throw Exception("FusionTorService.init has not been called!"); + } + + // Start the Tor service. + try { + await _tor!.start(torDataDirPath: _torDataDirPath!); + } catch (e, s) { + Logging.instance.w( + "FusionTorService.start failed: ", + error: e, + stackTrace: s, + ); + rethrow; + } + } +} + +//END_ON From cef53858e5cc14991c099e94b6b094d8d1a1f666 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 7 Oct 2025 12:10:28 -0600 Subject: [PATCH 20/30] remove unused code --- .../coins/tezos/api/tezos_account.dart | 58 -------- lib/services/coins/tezos/api/tezos_api.dart | 131 ------------------ .../coins/tezos/api/tezos_rpc_api.dart | 79 ----------- .../coins/tezos/api/tezos_transaction.dart | 43 ------ 4 files changed, 311 deletions(-) delete mode 100644 lib/services/coins/tezos/api/tezos_account.dart delete mode 100644 lib/services/coins/tezos/api/tezos_api.dart delete mode 100644 lib/services/coins/tezos/api/tezos_rpc_api.dart delete mode 100644 lib/services/coins/tezos/api/tezos_transaction.dart diff --git a/lib/services/coins/tezos/api/tezos_account.dart b/lib/services/coins/tezos/api/tezos_account.dart deleted file mode 100644 index 8bf9a9f64d..0000000000 --- a/lib/services/coins/tezos/api/tezos_account.dart +++ /dev/null @@ -1,58 +0,0 @@ -class TezosAccount { - final int id; - final String type; - final String address; - final String? publicKey; - final bool revealed; - final int balance; - final int counter; - - TezosAccount({ - required this.id, - required this.type, - required this.address, - required this.publicKey, - required this.revealed, - required this.balance, - required this.counter, - }); - - TezosAccount copyWith({ - int? id, - String? type, - String? address, - String? publicKey, - bool? revealed, - int? balance, - int? counter, - }) { - return TezosAccount( - id: id ?? this.id, - type: type ?? this.type, - address: address ?? this.address, - publicKey: publicKey ?? this.publicKey, - revealed: revealed ?? this.revealed, - balance: balance ?? this.balance, - counter: counter ?? this.counter, - ); - } - - factory TezosAccount.fromMap(Map map) { - return TezosAccount( - id: map['id'] as int, - type: map['type'] as String, - address: map['address'] as String, - publicKey: map['publicKey'] as String?, - revealed: map['revealed'] as bool, - balance: map['balance'] as int, - counter: map['counter'] as int, - ); - } - - @override - String toString() { - return 'UserData{id: $id, type: $type, address: $address, ' - 'publicKey: $publicKey, revealed: $revealed,' - ' balance: $balance, counter: $counter}'; - } -} diff --git a/lib/services/coins/tezos/api/tezos_api.dart b/lib/services/coins/tezos/api/tezos_api.dart deleted file mode 100644 index a85e6b25d1..0000000000 --- a/lib/services/coins/tezos/api/tezos_api.dart +++ /dev/null @@ -1,131 +0,0 @@ -import 'dart:convert'; - -import '../../../../app_config.dart'; -import '../../../../networking/http.dart'; -import '../../../../utilities/logger.dart'; -import '../../../../utilities/prefs.dart'; -import '../../../tor_service.dart'; -import 'tezos_account.dart'; -import 'tezos_transaction.dart'; - -abstract final class TezosAPI { - static final HTTP _client = HTTP(); - static const String _baseURL = 'https://api.tzkt.io'; - - static Future getCounter(String address) async { - try { - final uriString = "$_baseURL/v1/accounts/$address/counter"; - final response = await _client.get( - url: Uri.parse(uriString), - headers: {'Content-Type': 'application/json'}, - proxyInfo: !AppConfig.hasFeature(AppFeature.tor) - ? null - : Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, - ); - - final result = jsonDecode(response.body); - return result as int; - } catch (e, s) { - Logging.instance.e( - "Error occurred in TezosAPI while getting counter for $address: ", - error: e, - stackTrace: s, - ); - rethrow; - } - } - - static Future getAccount( - String address, { - String type = "user", - }) async { - try { - final uriString = "$_baseURL/v1/accounts/$address?legacy=false"; - final response = await _client.get( - url: Uri.parse(uriString), - headers: {'Content-Type': 'application/json'}, - proxyInfo: !AppConfig.hasFeature(AppFeature.tor) - ? null - : Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, - ); - - final result = jsonDecode(response.body) as Map; - - final account = TezosAccount.fromMap(Map.from(result)); - - return account; - } catch (e, s) { - Logging.instance.e( - "Error occurred in TezosAPI while getting account for $address: ", - error: e, - stackTrace: s, - ); - rethrow; - } - } - - static Future> getTransactions(String address) async { - try { - final transactionsCall = - "$_baseURL/v1/accounts/$address/operations?type=transaction"; - - final response = await _client.get( - url: Uri.parse(transactionsCall), - headers: {'Content-Type': 'application/json'}, - proxyInfo: !AppConfig.hasFeature(AppFeature.tor) - ? null - : Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, - ); - - final result = jsonDecode(response.body) as List; - - final List txs = []; - for (final tx in result) { - if (tx["type"] == "transaction") { - final theTx = TezosTransaction( - id: tx["id"] as int, - hash: tx["hash"] as String, - type: tx["type"] as String, - height: tx["level"] as int, - timestamp: - DateTime.parse( - tx["timestamp"].toString(), - ).toUtc().millisecondsSinceEpoch ~/ - 1000, - cycle: tx["cycle"] as int?, - counter: tx["counter"] as int, - opN: tx["op_n"] as int?, - opP: tx["op_p"] as int?, - status: tx["status"] as String, - gasLimit: tx["gasLimit"] as int, - gasUsed: tx["gasUsed"] as int, - storageLimit: tx["storageLimit"] as int?, - amountInMicroTez: tx["amount"] as int, - feeInMicroTez: - (tx["bakerFee"] as int? ?? 0) + - (tx["storageFee"] as int? ?? 0) + - (tx["allocationFee"] as int? ?? 0), - burnedAmountInMicroTez: tx["burned"] as int?, - senderAddress: tx["sender"]["address"] as String, - receiverAddress: tx["target"]["address"] as String, - ); - txs.add(theTx); - } - } - return txs; - } catch (e, s) { - Logging.instance.e( - "Error occurred in TezosAPI while getting transactions for $address: ", - error: e, - stackTrace: s, - ); - rethrow; - } - } -} diff --git a/lib/services/coins/tezos/api/tezos_rpc_api.dart b/lib/services/coins/tezos/api/tezos_rpc_api.dart deleted file mode 100644 index 67316c20ac..0000000000 --- a/lib/services/coins/tezos/api/tezos_rpc_api.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'dart:convert'; - -import '../../../../app_config.dart'; -import '../../../../networking/http.dart'; -import '../../../../utilities/logger.dart'; -import '../../../../utilities/prefs.dart'; -import '../../../tor_service.dart'; - -abstract final class TezosRpcAPI { - static final HTTP _client = HTTP(); - - static Future getBalance({ - required ({String host, int port}) nodeInfo, - required String address, - }) async { - try { - final String balanceCall = - "${nodeInfo.host}:${nodeInfo.port}/chains/main/blocks/head/context/contracts/$address/balance"; - - final response = await _client.get( - url: Uri.parse(balanceCall), - headers: {'Content-Type': 'application/json'}, - proxyInfo: !AppConfig.hasFeature(AppFeature.tor) - ? null - : Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, - ); - - final balance = BigInt.parse( - response.body.substring(1, response.body.length - 2), - ); - return balance; - } catch (e, s) { - Logging.instance.e( - "Error occurred in tezos_rpc_api.dart while getting balance for $address", - error: e, - stackTrace: s, - ); - } - return null; - } - - static Future getChainHeight({ - required ({String host, int port}) nodeInfo, - }) async { - try { - final api = - "${nodeInfo.host}:${nodeInfo.port}/chains/main/blocks/head/header/shell"; - - final response = await _client.get( - url: Uri.parse(api), - headers: {'Content-Type': 'application/json'}, - proxyInfo: !AppConfig.hasFeature(AppFeature.tor) - ? null - : Prefs.instance.useTor - ? TorService.sharedInstance.getProxyInfo() - : null, - ); - - final jsonParsedResponse = jsonDecode(response.body); - return int.parse(jsonParsedResponse["level"].toString()); - } catch (e, s) { - Logging.instance.e( - "Error occurred in tezos_rpc_api.dart while getting chain height for tezos", - error: e, - stackTrace: s, - ); - } - return null; - } - - static Future testNetworkConnection({ - required ({String host, int port}) nodeInfo, - }) async { - final result = await getChainHeight(nodeInfo: nodeInfo); - return result != null; - } -} diff --git a/lib/services/coins/tezos/api/tezos_transaction.dart b/lib/services/coins/tezos/api/tezos_transaction.dart deleted file mode 100644 index e1e6bc88b8..0000000000 --- a/lib/services/coins/tezos/api/tezos_transaction.dart +++ /dev/null @@ -1,43 +0,0 @@ -class TezosTransaction { - final int? id; - final String hash; - final String? type; - final int height; - final int timestamp; - final int? cycle; - final int? counter; - final int? opN; - final int? opP; - final String? status; - final bool? isSuccess; - final int? gasLimit; - final int? gasUsed; - final int? storageLimit; - final int amountInMicroTez; - final int feeInMicroTez; - final int? burnedAmountInMicroTez; - final String senderAddress; - final String receiverAddress; - - TezosTransaction({ - this.id, - required this.hash, - this.type, - required this.height, - required this.timestamp, - this.cycle, - this.counter, - this.opN, - this.opP, - this.status, - this.isSuccess, - this.gasLimit, - this.gasUsed, - this.storageLimit, - required this.amountInMicroTez, - required this.feeInMicroTez, - this.burnedAmountInMicroTez, - required this.senderAddress, - required this.receiverAddress, - }); -} From 661e3f8668e0a99b2726c24ed5758497faf7b969 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 7 Oct 2025 12:42:29 -0600 Subject: [PATCH 21/30] fix missed xelis import --- lib/utilities/test_node_connection.dart | 21 +++++-------------- .../interfaces/lib_xelis_interface.dart | 2 ++ ...XEL_lib_xelis_interface_impl.template.dart | 18 ++++++++++++++++ 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/lib/utilities/test_node_connection.dart b/lib/utilities/test_node_connection.dart index b849d2ddf2..c6b5ee00dd 100644 --- a/lib/utilities/test_node_connection.dart +++ b/lib/utilities/test_node_connection.dart @@ -6,7 +6,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:on_chain/ada/ada.dart'; import 'package:socks5_proxy/socks.dart'; -import 'package:xelis_dart_sdk/xelis_dart_sdk.dart' as xelis_sdk; import '../app_config.dart'; import '../networking/http.dart'; @@ -20,6 +19,7 @@ import '../wallets/crypto_currency/interfaces/electrumx_currency_interface.dart' import '../wallets/crypto_currency/intermediate/cryptonote_currency.dart'; import '../wallets/crypto_currency/intermediate/nano_currency.dart'; import '../wallets/wallet/impl/solana_wallet.dart'; +import '../wl_gen/interfaces/lib_xelis_interface.dart'; import 'connection_check/electrum_connection_check.dart'; import 'logger.dart'; import 'test_epic_box_connection.dart'; @@ -301,22 +301,11 @@ Future testNodeConnection({ case Xelis(): try { - final daemon = xelis_sdk.DaemonClient( - endPoint: "${formData.host!}:${formData.port!}", - secureWebSocket: formData.useSSL ?? false, - timeout: 5000, - ); - daemon.connect(); - - final xelis_sdk.GetInfoResult networkInfo = await daemon.getInfo(); - testPassed = networkInfo.height != null; - - daemon.disconnect(); - - Logging.instance.i( - "Xelis testNodeConnection result: \"${networkInfo.toString()}\"", + testPassed = await libXelis.testDaemonConnection( + "${formData.host!}:${formData.port!}", + formData.useSSL ?? false, ); - } catch (e, s) { + } catch (_) { testPassed = false; } break; diff --git a/lib/wl_gen/interfaces/lib_xelis_interface.dart b/lib/wl_gen/interfaces/lib_xelis_interface.dart index 533b90f901..1fc28f8d59 100644 --- a/lib/wl_gen/interfaces/lib_xelis_interface.dart +++ b/lib/wl_gen/interfaces/lib_xelis_interface.dart @@ -89,6 +89,8 @@ abstract class LibXelisInterface { Future getXelisBalanceRaw(String walletId); Future hasXelisBalance(String walletId); + + Future testDaemonConnection(String endPoint, bool useSSL); } // ============================================================================= diff --git a/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart b/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart index 08f1f9b4aa..8115f82493 100644 --- a/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart +++ b/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart @@ -329,6 +329,24 @@ final class _MwebdServerInterfaceImpl extends LibXelisInterface { @override Future hasXelisBalance(String walletId) => _wallets[walletId]!.hasXelisBalance(); + + @override + Future testDaemonConnection(String endPoint, bool useSSL) async { + final daemon = xelis_sdk.DaemonClient( + endPoint: endPoint, + secureWebSocket: useSSL, + timeout: 5000, + ); + daemon.connect(); + final xelis_sdk.GetInfoResult networkInfo = await daemon.getInfo(); + daemon.disconnect(); + + Logging.instance.i( + "Xelis testNodeConnection result: \"${networkInfo.toString()}\"", + ); + + return networkInfo.height != null; + } } extension _XelisNetworkConversion on CryptoCurrencyNetwork { From 747d753c5ab9242359f94f826829b7b891f5b57b Mon Sep 17 00:00:00 2001 From: Julian Date: Tue, 7 Oct 2025 13:49:59 -0600 Subject: [PATCH 22/30] fix missing coin checks in main.dart --- lib/main.dart | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 4de72171e0..968d595765 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -87,7 +87,9 @@ final openedFromSWBFileStringStateProvider = StateProvider( // miscellaneous box for later use void main(List args) async { // talker.info('initializing Rust lib ...'); - await libXelis.initRustLib(); + if (AppConfig.coins.whereType().isNotEmpty) { + await libXelis.initRustLib(); + } WidgetsFlutterBinding.ensureInitialized(); if (Util.isDesktop && args.length == 2 && args.first == "-d") { @@ -159,7 +161,10 @@ void main(List args) async { DB.instance.hive.registerAdapter(lib_monero_compat.WalletTypeAdapter()); - csMonero.setUseCsMoneroLoggerInternal(kDebugMode); + if (AppConfig.coins.whereType().isNotEmpty || + AppConfig.coins.whereType().isNotEmpty) { + csMonero.setUseCsMoneroLoggerInternal(kDebugMode); + } DB.instance.hive.init( (await StackFileSystem.applicationHiveDirectory()).path, @@ -175,8 +180,10 @@ void main(List args) async { debugConsoleLevel: kDebugMode ? Level.trace : null, ); - await libXelis.setupRustLogger(); - libXelis.startListeningToRustLogs(); + if (AppConfig.coins.whereType().isNotEmpty) { + await libXelis.setupRustLogger(); + libXelis.startListeningToRustLogs(); + } // setup lib spark logging initSparkLogging(Prefs.instance.logLevel); From 9fd474dc1d36eefc0c32823127a8149423907d12 Mon Sep 17 00:00:00 2001 From: Julian Date: Tue, 7 Oct 2025 14:02:31 -0600 Subject: [PATCH 23/30] update ios build files --- ios/.gitignore | 1 + ios/Podfile.lock | 257 ------------------ .../xcshareddata/xcschemes/Runner.xcscheme | 2 + ios/Runner/AppDelegate.swift | 2 - ios/Runner/Runner-Bridging-Header.h | 3 +- .../ios/Runner.xcodeproj/project.pbxproj | 34 --- 6 files changed, 4 insertions(+), 295 deletions(-) delete mode 100644 ios/Podfile.lock diff --git a/ios/.gitignore b/ios/.gitignore index c079131814..09ddc3777c 100644 --- a/ios/.gitignore +++ b/ios/.gitignore @@ -26,6 +26,7 @@ Flutter/flutter_assets/ Flutter/flutter_export_environment.sh ServiceDefinitions.json Runner/GeneratedPluginRegistrant.* +Podfile.lock # Exceptions to above rules. !default.mode1v3 diff --git a/ios/Podfile.lock b/ios/Podfile.lock deleted file mode 100644 index 3f6bee8e73..0000000000 --- a/ios/Podfile.lock +++ /dev/null @@ -1,257 +0,0 @@ -PODS: - - barcode_scan2 (0.0.1): - - Flutter - - MTBBarcodeScanner - - SwiftProtobuf - - coinlib_flutter (0.5.0): - - Flutter - - FlutterMacOS - - connectivity_plus (0.0.1): - - Flutter - - ReachabilitySwift - - cs_monero_flutter_libs_ios (0.0.1): - - Flutter - - cs_salvium_flutter_libs_ios (0.0.1): - - Flutter - - device_info_plus (0.0.1): - - Flutter - - devicelocale (0.0.1): - - Flutter - - DKImagePickerController/Core (4.3.4): - - DKImagePickerController/ImageDataManager - - DKImagePickerController/Resource - - DKImagePickerController/ImageDataManager (4.3.4) - - DKImagePickerController/PhotoGallery (4.3.4): - - DKImagePickerController/Core - - DKPhotoGallery - - DKImagePickerController/Resource (4.3.4) - - DKPhotoGallery (0.0.17): - - DKPhotoGallery/Core (= 0.0.17) - - DKPhotoGallery/Model (= 0.0.17) - - DKPhotoGallery/Preview (= 0.0.17) - - DKPhotoGallery/Resource (= 0.0.17) - - SDWebImage - - SwiftyGif - - DKPhotoGallery/Core (0.0.17): - - DKPhotoGallery/Model - - DKPhotoGallery/Preview - - SDWebImage - - SwiftyGif - - DKPhotoGallery/Model (0.0.17): - - SDWebImage - - SwiftyGif - - DKPhotoGallery/Preview (0.0.17): - - DKPhotoGallery/Model - - DKPhotoGallery/Resource - - SDWebImage - - SwiftyGif - - DKPhotoGallery/Resource (0.0.17): - - SDWebImage - - SwiftyGif - - file_picker (0.0.1): - - DKImagePickerController/PhotoGallery - - Flutter - - Flutter (1.0.0) - - flutter_libepiccash (0.0.1): - - Flutter - - flutter_libsparkmobile (0.0.1): - - Flutter - - flutter_local_notifications (0.0.1): - - Flutter - - flutter_native_splash (0.0.1): - - Flutter - - flutter_secure_storage (6.0.0): - - Flutter - - frostdart (0.0.1) - - integration_test (0.0.1): - - Flutter - - isar_flutter_libs (1.0.0): - - Flutter - - local_auth_darwin (0.0.1): - - Flutter - - FlutterMacOS - - MTBBarcodeScanner (5.0.11) - - package_info_plus (0.4.5): - - Flutter - - path_provider_foundation (0.0.1): - - Flutter - - FlutterMacOS - - permission_handler_apple (9.3.0): - - Flutter - - ReachabilitySwift (5.0.0) - - SDWebImage (5.13.2): - - SDWebImage/Core (= 5.13.2) - - SDWebImage/Core (5.13.2) - - share_plus (0.0.1): - - Flutter - - "sqlite3 (3.46.0+1)": - - "sqlite3/common (= 3.46.0+1)" - - "sqlite3/common (3.46.0+1)" - - "sqlite3/dbstatvtab (3.46.0+1)": - - sqlite3/common - - "sqlite3/fts5 (3.46.0+1)": - - sqlite3/common - - "sqlite3/perf-threadsafe (3.46.0+1)": - - sqlite3/common - - "sqlite3/rtree (3.46.0+1)": - - sqlite3/common - - sqlite3_flutter_libs (0.0.1): - - Flutter - - "sqlite3 (~> 3.46.0+1)" - - sqlite3/dbstatvtab - - sqlite3/fts5 - - sqlite3/perf-threadsafe - - sqlite3/rtree - - stack_wallet_backup (0.0.1): - - Flutter - - SwiftProtobuf (1.19.0) - - SwiftyGif (5.4.3) - - tor_ffi_plugin (0.0.1): - - Flutter - - url_launcher_ios (0.0.1): - - Flutter - - wakelock_plus (0.0.1): - - Flutter - - xelis_flutter (0.0.1): - - Flutter - -DEPENDENCIES: - - barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`) - - coinlib_flutter (from `.symlinks/plugins/coinlib_flutter/darwin`) - - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - - cs_monero_flutter_libs_ios (from `.symlinks/plugins/cs_monero_flutter_libs_ios/ios`) - - cs_salvium_flutter_libs_ios (from `.symlinks/plugins/cs_salvium_flutter_libs_ios/ios`) - - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - - devicelocale (from `.symlinks/plugins/devicelocale/ios`) - - file_picker (from `.symlinks/plugins/file_picker/ios`) - - Flutter (from `Flutter`) - - flutter_libepiccash (from `.symlinks/plugins/flutter_libepiccash/ios`) - - flutter_libsparkmobile (from `.symlinks/plugins/flutter_libsparkmobile/ios`) - - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) - - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - - frostdart (from `.symlinks/plugins/frostdart/ios`) - - integration_test (from `.symlinks/plugins/integration_test/ios`) - - isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`) - - local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`) - - package_info_plus (from `.symlinks/plugins/package_info_plus/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`) - - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`) - - stack_wallet_backup (from `.symlinks/plugins/stack_wallet_backup/ios`) - - tor_ffi_plugin (from `.symlinks/plugins/tor_ffi_plugin/ios`) - - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) - - xelis_flutter (from `.symlinks/plugins/xelis_flutter/ios`) - -SPEC REPOS: - trunk: - - DKImagePickerController - - DKPhotoGallery - - MTBBarcodeScanner - - ReachabilitySwift - - SDWebImage - - sqlite3 - - SwiftProtobuf - - SwiftyGif - -EXTERNAL SOURCES: - barcode_scan2: - :path: ".symlinks/plugins/barcode_scan2/ios" - coinlib_flutter: - :path: ".symlinks/plugins/coinlib_flutter/darwin" - connectivity_plus: - :path: ".symlinks/plugins/connectivity_plus/ios" - cs_monero_flutter_libs_ios: - :path: ".symlinks/plugins/cs_monero_flutter_libs_ios/ios" - cs_salvium_flutter_libs_ios: - :path: ".symlinks/plugins/cs_salvium_flutter_libs_ios/ios" - device_info_plus: - :path: ".symlinks/plugins/device_info_plus/ios" - devicelocale: - :path: ".symlinks/plugins/devicelocale/ios" - file_picker: - :path: ".symlinks/plugins/file_picker/ios" - Flutter: - :path: Flutter - flutter_libepiccash: - :path: ".symlinks/plugins/flutter_libepiccash/ios" - flutter_libsparkmobile: - :path: ".symlinks/plugins/flutter_libsparkmobile/ios" - flutter_local_notifications: - :path: ".symlinks/plugins/flutter_local_notifications/ios" - flutter_native_splash: - :path: ".symlinks/plugins/flutter_native_splash/ios" - flutter_secure_storage: - :path: ".symlinks/plugins/flutter_secure_storage/ios" - frostdart: - :path: ".symlinks/plugins/frostdart/ios" - integration_test: - :path: ".symlinks/plugins/integration_test/ios" - isar_flutter_libs: - :path: ".symlinks/plugins/isar_flutter_libs/ios" - local_auth_darwin: - :path: ".symlinks/plugins/local_auth_darwin/darwin" - package_info_plus: - :path: ".symlinks/plugins/package_info_plus/ios" - path_provider_foundation: - :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" - sqlite3_flutter_libs: - :path: ".symlinks/plugins/sqlite3_flutter_libs/ios" - stack_wallet_backup: - :path: ".symlinks/plugins/stack_wallet_backup/ios" - tor_ffi_plugin: - :path: ".symlinks/plugins/tor_ffi_plugin/ios" - url_launcher_ios: - :path: ".symlinks/plugins/url_launcher_ios/ios" - wakelock_plus: - :path: ".symlinks/plugins/wakelock_plus/ios" - xelis_flutter: - :path: ".symlinks/plugins/xelis_flutter/ios" - -SPEC CHECKSUMS: - barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0 - coinlib_flutter: 9275e8255ef67d3da33beb6e117d09ced4f46eb5 - connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a - cs_monero_flutter_libs_ios: fd353631682247f72a36493ff060d4328d6f720d - cs_salvium_flutter_libs_ios: f9d6ce540cb34d8cb8641822cf02fa0695a8d405 - device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d - devicelocale: 35ba84dc7f45f527c3001535d8c8d104edd5d926 - DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac - DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 - file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49 - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_libepiccash: 36241aa7d3126f6521529985ccb3dc5eaf7bb317 - flutter_libsparkmobile: 6373955cc3327a926d17059e7405dde2fb12f99f - flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086 - flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef - flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be - frostdart: 4c72b69ccac2f13ede744107db046a125acce597 - integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573 - isar_flutter_libs: fdf730ca925d05687f36d7f1d355e482529ed097 - local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3 - MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb - package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 - permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 - ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 - SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866 - share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 - sqlite3: 292c3e1bfe89f64e51ea7fc7dab9182a017c8630 - sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b - stack_wallet_backup: 5b8563aba5d8ffbf2ce1944331ff7294a0ec7c03 - SwiftProtobuf: 6ef3f0e422ef90d6605ca20b21a94f6c1324d6b3 - SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780 - tor_ffi_plugin: d80e291b649379c8176e1be739e49be007d4ef93 - url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe - wakelock_plus: 373cfe59b235a6dd5837d0fb88791d2f13a90d56 - xelis_flutter: a6a1ee1f1e47f5aeb42dc4a5889358b79d8d90fc - -PODFILE CHECKSUM: 57c8aed26fba39d3ec9424816221f294a07c58eb - -COCOAPODS: 1.16.2 diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c53e2b314e..9c12df59c6 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> Bool { - let dummy = get_mnemonic() - print(dummy) var flutter_native_splash = 1 UIApplication.shared.isStatusBarHidden = false diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h index d95502a5f1..7335fdf900 100644 --- a/ios/Runner/Runner-Bridging-Header.h +++ b/ios/Runner/Runner-Bridging-Header.h @@ -1,2 +1 @@ -#import "GeneratedPluginRegistrant.h" -#import "libepic_cash_wallet.h" \ No newline at end of file +#import "GeneratedPluginRegistrant.h" \ No newline at end of file diff --git a/scripts/app_config/templates/ios/Runner.xcodeproj/project.pbxproj b/scripts/app_config/templates/ios/Runner.xcodeproj/project.pbxproj index 300249f377..febbbb8af7 100644 --- a/scripts/app_config/templates/ios/Runner.xcodeproj/project.pbxproj +++ b/scripts/app_config/templates/ios/Runner.xcodeproj/project.pbxproj @@ -10,8 +10,6 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 7E1603B5288D73EA002F7A6F /* libepic_cash_wallet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E8A4F06288D5A9300F18717 /* libepic_cash_wallet.a */; }; - 7E729AE82893C1B1009BBD65 /* flutter_libepiccash.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E1603B3288D734C002F7A6F /* flutter_libepiccash.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -28,15 +26,6 @@ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7E1603B3288D734C002F7A6F /* flutter_libepiccash.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = flutter_libepiccash.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7E8A4F02288D57DE00F18717 /* flutter_libepiccash.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = flutter_libepiccash.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7E8A4F06288D5A9300F18717 /* libepic_cash_wallet.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libepic_cash_wallet.a; path = ../crypto_plugins/flutter_libepiccash/ios/libs/libepic_cash_wallet.a; sourceTree = ""; }; - 7E8A4F09288D5E8F00F18717 /* flutter_libepiccash.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = flutter_libepiccash.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7E8A4F0D288D5F3900F18717 /* flutter_libepiccash.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = flutter_libepiccash.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7E8A4F10288D602C00F18717 /* flutter_libepiccash.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = flutter_libepiccash.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7E8A4F15288D645200F18717 /* flutter_libepiccash.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = flutter_libepiccash.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7E8A4F19288D721300F18717 /* flutter_libepiccash.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = flutter_libepiccash.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7E8A4F1D288D72D100F18717 /* flutter_libepiccash.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = flutter_libepiccash.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -55,8 +44,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7E1603B5288D73EA002F7A6F /* libepic_cash_wallet.a in Frameworks */, - 7E729AE82893C1B1009BBD65 /* flutter_libepiccash.framework in Frameworks */, B49D91439948369648AB0603 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -77,15 +64,6 @@ 911C8C3F49E7889A970A9600 /* Frameworks */ = { isa = PBXGroup; children = ( - 7E1603B3288D734C002F7A6F /* flutter_libepiccash.framework */, - 7E8A4F1D288D72D100F18717 /* flutter_libepiccash.framework */, - 7E8A4F19288D721300F18717 /* flutter_libepiccash.framework */, - 7E8A4F15288D645200F18717 /* flutter_libepiccash.framework */, - 7E8A4F10288D602C00F18717 /* flutter_libepiccash.framework */, - 7E8A4F0D288D5F3900F18717 /* flutter_libepiccash.framework */, - 7E8A4F09288D5E8F00F18717 /* flutter_libepiccash.framework */, - 7E8A4F06288D5A9300F18717 /* libepic_cash_wallet.a */, - 7E8A4F02288D57DE00F18717 /* flutter_libepiccash.framework */, 51604430FD0FD1FA5C4767A0 /* Pods_Runner.framework */, ); name = Frameworks; @@ -493,7 +471,6 @@ "\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity/connectivity.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/cryptography_flutter/cryptography_flutter.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale/devicelocale.framework/Headers\"", - "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash/flutter_libepiccash.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libmonero/flutter_libmonero.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_local_notifications/flutter_local_notifications.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_native_splash/flutter_native_splash.framework/Headers\"", @@ -508,7 +485,6 @@ "\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup/stack_wallet_backup.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_ios/url_launcher_ios.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock/wakelock.framework/Headers\"", - "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/include", ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; @@ -519,7 +495,6 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", - "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", ); MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; ONLY_ACTIVE_ARCH = NO; @@ -530,7 +505,6 @@ STRIP_STYLE = "non-global"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; - SYSTEM_HEADER_SEARCH_PATHS = "\"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/linclude/\""; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_WORKSPACE = NO; VERSIONING_SYSTEM = "apple-generic"; @@ -673,7 +647,6 @@ "\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity/connectivity.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/cryptography_flutter/cryptography_flutter.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale/devicelocale.framework/Headers\"", - "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash/flutter_libepiccash.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libmonero/flutter_libmonero.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_local_notifications/flutter_local_notifications.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_native_splash/flutter_native_splash.framework/Headers\"", @@ -688,7 +661,6 @@ "\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup/stack_wallet_backup.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_ios/url_launcher_ios.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock/wakelock.framework/Headers\"", - "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/include", ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; @@ -699,7 +671,6 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", - "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", ); MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; ONLY_ACTIVE_ARCH = NO; @@ -711,7 +682,6 @@ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - SYSTEM_HEADER_SEARCH_PATHS = "\"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/linclude/\""; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_WORKSPACE = NO; VERSIONING_SYSTEM = "apple-generic"; @@ -745,7 +715,6 @@ "\"${PODS_CONFIGURATION_BUILD_DIR}/connectivity/connectivity.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/cryptography_flutter/cryptography_flutter.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/devicelocale/devicelocale.framework/Headers\"", - "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libepiccash/flutter_libepiccash.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_libmonero/flutter_libmonero.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_local_notifications/flutter_local_notifications.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_native_splash/flutter_native_splash.framework/Headers\"", @@ -760,7 +729,6 @@ "\"${PODS_CONFIGURATION_BUILD_DIR}/stack_wallet_backup/stack_wallet_backup.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_ios/url_launcher_ios.framework/Headers\"", "\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock/wakelock.framework/Headers\"", - "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/include", ); INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.0; @@ -771,7 +739,6 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", - "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", ); MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; ONLY_ACTIVE_ARCH = NO; @@ -782,7 +749,6 @@ STRIP_STYLE = "non-global"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; - SYSTEM_HEADER_SEARCH_PATHS = "\"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/linclude/\""; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_WORKSPACE = NO; VERSIONING_SYSTEM = "apple-generic"; From a734654c0af5b81441daa2496decdf4f9fcdf6df Mon Sep 17 00:00:00 2001 From: Julian Date: Tue, 7 Oct 2025 14:14:00 -0600 Subject: [PATCH 24/30] update gitignore --- .gitignore | 9 ++- linux/flutter/generated_plugin_registrant.cc | 59 ------------------- linux/flutter/generated_plugins.cmake | 42 ------------- macos/Flutter/GeneratedPluginRegistrant.swift | 56 ------------------ .../flutter/generated_plugin_registrant.cc | 59 ------------------- windows/flutter/generated_plugins.cmake | 45 -------------- 6 files changed, 8 insertions(+), 262 deletions(-) delete mode 100644 linux/flutter/generated_plugin_registrant.cc delete mode 100644 linux/flutter/generated_plugins.cmake delete mode 100644 macos/Flutter/GeneratedPluginRegistrant.swift delete mode 100644 windows/flutter/generated_plugin_registrant.cc delete mode 100644 windows/flutter/generated_plugins.cmake diff --git a/.gitignore b/.gitignore index e360da5157..0a8db4dcc3 100644 --- a/.gitignore +++ b/.gitignore @@ -114,4 +114,11 @@ crypto_plugins/*.diff /devtools_options.yaml # generated interfaces -lib/wl_gen/generated/ \ No newline at end of file +lib/wl_gen/generated/ + +# normally these aren't ignored but WL changes them... +/linux/flutter/generated_plugin_registrant.cc +/windows/flutter/generated_plugin_registrant.cc +/linux/flutter/generated_plugins.cmake +/windows/flutter/generated_plugins.cmake +/macos/Flutter/GeneratedPluginRegistrant.swift diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 6203e1d8ff..0000000000 --- a/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,59 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) cs_monero_flutter_libs_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "CsMoneroFlutterLibsLinuxPlugin"); - cs_monero_flutter_libs_linux_plugin_register_with_registrar(cs_monero_flutter_libs_linux_registrar); - g_autoptr(FlPluginRegistrar) cs_salvium_flutter_libs_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "CsSalviumFlutterLibsLinuxPlugin"); - cs_salvium_flutter_libs_linux_plugin_register_with_registrar(cs_salvium_flutter_libs_linux_registrar); - g_autoptr(FlPluginRegistrar) desktop_drop_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin"); - desktop_drop_plugin_register_with_registrar(desktop_drop_registrar); - g_autoptr(FlPluginRegistrar) devicelocale_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "DevicelocalePlugin"); - devicelocale_plugin_register_with_registrar(devicelocale_registrar); - g_autoptr(FlPluginRegistrar) flutter_libepiccash_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterLibepiccashPlugin"); - flutter_libepiccash_plugin_register_with_registrar(flutter_libepiccash_registrar); - g_autoptr(FlPluginRegistrar) flutter_libmwc_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterLibmwcPlugin"); - flutter_libmwc_plugin_register_with_registrar(flutter_libmwc_registrar); - g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); - flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); - g_autoptr(FlPluginRegistrar) isar_community_flutter_libs_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "IsarFlutterLibsPlugin"); - isar_flutter_libs_plugin_register_with_registrar(isar_community_flutter_libs_registrar); - g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin"); - sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar); - g_autoptr(FlPluginRegistrar) stack_wallet_backup_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "StackWalletBackupPlugin"); - stack_wallet_backup_plugin_register_with_registrar(stack_wallet_backup_registrar); - g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); - url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); - g_autoptr(FlPluginRegistrar) window_size_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "WindowSizePlugin"); - window_size_plugin_register_with_registrar(window_size_registrar); -} diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake deleted file mode 100644 index cb793bf5b0..0000000000 --- a/linux/flutter/generated_plugins.cmake +++ /dev/null @@ -1,42 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - cs_monero_flutter_libs_linux - cs_salvium_flutter_libs_linux - desktop_drop - devicelocale - flutter_libepiccash - flutter_libmwc - flutter_secure_storage_linux - isar_community_flutter_libs - sqlite3_flutter_libs - stack_wallet_backup - url_launcher_linux - window_size -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST - camera_linux - coinlib_flutter - flutter_libsparkmobile - flutter_mwebd - frostdart - tor_ffi_plugin - xelis_flutter -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index bee29fdb75..0000000000 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import camera_macos -import connectivity_plus -import cs_monero_flutter_libs_macos -import cs_salvium_flutter_libs_macos -import desktop_drop -import device_info_plus -import devicelocale -import file_picker -import flutter_libepiccash -import flutter_libmwc -import flutter_local_notifications -import flutter_secure_storage_macos -import isar_community_flutter_libs -import local_auth_darwin -import mobile_scanner -import package_info_plus -import path_provider_foundation -import share_plus -import sqlite3_flutter_libs -import stack_wallet_backup -import url_launcher_macos -import wakelock_plus -import window_size - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - CameraMacosPlugin.register(with: registry.registrar(forPlugin: "CameraMacosPlugin")) - ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) - CsMoneroFlutterLibsMacosPlugin.register(with: registry.registrar(forPlugin: "CsMoneroFlutterLibsMacosPlugin")) - CsSalviumFlutterLibsMacosPlugin.register(with: registry.registrar(forPlugin: "CsSalviumFlutterLibsMacosPlugin")) - DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin")) - DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) - DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) - FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) - FlutterLibepiccashPlugin.register(with: registry.registrar(forPlugin: "FlutterLibepiccashPlugin")) - FlutterLibmwcPlugin.register(with: registry.registrar(forPlugin: "FlutterLibmwcPlugin")) - FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) - FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) - IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin")) - LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin")) - MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) - FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) - Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) - StackWalletBackupPlugin.register(with: registry.registrar(forPlugin: "StackWalletBackupPlugin")) - UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) - WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) - WindowSizePlugin.register(with: registry.registrar(forPlugin: "WindowSizePlugin")) -} diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 9a0db709f5..0000000000 --- a/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,59 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void RegisterPlugins(flutter::PluginRegistry* registry) { - CameraWindowsRegisterWithRegistrar( - registry->GetRegistrarForPlugin("CameraWindows")); - ConnectivityPlusWindowsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); - CsMoneroFlutterLibsWindowsPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("CsMoneroFlutterLibsWindowsPluginCApi")); - CsSalviumFlutterLibsWindowsPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("CsSalviumFlutterLibsWindowsPluginCApi")); - DesktopDropPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("DesktopDropPlugin")); - FlutterLibepiccashPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("FlutterLibepiccashPluginCApi")); - FlutterLibmwcPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("FlutterLibmwcPluginCApi")); - FlutterSecureStorageWindowsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); - IsarFlutterLibsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("IsarFlutterLibsPlugin")); - LocalAuthPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("LocalAuthPlugin")); - PermissionHandlerWindowsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); - SharePlusWindowsPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); - Sqlite3FlutterLibsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); - StackWalletBackupPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("StackWalletBackupPluginCApi")); - UrlLauncherWindowsRegisterWithRegistrar( - registry->GetRegistrarForPlugin("UrlLauncherWindows")); - WindowSizePluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("WindowSizePlugin")); -} diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake deleted file mode 100644 index 1f19a41f02..0000000000 --- a/windows/flutter/generated_plugins.cmake +++ /dev/null @@ -1,45 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - camera_windows - connectivity_plus - cs_monero_flutter_libs_windows - cs_salvium_flutter_libs_windows - desktop_drop - flutter_libepiccash - flutter_libmwc - flutter_secure_storage_windows - isar_community_flutter_libs - local_auth_windows - permission_handler_windows - share_plus - sqlite3_flutter_libs - stack_wallet_backup - url_launcher_windows - window_size -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST - coinlib_flutter - flutter_libsparkmobile - flutter_mwebd - frostdart - tor_ffi_plugin - xelis_flutter -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) From bbba8faa06760f18f7c48046a8777dae7fd9a489 Mon Sep 17 00:00:00 2001 From: Julian Date: Tue, 7 Oct 2025 15:09:17 -0600 Subject: [PATCH 25/30] update flutter_libmwc with macos xcfw fix --- crypto_plugins/flutter_libmwc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto_plugins/flutter_libmwc b/crypto_plugins/flutter_libmwc index a692b7d685..f06b15071d 160000 --- a/crypto_plugins/flutter_libmwc +++ b/crypto_plugins/flutter_libmwc @@ -1 +1 @@ -Subproject commit a692b7d6859445791f6ba4270e4cf7e897506d78 +Subproject commit f06b15071dd6d9f2bb75d2373b410338762744e4 From 41f94a1c335003db91a65399ba91406141045be1 Mon Sep 17 00:00:00 2001 From: Julian Date: Tue, 7 Oct 2025 15:21:59 -0600 Subject: [PATCH 26/30] update macos build files --- macos/.gitignore | 2 + macos/Podfile | 2 +- macos/Podfile.lock | 199 ------------------ .../macos/Runner.xcodeproj/project.pbxproj | 41 ---- 4 files changed, 3 insertions(+), 241 deletions(-) delete mode 100644 macos/Podfile.lock diff --git a/macos/.gitignore b/macos/.gitignore index 746adbb6b9..08148cce59 100644 --- a/macos/.gitignore +++ b/macos/.gitignore @@ -5,3 +5,5 @@ # Xcode-related **/dgph **/xcuserdata/ + +Podfile.lock diff --git a/macos/Podfile b/macos/Podfile index c795730db8..b52666a103 100644 --- a/macos/Podfile +++ b/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.14' +platform :osx, '10.15' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/macos/Podfile.lock b/macos/Podfile.lock deleted file mode 100644 index 64b655098e..0000000000 --- a/macos/Podfile.lock +++ /dev/null @@ -1,199 +0,0 @@ -PODS: - - camera_macos (0.0.1): - - FlutterMacOS - - coinlib_flutter (0.5.0): - - Flutter - - FlutterMacOS - - connectivity_plus (0.0.1): - - FlutterMacOS - - ReachabilitySwift - - cs_monero_flutter_libs_macos (0.0.1): - - FlutterMacOS - - cs_salvium_flutter_libs_macos (0.0.1): - - FlutterMacOS - - desktop_drop (0.0.1): - - FlutterMacOS - - device_info_plus (0.0.1): - - FlutterMacOS - - devicelocale (0.0.1): - - FlutterMacOS - - file_picker (0.0.1): - - FlutterMacOS - - flutter_libepiccash (0.0.1): - - FlutterMacOS - - flutter_libsparkmobile (0.0.1): - - FlutterMacOS - - flutter_local_notifications (0.0.1): - - FlutterMacOS - - flutter_secure_storage_macos (6.1.1): - - FlutterMacOS - - FlutterMacOS (1.0.0) - - frostdart (0.0.1): - - FlutterMacOS - - isar_flutter_libs (1.0.0): - - FlutterMacOS - - local_auth_darwin (0.0.1): - - Flutter - - FlutterMacOS - - package_info_plus (0.0.1): - - FlutterMacOS - - path_provider_foundation (0.0.1): - - Flutter - - FlutterMacOS - - ReachabilitySwift (5.2.3) - - share_plus (0.0.1): - - FlutterMacOS - - "sqlite3 (3.46.0+1)": - - "sqlite3/common (= 3.46.0+1)" - - "sqlite3/common (3.46.0+1)" - - "sqlite3/dbstatvtab (3.46.0+1)": - - sqlite3/common - - "sqlite3/fts5 (3.46.0+1)": - - sqlite3/common - - "sqlite3/perf-threadsafe (3.46.0+1)": - - sqlite3/common - - "sqlite3/rtree (3.46.0+1)": - - sqlite3/common - - sqlite3_flutter_libs (0.0.1): - - FlutterMacOS - - "sqlite3 (~> 3.46.0+1)" - - sqlite3/dbstatvtab - - sqlite3/fts5 - - sqlite3/perf-threadsafe - - sqlite3/rtree - - stack_wallet_backup (0.0.1): - - FlutterMacOS - - tor_ffi_plugin (0.0.1) - - url_launcher_macos (0.0.1): - - FlutterMacOS - - wakelock_plus (0.0.1): - - FlutterMacOS - - window_size (0.0.2): - - FlutterMacOS - - xelis_flutter (0.0.1): - - FlutterMacOS - -DEPENDENCIES: - - camera_macos (from `Flutter/ephemeral/.symlinks/plugins/camera_macos/macos`) - - coinlib_flutter (from `Flutter/ephemeral/.symlinks/plugins/coinlib_flutter/darwin`) - - connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`) - - cs_monero_flutter_libs_macos (from `Flutter/ephemeral/.symlinks/plugins/cs_monero_flutter_libs_macos/macos`) - - cs_salvium_flutter_libs_macos (from `Flutter/ephemeral/.symlinks/plugins/cs_salvium_flutter_libs_macos/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`) - - file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`) - - flutter_libepiccash (from `Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos`) - - flutter_libsparkmobile (from `Flutter/ephemeral/.symlinks/plugins/flutter_libsparkmobile/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`) - - frostdart (from `Flutter/ephemeral/.symlinks/plugins/frostdart/macos`) - - isar_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos`) - - local_auth_darwin (from `Flutter/ephemeral/.symlinks/plugins/local_auth_darwin/darwin`) - - 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`) - - sqlite3_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos`) - - stack_wallet_backup (from `Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos`) - - tor_ffi_plugin (from `Flutter/ephemeral/.symlinks/plugins/tor_ffi_plugin/macos`) - - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - - wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`) - - window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`) - - xelis_flutter (from `Flutter/ephemeral/.symlinks/plugins/xelis_flutter/macos`) - -SPEC REPOS: - trunk: - - ReachabilitySwift - - sqlite3 - -EXTERNAL SOURCES: - camera_macos: - :path: Flutter/ephemeral/.symlinks/plugins/camera_macos/macos - coinlib_flutter: - :path: Flutter/ephemeral/.symlinks/plugins/coinlib_flutter/darwin - connectivity_plus: - :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos - cs_monero_flutter_libs_macos: - :path: Flutter/ephemeral/.symlinks/plugins/cs_monero_flutter_libs_macos/macos - cs_salvium_flutter_libs_macos: - :path: Flutter/ephemeral/.symlinks/plugins/cs_salvium_flutter_libs_macos/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 - file_picker: - :path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos - flutter_libepiccash: - :path: Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos - flutter_libsparkmobile: - :path: Flutter/ephemeral/.symlinks/plugins/flutter_libsparkmobile/macos - flutter_local_notifications: - :path: Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos - flutter_secure_storage_macos: - :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos - FlutterMacOS: - :path: Flutter/ephemeral - frostdart: - :path: Flutter/ephemeral/.symlinks/plugins/frostdart/macos - isar_flutter_libs: - :path: Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos - local_auth_darwin: - :path: Flutter/ephemeral/.symlinks/plugins/local_auth_darwin/darwin - 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 - sqlite3_flutter_libs: - :path: Flutter/ephemeral/.symlinks/plugins/sqlite3_flutter_libs/macos - stack_wallet_backup: - :path: Flutter/ephemeral/.symlinks/plugins/stack_wallet_backup/macos - tor_ffi_plugin: - :path: Flutter/ephemeral/.symlinks/plugins/tor_ffi_plugin/macos - url_launcher_macos: - :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos - wakelock_plus: - :path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos - window_size: - :path: Flutter/ephemeral/.symlinks/plugins/window_size/macos - xelis_flutter: - :path: Flutter/ephemeral/.symlinks/plugins/xelis_flutter/macos - -SPEC CHECKSUMS: - camera_macos: c2603f5eed16f05076cf17e12030d2ce55a77839 - coinlib_flutter: 9275e8255ef67d3da33beb6e117d09ced4f46eb5 - connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 - cs_monero_flutter_libs_macos: b901f94d39d1338f706312b026aba928d23582d4 - cs_salvium_flutter_libs_macos: 3c7b30fb8c82ee0fb0195280ddcc10c65ab5e082 - desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 - device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720 - devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225 - file_picker: e716a70a9fe5fd9e09ebc922d7541464289443af - flutter_libepiccash: be1560a04150c5cc85bcf08d236ec2b3d1f5d8da - flutter_libsparkmobile: df2d36af1691379c81249e7be7b68be3c81d388b - flutter_local_notifications: 4b427ffabf278fc6ea9484c97505e231166927a5 - flutter_secure_storage_macos: 59459653abe1adb92abbc8ea747d79f8d19866c9 - FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - frostdart: e6bf3119527ccfbcec1b8767da6ede5bb4c4f716 - isar_flutter_libs: 43385c99864c168fadba7c9adeddc5d38838ca6a - local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3 - package_info_plus: 12f1c5c2cfe8727ca46cbd0b26677728972d9a5b - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 - ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979 - share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 - sqlite3: 292c3e1bfe89f64e51ea7fc7dab9182a017c8630 - sqlite3_flutter_libs: 5ca46c1a04eddfbeeb5b16566164aa7ad1616e7b - stack_wallet_backup: 6ebc60b1bdcf11cf1f1cbad9aa78332e1e15778c - tor_ffi_plugin: 2566c1ed174688cca560fa0c64b7a799c66f07cb - url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404 - wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269 - window_size: 339dafa0b27a95a62a843042038fa6c3c48de195 - xelis_flutter: 34e05f3621e46381fb1b10d7c11f63764d3f7a80 - -PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 - -COCOAPODS: 1.16.2 diff --git a/scripts/app_config/templates/macos/Runner.xcodeproj/project.pbxproj b/scripts/app_config/templates/macos/Runner.xcodeproj/project.pbxproj index c0fe37d8ac..6769f9c249 100644 --- a/scripts/app_config/templates/macos/Runner.xcodeproj/project.pbxproj +++ b/scripts/app_config/templates/macos/Runner.xcodeproj/project.pbxproj @@ -29,9 +29,6 @@ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; 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 */; }; - F1FA2C4E2BA4B49F00BDA1BB /* frostdart.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F1FA2C4D2BA4B49F00BDA1BB /* frostdart.dylib */; settings = {ATTRIBUTES = (Weak, ); }; }; - F1FA2C502BA4B4CA00BDA1BB /* frostdart.dylib in Resources */ = {isa = PBXBuildFile; fileRef = F1FA2C4F2BA4B4CA00BDA1BB /* frostdart.dylib */; }; - F1FA2C512BA4B51E00BDA1BB /* frostdart.dylib in Bundle Framework */ = {isa = PBXBuildFile; fileRef = F1FA2C4D2BA4B49F00BDA1BB /* frostdart.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; F653CA022D33E8B60E11A9F3 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ @@ -52,20 +49,6 @@ }; /* End PBXContainerItemProxy section */ -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - F1FA2C512BA4B51E00BDA1BB /* frostdart.dylib in Bundle Framework */, - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* 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 = ""; }; @@ -94,8 +77,6 @@ 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; }; - F1FA2C4D2BA4B49F00BDA1BB /* frostdart.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = frostdart.dylib; path = ../crypto_plugins/frostdart/macos/frostdart.dylib; sourceTree = ""; }; - F1FA2C4F2BA4B4CA00BDA1BB /* frostdart.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = frostdart.dylib; path = ../crypto_plugins/frostdart/macos/frostdart.dylib; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -112,7 +93,6 @@ buildActionMask = 2147483647; files = ( B98151842A674143009D013C /* libsqlite3.0.tbd in Frameworks */, - F1FA2C4E2BA4B49F00BDA1BB /* frostdart.dylib in Frameworks */, F653CA022D33E8B60E11A9F3 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -142,7 +122,6 @@ 33CC10E42044A3C60003C045 = { isa = PBXGroup; children = ( - F1FA2C4F2BA4B4CA00BDA1BB /* frostdart.dylib */, 33FAB671232836740065AC1E /* Runner */, 33CEB47122A05771004F2AC0 /* Flutter */, 331C80D6294CF71000263BE5 /* RunnerTests */, @@ -199,7 +178,6 @@ D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( - F1FA2C4D2BA4B49F00BDA1BB /* frostdart.dylib */, B98151832A674143009D013C /* libsqlite3.0.tbd */, E6036BF01BF05EA773C76D22 /* Pods_Runner.framework */, 9206484E84CB0AD93E3E68CA /* Pods_RunnerTests.framework */, @@ -328,7 +306,6 @@ files = ( 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - F1FA2C502BA4B4CA00BDA1BB /* frostdart.dylib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -592,16 +569,13 @@ "\"${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}/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\"", ); INFOPLIST_FILE = Runner/Info.plist; @@ -612,10 +586,7 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}\"", - "\"${PODS_ROOT}/../Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos/libs\"", - "\"${PODS_ROOT}/../Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos\"", /usr/lib/swift, - "$(PATH)/crypto_plugins/frostdart/macos\n", ); PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; @@ -748,16 +719,13 @@ "\"${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}/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\"", ); INFOPLIST_FILE = Runner/Info.plist; @@ -768,10 +736,7 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}\"", - "\"${PODS_ROOT}/../Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos/libs\"", - "\"${PODS_ROOT}/../Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos\"", /usr/lib/swift, - "$(PATH)/crypto_plugins/frostdart/macos\n", ); PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -795,16 +760,13 @@ "\"${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}/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\"", ); INFOPLIST_FILE = Runner/Info.plist; @@ -815,10 +777,7 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}\"", - "\"${PODS_ROOT}/../Flutter/ephemeral/.symlinks/plugins/flutter_libepiccash/macos/libs\"", - "\"${PODS_ROOT}/../Flutter/ephemeral/.symlinks/plugins/isar_flutter_libs/macos\"", /usr/lib/swift, - "$(PATH)/crypto_plugins/frostdart/macos\n", ); PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; From da07f0de534d85aba3693fe4aa57b0178868c2ff Mon Sep 17 00:00:00 2001 From: Julian Date: Tue, 7 Oct 2025 15:27:14 -0600 Subject: [PATCH 27/30] class name fix --- tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart b/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart index 8115f82493..5380ddf256 100644 --- a/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart +++ b/tool/wl_templates/XEL_lib_xelis_interface_impl.template.dart @@ -24,9 +24,9 @@ LibXelisInterface _getInterface() => throw Exception("XEL not enabled!"); //END_OFF //ON -LibXelisInterface _getInterface() => _MwebdServerInterfaceImpl(); +LibXelisInterface _getInterface() => _LibXelisInterfaceImpl(); -final class _MwebdServerInterfaceImpl extends LibXelisInterface { +final class _LibXelisInterfaceImpl extends LibXelisInterface { final Map _wallets = {}; @override From 34cb2b53635962671094b6655cb3e1684e3ba640 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 8 Oct 2025 08:13:52 -0600 Subject: [PATCH 28/30] stupid hack. One day epic and mwc will be proper libraries... --- scripts/app_config/configure_campfire.sh | 3 +++ scripts/app_config/configure_stack_duo.sh | 3 +++ scripts/app_config/configure_stack_wallet.sh | 3 +++ .../app_config/platforms/linux/platform_config.sh | 4 +++- .../platforms/windows/platform_config.sh | 4 +++- scripts/app_config/templates/linux/CMakeLists.txt | 15 +++++++++++---- .../app_config/templates/windows/CMakeLists.txt | 15 +++++++++++---- 7 files changed, 37 insertions(+), 10 deletions(-) diff --git a/scripts/app_config/configure_campfire.sh b/scripts/app_config/configure_campfire.sh index c5e49296ac..04054b9ec4 100755 --- a/scripts/app_config/configure_campfire.sh +++ b/scripts/app_config/configure_campfire.sh @@ -40,6 +40,9 @@ dart "${APP_PROJECT_ROOT_DIR}/tool/gen_interfaces.dart" \ TOR \ FIRO +export INCLUDE_EPIC_SO="OFF" +export INCLUDE_MWC_SO="OFF" + pushd "${APP_PROJECT_ROOT_DIR}" BUILT_COMMIT_HASH=$(git log -1 --pretty=format:"%H") popd diff --git a/scripts/app_config/configure_stack_duo.sh b/scripts/app_config/configure_stack_duo.sh index 7a839ae3c0..148775aa3e 100755 --- a/scripts/app_config/configure_stack_duo.sh +++ b/scripts/app_config/configure_stack_duo.sh @@ -36,6 +36,9 @@ dart "${APP_PROJECT_ROOT_DIR}/tool/gen_interfaces.dart" \ TOR \ FROST +export INCLUDE_EPIC_SO="OFF" +export INCLUDE_MWC_SO="OFF" + pushd "${APP_PROJECT_ROOT_DIR}" BUILT_COMMIT_HASH=$(git log -1 --pretty=format:"%H") popd diff --git a/scripts/app_config/configure_stack_wallet.sh b/scripts/app_config/configure_stack_wallet.sh index e976821c8e..f468180d87 100755 --- a/scripts/app_config/configure_stack_wallet.sh +++ b/scripts/app_config/configure_stack_wallet.sh @@ -48,6 +48,9 @@ dart "${APP_PROJECT_ROOT_DIR}/tool/gen_interfaces.dart" \ XEL \ FROST +export INCLUDE_EPIC_SO="ON" +export INCLUDE_MWC_SO="ON" + pushd "${APP_PROJECT_ROOT_DIR}" BUILT_COMMIT_HASH=$(git log -1 --pretty=format:"%H") popd diff --git a/scripts/app_config/platforms/linux/platform_config.sh b/scripts/app_config/platforms/linux/platform_config.sh index 018f80def7..61dfdfb8bd 100755 --- a/scripts/app_config/platforms/linux/platform_config.sh +++ b/scripts/app_config/platforms/linux/platform_config.sh @@ -12,6 +12,8 @@ for (( i=0; i<=1; i++ )); do fi done -# Configure Linux for Duo. +# Configure Linux sed -i "s/${APP_BASIC_NAME_PLACEHOLDER}/${NEW_BASIC_NAME}/g" "${APP_PROJECT_ROOT_DIR}/${LINUX_TF_0}" sed -i "s/${APP_NAME_PLACEHOLDER}/${NEW_NAME}/g" "${APP_PROJECT_ROOT_DIR}/${LINUX_TF_1}" +sed -i "s/INCLUDE_EPIC_SO_FLAG/${INCLUDE_EPIC_SO}/g" "${APP_PROJECT_ROOT_DIR}/${LINUX_TF_0}" +sed -i "s/INCLUDE_MWC_SO_FLAG/${INCLUDE_MWC_SO}/g" "${APP_PROJECT_ROOT_DIR}/${LINUX_TF_0}" \ No newline at end of file diff --git a/scripts/app_config/platforms/windows/platform_config.sh b/scripts/app_config/platforms/windows/platform_config.sh index 4a0158c80e..cbce1d8108 100755 --- a/scripts/app_config/platforms/windows/platform_config.sh +++ b/scripts/app_config/platforms/windows/platform_config.sh @@ -12,7 +12,9 @@ for (( i=0; i<=2; i++ )); do fi done -# Configure Windows for Duo. +# Configure Windows sed -i "s/${APP_NAME_PLACEHOLDER}/${NEW_NAME}/g" "${APP_PROJECT_ROOT_DIR}/${WIN_TF_0}" sed -i "s/${APP_NAME_PLACEHOLDER}/${NEW_NAME}/g" "${APP_PROJECT_ROOT_DIR}/${WIN_TF_1}" sed -i "s/${APP_BASIC_NAME_PLACEHOLDER}/${NEW_BASIC_NAME}/g" "${APP_PROJECT_ROOT_DIR}/${WIN_TF_2}" +sed -i "s/INCLUDE_EPIC_SO_FLAG/${INCLUDE_EPIC_SO}/g" "${APP_PROJECT_ROOT_DIR}/${WIN_TF_2}" +sed -i "s/INCLUDE_MWC_SO_FLAG/${INCLUDE_MWC_SO}/g" "${APP_PROJECT_ROOT_DIR}/${WIN_TF_2}" diff --git a/scripts/app_config/templates/linux/CMakeLists.txt b/scripts/app_config/templates/linux/CMakeLists.txt index 4c140a4b95..4b0e69d628 100644 --- a/scripts/app_config/templates/linux/CMakeLists.txt +++ b/scripts/app_config/templates/linux/CMakeLists.txt @@ -9,6 +9,9 @@ set(BINARY_NAME "place_holder") # https://wiki.gnome.org/HowDoI/ChooseApplicationID set(APPLICATION_ID "com.place.holder") +set(INCLUDE_EPIC_SO INCLUDE_EPIC_SO_FLAG) +set(INCLUDE_MWC_SO INCLUDE_EPIC_SO_FLAG) + # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. cmake_policy(SET CMP0063 NEW) @@ -134,11 +137,15 @@ install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR} install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libepiccash/scripts/linux/build/rust/target/x86_64-unknown-linux-gnu/release/libepic_cash_wallet.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) +if(INCLUDE_EPIC_SO) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libepiccash/scripts/linux/build/rust/target/x86_64-unknown-linux-gnu/release/libepic_cash_wallet.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmwc/scripts/linux/build/rust/target/x86_64-unknown-linux-gnu/release/libmwc_wallet.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) +if(INCLUDE_MWC_SO) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmwc/scripts/linux/build/rust/target/x86_64-unknown-linux-gnu/release/libmwc_wallet.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/linux/build/jsoncpp/build/src/lib_json/libjsoncpp.so.1.7.4" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) diff --git a/scripts/app_config/templates/windows/CMakeLists.txt b/scripts/app_config/templates/windows/CMakeLists.txt index 7226cf1378..02532aa15e 100644 --- a/scripts/app_config/templates/windows/CMakeLists.txt +++ b/scripts/app_config/templates/windows/CMakeLists.txt @@ -6,6 +6,9 @@ project(place_holder LANGUAGES CXX) # the on-disk name of your application. set(BINARY_NAME "place_holder") +set(INCLUDE_EPIC_SO INCLUDE_EPIC_SO_FLAG) +set(INCLUDE_MWC_SO INCLUDE_EPIC_SO_FLAG) + # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. cmake_policy(SET CMP0063 NEW) @@ -80,11 +83,15 @@ install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR} install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libepiccash/scripts/windows/build/libepic_cash_wallet.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) +if(INCLUDE_EPIC_SO) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libepiccash/scripts/windows/build/libepic_cash_wallet.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmwc/scripts/windows/build/libmwc_cash_wallet.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) +if(INCLUDE_MWC_SO) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmwc/scripts/windows/build/libmwc_cash_wallet.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() if(PLUGIN_BUNDLED_LIBRARIES) install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" From 4bf95c6293b9462671a0928b2b5e901296c07d02 Mon Sep 17 00:00:00 2001 From: Julian Date: Wed, 8 Oct 2025 09:01:34 -0600 Subject: [PATCH 29/30] update frostdart with macos xcframework --- crypto_plugins/frostdart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto_plugins/frostdart b/crypto_plugins/frostdart index a8c2121925..acf903869a 160000 --- a/crypto_plugins/frostdart +++ b/crypto_plugins/frostdart @@ -1 +1 @@ -Subproject commit a8c21219259429299eb9c262510152e55ab05e67 +Subproject commit acf903869ae9ddc9e3f78501d7eac50a8a626439 From 183380a072da76ddeeb68a627b8df40976a38017 Mon Sep 17 00:00:00 2001 From: Julian Date: Wed, 8 Oct 2025 09:09:50 -0600 Subject: [PATCH 30/30] no spark logging without firo --- lib/main.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 968d595765..7bfba70f00 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -185,8 +185,10 @@ void main(List args) async { libXelis.startListeningToRustLogs(); } - // setup lib spark logging - initSparkLogging(Prefs.instance.logLevel); + if (AppConfig.coins.whereType().isNotEmpty) { + // setup lib spark logging + initSparkLogging(Prefs.instance.logLevel); + } if (AppConfig.appName == "Campfire" && !Util.isDesktop &&