diff --git a/cw_core/lib/wallet_type.dart b/cw_core/lib/wallet_type.dart index 9f3f6520f..a72c5e1a9 100644 --- a/cw_core/lib/wallet_type.dart +++ b/cw_core/lib/wallet_type.dart @@ -118,7 +118,7 @@ String walletTypeToDisplayName(WalletType type) { return 'Ethereum (ETH)'; case WalletType.nano: return 'Nano (XNO)'; - case WalletType.ethereum: + case WalletType.banano: return 'Banano (BAN)'; default: return ''; diff --git a/cw_nano/lib/nano_transaction_history.dart b/cw_nano/lib/nano_transaction_history.dart index 55aeae59f..7b904f320 100644 --- a/cw_nano/lib/nano_transaction_history.dart +++ b/cw_nano/lib/nano_transaction_history.dart @@ -22,7 +22,7 @@ abstract class NanoTransactionHistoryBase transactions[transaction.id] = transaction; @override - void addMany(Map transactions) => + void addMany(Map transactions) => this.transactions.addAll(transactions); } diff --git a/cw_nano/lib/nano_transaction_info.dart b/cw_nano/lib/nano_transaction_info.dart new file mode 100644 index 000000000..19db58b64 --- /dev/null +++ b/cw_nano/lib/nano_transaction_info.dart @@ -0,0 +1,79 @@ +import 'package:cw_core/transaction_info.dart'; +import 'package:cw_core/monero_amount_format.dart'; +import 'package:cw_monero/api/structs/transaction_info_row.dart'; +import 'package:cw_core/parseBoolFromString.dart'; +import 'package:cw_core/transaction_direction.dart'; +import 'package:cw_core/format_amount.dart'; +import 'package:cw_monero/api/transaction_history.dart'; + +class NanoTransactionInfo extends TransactionInfo { + NanoTransactionInfo(this.id, this.height, this.direction, this.date, this.isPending, this.amount, + this.accountIndex, this.addressIndex, this.amountRaw, this.confirmations); + + // NanoTransactionInfo.fromMap(Map map) + // : id = (map['hash'] ?? '') as String, + // height = (map['height'] ?? 0) as int, + // direction = map['direction'] != null + // ? parseTransactionDirectionFromNumber(map['direction'] as String) + // : TransactionDirection.incoming, + // date = DateTime.fromMillisecondsSinceEpoch( + // (int.tryParse(map['timestamp'] as String? ?? '') ?? 0) * 1000), + // isPending = parseBoolFromString(map['isPending'] as String), + // amount = map['amount'] as int, + // accountIndex = int.parse(map['accountIndex'] as String), + // addressIndex = map['addressIndex'] as int, + // confirmations = map['confirmations'] as int, + // key = getTxKey((map['hash'] ?? '') as String), + // fee = map['fee'] as int? ?? 0 { + // additionalInfo = { + // 'key': key, + // 'accountIndex': accountIndex, + // 'addressIndex': addressIndex + // }; + // } + + // NanoTransactionInfo.fromRow(TransactionInfoRow row) + // : id = row.getHash(), + // height = row.blockHeight, + // direction = parseTransactionDirectionFromInt(row.direction), + // date = DateTime.fromMillisecondsSinceEpoch(row.getDatetime() * 1000), + // isPending = row.isPending != 0, + // amount = row.getAmount(), + // accountIndex = row.subaddrAccount, + // addressIndex = row.subaddrIndex, + // confirmations = row.confirmations, + // key = getTxKey(row.getHash()), + // fee = row.fee { + // additionalInfo = { + // 'key': key, + // 'accountIndex': accountIndex, + // 'addressIndex': addressIndex + // }; + // } + + final String id; + final int height; + final TransactionDirection direction; + final DateTime date; + final int accountIndex; + final bool isPending; + final int amount; + final BigInt amountRaw; + final int addressIndex; + final int confirmations; + String? recipientAddress; + String? key; + String? _fiatAmount; + + @override + String amountFormatted() => '${formatAmount(moneroAmountToString(amount: amount))} XNO'; + + @override + String fiatAmount() => _fiatAmount ?? ''; + + @override + void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount); + + @override + String feeFormatted() => '${formatAmount(moneroAmountToString(amount: 0))} XNO'; +} diff --git a/cw_nano/lib/nano_wallet.dart b/cw_nano/lib/nano_wallet.dart index a1c45ebc9..b2ddb73ec 100644 --- a/cw_nano/lib/nano_wallet.dart +++ b/cw_nano/lib/nano_wallet.dart @@ -1,60 +1,403 @@ +// import 'dart:async'; +// import 'dart:io'; +// import 'package:cw_core/pathForWallet.dart'; +// import 'package:cw_core/transaction_priority.dart'; +// import 'package:cw_nano/nano_balance.dart'; +// import 'package:cw_nano/nano_transaction_history.dart'; +// import 'package:cw_nano/nano_transaction_info.dart'; +// import 'package:cw_nano/nano_wallet_addresses.dart'; +// import 'package:cw_nano/nano_wallet_keys.dart'; +// import 'package:mobx/mobx.dart'; +// import 'package:cw_core/pending_transaction.dart'; +// import 'package:cw_core/wallet_base.dart'; +// import 'package:cw_core/sync_status.dart'; +// import 'package:cw_core/wallet_info.dart'; +// import 'package:cw_core/node.dart'; +// import 'package:cw_core/crypto_currency.dart'; + +// part 'nano_wallet.g.dart'; + +// const moneroBlockSize = 1000; + +// class NanoWallet = NanoWalletBase with _$NanoWallet; + +// abstract class NanoWalletBase +// extends WalletBase with Store { +// NanoWalletBase({ +// required WalletInfo walletInfo, +// required String mnemonic, +// required String password, +// }) : balance = ObservableMap.of({ +// CryptoCurrency.nano: +// NanoBalance(currentBalance: BigInt.zero, receivableBalance: BigInt.zero) +// }), +// _isTransactionUpdating = false, +// _hasSyncAfterStartup = false, +// _password = password, +// _mnemonic = mnemonic, +// walletAddresses = NanoWalletAddresses(walletInfo), +// syncStatus = NotConnectedSyncStatus(), +// super(walletInfo) { +// transactionHistory = NanoTransactionHistory(); + +// // _onAccountChangeReaction = reaction((_) => walletAddresses.account, (Account? account) { +// // if (account == null) { +// // return; +// // } + +// // balance = ObservableMap.of({ +// // currency: MoneroBalance( +// // fullBalance: monero_wallet.getFullBalance(accountIndex: account.id), +// // unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: account.id)) +// // }); +// // walletAddresses.updateSubaddressList(accountIndex: account.id); +// // }); +// } + +// final String _mnemonic; +// final String _password; + +// static const int _autoSaveInterval = 30; + +// @override +// NanoWalletAddresses walletAddresses; + +// @override +// @observable +// SyncStatus syncStatus; + +// @override +// @observable +// ObservableMap balance; + +// @override +// String get seed { +// throw UnimplementedError(); +// } + +// @override +// NanoWalletKeys get keys => NanoWalletKeys( +// derivationType: "bip39", +// mnemonic: "todo", +// privateKey: "todo", +// ); +// // NanoWalletKeys get keys => NanoWalletKeys( +// // privateSpendKey: monero_wallet.getSecretSpendKey(), +// // privateViewKey: monero_wallet.getSecretViewKey(), +// // publicSpendKey: monero_wallet.getPublicSpendKey(), +// // publicViewKey: monero_wallet.getPublicViewKey()); + +// // SyncListener? _listener; +// ReactionDisposer? _onAccountChangeReaction; +// bool _isTransactionUpdating; +// bool _hasSyncAfterStartup; +// Timer? _autoSaveTimer; + +// Future 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)) +// // }); +// // _setListeners(); +// // await updateTransactions(); + +// // if (walletInfo.isRecovery) { +// // monero_wallet.setRecoveringFromSeed(isRecovery: walletInfo.isRecovery); + +// // if (monero_wallet.getCurrentHeight() <= 1) { +// // monero_wallet.setRefreshFromBlockHeight(height: walletInfo.restoreHeight); +// // } +// // } + +// // _autoSaveTimer = Timer.periodic( +// // Duration(seconds: _autoSaveInterval), +// // (_) async => await save()); +// } + +// @override +// Future? updateBalance() => null; + +// @override +// void close() { +// // _listener?.stop(); +// // _onAccountChangeReaction?.reaction.dispose(); +// // _autoSaveTimer?.cancel(); +// } + +// @override +// Future connectToNode({required Node node}) async { +// // try { +// // syncStatus = ConnectingSyncStatus(); +// // await monero_wallet.setupNode( +// // address: node.uri.toString(), useSSL: node.isSSL, isLightWallet: false); + +// // monero_wallet.setTrustedDaemon(node.trusted); +// // syncStatus = ConnectedSyncStatus(); +// // } catch (e) { +// // syncStatus = FailedSyncStatus(); +// // print(e); +// // } +// } + +// @override +// Future startSync() async { +// // try { +// // _setInitialHeight(); +// // } catch (_) {} + +// // try { +// // syncStatus = AttemptingSyncStatus(); +// // monero_wallet.startRefresh(); +// // _setListeners(); +// // _listener?.start(); +// // } catch (e) { +// // syncStatus = FailedSyncStatus(); +// // print(e); +// // rethrow; +// // } +// } + +// @override +// Future createTransaction(Object credentials) async { +// // final _credentials = credentials as MoneroTransactionCreationCredentials; +// // return null; +// throw UnimplementedError(); +// } + +// @override +// int calculateEstimatedFee(TransactionPriority priority, int? amount) { +// // FIXME: hardcoded value; +// return 0; +// } + +// @override +// Future save() async { +// // await walletAddresses.updateAddressesInBox(); +// // await backupWalletFiles(name); +// // await monero_wallet.store(); +// } + +// @override +// Future renameWalletFiles(String newWalletName) async { +// final currentWalletDirPath = await pathForWalletDir(name: name, type: type); + +// try { +// // -- rename the waller folder -- +// final currentWalletDir = Directory(await pathForWalletDir(name: name, type: type)); +// final newWalletDirPath = await pathForWalletDir(name: newWalletName, type: type); +// await currentWalletDir.rename(newWalletDirPath); + +// // -- use new waller folder to rename files with old names still -- +// final renamedWalletPath = newWalletDirPath + '/$name'; + +// final currentCacheFile = File(renamedWalletPath); +// final currentKeysFile = File('$renamedWalletPath.keys'); +// final currentAddressListFile = File('$renamedWalletPath.address.txt'); + +// final newWalletPath = await pathForWallet(name: newWalletName, type: type); + +// if (currentCacheFile.existsSync()) { +// await currentCacheFile.rename(newWalletPath); +// } +// if (currentKeysFile.existsSync()) { +// await currentKeysFile.rename('$newWalletPath.keys'); +// } +// if (currentAddressListFile.existsSync()) { +// await currentAddressListFile.rename('$newWalletPath.address.txt'); +// } +// } catch (e) { +// final currentWalletPath = await pathForWallet(name: name, type: type); + +// final currentCacheFile = File(currentWalletPath); +// final currentKeysFile = File('$currentWalletPath.keys'); +// final currentAddressListFile = File('$currentWalletPath.address.txt'); + +// final newWalletPath = await pathForWallet(name: newWalletName, type: type); + +// // Copies current wallet files into new wallet name's dir and files +// if (currentCacheFile.existsSync()) { +// await currentCacheFile.copy(newWalletPath); +// } +// if (currentKeysFile.existsSync()) { +// await currentKeysFile.copy('$newWalletPath.keys'); +// } +// if (currentAddressListFile.existsSync()) { +// await currentAddressListFile.copy('$newWalletPath.address.txt'); +// } + +// // Delete old name's dir and files +// await Directory(currentWalletDirPath).delete(recursive: true); +// } +// } + +// @override +// Future changePassword(String password) async { +// // monero_wallet.setPasswordSync(password); +// } + +// // Future getNodeHeight() async => nano_wallet.getNodeHeight(); + +// // Future isConnected() async => nano_wallet.isConnected(); + +// Future setAsRecovered() async { +// walletInfo.isRecovery = false; +// await walletInfo.save(); +// } + +// @override +// Future rescan({required int height}) async { +// // walletInfo.restoreHeight = height; +// // walletInfo.isRecovery = true; +// // monero_wallet.setRefreshFromBlockHeight(height: height); +// // monero_wallet.rescanBlockchainAsync(); +// // await startSync(); +// // _askForUpdateBalance(); +// // walletAddresses.accountList.update(); +// // await _askForUpdateTransactionHistory(); +// // await save(); +// // await walletInfo.save(); +// } + +// // String getTransactionAddress(int accountIndex, int addressIndex) => +// // monero_wallet.getAddress(accountIndex: accountIndex, addressIndex: addressIndex); + +// @override +// Future> fetchTransactions() async { +// throw UnimplementedError(); +// // monero_transaction_history.refreshTransactions(); +// // return _getAllTransactions(null) +// // .fold>({}, +// // (Map acc, MoneroTransactionInfo tx) { +// // acc[tx.id] = tx; +// // return acc; +// // }); +// } + +// Future updateTransactions() async { +// // try { +// // if (_isTransactionUpdating) { +// // return; +// // } + +// // _isTransactionUpdating = true; +// // final transactions = await fetchTransactions(); +// // transactionHistory.addMany(transactions); +// // await transactionHistory.save(); +// // _isTransactionUpdating = false; +// // } catch (e) { +// // print(e); +// // _isTransactionUpdating = false; +// // } +// } + +// // String getSubaddressLabel(int accountIndex, int addressIndex) { +// // // return monero_wallet.getSubaddressLabel(accountIndex, addressIndex); +// // } + +// // List _getAllTransactions(dynamic _) => monero_transaction_history +// // .getAllTransations() +// // .map((row) => MoneroTransactionInfo.fromRow(row)) +// // .toList(); + +// void _setListeners() { +// // _listener?.stop(); +// // _listener = monero_wallet.setListeners(_onNewBlock, _onNewTransaction); +// } + +// void _setInitialHeight() { +// // if (walletInfo.isRecovery) { +// // return; +// // } + +// // final currentHeight = getCurrentHeight(); + +// // if (currentHeight <= 1) { +// // final height = _getHeightByDate(walletInfo.date); +// // monero_wallet.setRecoveringFromSeed(isRecovery: true); +// // monero_wallet.setRefreshFromBlockHeight(height: height); +// // } +// } + +// Future _askForUpdateTransactionHistory() async => await updateTransactions(); + +// // int _getFullBalance() => monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id); + +// // int _getUnlockedBalance() => +// // monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id); + +// void _onNewTransaction() async { +// // try { +// // await _askForUpdateTransactionHistory(); +// // _askForUpdateBalance(); +// // await Future.delayed(Duration(seconds: 1)); +// // } catch (e) { +// // print(e.toString()); +// // } +// } +// } + +import 'dart:convert'; + +import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/erc20_token.dart'; +import 'package:cw_core/node.dart'; +import 'package:cw_core/pathForWallet.dart'; +import 'package:cw_core/pending_transaction.dart'; +import 'package:cw_core/sync_status.dart'; +import 'package:cw_core/transaction_priority.dart'; +import 'package:cw_core/wallet_addresses.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cw_nano/nano_balance.dart'; +import 'package:cw_nano/nano_transaction_history.dart'; +import 'package:cw_nano/nano_transaction_info.dart'; +import 'package:mobx/mobx.dart'; +import 'package:web3dart/credentials.dart'; import 'dart:async'; 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_core/monero_wallet_utils.dart'; -import 'package:cw_nano/nano_balance.dart'; -import 'package:mobx/mobx.dart'; -import 'package:cw_core/monero_wallet_keys.dart'; -import 'package:cw_core/monero_balance.dart'; -import 'package:cw_core/account.dart'; -import 'package:cw_core/pending_transaction.dart'; +import 'package:cw_nano/nano_wallet_addresses.dart'; +import 'package:cw_nano/nano_wallet_keys.dart'; import 'package:cw_core/wallet_base.dart'; -import 'package:cw_core/sync_status.dart'; -import 'package:cw_core/wallet_info.dart'; -import 'package:cw_core/node.dart'; -import 'package:cw_core/monero_transaction_priority.dart'; -import 'package:cw_core/crypto_currency.dart'; +import 'package:web3dart/web3dart.dart'; part 'nano_wallet.g.dart'; -const moneroBlockSize = 1000; - class NanoWallet = NanoWalletBase with _$NanoWallet; abstract class NanoWalletBase extends WalletBase with Store { - NanoWalletBase({required WalletInfo walletInfo}) - : balance = ObservableMap.of({ - CryptoCurrency.nano: NanoBalance( - currentBalance: nano_wallet.getFullBalance(accountIndex: 0), - receivableBalance: nano_wallet.getFullBalance(accountIndex: 0)) - }), + NanoWalletBase({ + required WalletInfo walletInfo, + required String mnemonic, + required String password, + NanoBalance? initialBalance, + }) : syncStatus = NotConnectedSyncStatus(), + _password = password, + _mnemonic = mnemonic, _isTransactionUpdating = false, - _hasSyncAfterStartup = false, + _priorityFees = [], walletAddresses = NanoWalletAddresses(walletInfo), - syncStatus = NotConnectedSyncStatus(), + balance = ObservableMap.of({ + CryptoCurrency.eth: initialBalance ?? + NanoBalance(currentBalance: BigInt.zero, receivableBalance: BigInt.zero) + }), super(walletInfo) { - transactionHistory = MoneroTransactionHistory(); - _onAccountChangeReaction = reaction((_) => walletAddresses.account, (Account? account) { - if (account == null) { - return; - } - - balance = ObservableMap.of({ - currency: MoneroBalance( - fullBalance: monero_wallet.getFullBalance(accountIndex: account.id), - unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: account.id)) - }); - walletAddresses.updateSubaddressList(accountIndex: account.id); - }); + print("@@@@@ initializing nano wallet @@@@@"); + this.walletInfo = walletInfo; + transactionHistory = NanoTransactionHistory(); } - static const int _autoSaveInterval = 30; + final String _mnemonic; + final String _password; + + List _priorityFees; + int? _gasPrice; + bool _isTransactionUpdating; @override - NanoWalletAddresses walletAddresses; + WalletAddresses walletAddresses; @override @observable @@ -62,324 +405,170 @@ abstract class NanoWalletBase @override @observable - ObservableMap balance; - - @override - String get seed => monero_wallet.getSeed(); - - @override - NanoWalletKeys get keys => NanoWalletKeys( - privateSpendKey: monero_wallet.getSecretSpendKey(), - privateViewKey: monero_wallet.getSecretViewKey(), - publicSpendKey: monero_wallet.getPublicSpendKey(), - publicViewKey: monero_wallet.getPublicViewKey()); - - SyncListener? _listener; - ReactionDisposer? _onAccountChangeReaction; - bool _isTransactionUpdating; - bool _hasSyncAfterStartup; - Timer? _autoSaveTimer; + late ObservableMap balance; Future 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)) - }); - _setListeners(); - await updateTransactions(); - - if (walletInfo.isRecovery) { - monero_wallet.setRecoveringFromSeed(isRecovery: walletInfo.isRecovery); - - if (monero_wallet.getCurrentHeight() <= 1) { - monero_wallet.setRefreshFromBlockHeight(height: walletInfo.restoreHeight); - } - } - - // _autoSaveTimer = Timer.periodic( - // Duration(seconds: _autoSaveInterval), - // (_) async => await save()); - } - - @override - Future? updateBalance() => null; - - @override - void close() { - _listener?.stop(); - _onAccountChangeReaction?.reaction.dispose(); - _autoSaveTimer?.cancel(); - } - - @override - Future connectToNode({required Node node}) async { - try { - syncStatus = ConnectingSyncStatus(); - await monero_wallet.setupNode( - address: node.uri.toString(), useSSL: node.isSSL, isLightWallet: false); - - monero_wallet.setTrustedDaemon(node.trusted); - syncStatus = ConnectedSyncStatus(); - } catch (e) { - syncStatus = FailedSyncStatus(); - print(e); - } - } - - @override - Future startSync() async { - try { - _setInitialHeight(); - } catch (_) {} - - try { - syncStatus = AttemptingSyncStatus(); - monero_wallet.startRefresh(); - _setListeners(); - _listener?.start(); - } catch (e) { - syncStatus = FailedSyncStatus(); - print(e); - rethrow; - } - } - - @override - Future createTransaction(Object credentials) async { - // final _credentials = credentials as MoneroTransactionCreationCredentials; - // return null; - throw UnimplementedError(); + // erc20TokensBox = await Hive.openBox(Erc20Token.boxName); + // await walletAddresses.init(); + // await transactionHistory.init(); + // _privateKey = await getPrivateKey(_mnemonic, _password); + // walletAddresses.address = _privateKey.address.toString(); + // await save(); } @override int calculateEstimatedFee(TransactionPriority priority, int? amount) { - // FIXME: hardcoded value; return 0; } + @override + Future changePassword(String password) { + throw UnimplementedError("changePassword"); + } + + @override + void close() { + // _client.stop(); + } + + @action + @override + Future connectToNode({required Node node}) async { + throw UnimplementedError(); + } + + @override + Future createTransaction(Object credentials) async { + throw UnimplementedError(); + } + + Future updateTransactions() async { + throw UnimplementedError(); + } + + @override + Future> fetchTransactions() async { + throw UnimplementedError(); + // final address = _privateKey.address.hex; + // final transactions = await _client.fetchTransactions(address); + + // final List>> erc20TokensTransactions = []; + + // for (var token in balance.keys) { + // if (token is Erc20Token) { + // erc20TokensTransactions.add(_client.fetchTransactions( + // address, + // contractAddress: token.contractAddress, + // )); + // } + // } + + // final tokensTransaction = await Future.wait(erc20TokensTransactions); + // transactions.addAll(tokensTransaction.expand((element) => element)); + + // final Map result = {}; + + // for (var transactionModel in transactions) { + // if (transactionModel.isError) { + // continue; + // } + + // result[transactionModel.hash] = EthereumTransactionInfo( + // id: transactionModel.hash, + // height: transactionModel.blockNumber, + // ethAmount: transactionModel.amount, + // direction: transactionModel.from == address + // ? TransactionDirection.outgoing + // : TransactionDirection.incoming, + // isPending: false, + // date: transactionModel.date, + // confirmations: transactionModel.confirmations, + // ethFee: BigInt.from(transactionModel.gasUsed) * transactionModel.gasPrice, + // exponent: transactionModel.tokenDecimal ?? 18, + // tokenSymbol: transactionModel.tokenSymbol ?? "ETH", + // ); + // } + + // return result; + } + + @override + Object get keys => throw UnimplementedError("keys"); + + @override + Future rescan({required int height}) { + throw UnimplementedError("rescan"); + } + @override Future save() async { - await walletAddresses.updateAddressesInBox(); - await backupWalletFiles(name); - await monero_wallet.store(); + throw UnimplementedError(); + } + + @override + String get seed => _mnemonic; + + @action + @override + Future startSync() async { + throw UnimplementedError(); + } + + int feeRate(TransactionPriority priority) { + throw UnimplementedError(); + } + + // Future makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); + + // String toJSON() => json.encode({ + // 'mnemonic': _mnemonic, + // 'balance': balance[currency]!.toJSON(), + // }); + + static Future open({ + required String name, + required String password, + required WalletInfo walletInfo, + }) async { + throw UnimplementedError(); + } + + Future _updateBalance() async { + // balance[currency] = await _fetchEthBalance(); + + // await _fetchErc20Balances(); + await save(); + } + + Future _fetchErc20Balances() async { + throw UnimplementedError(); + } + + Future getPrivateKey(String mnemonic, String password) async { + throw UnimplementedError(); + } + + Future? updateBalance() async => await _updateBalance(); + + Future addErc20Token(Erc20Token token) async { + throw UnimplementedError(); + } + + Future deleteErc20Token(Erc20Token token) async { + throw UnimplementedError(); + } + + void _onNewTransaction(FilterEvent event) { + throw UnimplementedError(); + } + + void addInitialTokens() { + throw UnimplementedError(); } @override Future renameWalletFiles(String newWalletName) async { - final currentWalletDirPath = await pathForWalletDir(name: name, type: type); - - try { - // -- rename the waller folder -- - final currentWalletDir = Directory(await pathForWalletDir(name: name, type: type)); - final newWalletDirPath = await pathForWalletDir(name: newWalletName, type: type); - await currentWalletDir.rename(newWalletDirPath); - - // -- use new waller folder to rename files with old names still -- - final renamedWalletPath = newWalletDirPath + '/$name'; - - final currentCacheFile = File(renamedWalletPath); - final currentKeysFile = File('$renamedWalletPath.keys'); - final currentAddressListFile = File('$renamedWalletPath.address.txt'); - - final newWalletPath = await pathForWallet(name: newWalletName, type: type); - - if (currentCacheFile.existsSync()) { - await currentCacheFile.rename(newWalletPath); - } - if (currentKeysFile.existsSync()) { - await currentKeysFile.rename('$newWalletPath.keys'); - } - if (currentAddressListFile.existsSync()) { - await currentAddressListFile.rename('$newWalletPath.address.txt'); - } - } catch (e) { - final currentWalletPath = await pathForWallet(name: name, type: type); - - final currentCacheFile = File(currentWalletPath); - final currentKeysFile = File('$currentWalletPath.keys'); - final currentAddressListFile = File('$currentWalletPath.address.txt'); - - final newWalletPath = await pathForWallet(name: newWalletName, type: type); - - // Copies current wallet files into new wallet name's dir and files - if (currentCacheFile.existsSync()) { - await currentCacheFile.copy(newWalletPath); - } - if (currentKeysFile.existsSync()) { - await currentKeysFile.copy('$newWalletPath.keys'); - } - if (currentAddressListFile.existsSync()) { - await currentAddressListFile.copy('$newWalletPath.address.txt'); - } - - // Delete old name's dir and files - await Directory(currentWalletDirPath).delete(recursive: true); - } - } - - @override - Future changePassword(String password) async { - monero_wallet.setPasswordSync(password); - } - - Future getNodeHeight() async => monero_wallet.getNodeHeight(); - - Future isConnected() async => monero_wallet.isConnected(); - - Future setAsRecovered() async { - walletInfo.isRecovery = false; - await walletInfo.save(); - } - - @override - Future rescan({required int height}) async { - walletInfo.restoreHeight = height; - walletInfo.isRecovery = true; - monero_wallet.setRefreshFromBlockHeight(height: height); - monero_wallet.rescanBlockchainAsync(); - await startSync(); - _askForUpdateBalance(); - walletAddresses.accountList.update(); - await _askForUpdateTransactionHistory(); - await save(); - await walletInfo.save(); - } - - String getTransactionAddress(int accountIndex, int addressIndex) => - monero_wallet.getAddress(accountIndex: accountIndex, addressIndex: addressIndex); - - @override - Future> fetchTransactions() async { - monero_transaction_history.refreshTransactions(); - return _getAllTransactions(null) - .fold>({}, - (Map acc, MoneroTransactionInfo tx) { - acc[tx.id] = tx; - return acc; - }); - } - - Future updateTransactions() async { - try { - if (_isTransactionUpdating) { - return; - } - - _isTransactionUpdating = true; - final transactions = await fetchTransactions(); - transactionHistory.addMany(transactions); - await transactionHistory.save(); - _isTransactionUpdating = false; - } catch (e) { - print(e); - _isTransactionUpdating = false; - } - } - - String getSubaddressLabel(int accountIndex, int addressIndex) { - return monero_wallet.getSubaddressLabel(accountIndex, addressIndex); - } - - List _getAllTransactions(dynamic _) => monero_transaction_history - .getAllTransations() - .map((row) => MoneroTransactionInfo.fromRow(row)) - .toList(); - - void _setListeners() { - _listener?.stop(); - _listener = monero_wallet.setListeners(_onNewBlock, _onNewTransaction); - } - - void _setInitialHeight() { - if (walletInfo.isRecovery) { - return; - } - - final currentHeight = getCurrentHeight(); - - if (currentHeight <= 1) { - final height = _getHeightByDate(walletInfo.date); - monero_wallet.setRecoveringFromSeed(isRecovery: true); - monero_wallet.setRefreshFromBlockHeight(height: height); - } - } - - int _getHeightDistance(DateTime date) { - final distance = DateTime.now().millisecondsSinceEpoch - date.millisecondsSinceEpoch; - final daysTmp = (distance / 86400).round(); - final days = daysTmp < 1 ? 1 : daysTmp; - - return days * 1000; - } - - int _getHeightByDate(DateTime date) { - final nodeHeight = monero_wallet.getNodeHeightSync(); - final heightDistance = _getHeightDistance(date); - - if (nodeHeight <= 0) { - return 0; - } - - return nodeHeight - heightDistance; - } - - void _askForUpdateBalance() { - final unlockedBalance = _getUnlockedBalance(); - final fullBalance = _getFullBalance(); - - if (balance[currency]!.fullBalance != fullBalance || - balance[currency]!.unlockedBalance != unlockedBalance) { - balance[currency] = MoneroBalance(fullBalance: fullBalance, unlockedBalance: unlockedBalance); - } - } - - Future _askForUpdateTransactionHistory() async => await updateTransactions(); - - int _getFullBalance() => monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id); - - int _getUnlockedBalance() => - monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id); - - void _onNewBlock(int height, int blocksLeft, double ptc) async { - try { - if (walletInfo.isRecovery) { - await _askForUpdateTransactionHistory(); - _askForUpdateBalance(); - walletAddresses.accountList.update(); - } - - if (blocksLeft < 100) { - await _askForUpdateTransactionHistory(); - _askForUpdateBalance(); - walletAddresses.accountList.update(); - syncStatus = SyncedSyncStatus(); - - if (!_hasSyncAfterStartup) { - _hasSyncAfterStartup = true; - await save(); - } - - if (walletInfo.isRecovery) { - await setAsRecovered(); - } - } else { - syncStatus = SyncingSyncStatus(blocksLeft, ptc); - } - } catch (e) { - print(e.toString()); - } - } - - void _onNewTransaction() async { - try { - await _askForUpdateTransactionHistory(); - _askForUpdateBalance(); - await Future.delayed(Duration(seconds: 1)); - } catch (e) { - print(e.toString()); - } + print("rename"); + throw UnimplementedError(); } } diff --git a/cw_nano/lib/nano_wallet_addresses.dart b/cw_nano/lib/nano_wallet_addresses.dart index 0ab86a511..f0ffd59db 100644 --- a/cw_nano/lib/nano_wallet_addresses.dart +++ b/cw_nano/lib/nano_wallet_addresses.dart @@ -5,25 +5,23 @@ import 'package:cw_nano/nano_account_list.dart'; import 'package:cw_core/subaddress.dart'; import 'package:mobx/mobx.dart'; -part 'monero_wallet_addresses.g.dart'; +part 'nano_wallet_addresses.g.dart'; -class NanoWalletAddresses = NanoWalletAddressesBase - with _$NanoWalletAddresses; +class NanoWalletAddresses = NanoWalletAddressesBase with _$NanoWalletAddresses; abstract class NanoWalletAddressesBase extends WalletAddresses with Store { NanoWalletAddressesBase(WalletInfo walletInfo) - : accountList = NanoAccountList(), - address = '', - super(walletInfo); + : accountList = NanoAccountList(), + address = '', + super(walletInfo); @override @observable String address; - + @observable Account? account; - NanoAccountList accountList; @override @@ -32,6 +30,11 @@ abstract class NanoWalletAddressesBase extends WalletAddresses with Store { account = accountList.accounts.first; } + @override + Future updateAddressesInBox() async { + throw UnimplementedError(); + } + bool validate() { accountList.update(); final accountListLength = accountList.accounts.length ?? 0; @@ -42,4 +45,4 @@ abstract class NanoWalletAddressesBase extends WalletAddresses with Store { 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 index 6e7b518e6..4524608fe 100644 --- a/cw_nano/lib/nano_wallet_creation_credentials.dart +++ b/cw_nano/lib/nano_wallet_creation_credentials.dart @@ -26,22 +26,22 @@ import 'package:cw_core/wallet_info.dart'; // } -class NanoNewWalletCredentials extends WalletCredentials { - NanoNewWalletCredentials({required String name, required this.language, String? password}) - : super(name: name, password: password); +// class NanoNewWalletCredentials extends WalletCredentials { +// NanoNewWalletCredentials({required String name, required this.language, String? password}) +// : super(name: name, password: password); - final String language; -} +// final String language; +// } -class NanoRestoreWalletFromSeedCredentials extends WalletCredentials { - NanoRestoreWalletFromSeedCredentials( - {required String name, required this.mnemonic, int height = 0, String? password}) - : super(name: name, password: password, height: height); +// class NanoRestoreWalletFromSeedCredentials extends WalletCredentials { +// NanoRestoreWalletFromSeedCredentials( +// {required String name, required this.mnemonic, int height = 0, String? password}) +// : super(name: name, password: password, height: height); - final String mnemonic; -} +// final String mnemonic; +// } -class NanoWalletLoadingException implements Exception { - @override - String toString() => 'Failure to load the wallet.'; -} +// class NanoWalletLoadingException implements Exception { +// @override +// String toString() => 'Failure to load the wallet.'; +// } diff --git a/cw_nano/lib/nano_wallet_keys.dart b/cw_nano/lib/nano_wallet_keys.dart new file mode 100644 index 000000000..aa569f59a --- /dev/null +++ b/cw_nano/lib/nano_wallet_keys.dart @@ -0,0 +1,11 @@ +class NanoWalletKeys { + const NanoWalletKeys({ + required this.mnemonic, + required this.privateKey, + required this.derivationType, + }); + + final String privateKey; + final String derivationType; + final String mnemonic; +} diff --git a/cw_nano/lib/nano_wallet_service.dart b/cw_nano/lib/nano_wallet_service.dart index 08044f438..c20bc385d 100644 --- a/cw_nano/lib/nano_wallet_service.dart +++ b/cw_nano/lib/nano_wallet_service.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_type.dart'; @@ -11,93 +12,152 @@ import 'package:cw_nano/nano_wallet_creation_credentials.dart'; import 'package:hive/hive.dart'; import 'package:bip39/bip39.dart' as bip39; +class NanoNewWalletCredentials extends WalletCredentials { + NanoNewWalletCredentials({required String name, String? password}) + : super(name: name, password: password); +} +class NanoRestoreWalletFromSeedCredentials extends WalletCredentials { + NanoRestoreWalletFromSeedCredentials( + {required String name, required this.mnemonic, int height = 0, String? password}) + : super(name: name, password: password, height: height); + + final String mnemonic; +} + +class NanoWalletLoadingException implements Exception { + @override + String toString() => 'Failure to load the wallet.'; +} + +class NanoRestoreWalletFromKeysCredentials extends WalletCredentials { + NanoRestoreWalletFromKeysCredentials( + {required String name, + required String password, + required this.language, + required this.address, + required this.viewKey, + required this.spendKey, + int height = 0}) + : super(name: name, password: password, height: height); + + final String language; + final String address; + final String viewKey; + final String spendKey; +} class NanoWalletService extends WalletService { + NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials> { NanoWalletService(this.walletInfoSource); final Box walletInfoSource; + static bool walletFilesExist(String path) => + !File(path).existsSync() && !File('$path.keys').existsSync(); + @override - Future create(NanoNewWalletCredentials credentials) async { + WalletType getType() => WalletType.nano; + + @override + Future create(NanoNewWalletCredentials credentials) async { + print("nano_wallet_service create"); final mnemonic = bip39.generateMnemonic(); + print("gened"); final wallet = NanoWallet( walletInfo: credentials.walletInfo!, mnemonic: mnemonic, password: credentials.password!, ); - - await wallet.init(); - await wallet.save(); - + print("nano_wallet created"); + // await wallet.init(); + // await wallet.save(); + throw Exception("stop"); return wallet; } @override - WalletType getType() => WalletType.ethereum; + Future remove(String wallet) async { + final path = await pathForWalletDir(name: wallet, type: getType()); + final file = Directory(path); + final isExist = file.existsSync(); - @override - Future isWalletExit(String name) async => - File(await pathForWallet(name: name, type: getType())).existsSync(); + if (isExist) { + await file.delete(recursive: true); + } - @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; + final walletInfo = walletInfoSource.values + .firstWhere((info) => info.id == WalletBase.idFor(wallet, getType())); + await walletInfoSource.delete(walletInfo.key); } @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); + // final currentWalletInfo = walletInfoSource.values + // .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType())); + // final currentWallet = NanoWallet(walletInfo: currentWalletInfo); - await currentWallet.renameWalletFiles(newName); + // await currentWallet.renameWalletFiles(newName); - final newWalletInfo = currentWalletInfo; - newWalletInfo.id = WalletBase.idFor(newName, getType()); - newWalletInfo.name = newName; + // final newWalletInfo = currentWalletInfo; + // newWalletInfo.id = WalletBase.idFor(newName, getType()); + // newWalletInfo.name = newName; - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo); + // await walletInfoSource.put(currentWalletInfo.key, newWalletInfo); + } + + @override + Future restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials) async { + throw UnimplementedError(); + // try { + // final path = await pathForWallet(name: credentials.name, type: getType()); + // await monero_wallet_manager.restoreFromKeys( + // path: path, + // password: credentials.password!, + // language: credentials.language, + // restoreHeight: credentials.height!, + // address: credentials.address, + // viewKey: credentials.viewKey, + // spendKey: credentials.spendKey); + // final wallet = NanoWallet(walletInfo: credentials.walletInfo!); + // await wallet.init(); + + // return wallet; + // } catch (e) { + // // TODO: Implement Exception for wallet list service. + // print('NanoWalletsManager Error: $e'); + // rethrow; + // } + } + + @override + Future restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials) async { + throw UnimplementedError(); + // try { + // final path = await pathForWallet(name: credentials.name, type: getType()); + // await monero_wallet_manager.restoreFromSeed( + // path: path, + // password: credentials.password!, + // seed: credentials.mnemonic, + // restoreHeight: credentials.height!); + // final wallet = NanoWallet(walletInfo: credentials.walletInfo!); + // await wallet.init(); + + // return wallet; + // } catch (e) { + // // TODO: Implement Exception for wallet list service. + // print('NanoWalletsManager Error: $e'); + // rethrow; + // } + } + + @override + Future isWalletExit(String s) async { + throw UnimplementedError(); + } + + @override + Future openWallet(String s, String s2) async { + throw UnimplementedError(); } } diff --git a/lib/nano/cw_nano.dart b/lib/nano/cw_nano.dart index bcd2bf07b..59c00bb03 100644 --- a/lib/nano/cw_nano.dart +++ b/lib/nano/cw_nano.dart @@ -13,8 +13,8 @@ class CWNano extends Nano { @override WalletService createNanoWalletService(Box walletInfoSource) { - // return NanoWalletService(walletInfoSource); - throw UnimplementedError(); + return NanoWalletService(walletInfoSource); + // throw UnimplementedError(); } NanoWalletDetails getNanoWalletDetails(Object wallet) { @@ -25,23 +25,14 @@ class CWNano extends Nano { throw UnimplementedError(); } - // @override - // WalletCredentials createNanoNewWalletCredentials({ - // required String name, - // WalletInfo? walletInfo, - // }) => - // NanoNewWalletCredentials(name: name, walletInfo: walletInfo); - @override WalletCredentials createNanoNewWalletCredentials({ required String name, - required String language, String? password, }) { return NanoNewWalletCredentials( name: name, password: password, - language: language, ); } diff --git a/lib/nano/nano.dart b/lib/nano/nano.dart index 5b756f094..37bfabd4f 100644 --- a/lib/nano/nano.dart +++ b/lib/nano/nano.dart @@ -1,3 +1,4 @@ +import 'package:cw_nano/nano_wallet_service.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; @@ -43,7 +44,6 @@ abstract class Nano { WalletCredentials createNanoNewWalletCredentials({ required String name, - required String language, String password, }); diff --git a/tool/configure.dart b/tool/configure.dart index 9aa6d2795..97823156f 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -602,7 +602,6 @@ abstract class Nano { WalletCredentials createNanoNewWalletCredentials({ required String name, - required String language, String password, });