From e3624880c5d17f5259d93c32c2fdf3a1b1c1dfc0 Mon Sep 17 00:00:00 2001 From: fosse Date: Mon, 24 Jul 2023 16:23:09 -0400 Subject: [PATCH] config changes --- cw_nano/lib/api/account_list.dart | 83 ++++ cw_nano/lib/api/convert_utf8_to_string.dart | 8 + .../connection_to_node_exception.dart | 5 + .../creation_transaction_exception.dart | 8 + .../exceptions/setup_wallet_exception.dart | 5 + .../exceptions/wallet_creation_exception.dart | 8 + .../exceptions/wallet_opening_exception.dart | 8 + .../wallet_restore_from_keys_exception.dart | 5 + .../wallet_restore_from_seed_exception.dart | 5 + cw_nano/lib/api/monero_api.dart | 6 + cw_nano/lib/api/monero_output.dart | 6 + cw_nano/lib/api/signatures.dart | 132 +++++++ cw_nano/lib/api/structs/account_row.dart | 12 + .../lib/api/structs/pending_transaction.dart | 39 ++ cw_nano/lib/api/structs/subaddress_row.dart | 15 + .../lib/api/structs/transaction_info_row.dart | 41 ++ cw_nano/lib/api/structs/ut8_box.dart | 8 + cw_nano/lib/api/subaddress_list.dart | 97 +++++ cw_nano/lib/api/transaction_history.dart | 234 +++++++++++ cw_nano/lib/api/types.dart | 130 ++++++ cw_nano/lib/api/wallet.dart | 374 ++++++++++++++++++ cw_nano/lib/api/wallet_manager.dart | 254 ++++++++++++ cw_nano/lib/nano_account_list.dart | 82 ++++ cw_nano/lib/nano_balance.dart | 1 - cw_nano/lib/nano_transaction_history.dart | 28 ++ cw_nano/lib/nano_wallet.dart | 198 +++------- cw_nano/lib/nano_wallet_addresses.dart | 45 +++ .../lib/nano_wallet_creation_credentials.dart | 26 ++ cw_nano/lib/nano_wallet_service.dart | 103 +++++ lib/nano/cw_nano.dart | 190 ++++----- lib/nano/nano.dart | 100 +++-- lib/view_model/wallet_new_vm.dart | 3 + model_generator.sh | 1 + scripts/android/pubspec_gen.sh | 2 +- tool/configure.dart | 190 +++++++-- 35 files changed, 2108 insertions(+), 344 deletions(-) create mode 100644 cw_nano/lib/api/account_list.dart create mode 100644 cw_nano/lib/api/convert_utf8_to_string.dart create mode 100644 cw_nano/lib/api/exceptions/connection_to_node_exception.dart create mode 100644 cw_nano/lib/api/exceptions/creation_transaction_exception.dart create mode 100644 cw_nano/lib/api/exceptions/setup_wallet_exception.dart create mode 100644 cw_nano/lib/api/exceptions/wallet_creation_exception.dart create mode 100644 cw_nano/lib/api/exceptions/wallet_opening_exception.dart create mode 100644 cw_nano/lib/api/exceptions/wallet_restore_from_keys_exception.dart create mode 100644 cw_nano/lib/api/exceptions/wallet_restore_from_seed_exception.dart create mode 100644 cw_nano/lib/api/monero_api.dart create mode 100644 cw_nano/lib/api/monero_output.dart create mode 100644 cw_nano/lib/api/signatures.dart create mode 100644 cw_nano/lib/api/structs/account_row.dart create mode 100644 cw_nano/lib/api/structs/pending_transaction.dart create mode 100644 cw_nano/lib/api/structs/subaddress_row.dart create mode 100644 cw_nano/lib/api/structs/transaction_info_row.dart create mode 100644 cw_nano/lib/api/structs/ut8_box.dart create mode 100644 cw_nano/lib/api/subaddress_list.dart create mode 100644 cw_nano/lib/api/transaction_history.dart create mode 100644 cw_nano/lib/api/types.dart create mode 100644 cw_nano/lib/api/wallet.dart create mode 100644 cw_nano/lib/api/wallet_manager.dart create mode 100644 cw_nano/lib/nano_account_list.dart create mode 100644 cw_nano/lib/nano_transaction_history.dart create mode 100644 cw_nano/lib/nano_wallet_addresses.dart create mode 100644 cw_nano/lib/nano_wallet_creation_credentials.dart create mode 100644 cw_nano/lib/nano_wallet_service.dart mode change 100644 => 100755 model_generator.sh diff --git a/cw_nano/lib/api/account_list.dart b/cw_nano/lib/api/account_list.dart new file mode 100644 index 000000000..a5d1130ea --- /dev/null +++ b/cw_nano/lib/api/account_list.dart @@ -0,0 +1,83 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:cw_nano/api/signatures.dart'; +import 'package:cw_nano/api/types.dart'; +import 'package:cw_nano/api/monero_api.dart'; +import 'package:cw_nano/api/structs/account_row.dart'; +import 'package:flutter/foundation.dart'; +import 'package:cw_nano/api/wallet.dart'; + +final accountSizeNative = moneroApi + .lookup>('account_size') + .asFunction(); + +final accountRefreshNative = moneroApi + .lookup>('account_refresh') + .asFunction(); + +final accountGetAllNative = moneroApi + .lookup>('account_get_all') + .asFunction(); + +final accountAddNewNative = moneroApi + .lookup>('account_add_row') + .asFunction(); + +final accountSetLabelNative = moneroApi + .lookup>('account_set_label_row') + .asFunction(); + +bool isUpdating = false; + +void refreshAccounts() { + try { + isUpdating = true; + accountRefreshNative(); + isUpdating = false; + } catch (e) { + isUpdating = false; + rethrow; + } +} + +List getAllAccount() { + final size = accountSizeNative(); + final accountAddressesPointer = accountGetAllNative(); + final accountAddresses = accountAddressesPointer.asTypedList(size); + + return accountAddresses + .map((addr) => Pointer.fromAddress(addr).ref) + .toList(); +} + +void addAccountSync({required String label}) { + final labelPointer = label.toNativeUtf8(); + accountAddNewNative(labelPointer); + calloc.free(labelPointer); +} + +void setLabelForAccountSync({required int accountIndex, required String label}) { + final labelPointer = label.toNativeUtf8(); + accountSetLabelNative(accountIndex, labelPointer); + calloc.free(labelPointer); +} + +void _addAccount(String label) => addAccountSync(label: label); + +void _setLabelForAccount(Map args) { + final label = args['label'] as String; + final accountIndex = args['accountIndex'] as int; + + setLabelForAccountSync(label: label, accountIndex: accountIndex); +} + +Future addAccount({required String label}) async { + await compute(_addAccount, label); + await store(); +} + +Future setLabelForAccount({required int accountIndex, required String label}) async { + await compute( + _setLabelForAccount, {'accountIndex': accountIndex, 'label': label}); + await store(); +} \ No newline at end of file diff --git a/cw_nano/lib/api/convert_utf8_to_string.dart b/cw_nano/lib/api/convert_utf8_to_string.dart new file mode 100644 index 000000000..41a6b648a --- /dev/null +++ b/cw_nano/lib/api/convert_utf8_to_string.dart @@ -0,0 +1,8 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +String convertUTF8ToString({required Pointer pointer}) { + final str = pointer.toDartString(); + calloc.free(pointer); + return str; +} \ No newline at end of file diff --git a/cw_nano/lib/api/exceptions/connection_to_node_exception.dart b/cw_nano/lib/api/exceptions/connection_to_node_exception.dart new file mode 100644 index 000000000..483b0a174 --- /dev/null +++ b/cw_nano/lib/api/exceptions/connection_to_node_exception.dart @@ -0,0 +1,5 @@ +class ConnectionToNodeException implements Exception { + ConnectionToNodeException({required this.message}); + + final String message; +} \ No newline at end of file diff --git a/cw_nano/lib/api/exceptions/creation_transaction_exception.dart b/cw_nano/lib/api/exceptions/creation_transaction_exception.dart new file mode 100644 index 000000000..7b55ec074 --- /dev/null +++ b/cw_nano/lib/api/exceptions/creation_transaction_exception.dart @@ -0,0 +1,8 @@ +class CreationTransactionException implements Exception { + CreationTransactionException({required this.message}); + + final String message; + + @override + String toString() => message; +} \ No newline at end of file diff --git a/cw_nano/lib/api/exceptions/setup_wallet_exception.dart b/cw_nano/lib/api/exceptions/setup_wallet_exception.dart new file mode 100644 index 000000000..b6e0c1f18 --- /dev/null +++ b/cw_nano/lib/api/exceptions/setup_wallet_exception.dart @@ -0,0 +1,5 @@ +class SetupWalletException implements Exception { + SetupWalletException({required this.message}); + + final String message; +} \ No newline at end of file diff --git a/cw_nano/lib/api/exceptions/wallet_creation_exception.dart b/cw_nano/lib/api/exceptions/wallet_creation_exception.dart new file mode 100644 index 000000000..6052366b9 --- /dev/null +++ b/cw_nano/lib/api/exceptions/wallet_creation_exception.dart @@ -0,0 +1,8 @@ +class WalletCreationException implements Exception { + WalletCreationException({required this.message}); + + final String message; + + @override + String toString() => message; +} \ No newline at end of file diff --git a/cw_nano/lib/api/exceptions/wallet_opening_exception.dart b/cw_nano/lib/api/exceptions/wallet_opening_exception.dart new file mode 100644 index 000000000..df7a850a4 --- /dev/null +++ b/cw_nano/lib/api/exceptions/wallet_opening_exception.dart @@ -0,0 +1,8 @@ +class WalletOpeningException implements Exception { + WalletOpeningException({required this.message}); + + final String message; + + @override + String toString() => message; +} \ No newline at end of file diff --git a/cw_nano/lib/api/exceptions/wallet_restore_from_keys_exception.dart b/cw_nano/lib/api/exceptions/wallet_restore_from_keys_exception.dart new file mode 100644 index 000000000..c6b6c6ef7 --- /dev/null +++ b/cw_nano/lib/api/exceptions/wallet_restore_from_keys_exception.dart @@ -0,0 +1,5 @@ +class WalletRestoreFromKeysException implements Exception { + WalletRestoreFromKeysException({required this.message}); + + final String message; +} \ No newline at end of file diff --git a/cw_nano/lib/api/exceptions/wallet_restore_from_seed_exception.dart b/cw_nano/lib/api/exceptions/wallet_restore_from_seed_exception.dart new file mode 100644 index 000000000..004cd7958 --- /dev/null +++ b/cw_nano/lib/api/exceptions/wallet_restore_from_seed_exception.dart @@ -0,0 +1,5 @@ +class WalletRestoreFromSeedException implements Exception { + WalletRestoreFromSeedException({required this.message}); + + final String message; +} \ No newline at end of file diff --git a/cw_nano/lib/api/monero_api.dart b/cw_nano/lib/api/monero_api.dart new file mode 100644 index 000000000..398d737d1 --- /dev/null +++ b/cw_nano/lib/api/monero_api.dart @@ -0,0 +1,6 @@ +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary moneroApi = Platform.isAndroid + ? DynamicLibrary.open("libcw_monero.so") + : DynamicLibrary.open("cw_monero.framework/cw_monero"); \ No newline at end of file diff --git a/cw_nano/lib/api/monero_output.dart b/cw_nano/lib/api/monero_output.dart new file mode 100644 index 000000000..5b468cb48 --- /dev/null +++ b/cw_nano/lib/api/monero_output.dart @@ -0,0 +1,6 @@ +class MoneroOutput { + MoneroOutput({required this.address, required this.amount}); + + final String address; + final String amount; +} \ No newline at end of file diff --git a/cw_nano/lib/api/signatures.dart b/cw_nano/lib/api/signatures.dart new file mode 100644 index 000000000..0b14fd557 --- /dev/null +++ b/cw_nano/lib/api/signatures.dart @@ -0,0 +1,132 @@ +import 'dart:ffi'; +import 'package:cw_monero/api/structs/pending_transaction.dart'; +import 'package:cw_monero/api/structs/ut8_box.dart'; +import 'package:ffi/ffi.dart'; + +typedef create_wallet = Int8 Function( + Pointer, Pointer, Pointer, Int32, Pointer); + +typedef restore_wallet_from_seed = Int8 Function( + Pointer, Pointer, Pointer, Int32, Int64, Pointer); + +typedef restore_wallet_from_keys = Int8 Function(Pointer, Pointer, + Pointer, Pointer, Pointer, Pointer, Int32, Int64, Pointer); + +typedef is_wallet_exist = Int8 Function(Pointer); + +typedef load_wallet = Int8 Function(Pointer, Pointer, Int8); + +typedef error_string = Pointer Function(); + +typedef get_filename = Pointer Function(); + +typedef get_seed = Pointer Function(); + +typedef get_address = Pointer Function(Int32, Int32); + +typedef get_full_balanace = Int64 Function(Int32); + +typedef get_unlocked_balanace = Int64 Function(Int32); + +typedef get_current_height = Int64 Function(); + +typedef get_node_height = Int64 Function(); + +typedef is_connected = Int8 Function(); + +typedef setup_node = Int8 Function( + Pointer, Pointer?, Pointer?, Int8, Int8, Pointer); + +typedef start_refresh = Void Function(); + +typedef connect_to_node = Int8 Function(); + +typedef set_refresh_from_block_height = Void Function(Int64); + +typedef set_recovering_from_seed = Void Function(Int8); + +typedef store_c = Void Function(Pointer); + +typedef set_password = Int8 Function(Pointer password, Pointer error); + +typedef set_listener = Void Function(); + +typedef get_syncing_height = Int64 Function(); + +typedef is_needed_to_refresh = Int8 Function(); + +typedef is_new_transaction_exist = Int8 Function(); + +typedef subaddrress_size = Int32 Function(); + +typedef subaddrress_refresh = Void Function(Int32); + +typedef subaddress_get_all = Pointer Function(); + +typedef subaddress_add_new = Void Function( + Int32 accountIndex, Pointer label); + +typedef subaddress_set_label = Void Function( + Int32 accountIndex, Int32 addressIndex, Pointer label); + +typedef account_size = Int32 Function(); + +typedef account_refresh = Void Function(); + +typedef account_get_all = Pointer Function(); + +typedef account_add_new = Void Function(Pointer label); + +typedef account_set_label = Void Function( + Int32 accountIndex, Pointer label); + +typedef transactions_refresh = Void Function(); + +typedef get_tx_key = Pointer? Function(Pointer txId); + +typedef transactions_count = Int64 Function(); + +typedef transactions_get_all = Pointer Function(); + +typedef transaction_create = Int8 Function( + Pointer address, + Pointer paymentId, + Pointer amount, + Int8 priorityRaw, + Int32 subaddrAccount, + Pointer error, + Pointer pendingTransaction); + +typedef transaction_create_mult_dest = Int8 Function( + Pointer> addresses, + Pointer paymentId, + Pointer> amounts, + Int32 size, + Int8 priorityRaw, + Int32 subaddrAccount, + Pointer error, + Pointer pendingTransaction); + +typedef transaction_commit = Int8 Function(Pointer, Pointer); + +typedef secret_view_key = Pointer Function(); + +typedef public_view_key = Pointer Function(); + +typedef secret_spend_key = Pointer Function(); + +typedef public_spend_key = Pointer Function(); + +typedef close_current_wallet = Void Function(); + +typedef on_startup = Void Function(); + +typedef rescan_blockchain = Void Function(); + +typedef get_subaddress_label = Pointer Function( + Int32 accountIndex, + Int32 addressIndex); + +typedef set_trusted_daemon = Void Function(Int8 trusted); + +typedef trusted_daemon = Int8 Function(); \ No newline at end of file diff --git a/cw_nano/lib/api/structs/account_row.dart b/cw_nano/lib/api/structs/account_row.dart new file mode 100644 index 000000000..aa492ee0f --- /dev/null +++ b/cw_nano/lib/api/structs/account_row.dart @@ -0,0 +1,12 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class AccountRow extends Struct { + @Int64() + external int id; + + external Pointer label; + + String getLabel() => label.toDartString(); + int getId() => id; +} diff --git a/cw_nano/lib/api/structs/pending_transaction.dart b/cw_nano/lib/api/structs/pending_transaction.dart new file mode 100644 index 000000000..656ed333f --- /dev/null +++ b/cw_nano/lib/api/structs/pending_transaction.dart @@ -0,0 +1,39 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class PendingTransactionRaw extends Struct { + @Int64() + external int amount; + + @Int64() + external int fee; + + external Pointer hash; + + external Pointer hex; + + external Pointer txKey; + + String getHash() => hash.toDartString(); + + String getHex() => hex.toDartString(); + + String getKey() => txKey.toDartString(); +} + +class PendingTransactionDescription { + PendingTransactionDescription({ + required this.amount, + required this.fee, + required this.hash, + required this.hex, + required this.txKey, + required this.pointerAddress}); + + final int amount; + final int fee; + final String hash; + final String hex; + final String txKey; + final int pointerAddress; +} \ No newline at end of file diff --git a/cw_nano/lib/api/structs/subaddress_row.dart b/cw_nano/lib/api/structs/subaddress_row.dart new file mode 100644 index 000000000..d593a793d --- /dev/null +++ b/cw_nano/lib/api/structs/subaddress_row.dart @@ -0,0 +1,15 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class SubaddressRow extends Struct { + @Int64() + external int id; + + external Pointer address; + + external Pointer label; + + String getLabel() => label.toDartString(); + String getAddress() => address.toDartString(); + int getId() => id; +} \ No newline at end of file diff --git a/cw_nano/lib/api/structs/transaction_info_row.dart b/cw_nano/lib/api/structs/transaction_info_row.dart new file mode 100644 index 000000000..bdcc64d3f --- /dev/null +++ b/cw_nano/lib/api/structs/transaction_info_row.dart @@ -0,0 +1,41 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class TransactionInfoRow extends Struct { + @Uint64() + external int amount; + + @Uint64() + external int fee; + + @Uint64() + external int blockHeight; + + @Uint64() + external int confirmations; + + @Uint32() + external int subaddrAccount; + + @Int8() + external int direction; + + @Int8() + external int isPending; + + @Uint32() + external int subaddrIndex; + + external Pointer hash; + + external Pointer paymentId; + + @Int64() + external int datetime; + + int getDatetime() => datetime; + int getAmount() => amount >= 0 ? amount : amount * -1; + bool getIsPending() => isPending != 0; + String getHash() => hash.toDartString(); + String getPaymentId() => paymentId.toDartString(); +} diff --git a/cw_nano/lib/api/structs/ut8_box.dart b/cw_nano/lib/api/structs/ut8_box.dart new file mode 100644 index 000000000..53e678c88 --- /dev/null +++ b/cw_nano/lib/api/structs/ut8_box.dart @@ -0,0 +1,8 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +class Utf8Box extends Struct { + external Pointer value; + + String getValue() => value.toDartString(); +} diff --git a/cw_nano/lib/api/subaddress_list.dart b/cw_nano/lib/api/subaddress_list.dart new file mode 100644 index 000000000..1c1f1253f --- /dev/null +++ b/cw_nano/lib/api/subaddress_list.dart @@ -0,0 +1,97 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:flutter/foundation.dart'; +import 'package:cw_monero/api/signatures.dart'; +import 'package:cw_monero/api/types.dart'; +import 'package:cw_monero/api/monero_api.dart'; +import 'package:cw_monero/api/structs/subaddress_row.dart'; +import 'package:cw_monero/api/wallet.dart'; + +final subaddressSizeNative = moneroApi + .lookup>('subaddrress_size') + .asFunction(); + +final subaddressRefreshNative = moneroApi + .lookup>('subaddress_refresh') + .asFunction(); + +final subaddrressGetAllNative = moneroApi + .lookup>('subaddrress_get_all') + .asFunction(); + +final subaddrressAddNewNative = moneroApi + .lookup>('subaddress_add_row') + .asFunction(); + +final subaddrressSetLabelNative = moneroApi + .lookup>('subaddress_set_label') + .asFunction(); + +bool isUpdating = false; + +void refreshSubaddresses({required int accountIndex}) { + try { + isUpdating = true; + subaddressRefreshNative(accountIndex); + isUpdating = false; + } catch (e) { + isUpdating = false; + rethrow; + } +} + +List getAllSubaddresses() { + final size = subaddressSizeNative(); + final subaddressAddressesPointer = subaddrressGetAllNative(); + final subaddressAddresses = subaddressAddressesPointer.asTypedList(size); + + return subaddressAddresses + .map((addr) => Pointer.fromAddress(addr).ref) + .toList(); +} + +void addSubaddressSync({required int accountIndex, required String label}) { + final labelPointer = label.toNativeUtf8(); + subaddrressAddNewNative(accountIndex, labelPointer); + calloc.free(labelPointer); +} + +void setLabelForSubaddressSync( + {required int accountIndex, required int addressIndex, required String label}) { + final labelPointer = label.toNativeUtf8(); + + subaddrressSetLabelNative(accountIndex, addressIndex, labelPointer); + calloc.free(labelPointer); +} + +void _addSubaddress(Map args) { + final label = args['label'] as String; + final accountIndex = args['accountIndex'] as int; + + addSubaddressSync(accountIndex: accountIndex, label: label); +} + +void _setLabelForSubaddress(Map args) { + final label = args['label'] as String; + final accountIndex = args['accountIndex'] as int; + final addressIndex = args['addressIndex'] as int; + + setLabelForSubaddressSync( + accountIndex: accountIndex, addressIndex: addressIndex, label: label); +} + +Future addSubaddress({required int accountIndex, required String label}) async { + await compute, void>( + _addSubaddress, {'accountIndex': accountIndex, 'label': label}); + await store(); +} + +Future setLabelForSubaddress( + {required int accountIndex, required int addressIndex, required String label}) async { + await compute, void>(_setLabelForSubaddress, { + 'accountIndex': accountIndex, + 'addressIndex': addressIndex, + 'label': label + }); + await store(); +} diff --git a/cw_nano/lib/api/transaction_history.dart b/cw_nano/lib/api/transaction_history.dart new file mode 100644 index 000000000..0fc507500 --- /dev/null +++ b/cw_nano/lib/api/transaction_history.dart @@ -0,0 +1,234 @@ +import 'dart:ffi'; +import 'package:cw_monero/api/convert_utf8_to_string.dart'; +import 'package:cw_monero/api/monero_output.dart'; +import 'package:cw_monero/api/structs/ut8_box.dart'; +import 'package:ffi/ffi.dart'; +import 'package:flutter/foundation.dart'; +import 'package:cw_monero/api/signatures.dart'; +import 'package:cw_monero/api/types.dart'; +import 'package:cw_monero/api/monero_api.dart'; +import 'package:cw_monero/api/structs/transaction_info_row.dart'; +import 'package:cw_monero/api/structs/pending_transaction.dart'; +import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart'; + +final transactionsRefreshNative = moneroApi + .lookup>('transactions_refresh') + .asFunction(); + +final transactionsCountNative = moneroApi + .lookup>('transactions_count') + .asFunction(); + +final transactionsGetAllNative = moneroApi + .lookup>('transactions_get_all') + .asFunction(); + +final transactionCreateNative = moneroApi + .lookup>('transaction_create') + .asFunction(); + +final transactionCreateMultDestNative = moneroApi + .lookup>('transaction_create_mult_dest') + .asFunction(); + +final transactionCommitNative = moneroApi + .lookup>('transaction_commit') + .asFunction(); + +final getTxKeyNative = moneroApi + .lookup>('get_tx_key') + .asFunction(); + +String getTxKey(String txId) { + final txIdPointer = txId.toNativeUtf8(); + final keyPointer = getTxKeyNative(txIdPointer); + + calloc.free(txIdPointer); + + if (keyPointer != null) { + return convertUTF8ToString(pointer: keyPointer); + } + + return ''; +} + +void refreshTransactions() => transactionsRefreshNative(); + +int countOfTransactions() => transactionsCountNative(); + +List getAllTransations() { + final size = transactionsCountNative(); + final transactionsPointer = transactionsGetAllNative(); + final transactionsAddresses = transactionsPointer.asTypedList(size); + + return transactionsAddresses + .map((addr) => Pointer.fromAddress(addr).ref) + .toList(); +} + +PendingTransactionDescription createTransactionSync( + {required String address, + required String paymentId, + required int priorityRaw, + String? amount, + int accountIndex = 0}) { + final addressPointer = address.toNativeUtf8(); + final paymentIdPointer = paymentId.toNativeUtf8(); + final amountPointer = amount != null ? amount.toNativeUtf8() : nullptr; + final errorMessagePointer = calloc(); + final pendingTransactionRawPointer = calloc(); + final created = transactionCreateNative( + addressPointer, + paymentIdPointer, + amountPointer, + priorityRaw, + accountIndex, + errorMessagePointer, + pendingTransactionRawPointer) != + 0; + + calloc.free(addressPointer); + calloc.free(paymentIdPointer); + + if (amountPointer != nullptr) { + calloc.free(amountPointer); + } + + if (!created) { + final message = errorMessagePointer.ref.getValue(); + calloc.free(errorMessagePointer); + throw CreationTransactionException(message: message); + } + + return PendingTransactionDescription( + amount: pendingTransactionRawPointer.ref.amount, + fee: pendingTransactionRawPointer.ref.fee, + hash: pendingTransactionRawPointer.ref.getHash(), + hex: pendingTransactionRawPointer.ref.getHex(), + txKey: pendingTransactionRawPointer.ref.getKey(), + pointerAddress: pendingTransactionRawPointer.address); +} + +PendingTransactionDescription createTransactionMultDestSync( + {required List outputs, + required String paymentId, + required int priorityRaw, + int accountIndex = 0}) { + final int size = outputs.length; + final List> addressesPointers = outputs.map((output) => + output.address.toNativeUtf8()).toList(); + final Pointer> addressesPointerPointer = calloc(size); + final List> amountsPointers = outputs.map((output) => + output.amount.toNativeUtf8()).toList(); + final Pointer> amountsPointerPointer = calloc(size); + + for (int i = 0; i < size; i++) { + addressesPointerPointer[i] = addressesPointers[i]; + amountsPointerPointer[i] = amountsPointers[i]; + } + + final paymentIdPointer = paymentId.toNativeUtf8(); + final errorMessagePointer = calloc(); + final pendingTransactionRawPointer = calloc(); + final created = transactionCreateMultDestNative( + addressesPointerPointer, + paymentIdPointer, + amountsPointerPointer, + size, + priorityRaw, + accountIndex, + errorMessagePointer, + pendingTransactionRawPointer) != + 0; + + calloc.free(addressesPointerPointer); + calloc.free(amountsPointerPointer); + + addressesPointers.forEach((element) => calloc.free(element)); + amountsPointers.forEach((element) => calloc.free(element)); + + calloc.free(paymentIdPointer); + + if (!created) { + final message = errorMessagePointer.ref.getValue(); + calloc.free(errorMessagePointer); + throw CreationTransactionException(message: message); + } + + return PendingTransactionDescription( + amount: pendingTransactionRawPointer.ref.amount, + fee: pendingTransactionRawPointer.ref.fee, + hash: pendingTransactionRawPointer.ref.getHash(), + hex: pendingTransactionRawPointer.ref.getHex(), + txKey: pendingTransactionRawPointer.ref.getKey(), + pointerAddress: pendingTransactionRawPointer.address); +} + +void commitTransactionFromPointerAddress({required int address}) => commitTransaction( + transactionPointer: Pointer.fromAddress(address)); + +void commitTransaction({required Pointer transactionPointer}) { + final errorMessagePointer = calloc(); + final isCommited = + transactionCommitNative(transactionPointer, errorMessagePointer) != 0; + + if (!isCommited) { + final message = errorMessagePointer.ref.getValue(); + calloc.free(errorMessagePointer); + throw CreationTransactionException(message: message); + } +} + +PendingTransactionDescription _createTransactionSync(Map args) { + final address = args['address'] as String; + final paymentId = args['paymentId'] as String; + final amount = args['amount'] as String?; + final priorityRaw = args['priorityRaw'] as int; + final accountIndex = args['accountIndex'] as int; + + return createTransactionSync( + address: address, + paymentId: paymentId, + amount: amount, + priorityRaw: priorityRaw, + accountIndex: accountIndex); +} + +PendingTransactionDescription _createTransactionMultDestSync(Map args) { + final outputs = args['outputs'] as List; + final paymentId = args['paymentId'] as String; + final priorityRaw = args['priorityRaw'] as int; + final accountIndex = args['accountIndex'] as int; + + return createTransactionMultDestSync( + outputs: outputs, + paymentId: paymentId, + priorityRaw: priorityRaw, + accountIndex: accountIndex); +} + +Future createTransaction( + {required String address, + required int priorityRaw, + String? amount, + String paymentId = '', + int accountIndex = 0}) => + compute(_createTransactionSync, { + 'address': address, + 'paymentId': paymentId, + 'amount': amount, + 'priorityRaw': priorityRaw, + 'accountIndex': accountIndex + }); + +Future createTransactionMultDest( + {required List outputs, + required int priorityRaw, + String paymentId = '', + int accountIndex = 0}) => + compute(_createTransactionMultDestSync, { + 'outputs': outputs, + 'paymentId': paymentId, + 'priorityRaw': priorityRaw, + 'accountIndex': accountIndex + }); diff --git a/cw_nano/lib/api/types.dart b/cw_nano/lib/api/types.dart new file mode 100644 index 000000000..c5918c12a --- /dev/null +++ b/cw_nano/lib/api/types.dart @@ -0,0 +1,130 @@ +import 'dart:ffi'; +import 'package:cw_monero/api/structs/pending_transaction.dart'; +import 'package:cw_monero/api/structs/ut8_box.dart'; +import 'package:ffi/ffi.dart'; + +typedef CreateWallet = int Function( + Pointer, Pointer, Pointer, int, Pointer); + +typedef RestoreWalletFromSeed = int Function( + Pointer, Pointer, Pointer, int, int, Pointer); + +typedef RestoreWalletFromKeys = int Function(Pointer, Pointer, + Pointer, Pointer, Pointer, Pointer, int, int, Pointer); + +typedef IsWalletExist = int Function(Pointer); + +typedef LoadWallet = int Function(Pointer, Pointer, int); + +typedef ErrorString = Pointer Function(); + +typedef GetFilename = Pointer Function(); + +typedef GetSeed = Pointer Function(); + +typedef GetAddress = Pointer Function(int, int); + +typedef GetFullBalance = int Function(int); + +typedef GetUnlockedBalance = int Function(int); + +typedef GetCurrentHeight = int Function(); + +typedef GetNodeHeight = int Function(); + +typedef IsConnected = int Function(); + +typedef SetupNode = int Function( + Pointer, Pointer?, Pointer?, int, int, Pointer); + +typedef StartRefresh = void Function(); + +typedef ConnectToNode = int Function(); + +typedef SetRefreshFromBlockHeight = void Function(int); + +typedef SetRecoveringFromSeed = void Function(int); + +typedef Store = void Function(Pointer); + +typedef SetPassword = int Function(Pointer password, Pointer error); + +typedef SetListener = void Function(); + +typedef GetSyncingHeight = int Function(); + +typedef IsNeededToRefresh = int Function(); + +typedef IsNewTransactionExist = int Function(); + +typedef SubaddressSize = int Function(); + +typedef SubaddressRefresh = void Function(int); + +typedef SubaddressGetAll = Pointer Function(); + +typedef SubaddressAddNew = void Function(int accountIndex, Pointer label); + +typedef SubaddressSetLabel = void Function( + int accountIndex, int addressIndex, Pointer label); + +typedef AccountSize = int Function(); + +typedef AccountRefresh = void Function(); + +typedef AccountGetAll = Pointer Function(); + +typedef AccountAddNew = void Function(Pointer label); + +typedef AccountSetLabel = void Function(int accountIndex, Pointer label); + +typedef TransactionsRefresh = void Function(); + +typedef GetTxKey = Pointer? Function(Pointer txId); + +typedef TransactionsCount = int Function(); + +typedef TransactionsGetAll = Pointer Function(); + +typedef TransactionCreate = int Function( + Pointer address, + Pointer paymentId, + Pointer amount, + int priorityRaw, + int subaddrAccount, + Pointer error, + Pointer pendingTransaction); + +typedef TransactionCreateMultDest = int Function( + Pointer> addresses, + Pointer paymentId, + Pointer> amounts, + int size, + int priorityRaw, + int subaddrAccount, + Pointer error, + Pointer pendingTransaction); + +typedef TransactionCommit = int Function(Pointer, Pointer); + +typedef SecretViewKey = Pointer Function(); + +typedef PublicViewKey = Pointer Function(); + +typedef SecretSpendKey = Pointer Function(); + +typedef PublicSpendKey = Pointer Function(); + +typedef CloseCurrentWallet = void Function(); + +typedef OnStartup = void Function(); + +typedef RescanBlockchainAsync = void Function(); + +typedef GetSubaddressLabel = Pointer Function( + int accountIndex, + int addressIndex); + +typedef SetTrustedDaemon = void Function(int); + +typedef TrustedDaemon = int Function(); \ No newline at end of file diff --git a/cw_nano/lib/api/wallet.dart b/cw_nano/lib/api/wallet.dart new file mode 100644 index 000000000..7ddbf29dc --- /dev/null +++ b/cw_nano/lib/api/wallet.dart @@ -0,0 +1,374 @@ +import 'dart:async'; +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:cw_monero/api/structs/ut8_box.dart'; +import 'package:cw_monero/api/convert_utf8_to_string.dart'; +import 'package:cw_monero/api/signatures.dart'; +import 'package:cw_monero/api/types.dart'; +import 'package:cw_monero/api/monero_api.dart'; +import 'package:cw_monero/api/exceptions/setup_wallet_exception.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +int _boolToInt(bool value) => value ? 1 : 0; + +final getFileNameNative = moneroApi + .lookup>('get_filename') + .asFunction(); + +final getSeedNative = + moneroApi.lookup>('seed').asFunction(); + +final getAddressNative = moneroApi + .lookup>('get_address') + .asFunction(); + +final getFullBalanceNative = moneroApi + .lookup>('get_full_balance') + .asFunction(); + +final getUnlockedBalanceNative = moneroApi + .lookup>('get_unlocked_balance') + .asFunction(); + +final getCurrentHeightNative = moneroApi + .lookup>('get_current_height') + .asFunction(); + +final getNodeHeightNative = moneroApi + .lookup>('get_node_height') + .asFunction(); + +final isConnectedNative = moneroApi + .lookup>('is_connected') + .asFunction(); + +final setupNodeNative = moneroApi + .lookup>('setup_node') + .asFunction(); + +final startRefreshNative = moneroApi + .lookup>('start_refresh') + .asFunction(); + +final connecToNodeNative = moneroApi + .lookup>('connect_to_node') + .asFunction(); + +final setRefreshFromBlockHeightNative = moneroApi + .lookup>( + 'set_refresh_from_block_height') + .asFunction(); + +final setRecoveringFromSeedNative = moneroApi + .lookup>( + 'set_recovering_from_seed') + .asFunction(); + +final storeNative = + moneroApi.lookup>('store').asFunction(); + +final setPasswordNative = + moneroApi.lookup>('set_password').asFunction(); + +final setListenerNative = moneroApi + .lookup>('set_listener') + .asFunction(); + +final getSyncingHeightNative = moneroApi + .lookup>('get_syncing_height') + .asFunction(); + +final isNeededToRefreshNative = moneroApi + .lookup>('is_needed_to_refresh') + .asFunction(); + +final isNewTransactionExistNative = moneroApi + .lookup>( + 'is_new_transaction_exist') + .asFunction(); + +final getSecretViewKeyNative = moneroApi + .lookup>('secret_view_key') + .asFunction(); + +final getPublicViewKeyNative = moneroApi + .lookup>('public_view_key') + .asFunction(); + +final getSecretSpendKeyNative = moneroApi + .lookup>('secret_spend_key') + .asFunction(); + +final getPublicSpendKeyNative = moneroApi + .lookup>('public_spend_key') + .asFunction(); + +final closeCurrentWalletNative = moneroApi + .lookup>('close_current_wallet') + .asFunction(); + +final onStartupNative = moneroApi + .lookup>('on_startup') + .asFunction(); + +final rescanBlockchainAsyncNative = moneroApi + .lookup>('rescan_blockchain') + .asFunction(); + +final getSubaddressLabelNative = moneroApi + .lookup>('get_subaddress_label') + .asFunction(); + +final setTrustedDaemonNative = moneroApi + .lookup>('set_trusted_daemon') + .asFunction(); + +final trustedDaemonNative = moneroApi + .lookup>('trusted_daemon') + .asFunction(); + +int getSyncingHeight() => getSyncingHeightNative(); + +bool isNeededToRefresh() => isNeededToRefreshNative() != 0; + +bool isNewTransactionExist() => isNewTransactionExistNative() != 0; + +String getFilename() => convertUTF8ToString(pointer: getFileNameNative()); + +String getSeed() => convertUTF8ToString(pointer: getSeedNative()); + +String getAddress({int accountIndex = 0, int addressIndex = 0}) => + convertUTF8ToString(pointer: getAddressNative(accountIndex, addressIndex)); + +int getFullBalance({int accountIndex = 0}) => + getFullBalanceNative(accountIndex); + +int getUnlockedBalance({int accountIndex = 0}) => + getUnlockedBalanceNative(accountIndex); + +int getCurrentHeight() => getCurrentHeightNative(); + +int getNodeHeightSync() => getNodeHeightNative(); + +bool isConnectedSync() => isConnectedNative() != 0; + +bool setupNodeSync( + {required String address, + String? login, + String? password, + bool useSSL = false, + bool isLightWallet = false}) { + final addressPointer = address.toNativeUtf8(); + Pointer? loginPointer; + Pointer? passwordPointer; + + if (login != null) { + loginPointer = login.toNativeUtf8(); + } + + if (password != null) { + passwordPointer = password.toNativeUtf8(); + } + + final errorMessagePointer = ''.toNativeUtf8(); + final isSetupNode = setupNodeNative( + addressPointer, + loginPointer, + passwordPointer, + _boolToInt(useSSL), + _boolToInt(isLightWallet), + errorMessagePointer) != + 0; + + calloc.free(addressPointer); + + if (loginPointer != null) { + calloc.free(loginPointer); + } + + if (passwordPointer != null) { + calloc.free(passwordPointer); + } + + if (!isSetupNode) { + throw SetupWalletException( + message: convertUTF8ToString(pointer: errorMessagePointer)); + } + + return isSetupNode; +} + +void startRefreshSync() => startRefreshNative(); + +Future connectToNode() async => connecToNodeNative() != 0; + +void setRefreshFromBlockHeight({required int height}) => + setRefreshFromBlockHeightNative(height); + +void setRecoveringFromSeed({required bool isRecovery}) => + setRecoveringFromSeedNative(_boolToInt(isRecovery)); + +void storeSync() { + final pathPointer = ''.toNativeUtf8(); + storeNative(pathPointer); + calloc.free(pathPointer); +} + +void setPasswordSync(String password) { + final passwordPointer = password.toNativeUtf8(); + final errorMessagePointer = calloc(); + final changed = setPasswordNative(passwordPointer, errorMessagePointer) != 0; + calloc.free(passwordPointer); + + if (!changed) { + final message = errorMessagePointer.ref.getValue(); + calloc.free(errorMessagePointer); + throw Exception(message); + } + + calloc.free(errorMessagePointer); +} + +void closeCurrentWallet() => closeCurrentWalletNative(); + +String getSecretViewKey() => + convertUTF8ToString(pointer: getSecretViewKeyNative()); + +String getPublicViewKey() => + convertUTF8ToString(pointer: getPublicViewKeyNative()); + +String getSecretSpendKey() => + convertUTF8ToString(pointer: getSecretSpendKeyNative()); + +String getPublicSpendKey() => + convertUTF8ToString(pointer: getPublicSpendKeyNative()); + +class SyncListener { + SyncListener(this.onNewBlock, this.onNewTransaction) + : _cachedBlockchainHeight = 0, + _lastKnownBlockHeight = 0, + _initialSyncHeight = 0; + + + void Function(int, int, double) onNewBlock; + void Function() onNewTransaction; + + Timer? _updateSyncInfoTimer; + int _cachedBlockchainHeight; + int _lastKnownBlockHeight; + int _initialSyncHeight; + + Future getNodeHeightOrUpdate(int baseHeight) async { + if (_cachedBlockchainHeight < baseHeight || _cachedBlockchainHeight == 0) { + _cachedBlockchainHeight = await getNodeHeight(); + } + + return _cachedBlockchainHeight; + } + + void start() { + _cachedBlockchainHeight = 0; + _lastKnownBlockHeight = 0; + _initialSyncHeight = 0; + _updateSyncInfoTimer ??= + Timer.periodic(Duration(milliseconds: 1200), (_) async { + if (isNewTransactionExist()) { + onNewTransaction(); + } + + var syncHeight = getSyncingHeight(); + + if (syncHeight <= 0) { + syncHeight = getCurrentHeight(); + } + + if (_initialSyncHeight <= 0) { + _initialSyncHeight = syncHeight; + } + + final bchHeight = await getNodeHeightOrUpdate(syncHeight); + + if (_lastKnownBlockHeight == syncHeight || syncHeight == null) { + return; + } + + _lastKnownBlockHeight = syncHeight; + final track = bchHeight - _initialSyncHeight; + final diff = track - (bchHeight - syncHeight); + final ptc = diff <= 0 ? 0.0 : diff / track; + final left = bchHeight - syncHeight; + + if (syncHeight < 0 || left < 0) { + return; + } + + // 1. Actual new height; 2. Blocks left to finish; 3. Progress in percents; + onNewBlock?.call(syncHeight, left, ptc); + }); + } + + void stop() => _updateSyncInfoTimer?.cancel(); +} + +SyncListener setListeners(void Function(int, int, double) onNewBlock, + void Function() onNewTransaction) { + final listener = SyncListener(onNewBlock, onNewTransaction); + setListenerNative(); + return listener; +} + +void onStartup() => onStartupNative(); + +void _storeSync(Object _) => storeSync(); + +bool _setupNodeSync(Map args) { + final address = args['address'] as String; + final login = (args['login'] ?? '') as String; + final password = (args['password'] ?? '') as String; + final useSSL = args['useSSL'] as bool; + final isLightWallet = args['isLightWallet'] as bool; + + return setupNodeSync( + address: address, + login: login, + password: password, + useSSL: useSSL, + isLightWallet: isLightWallet); +} + +bool _isConnected(Object _) => isConnectedSync(); + +int _getNodeHeight(Object _) => getNodeHeightSync(); + +void startRefresh() => startRefreshSync(); + +Future setupNode( + {required String address, + String? login, + String? password, + bool useSSL = false, + bool isLightWallet = false}) => + compute, void>(_setupNodeSync, { + 'address': address, + 'login': login , + 'password': password, + 'useSSL': useSSL, + 'isLightWallet': isLightWallet + }); + +Future store() => compute(_storeSync, 0); + +Future isConnected() => compute(_isConnected, 0); + +Future getNodeHeight() => compute(_getNodeHeight, 0); + +void rescanBlockchainAsync() => rescanBlockchainAsyncNative(); + +String getSubaddressLabel(int accountIndex, int addressIndex) { + return convertUTF8ToString(pointer: getSubaddressLabelNative(accountIndex, addressIndex)); +} + +Future setTrustedDaemon(bool trusted) async => setTrustedDaemonNative(_boolToInt(trusted)); + +Future trustedDaemon() async => trustedDaemonNative() != 0; \ No newline at end of file diff --git a/cw_nano/lib/api/wallet_manager.dart b/cw_nano/lib/api/wallet_manager.dart new file mode 100644 index 000000000..093d7e63b --- /dev/null +++ b/cw_nano/lib/api/wallet_manager.dart @@ -0,0 +1,254 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:flutter/foundation.dart'; +import 'package:cw_monero/api/convert_utf8_to_string.dart'; +import 'package:cw_monero/api/signatures.dart'; +import 'package:cw_monero/api/types.dart'; +import 'package:cw_monero/api/monero_api.dart'; +import 'package:cw_monero/api/wallet.dart'; +import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart'; +import 'package:cw_monero/api/exceptions/wallet_creation_exception.dart'; +import 'package:cw_monero/api/exceptions/wallet_restore_from_keys_exception.dart'; +import 'package:cw_monero/api/exceptions/wallet_restore_from_seed_exception.dart'; + +final createWalletNative = moneroApi + .lookup>('create_wallet') + .asFunction(); + +final restoreWalletFromSeedNative = moneroApi + .lookup>( + 'restore_wallet_from_seed') + .asFunction(); + +final restoreWalletFromKeysNative = moneroApi + .lookup>( + 'restore_wallet_from_keys') + .asFunction(); + +final isWalletExistNative = moneroApi + .lookup>('is_wallet_exist') + .asFunction(); + +final loadWalletNative = moneroApi + .lookup>('load_wallet') + .asFunction(); + +final errorStringNative = moneroApi + .lookup>('error_string') + .asFunction(); + +void createWalletSync( + {required String path, + required String password, + required String language, + int nettype = 0}) { + final pathPointer = path.toNativeUtf8(); + final passwordPointer = password.toNativeUtf8(); + final languagePointer = language.toNativeUtf8(); + final errorMessagePointer = ''.toNativeUtf8(); + final isWalletCreated = createWalletNative(pathPointer, passwordPointer, + languagePointer, nettype, errorMessagePointer) != + 0; + + calloc.free(pathPointer); + calloc.free(passwordPointer); + calloc.free(languagePointer); + + if (!isWalletCreated) { + throw WalletCreationException( + message: convertUTF8ToString(pointer: errorMessagePointer)); + } + + // setupNodeSync(address: "node.moneroworld.com:18089"); +} + +bool isWalletExistSync({required String path}) { + final pathPointer = path.toNativeUtf8(); + final isExist = isWalletExistNative(pathPointer) != 0; + + calloc.free(pathPointer); + + return isExist; +} + +void restoreWalletFromSeedSync( + {required String path, + required String password, + required String seed, + int nettype = 0, + int restoreHeight = 0}) { + final pathPointer = path.toNativeUtf8(); + final passwordPointer = password.toNativeUtf8(); + final seedPointer = seed.toNativeUtf8(); + final errorMessagePointer = ''.toNativeUtf8(); + final isWalletRestored = restoreWalletFromSeedNative( + pathPointer, + passwordPointer, + seedPointer, + nettype, + restoreHeight, + errorMessagePointer) != + 0; + + calloc.free(pathPointer); + calloc.free(passwordPointer); + calloc.free(seedPointer); + + if (!isWalletRestored) { + throw WalletRestoreFromSeedException( + message: convertUTF8ToString(pointer: errorMessagePointer)); + } +} + +void restoreWalletFromKeysSync( + {required String path, + required String password, + required String language, + required String address, + required String viewKey, + required String spendKey, + int nettype = 0, + int restoreHeight = 0}) { + final pathPointer = path.toNativeUtf8(); + final passwordPointer = password.toNativeUtf8(); + final languagePointer = language.toNativeUtf8(); + final addressPointer = address.toNativeUtf8(); + final viewKeyPointer = viewKey.toNativeUtf8(); + final spendKeyPointer = spendKey.toNativeUtf8(); + final errorMessagePointer = ''.toNativeUtf8(); + final isWalletRestored = restoreWalletFromKeysNative( + pathPointer, + passwordPointer, + languagePointer, + addressPointer, + viewKeyPointer, + spendKeyPointer, + nettype, + restoreHeight, + errorMessagePointer) != + 0; + + calloc.free(pathPointer); + calloc.free(passwordPointer); + calloc.free(languagePointer); + calloc.free(addressPointer); + calloc.free(viewKeyPointer); + calloc.free(spendKeyPointer); + + if (!isWalletRestored) { + throw WalletRestoreFromKeysException( + message: convertUTF8ToString(pointer: errorMessagePointer)); + } +} + +void loadWallet({ + required String path, + required String password, + int nettype = 0}) { + final pathPointer = path.toNativeUtf8(); + final passwordPointer = password.toNativeUtf8(); + final loaded = loadWalletNative(pathPointer, passwordPointer, nettype) != 0; + calloc.free(pathPointer); + calloc.free(passwordPointer); + + if (!loaded) { + throw WalletOpeningException( + message: convertUTF8ToString(pointer: errorStringNative())); + } +} + +void _createWallet(Map args) { + final path = args['path'] as String; + final password = args['password'] as String; + final language = args['language'] as String; + + createWalletSync(path: path, password: password, language: language); +} + +void _restoreFromSeed(Map args) { + final path = args['path'] as String; + final password = args['password'] as String; + final seed = args['seed'] as String; + final restoreHeight = args['restoreHeight'] as int; + + restoreWalletFromSeedSync( + path: path, password: password, seed: seed, restoreHeight: restoreHeight); +} + +void _restoreFromKeys(Map args) { + final path = args['path'] as String; + final password = args['password'] as String; + final language = args['language'] as String; + final restoreHeight = args['restoreHeight'] as int; + final address = args['address'] as String; + final viewKey = args['viewKey'] as String; + final spendKey = args['spendKey'] as String; + + restoreWalletFromKeysSync( + path: path, + password: password, + language: language, + restoreHeight: restoreHeight, + address: address, + viewKey: viewKey, + spendKey: spendKey); +} + +Future _openWallet(Map args) async => + loadWallet(path: args['path'] as String, password: args['password'] as String); + +bool _isWalletExist(String path) => isWalletExistSync(path: path); + +void openWallet({required String path, required String password, int nettype = 0}) async => + loadWallet(path: path, password: password, nettype: nettype); + +Future openWalletAsync(Map args) async => + compute(_openWallet, args); + +Future createWallet( + {required String path, + required String password, + required String language, + int nettype = 0}) async => + compute(_createWallet, { + 'path': path, + 'password': password, + 'language': language, + 'nettype': nettype + }); + +Future restoreFromSeed( + {required String path, + required String password, + required String seed, + int nettype = 0, + int restoreHeight = 0}) async => + compute, void>(_restoreFromSeed, { + 'path': path, + 'password': password, + 'seed': seed, + 'nettype': nettype, + 'restoreHeight': restoreHeight + }); + +Future restoreFromKeys( + {required String path, + required String password, + required String language, + required String address, + required String viewKey, + required String spendKey, + int nettype = 0, + int restoreHeight = 0}) async => + compute, void>(_restoreFromKeys, { + 'path': path, + 'password': password, + 'language': language, + 'address': address, + 'viewKey': viewKey, + 'spendKey': spendKey, + 'nettype': nettype, + 'restoreHeight': restoreHeight + }); + +Future isWalletExist({required String path}) => compute(_isWalletExist, path); diff --git a/cw_nano/lib/nano_account_list.dart b/cw_nano/lib/nano_account_list.dart new file mode 100644 index 000000000..6b0367aac --- /dev/null +++ b/cw_nano/lib/nano_account_list.dart @@ -0,0 +1,82 @@ +import 'package:cw_core/monero_amount_format.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cw_core/account.dart'; +import 'package:cw_nano/api/account_list.dart' as account_list; +import 'package:cw_nano/api/wallet.dart' as monero_wallet; + +part 'nano_account_list.g.dart'; + +class NanoAccountList = NanoAccountListBase with _$NanoAccountList; + +abstract class NanoAccountListBase with Store { + NanoAccountListBase() + : accounts = ObservableList(), + _isRefreshing = false, + _isUpdating = false { + refresh(); + } + + @observable + ObservableList accounts; + bool _isRefreshing; + bool _isUpdating; + + void update() async { + if (_isUpdating) { + return; + } + + try { + _isUpdating = true; + refresh(); + final accounts = getAll(); + + if (accounts.isNotEmpty) { + this.accounts.clear(); + this.accounts.addAll(accounts); + } + + _isUpdating = false; + } catch (e) { + _isUpdating = false; + rethrow; + } + } + + List getAll() => account_list.getAllAccount().map((accountRow) { + final accountIndex = accountRow.getId(); + final balance = monero_wallet.getFullBalance(accountIndex: accountIndex); + + return Account( + id: accountRow.getId(), + label: accountRow.getLabel(), + balance: moneroAmountToString(amount: balance), + ); + }).toList(); + + Future addAccount({required String label}) async { + await account_list.addAccount(label: label); + update(); + } + + Future setLabelAccount({required int accountIndex, required String label}) async { + await account_list.setLabelForAccount(accountIndex: accountIndex, label: label); + update(); + } + + void refresh() { + if (_isRefreshing) { + return; + } + + try { + _isRefreshing = true; + account_list.refreshAccounts(); + _isRefreshing = false; + } catch (e) { + _isRefreshing = false; + print(e); + rethrow; + } + } +} diff --git a/cw_nano/lib/nano_balance.dart b/cw_nano/lib/nano_balance.dart index d83bb4eaf..3a1582d47 100644 --- a/cw_nano/lib/nano_balance.dart +++ b/cw_nano/lib/nano_balance.dart @@ -24,7 +24,6 @@ class NanoBalance extends Balance { // super(moneroParseAmount(amount: formattedReceivableBalance), // moneroParseAmount(amount: formattedCurrentBalance)); - @override String get formattedAvailableBalance => "error"; diff --git a/cw_nano/lib/nano_transaction_history.dart b/cw_nano/lib/nano_transaction_history.dart new file mode 100644 index 000000000..55aeae59f --- /dev/null +++ b/cw_nano/lib/nano_transaction_history.dart @@ -0,0 +1,28 @@ +import 'dart:core'; +import 'package:mobx/mobx.dart'; +import 'package:cw_core/transaction_history.dart'; +import 'package:cw_nano/nano_transaction_info.dart'; + +part 'nano_transaction_history.g.dart'; + +class NanoTransactionHistory = NanoTransactionHistoryBase + with _$NanoTransactionHistory; + +abstract class NanoTransactionHistoryBase + extends TransactionHistoryBase with Store { + NanoTransactionHistoryBase() { + transactions = ObservableMap(); + } + + @override + Future save() async {} + + @override + void addOne(NanoTransactionInfo transaction) => + transactions[transaction.id] = transaction; + + @override + void addMany(Map transactions) => + this.transactions.addAll(transactions); + +} diff --git a/cw_nano/lib/nano_wallet.dart b/cw_nano/lib/nano_wallet.dart index a6862cbc9..a1c45ebc9 100644 --- a/cw_nano/lib/nano_wallet.dart +++ b/cw_nano/lib/nano_wallet.dart @@ -3,23 +3,11 @@ import 'dart:io'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/monero_amount_format.dart'; -import 'package:cw_monero/monero_transaction_creation_exception.dart'; -import 'package:cw_monero/monero_transaction_info.dart'; -import 'package:cw_monero/monero_wallet_addresses.dart'; import 'package:cw_core/monero_wallet_utils.dart'; -import 'package:cw_monero/api/structs/pending_transaction.dart'; +import 'package:cw_nano/nano_balance.dart'; import 'package:mobx/mobx.dart'; -import 'package:cw_monero/api/transaction_history.dart' - as monero_transaction_history; -import 'package:cw_monero/api/wallet.dart'; -import 'package:cw_monero/api/wallet.dart' as monero_wallet; -import 'package:cw_monero/api/transaction_history.dart' as transaction_history; -import 'package:cw_monero/api/monero_output.dart'; -import 'package:cw_monero/monero_transaction_creation_credentials.dart'; -import 'package:cw_monero/pending_monero_transaction.dart'; import 'package:cw_core/monero_wallet_keys.dart'; import 'package:cw_core/monero_balance.dart'; -import 'package:cw_monero/monero_transaction_history.dart'; import 'package:cw_core/account.dart'; import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/wallet_base.dart'; @@ -35,33 +23,30 @@ const moneroBlockSize = 1000; class NanoWallet = NanoWalletBase with _$NanoWallet; -abstract class NanoWalletBase extends WalletBase with Store { +abstract class NanoWalletBase + extends WalletBase with Store { NanoWalletBase({required WalletInfo walletInfo}) - : balance = ObservableMap.of({ - CryptoCurrency.xmr: MoneroBalance( - fullBalance: monero_wallet.getFullBalance(accountIndex: 0), - unlockedBalance: monero_wallet.getFullBalance(accountIndex: 0)) - }), + : balance = ObservableMap.of({ + CryptoCurrency.nano: NanoBalance( + currentBalance: nano_wallet.getFullBalance(accountIndex: 0), + receivableBalance: nano_wallet.getFullBalance(accountIndex: 0)) + }), _isTransactionUpdating = false, _hasSyncAfterStartup = false, walletAddresses = NanoWalletAddresses(walletInfo), syncStatus = NotConnectedSyncStatus(), super(walletInfo) { transactionHistory = MoneroTransactionHistory(); - _onAccountChangeReaction = reaction((_) => walletAddresses.account, - (Account? account) { + _onAccountChangeReaction = reaction((_) => walletAddresses.account, (Account? account) { if (account == null) { return; } - balance = ObservableMap.of( - { - currency: MoneroBalance( + balance = ObservableMap.of({ + currency: MoneroBalance( fullBalance: monero_wallet.getFullBalance(accountIndex: account.id), - unlockedBalance: - monero_wallet.getUnlockedBalance(accountIndex: account.id)) - }); + unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: account.id)) + }); walletAddresses.updateSubaddressList(accountIndex: account.id); }); } @@ -97,12 +82,12 @@ abstract class NanoWalletBase extends WalletBase init() async { await walletAddresses.init(); - balance = ObservableMap.of( - { - currency: MoneroBalance( - fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id), - unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id)) - }); + balance = ObservableMap.of({ + currency: MoneroBalance( + fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id), + unlockedBalance: + monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id)) + }); _setListeners(); await updateTransactions(); @@ -110,8 +95,7 @@ abstract class NanoWalletBase extends WalletBase await save()); } + @override Future? updateBalance() => null; @@ -134,9 +119,7 @@ abstract class NanoWalletBase extends WalletBase createTransaction(Object credentials) async { - final _credentials = credentials as MoneroTransactionCreationCredentials; - final outputs = _credentials.outputs; - final hasMultiDestination = outputs.length > 1; - final unlockedBalance = - monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id); - - PendingTransactionDescription pendingTransactionDescription; - - if (!(syncStatus is SyncedSyncStatus)) { - throw MoneroTransactionCreationException('The wallet is not synced.'); - } - - if (hasMultiDestination) { - if (outputs.any((item) => item.sendAll - || (item.formattedCryptoAmount ?? 0) <= 0)) { - throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.'); - } - - final int totalAmount = outputs.fold(0, (acc, value) => - acc + (value.formattedCryptoAmount ?? 0)); - - if (unlockedBalance < totalAmount) { - throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.'); - } - - final moneroOutputs = outputs.map((output) { - final outputAddress = output.isParsedAddress - ? output.extractedAddress - : output.address; - - return MoneroOutput( - address: outputAddress!, - amount: output.cryptoAmount!.replaceAll(',', '.')); - }).toList(); - - pendingTransactionDescription = - await transaction_history.createTransactionMultDest( - outputs: moneroOutputs, - priorityRaw: _credentials.priority.serialize(), - accountIndex: walletAddresses.account!.id); - } else { - final output = outputs.first; - final address = output.isParsedAddress - ? output.extractedAddress - : output.address; - final amount = output.sendAll - ? null - : output.cryptoAmount!.replaceAll(',', '.'); - final formattedAmount = output.sendAll - ? null - : output.formattedCryptoAmount; - - if ((formattedAmount != null && unlockedBalance < formattedAmount) || - (formattedAmount == null && unlockedBalance <= 0)) { - final formattedBalance = moneroAmountToString(amount: unlockedBalance); - - throw MoneroTransactionCreationException( - 'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.'); - } - - pendingTransactionDescription = - await transaction_history.createTransaction( - address: address!, - amount: amount, - priorityRaw: _credentials.priority.serialize(), - accountIndex: walletAddresses.account!.id); - } - - return PendingMoneroTransaction(pendingTransactionDescription); + // final _credentials = credentials as MoneroTransactionCreationCredentials; + // return null; + throw UnimplementedError(); } @override int calculateEstimatedFee(TransactionPriority priority, int? amount) { // FIXME: hardcoded value; - - if (priority is MoneroTransactionPriority) { - switch (priority) { - case MoneroTransactionPriority.slow: - return 24590000; - case MoneroTransactionPriority.automatic: - return 123050000; - case MoneroTransactionPriority.medium: - return 245029999; - case MoneroTransactionPriority.fast: - return 614530000; - case MoneroTransactionPriority.fastest: - return 26021600000; - } - } - return 0; } @@ -272,10 +173,8 @@ abstract class NanoWalletBase extends WalletBase - monero_wallet.getAddress( - accountIndex: accountIndex, - addressIndex: addressIndex); + monero_wallet.getAddress(accountIndex: accountIndex, addressIndex: addressIndex); @override Future> fetchTransactions() async { monero_transaction_history.refreshTransactions(); - return _getAllTransactions(null).fold>( - {}, - (Map acc, MoneroTransactionInfo tx) { + return _getAllTransactions(null) + .fold>({}, + (Map acc, MoneroTransactionInfo tx) { acc[tx.id] = tx; return acc; }); @@ -388,11 +283,10 @@ abstract class NanoWalletBase extends WalletBase _getAllTransactions(dynamic _) => - monero_transaction_history - .getAllTransations() - .map((row) => MoneroTransactionInfo.fromRow(row)) - .toList(); + List _getAllTransactions(dynamic _) => monero_transaction_history + .getAllTransations() + .map((row) => MoneroTransactionInfo.fromRow(row)) + .toList(); void _setListeners() { _listener?.stop(); @@ -414,8 +308,7 @@ abstract class NanoWalletBase extends WalletBase _askForUpdateTransactionHistory() async => - await updateTransactions(); + Future _askForUpdateTransactionHistory() async => await updateTransactions(); - int _getFullBalance() => - monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id); + int _getFullBalance() => monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id); int _getUnlockedBalance() => monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id); @@ -468,9 +358,9 @@ abstract class NanoWalletBase extends WalletBase init() async { + accountList.update(); + account = accountList.accounts.first; + } + + bool validate() { + accountList.update(); + final accountListLength = accountList.accounts.length ?? 0; + + if (accountListLength <= 0) { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/cw_nano/lib/nano_wallet_creation_credentials.dart b/cw_nano/lib/nano_wallet_creation_credentials.dart new file mode 100644 index 000000000..f13285d84 --- /dev/null +++ b/cw_nano/lib/nano_wallet_creation_credentials.dart @@ -0,0 +1,26 @@ +import 'package:cw_core/wallet_credentials.dart'; +import 'package:cw_core/wallet_info.dart'; + +class NanoNewWalletCredentials extends WalletCredentials { + NanoNewWalletCredentials({required String name, WalletInfo? walletInfo}) + : super(name: name, walletInfo: walletInfo); +} + +class NanoRestoreWalletFromSeedCredentials extends WalletCredentials { + NanoRestoreWalletFromSeedCredentials( + {required String name, + required String password, + required this.mnemonic, + WalletInfo? walletInfo}) + : super(name: name, password: password, walletInfo: walletInfo); + + final String mnemonic; +} + +class NanoRestoreWalletFromWIFCredentials extends WalletCredentials { + NanoRestoreWalletFromWIFCredentials( + {required String name, required String password, required this.wif, WalletInfo? walletInfo}) + : super(name: name, password: password, walletInfo: walletInfo); + + final String wif; +} diff --git a/cw_nano/lib/nano_wallet_service.dart b/cw_nano/lib/nano_wallet_service.dart new file mode 100644 index 000000000..08044f438 --- /dev/null +++ b/cw_nano/lib/nano_wallet_service.dart @@ -0,0 +1,103 @@ +import 'dart:io'; + +import 'package:cw_core/pathForWallet.dart'; +import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/wallet_service.dart'; +import 'package:cw_core/wallet_type.dart'; +// import 'package:cw_nano/nano_mnemonics.dart'; +import 'package:cw_nano/nano_wallet.dart'; +import 'package:cw_nano/nano_wallet_creation_credentials.dart'; +import 'package:hive/hive.dart'; +import 'package:bip39/bip39.dart' as bip39; + + + +class NanoWalletService extends WalletService { + NanoWalletService(this.walletInfoSource); + + final Box walletInfoSource; + + @override + Future create(NanoNewWalletCredentials credentials) async { + final mnemonic = bip39.generateMnemonic(); + final wallet = NanoWallet( + walletInfo: credentials.walletInfo!, + mnemonic: mnemonic, + password: credentials.password!, + ); + + await wallet.init(); + await wallet.save(); + + return wallet; + } + + @override + WalletType getType() => WalletType.ethereum; + + @override + Future isWalletExit(String name) async => + File(await pathForWallet(name: name, type: getType())).existsSync(); + + @override + Future openWallet(String name, String password) async { + final walletInfo = + walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType())); + final wallet = await NanoWalletBase.open( + name: name, + password: password, + walletInfo: walletInfo, + ); + + await wallet.init(); + await wallet.save(); + + return wallet; + } + + @override + Future remove(String wallet) async => + File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true); + + @override + Future restoreFromKeys(credentials) { + throw UnimplementedError(); + } + + @override + Future restoreFromSeed( + NanoRestoreWalletFromSeedCredentials credentials) async { + // if (!bip39.validateMnemonic(credentials.mnemonic)) { + // throw EthereumMnemonicIsIncorrectException(); + // } + + final wallet = await NanoWallet( + // password: credentials.password!, + // mnemonic: credentials.mnemonic, + walletInfo: credentials.walletInfo!, + ); + + await wallet.init(); + await wallet.save(); + + return wallet; + } + + @override + Future rename(String currentName, String password, String newName) async { + final currentWalletInfo = walletInfoSource.values + .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType())); + final currentWallet = await NanoWalletBase.open( + password: password, name: currentName, walletInfo: currentWalletInfo); + + await currentWallet.renameWalletFiles(newName); + + final newWalletInfo = currentWalletInfo; + newWalletInfo.id = WalletBase.idFor(newName, getType()); + newWalletInfo.name = newName; + + await walletInfoSource.put(currentWalletInfo.key, newWalletInfo); + } +} diff --git a/lib/nano/cw_nano.dart b/lib/nano/cw_nano.dart index dfd15abaa..f644fc6c2 100644 --- a/lib/nano/cw_nano.dart +++ b/lib/nano/cw_nano.dart @@ -1,121 +1,105 @@ part of 'nano.dart'; + +// class CWMoneroAccountList extends MoneroAccountList { +// CWMoneroAccountList(this._wallet); +// final Object _wallet; + +// @override +// @computed +// ObservableList get accounts { +// final moneroWallet = _wallet as MoneroWallet; +// final accounts = moneroWallet.walletAddresses.accountList +// .accounts +// .map((acc) => Account(id: acc.id, label: acc.label, balance: acc.balance)) +// .toList(); +// return ObservableList.of(accounts); +// } + +// @override +// void update(Object wallet) { +// final moneroWallet = wallet as MoneroWallet; +// moneroWallet.walletAddresses.accountList.update(); +// } + +// @override +// void refresh(Object wallet) { +// final moneroWallet = wallet as MoneroWallet; +// moneroWallet.walletAddresses.accountList.refresh(); +// } + +// @override +// List getAll(Object wallet) { +// final moneroWallet = wallet as MoneroWallet; +// return moneroWallet.walletAddresses.accountList +// .getAll() +// .map((acc) => Account(id: acc.id, label: acc.label, balance: acc.balance)) +// .toList(); +// } + +// @override +// Future addAccount(Object wallet, {required String label}) async { +// final moneroWallet = wallet as MoneroWallet; +// await moneroWallet.walletAddresses.accountList.addAccount(label: label); +// } + +// @override +// Future setLabelAccount(Object wallet, {required int accountIndex, required String label}) async { +// final moneroWallet = wallet as MoneroWallet; +// await moneroWallet.walletAddresses.accountList +// .setLabelAccount( +// accountIndex: accountIndex, +// label: label); +// } +// } + class CWNano extends Nano { - @override - List getNanoWordList(String language) => EthereumMnemonics.englishWordlist; - WalletService createNanoWalletService(Box walletInfoSource) => - EthereumWalletService(walletInfoSource); + // @override + // NanoAccountList getAccountList(Object wallet) { + // return CWNanoAccountList(wallet); + // } @override - WalletCredentials createEthereumNewWalletCredentials({ + List getNanoWordList(String language) { + throw UnimplementedError(); + } + + @override + WalletService createNanoWalletService(Box walletInfoSource) { + return NanoWalletService(walletInfoSource); + } + + // @override + // WalletCredentials createNanoNewWalletCredentials({ + // required String name, + // WalletInfo? walletInfo, + // }) => + // NanoNewWalletCredentials(name: name, walletInfo: walletInfo); + + @override + WalletCredentials createNanoNewWalletCredentials({ required String name, - WalletInfo? walletInfo, - }) => - EthereumNewWalletCredentials(name: name, walletInfo: walletInfo); - - @override - WalletCredentials createEthereumRestoreWalletFromSeedCredentials({ - required String name, - required String mnemonic, - required String password, - }) => - EthereumRestoreWalletFromSeedCredentials(name: name, password: password, mnemonic: mnemonic); - - @override - String getAddress(WalletBase wallet) => (wallet as EthereumWallet).walletAddresses.address; - - @override - TransactionPriority getDefaultTransactionPriority() => EthereumTransactionPriority.medium; - - @override - List getTransactionPriorities() => EthereumTransactionPriority.all; - - @override - TransactionPriority deserializeEthereumTransactionPriority(int raw) => - EthereumTransactionPriority.deserialize(raw: raw); - - @override - int getEstimatedFee(Object wallet, TransactionPriority priority) { - final ethereumWallet = wallet as EthereumWallet; - return ethereumWallet.feeRate(priority); - } - - Object createEthereumTransactionCredentials( - List outputs, { - required TransactionPriority priority, - required CryptoCurrency currency, - int? feeRate, - }) => - EthereumTransactionCredentials( - outputs - .map((out) => OutputInfo( - fiatAmount: out.fiatAmount, - cryptoAmount: out.cryptoAmount, - address: out.address, - note: out.note, - sendAll: out.sendAll, - extractedAddress: out.extractedAddress, - isParsedAddress: out.isParsedAddress, - formattedCryptoAmount: out.formattedCryptoAmount)) - .toList(), - priority: priority as EthereumTransactionPriority, - currency: currency, - feeRate: feeRate, - ); - - Object createEthereumTransactionCredentialsRaw( - List outputs, { - TransactionPriority? priority, - required CryptoCurrency currency, - required int feeRate, - }) => - EthereumTransactionCredentials( - outputs, - priority: priority as EthereumTransactionPriority?, - currency: currency, - feeRate: feeRate, - ); - - @override - int formatterEthereumParseAmount(String amount) => EthereumFormatter.parseEthereumAmount(amount); - - @override - double formatterEthereumAmountToDouble({required TransactionInfo transaction}) { - transaction as EthereumTransactionInfo; - return cryptoAmountToDouble( - amount: transaction.amount, divider: BigInt.from(10).pow(transaction.exponent).toInt()); + required String language, + String? password, + }) { + return NanoNewWalletCredentials(name: name, password: password, language: language); } @override - List getERC20Currencies(WalletBase wallet) { - final ethereumWallet = wallet as EthereumWallet; - return ethereumWallet.erc20Currencies; + TransactionHistoryBase getTransactionHistory(Object wallet) { + // final moneroWallet = wallet as MoneroWallet; + // return moneroWallet.transactionHistory; + throw UnimplementedError(); } @override - Future addErc20Token(WalletBase wallet, Erc20Token token) async => - await (wallet as EthereumWallet).addErc20Token(token); - - @override - Future deleteErc20Token(WalletBase wallet, Erc20Token token) async => - await (wallet as EthereumWallet).deleteErc20Token(token); - - @override - Future getErc20Token(WalletBase wallet, String contractAddress) async { - final ethereumWallet = wallet as EthereumWallet; - return await ethereumWallet.getErc20Token(contractAddress); + void onStartup() { + // monero_wallet_api.onStartup(); } @override - CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) { - transaction as EthereumTransactionInfo; - if (transaction.tokenSymbol == CryptoCurrency.eth.title) { - return CryptoCurrency.eth; - } - - wallet as EthereumWallet; - return wallet.erc20Currencies - .firstWhere((element) => transaction.tokenSymbol == element.symbol); + List getMoneroWordList(String language) { + throw UnimplementedError(); } } diff --git a/lib/nano/nano.dart b/lib/nano/nano.dart index ca043af14..ec2e1acaf 100644 --- a/lib/nano/nano.dart +++ b/lib/nano/nano.dart @@ -1,61 +1,59 @@ - -import 'package:cake_wallet/view_model/send/output.dart'; -import 'package:cw_core/crypto_amount_format.dart'; -import 'package:cw_core/crypto_currency.dart'; -import 'package:cw_core/erc20_token.dart'; -import 'package:cw_core/output_info.dart'; -import 'package:cw_core/transaction_info.dart'; -import 'package:cw_core/transaction_priority.dart'; -import 'package:cw_core/wallet_base.dart'; +import 'package:mobx/mobx.dart'; import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/wallet_service.dart'; -import 'package:cw_ethereum/ethereum_formatter.dart'; -import 'package:cw_ethereum/ethereum_mnemonics.dart'; -import 'package:cw_ethereum/ethereum_transaction_credentials.dart'; -import 'package:cw_ethereum/ethereum_transaction_info.dart'; -import 'package:cw_ethereum/ethereum_wallet.dart'; -import 'package:cw_ethereum/ethereum_wallet_creation_credentials.dart'; -import 'package:cw_ethereum/ethereum_wallet_service.dart'; -import 'package:cw_ethereum/ethereum_transaction_priority.dart'; import 'package:hive/hive.dart'; +import 'package:cw_nano/api/wallet.dart' as nano_wallet_api; +import 'package:cw_nano/nano_balance.dart'; +import 'package:cw_nano/nano_wallet_creation_credentials.dart'; part 'cw_nano.dart'; Nano? nano = CWNano(); -abstract class Nano { - List getNanoWordList(String language); - WalletService createNanoWalletService(Box walletInfoSource); - WalletCredentials createEthereumNewWalletCredentials({required String name, WalletInfo? walletInfo}); - WalletCredentials createEthereumRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password}); - String getAddress(WalletBase wallet); - TransactionPriority getDefaultTransactionPriority(); - List getTransactionPriorities(); - TransactionPriority deserializeEthereumTransactionPriority(int raw); - int getEstimatedFee(Object wallet, TransactionPriority priority); - - Object createEthereumTransactionCredentials( - List outputs, { - required TransactionPriority priority, - required CryptoCurrency currency, - int? feeRate, - }); - - Object createEthereumTransactionCredentialsRaw( - List outputs, { - TransactionPriority? priority, - required CryptoCurrency currency, - required int feeRate, - }); - - int formatterEthereumParseAmount(String amount); - double formatterEthereumAmountToDouble({required TransactionInfo transaction}); - List getERC20Currencies(WalletBase wallet); - Future addErc20Token(WalletBase wallet, Erc20Token token); - Future deleteErc20Token(WalletBase wallet, Erc20Token token); - Future getErc20Token(WalletBase wallet, String contractAddress); - - CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction); +class Account { + Account({required this.id, required this.label, this.balance}); + final int id; + final String label; + final String? balance; +} + +abstract class NanoWalletDetails { + @observable + late Account account; + + @observable + late NanoBalance balance; +} + +abstract class Nano { + // NanoAccountList getAccountList(Object wallet); + + WalletService createNanoWalletService(Box walletInfoSource); + + TransactionHistoryBase getTransactionHistory(Object wallet); + + NanoWalletDetails getNanoWalletDetails(Object wallet); + + WalletCredentials createNanoNewWalletCredentials({ + required String name, + required String language, + String password, + }); + + String getTransactionAddress(Object wallet, int accountIndex, int addressIndex); + + void onStartup(); + + List getNanoWordList(String language); +} + +abstract class NanoAccountList { + ObservableList get accounts; + void update(Object wallet); + void refresh(Object wallet); + List getAll(Object wallet); + Future addAccount(Object wallet, {required String label}); + Future setLabelAccount(Object wallet, {required int accountIndex, required String label}); } - \ No newline at end of file diff --git a/lib/view_model/wallet_new_vm.dart b/lib/view_model/wallet_new_vm.dart index dcb9785e7..26f4f666f 100644 --- a/lib/view_model/wallet_new_vm.dart +++ b/lib/view_model/wallet_new_vm.dart @@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/monero/monero.dart'; +import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cake_wallet/core/wallet_creation_service.dart'; @@ -45,6 +46,8 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store { name: name, language: options as String); case WalletType.ethereum: return ethereum!.createEthereumNewWalletCredentials(name: name); + case WalletType.nano: + return nano!.createNanoNewWalletCredentials(name: name, language: options as String); default: throw Exception('Unexpected type: ${type.toString()}');; } diff --git a/model_generator.sh b/model_generator.sh old mode 100644 new mode 100755 index d6e417843..0e4345c25 --- a/model_generator.sh +++ b/model_generator.sh @@ -3,4 +3,5 @@ cd cw_monero && flutter pub get && flutter packages pub run build_runner build - cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. +cd cw_nano && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd .. flutter packages pub run build_runner build --delete-conflicting-outputs \ No newline at end of file diff --git a/scripts/android/pubspec_gen.sh b/scripts/android/pubspec_gen.sh index 72703150a..c74108bf1 100755 --- a/scripts/android/pubspec_gen.sh +++ b/scripts/android/pubspec_gen.sh @@ -10,7 +10,7 @@ case $APP_ANDROID_TYPE in CONFIG_ARGS="--monero" ;; $CAKEWALLET) - CONFIG_ARGS="--monero --bitcoin --haven --ethereum" + CONFIG_ARGS="--monero --bitcoin --haven --ethereum --nano" ;; $HAVEN) CONFIG_ARGS="--haven" diff --git a/tool/configure.dart b/tool/configure.dart index a34d06e47..e78671016 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -14,12 +14,32 @@ Future main(List args) async { final hasMonero = args.contains('${prefix}monero'); final hasHaven = args.contains('${prefix}haven'); final hasEthereum = args.contains('${prefix}ethereum'); + final hasNano = args.contains('${prefix}nano'); + final hasBanano = args.contains('${prefix}banano'); + await generateBitcoin(hasBitcoin); await generateMonero(hasMonero); await generateHaven(hasHaven); await generateEthereum(hasEthereum); - await generatePubspec(hasMonero: hasMonero, hasBitcoin: hasBitcoin, hasHaven: hasHaven, hasEthereum: hasEthereum); - await generateWalletTypes(hasMonero: hasMonero, hasBitcoin: hasBitcoin, hasHaven: hasHaven, hasEthereum: hasEthereum); + await generateNano(hasNano); + // await generateBanano(hasEthereum); + + await generatePubspec( + hasMonero: hasMonero, + hasBitcoin: hasBitcoin, + hasHaven: hasHaven, + hasEthereum: hasEthereum, + hasNano: hasNano, + hasBanano: hasBanano, + ); + await generateWalletTypes( + hasMonero: hasMonero, + hasBitcoin: hasBitcoin, + hasHaven: hasHaven, + hasEthereum: hasEthereum, + hasNano: hasNano, + hasBanano: hasBanano, + ); } Future generateBitcoin(bool hasImplementation) async { @@ -105,12 +125,12 @@ abstract class Bitcoin { const bitcoinEmptyDefinition = 'Bitcoin? bitcoin;\n'; const bitcoinCWDefinition = 'Bitcoin? bitcoin = CWBitcoin();\n'; - final output = '$bitcoinCommonHeaders\n' - + (hasImplementation ? '$bitcoinCWHeaders\n' : '\n') - + (hasImplementation ? '$bitcoinCwPart\n\n' : '\n') - + (hasImplementation ? bitcoinCWDefinition : bitcoinEmptyDefinition) - + '\n' - + bitcoinContent; + final output = '$bitcoinCommonHeaders\n' + + (hasImplementation ? '$bitcoinCWHeaders\n' : '\n') + + (hasImplementation ? '$bitcoinCwPart\n\n' : '\n') + + (hasImplementation ? bitcoinCWDefinition : bitcoinEmptyDefinition) + + '\n' + + bitcoinContent; if (outputFile.existsSync()) { await outputFile.delete(); @@ -279,12 +299,12 @@ abstract class MoneroAccountList { const moneroEmptyDefinition = 'Monero? monero;\n'; const moneroCWDefinition = 'Monero? monero = CWMonero();\n'; - final output = '$moneroCommonHeaders\n' - + (hasImplementation ? '$moneroCWHeaders\n' : '\n') - + (hasImplementation ? '$moneroCwPart\n\n' : '\n') - + (hasImplementation ? moneroCWDefinition : moneroEmptyDefinition) - + '\n' - + moneroContent; + final output = '$moneroCommonHeaders\n' + + (hasImplementation ? '$moneroCWHeaders\n' : '\n') + + (hasImplementation ? '$moneroCwPart\n\n' : '\n') + + (hasImplementation ? moneroCWDefinition : moneroEmptyDefinition) + + '\n' + + moneroContent; if (outputFile.existsSync()) { await outputFile.delete(); @@ -294,7 +314,6 @@ abstract class MoneroAccountList { } Future generateHaven(bool hasImplementation) async { - final outputFile = File(havenOutputPath); const havenCommonHeaders = """ import 'package:mobx/mobx.dart'; @@ -459,12 +478,12 @@ abstract class HavenAccountList { const havenEmptyDefinition = 'Haven? haven;\n'; const havenCWDefinition = 'Haven? haven = CWHaven();\n'; - final output = '$havenCommonHeaders\n' - + (hasImplementation ? '$havenCWHeaders\n' : '\n') - + (hasImplementation ? '$havenCwPart\n\n' : '\n') - + (hasImplementation ? havenCWDefinition : havenEmptyDefinition) - + '\n' - + havenContent; + final output = '$havenCommonHeaders\n' + + (hasImplementation ? '$havenCWHeaders\n' : '\n') + + (hasImplementation ? '$havenCwPart\n\n' : '\n') + + (hasImplementation ? havenCWDefinition : havenEmptyDefinition) + + '\n' + + havenContent; if (outputFile.existsSync()) { await outputFile.delete(); @@ -474,7 +493,6 @@ abstract class HavenAccountList { } Future generateEthereum(bool hasImplementation) async { - final outputFile = File(ethereumOutputPath); const ethereumCommonHeaders = """ """; @@ -541,12 +559,12 @@ abstract class Ethereum { const ethereumEmptyDefinition = 'Ethereum? ethereum;\n'; const ethereumCWDefinition = 'Ethereum? ethereum = CWEthereum();\n'; - final output = '$ethereumCommonHeaders\n' - + (hasImplementation ? '$ethereumCWHeaders\n' : '\n') - + (hasImplementation ? '$ethereumCwPart\n\n' : '\n') - + (hasImplementation ? ethereumCWDefinition : ethereumEmptyDefinition) - + '\n' - + ethereumContent; + final output = '$ethereumCommonHeaders\n' + + (hasImplementation ? '$ethereumCWHeaders\n' : '\n') + + (hasImplementation ? '$ethereumCwPart\n\n' : '\n') + + (hasImplementation ? ethereumCWDefinition : ethereumEmptyDefinition) + + '\n' + + ethereumContent; if (outputFile.existsSync()) { await outputFile.delete(); @@ -555,8 +573,81 @@ abstract class Ethereum { await outputFile.writeAsString(output); } -Future generatePubspec({required bool hasMonero, required bool hasBitcoin, required bool hasHaven, required bool hasEthereum}) async { - const cwCore = """ +Future generateNano(bool hasImplementation) async { + final outputFile = File(ethereumOutputPath); + const nanoCommonHeaders = """ +"""; + const nanoCWHeaders = """ +import 'package:mobx/mobx.dart'; +import 'package:cw_core/wallet_credentials.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/transaction_history.dart'; +import 'package:cw_core/wallet_service.dart'; +import 'package:hive/hive.dart'; +import 'package:cw_nano/api/wallet.dart' as nano_wallet_api; +import 'package:cw_nano/nano_balance.dart'; +import 'package:cw_nano/nano_wallet_creation_credentials.dart'; +"""; + const nanoCwPart = "part 'cw_nano.dart';"; + const nanoContent = """ +abstract class Nano { + // NanoAccountList getAccountList(Object wallet); + + WalletService createNanoWalletService(Box walletInfoSource); + + TransactionHistoryBase getTransactionHistory(Object wallet); + + NanoWalletDetails getNanoWalletDetails(Object wallet); + + WalletCredentials createNanoNewWalletCredentials({ + required String name, + required String language, + String password, + }); + + String getTransactionAddress(Object wallet, int accountIndex, int addressIndex); + + void onStartup(); + + List getNanoWordList(String language); +} + +abstract class NanoAccountList { + ObservableList get accounts; + void update(Object wallet); + void refresh(Object wallet); + List getAll(Object wallet); + Future addAccount(Object wallet, {required String label}); + Future setLabelAccount(Object wallet, {required int accountIndex, required String label}); +} + """; + + const nanoEmptyDefinition = 'Nano? nano;\n'; + const nanoCWDefinition = 'Nano? nano = CWNano();\n'; + + final output = '$nanoCommonHeaders\n' + + (hasImplementation ? '$nanoCWHeaders\n' : '\n') + + (hasImplementation ? '$nanoCwPart\n\n' : '\n') + + (hasImplementation ? nanoCWDefinition : nanoEmptyDefinition) + + '\n' + + nanoContent; + + if (outputFile.existsSync()) { + await outputFile.delete(); + } + + await outputFile.writeAsString(output); +} + +Future generatePubspec({ + required bool hasMonero, + required bool hasBitcoin, + required bool hasHaven, + required bool hasEthereum, + required bool hasNano, + required bool hasBanano, +}) async { + const cwCore = """ cw_core: path: ./cw_core """; @@ -580,6 +671,14 @@ Future generatePubspec({required bool hasMonero, required bool hasBitcoin, cw_ethereum: path: ./cw_ethereum """; + const cwNano = """ + cw_nano: + path: ./cw_nano + """; + const cwBanano = """ + cw_nano: + path: ./cw_banano + """; final inputFile = File(pubspecOutputPath); final inputText = await inputFile.readAsString(); final inputLines = inputText.split('\n'); @@ -604,11 +703,19 @@ Future generatePubspec({required bool hasMonero, required bool hasBitcoin, output += '\n$cwEthereum'; } + if (hasNano) { + output += '\n$cwNano'; + } + + if (hasBanano) { + output += '\n$cwBanano'; + } + final outputLines = output.split('\n'); inputLines.insertAll(dependenciesIndex + 1, outputLines); final outputContent = inputLines.join('\n'); final outputFile = File(pubspecOutputPath); - + if (outputFile.existsSync()) { await outputFile.delete(); } @@ -616,9 +723,16 @@ Future generatePubspec({required bool hasMonero, required bool hasBitcoin, await outputFile.writeAsString(outputContent); } -Future generateWalletTypes({required bool hasMonero, required bool hasBitcoin, required bool hasHaven, required bool hasEthereum}) async { +Future generateWalletTypes({ + required bool hasMonero, + required bool hasBitcoin, + required bool hasHaven, + required bool hasEthereum, + required bool hasNano, + required bool hasBanano, +}) async { final walletTypesFile = File(walletTypesPath); - + if (walletTypesFile.existsSync()) { await walletTypesFile.delete(); } @@ -647,6 +761,14 @@ Future generateWalletTypes({required bool hasMonero, required bool hasBitc outputContent += '\tWalletType.haven,\n'; } + if (hasNano) { + outputContent += '\tWalletType.nano,\n'; + } + + if (hasBanano) { + outputContent += '\tWalletType.banano,\n'; + } + outputContent += '];\n'; await walletTypesFile.writeAsString(outputContent); -} \ No newline at end of file +}