This commit is contained in:
fosse 2023-07-25 11:21:49 -04:00
parent ae0727ad33
commit c6797cc5ef
11 changed files with 776 additions and 444 deletions

View file

@ -118,7 +118,7 @@ String walletTypeToDisplayName(WalletType type) {
return 'Ethereum (ETH)'; return 'Ethereum (ETH)';
case WalletType.nano: case WalletType.nano:
return 'Nano (XNO)'; return 'Nano (XNO)';
case WalletType.ethereum: case WalletType.banano:
return 'Banano (BAN)'; return 'Banano (BAN)';
default: default:
return ''; return '';

View file

@ -22,7 +22,7 @@ abstract class NanoTransactionHistoryBase
transactions[transaction.id] = transaction; transactions[transaction.id] = transaction;
@override @override
void addMany(Map<String, MoneroTransactionInfo> transactions) => void addMany(Map<String, NanoTransactionInfo> transactions) =>
this.transactions.addAll(transactions); this.transactions.addAll(transactions);
} }

View file

@ -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<String, Object?> 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 = <String, dynamic>{
// '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 = <String, dynamic>{
// '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';
}

View file

@ -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<NanoBalance, NanoTransactionHistory, NanoTransactionInfo> with Store {
// NanoWalletBase({
// required WalletInfo walletInfo,
// required String mnemonic,
// required String password,
// }) : balance = ObservableMap<CryptoCurrency, NanoBalance>.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<CryptoCurrency, MoneroBalance>.of(<CryptoCurrency, MoneroBalance>{
// // 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<CryptoCurrency, NanoBalance> 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<void> init() async {
// // await walletAddresses.init();
// // balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(<CryptoCurrency, MoneroBalance>{
// // 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<void>? updateBalance() => null;
// @override
// void close() {
// // _listener?.stop();
// // _onAccountChangeReaction?.reaction.dispose();
// // _autoSaveTimer?.cancel();
// }
// @override
// Future<void> 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<void> startSync() async {
// // try {
// // _setInitialHeight();
// // } catch (_) {}
// // try {
// // syncStatus = AttemptingSyncStatus();
// // monero_wallet.startRefresh();
// // _setListeners();
// // _listener?.start();
// // } catch (e) {
// // syncStatus = FailedSyncStatus();
// // print(e);
// // rethrow;
// // }
// }
// @override
// Future<PendingTransaction> 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<void> save() async {
// // await walletAddresses.updateAddressesInBox();
// // await backupWalletFiles(name);
// // await monero_wallet.store();
// }
// @override
// Future<void> 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<void> changePassword(String password) async {
// // monero_wallet.setPasswordSync(password);
// }
// // Future<int> getNodeHeight() async => nano_wallet.getNodeHeight();
// // Future<bool> isConnected() async => nano_wallet.isConnected();
// Future<void> setAsRecovered() async {
// walletInfo.isRecovery = false;
// await walletInfo.save();
// }
// @override
// Future<void> 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<Map<String, NanoTransactionInfo>> fetchTransactions() async {
// throw UnimplementedError();
// // monero_transaction_history.refreshTransactions();
// // return _getAllTransactions(null)
// // .fold<Map<String, MoneroTransactionInfo>>(<String, MoneroTransactionInfo>{},
// // (Map<String, MoneroTransactionInfo> acc, MoneroTransactionInfo tx) {
// // acc[tx.id] = tx;
// // return acc;
// // });
// }
// Future<void> 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<MoneroTransactionInfo> _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<void> _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<void>.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:async';
import 'dart:io'; import 'dart:io';
import 'package:cw_core/pathForWallet.dart'; import 'package:cw_nano/nano_wallet_addresses.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_nano/nano_wallet_keys.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_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/sync_status.dart'; import 'package:web3dart/web3dart.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';
part 'nano_wallet.g.dart'; part 'nano_wallet.g.dart';
const moneroBlockSize = 1000;
class NanoWallet = NanoWalletBase with _$NanoWallet; class NanoWallet = NanoWalletBase with _$NanoWallet;
abstract class NanoWalletBase abstract class NanoWalletBase
extends WalletBase<NanoBalance, NanoTransactionHistory, NanoTransactionInfo> with Store { extends WalletBase<NanoBalance, NanoTransactionHistory, NanoTransactionInfo> with Store {
NanoWalletBase({required WalletInfo walletInfo}) NanoWalletBase({
: balance = ObservableMap<CryptoCurrency, NanoBalance>.of({ required WalletInfo walletInfo,
CryptoCurrency.nano: NanoBalance( required String mnemonic,
currentBalance: nano_wallet.getFullBalance(accountIndex: 0), required String password,
receivableBalance: nano_wallet.getFullBalance(accountIndex: 0)) NanoBalance? initialBalance,
}), }) : syncStatus = NotConnectedSyncStatus(),
_password = password,
_mnemonic = mnemonic,
_isTransactionUpdating = false, _isTransactionUpdating = false,
_hasSyncAfterStartup = false, _priorityFees = [],
walletAddresses = NanoWalletAddresses(walletInfo), walletAddresses = NanoWalletAddresses(walletInfo),
syncStatus = NotConnectedSyncStatus(), balance = ObservableMap<CryptoCurrency, NanoBalance>.of({
CryptoCurrency.eth: initialBalance ??
NanoBalance(currentBalance: BigInt.zero, receivableBalance: BigInt.zero)
}),
super(walletInfo) { super(walletInfo) {
transactionHistory = MoneroTransactionHistory(); print("@@@@@ initializing nano wallet @@@@@");
_onAccountChangeReaction = reaction((_) => walletAddresses.account, (Account? account) { this.walletInfo = walletInfo;
if (account == null) { transactionHistory = NanoTransactionHistory();
return;
} }
balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(<CryptoCurrency, MoneroBalance>{ final String _mnemonic;
currency: MoneroBalance( final String _password;
fullBalance: monero_wallet.getFullBalance(accountIndex: account.id),
unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: account.id))
});
walletAddresses.updateSubaddressList(accountIndex: account.id);
});
}
static const int _autoSaveInterval = 30; List<int> _priorityFees;
int? _gasPrice;
bool _isTransactionUpdating;
@override @override
NanoWalletAddresses walletAddresses; WalletAddresses walletAddresses;
@override @override
@observable @observable
@ -62,324 +405,170 @@ abstract class NanoWalletBase
@override @override
@observable @observable
ObservableMap<CryptoCurrency, MoneroBalance> balance; late ObservableMap<CryptoCurrency, NanoBalance> 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;
Future<void> init() async { Future<void> init() async {
await walletAddresses.init(); // erc20TokensBox = await Hive.openBox<Erc20Token>(Erc20Token.boxName);
balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(<CryptoCurrency, MoneroBalance>{ // await walletAddresses.init();
currency: MoneroBalance( // await transactionHistory.init();
fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id), // _privateKey = await getPrivateKey(_mnemonic, _password);
unlockedBalance: // walletAddresses.address = _privateKey.address.toString();
monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id)) // await save();
});
_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<void>? updateBalance() => null;
@override
void close() {
_listener?.stop();
_onAccountChangeReaction?.reaction.dispose();
_autoSaveTimer?.cancel();
}
@override
Future<void> 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<void> startSync() async {
try {
_setInitialHeight();
} catch (_) {}
try {
syncStatus = AttemptingSyncStatus();
monero_wallet.startRefresh();
_setListeners();
_listener?.start();
} catch (e) {
syncStatus = FailedSyncStatus();
print(e);
rethrow;
}
}
@override
Future<PendingTransaction> createTransaction(Object credentials) async {
// final _credentials = credentials as MoneroTransactionCreationCredentials;
// return null;
throw UnimplementedError();
} }
@override @override
int calculateEstimatedFee(TransactionPriority priority, int? amount) { int calculateEstimatedFee(TransactionPriority priority, int? amount) {
// FIXME: hardcoded value;
return 0; return 0;
} }
@override
Future<void> changePassword(String password) {
throw UnimplementedError("changePassword");
}
@override
void close() {
// _client.stop();
}
@action
@override
Future<void> connectToNode({required Node node}) async {
throw UnimplementedError();
}
@override
Future<PendingTransaction> createTransaction(Object credentials) async {
throw UnimplementedError();
}
Future<void> updateTransactions() async {
throw UnimplementedError();
}
@override
Future<Map<String, NanoTransactionInfo>> fetchTransactions() async {
throw UnimplementedError();
// final address = _privateKey.address.hex;
// final transactions = await _client.fetchTransactions(address);
// final List<Future<List<EthereumTransactionModel>>> 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<String, EthereumTransactionInfo> 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<void> rescan({required int height}) {
throw UnimplementedError("rescan");
}
@override @override
Future<void> save() async { Future<void> save() async {
await walletAddresses.updateAddressesInBox(); throw UnimplementedError();
await backupWalletFiles(name); }
await monero_wallet.store();
@override
String get seed => _mnemonic;
@action
@override
Future<void> startSync() async {
throw UnimplementedError();
}
int feeRate(TransactionPriority priority) {
throw UnimplementedError();
}
// Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
// String toJSON() => json.encode({
// 'mnemonic': _mnemonic,
// 'balance': balance[currency]!.toJSON(),
// });
static Future<NanoWallet> open({
required String name,
required String password,
required WalletInfo walletInfo,
}) async {
throw UnimplementedError();
}
Future<void> _updateBalance() async {
// balance[currency] = await _fetchEthBalance();
// await _fetchErc20Balances();
await save();
}
Future<void> _fetchErc20Balances() async {
throw UnimplementedError();
}
Future<EthPrivateKey> getPrivateKey(String mnemonic, String password) async {
throw UnimplementedError();
}
Future<void>? updateBalance() async => await _updateBalance();
Future<void> addErc20Token(Erc20Token token) async {
throw UnimplementedError();
}
Future<void> deleteErc20Token(Erc20Token token) async {
throw UnimplementedError();
}
void _onNewTransaction(FilterEvent event) {
throw UnimplementedError();
}
void addInitialTokens() {
throw UnimplementedError();
} }
@override @override
Future<void> renameWalletFiles(String newWalletName) async { Future<void> renameWalletFiles(String newWalletName) async {
final currentWalletDirPath = await pathForWalletDir(name: name, type: type); print("rename");
throw UnimplementedError();
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<void> changePassword(String password) async {
monero_wallet.setPasswordSync(password);
}
Future<int> getNodeHeight() async => monero_wallet.getNodeHeight();
Future<bool> isConnected() async => monero_wallet.isConnected();
Future<void> setAsRecovered() async {
walletInfo.isRecovery = false;
await walletInfo.save();
}
@override
Future<void> 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<Map<String, MoneroTransactionInfo>> fetchTransactions() async {
monero_transaction_history.refreshTransactions();
return _getAllTransactions(null)
.fold<Map<String, MoneroTransactionInfo>>(<String, MoneroTransactionInfo>{},
(Map<String, MoneroTransactionInfo> acc, MoneroTransactionInfo tx) {
acc[tx.id] = tx;
return acc;
});
}
Future<void> 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<MoneroTransactionInfo> _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<void> _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<void>.delayed(Duration(seconds: 1));
} catch (e) {
print(e.toString());
}
} }
} }

View file

@ -5,10 +5,9 @@ import 'package:cw_nano/nano_account_list.dart';
import 'package:cw_core/subaddress.dart'; import 'package:cw_core/subaddress.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
part 'monero_wallet_addresses.g.dart'; part 'nano_wallet_addresses.g.dart';
class NanoWalletAddresses = NanoWalletAddressesBase class NanoWalletAddresses = NanoWalletAddressesBase with _$NanoWalletAddresses;
with _$NanoWalletAddresses;
abstract class NanoWalletAddressesBase extends WalletAddresses with Store { abstract class NanoWalletAddressesBase extends WalletAddresses with Store {
NanoWalletAddressesBase(WalletInfo walletInfo) NanoWalletAddressesBase(WalletInfo walletInfo)
@ -23,7 +22,6 @@ abstract class NanoWalletAddressesBase extends WalletAddresses with Store {
@observable @observable
Account? account; Account? account;
NanoAccountList accountList; NanoAccountList accountList;
@override @override
@ -32,6 +30,11 @@ abstract class NanoWalletAddressesBase extends WalletAddresses with Store {
account = accountList.accounts.first; account = accountList.accounts.first;
} }
@override
Future<void> updateAddressesInBox() async {
throw UnimplementedError();
}
bool validate() { bool validate() {
accountList.update(); accountList.update();
final accountListLength = accountList.accounts.length ?? 0; final accountListLength = accountList.accounts.length ?? 0;

View file

@ -26,22 +26,22 @@ import 'package:cw_core/wallet_info.dart';
// } // }
class NanoNewWalletCredentials extends WalletCredentials { // class NanoNewWalletCredentials extends WalletCredentials {
NanoNewWalletCredentials({required String name, required this.language, String? password}) // NanoNewWalletCredentials({required String name, required this.language, String? password})
: super(name: name, password: password); // : super(name: name, password: password);
final String language; // final String language;
} // }
class NanoRestoreWalletFromSeedCredentials extends WalletCredentials { // class NanoRestoreWalletFromSeedCredentials extends WalletCredentials {
NanoRestoreWalletFromSeedCredentials( // NanoRestoreWalletFromSeedCredentials(
{required String name, required this.mnemonic, int height = 0, String? password}) // {required String name, required this.mnemonic, int height = 0, String? password})
: super(name: name, password: password, height: height); // : super(name: name, password: password, height: height);
final String mnemonic; // final String mnemonic;
} // }
class NanoWalletLoadingException implements Exception { // class NanoWalletLoadingException implements Exception {
@override // @override
String toString() => 'Failure to load the wallet.'; // String toString() => 'Failure to load the wallet.';
} // }

View file

@ -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;
}

View file

@ -2,6 +2,7 @@ import 'dart:io';
import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_base.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_info.dart';
import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.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:hive/hive.dart';
import 'package:bip39/bip39.dart' as bip39; 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<NanoNewWalletCredentials, class NanoWalletService extends WalletService<NanoNewWalletCredentials,
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromWIFCredentials> { NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials> {
NanoWalletService(this.walletInfoSource); NanoWalletService(this.walletInfoSource);
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
static bool walletFilesExist(String path) =>
!File(path).existsSync() && !File('$path.keys').existsSync();
@override @override
Future<NanoWallet> create(NanoNewWalletCredentials credentials) async { WalletType getType() => WalletType.nano;
@override
Future<WalletBase> create(NanoNewWalletCredentials credentials) async {
print("nano_wallet_service create");
final mnemonic = bip39.generateMnemonic(); final mnemonic = bip39.generateMnemonic();
print("gened");
final wallet = NanoWallet( final wallet = NanoWallet(
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
mnemonic: mnemonic, mnemonic: mnemonic,
password: credentials.password!, password: credentials.password!,
); );
print("nano_wallet created");
await wallet.init(); // await wallet.init();
await wallet.save(); // await wallet.save();
throw Exception("stop");
return wallet; return wallet;
} }
@override @override
WalletType getType() => WalletType.ethereum; Future<void> remove(String wallet) async {
final path = await pathForWalletDir(name: wallet, type: getType());
final file = Directory(path);
final isExist = file.existsSync();
@override if (isExist) {
Future<bool> isWalletExit(String name) async => await file.delete(recursive: true);
File(await pathForWallet(name: name, type: getType())).existsSync();
@override
Future<NanoWallet> 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 final walletInfo = walletInfoSource.values
Future<void> remove(String wallet) async => .firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true); await walletInfoSource.delete(walletInfo.key);
@override
Future<NanoWallet> restoreFromKeys(credentials) {
throw UnimplementedError();
}
@override
Future<NanoWallet> 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 @override
Future<void> rename(String currentName, String password, String newName) async { Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values // final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType())); // .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = await NanoWalletBase.open( // final currentWallet = NanoWallet(walletInfo: currentWalletInfo);
password: password, name: currentName, walletInfo: currentWalletInfo);
await currentWallet.renameWalletFiles(newName); // await currentWallet.renameWalletFiles(newName);
final newWalletInfo = currentWalletInfo; // final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType()); // newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName; // newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo); // await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
}
@override
Future<NanoWallet> 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<NanoWallet> 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<bool> isWalletExit(String s) async {
throw UnimplementedError();
}
@override
Future<WalletBase> openWallet(String s, String s2) async {
throw UnimplementedError();
} }
} }

View file

@ -13,8 +13,8 @@ class CWNano extends Nano {
@override @override
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource) { WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource) {
// return NanoWalletService(walletInfoSource); return NanoWalletService(walletInfoSource);
throw UnimplementedError(); // throw UnimplementedError();
} }
NanoWalletDetails getNanoWalletDetails(Object wallet) { NanoWalletDetails getNanoWalletDetails(Object wallet) {
@ -25,23 +25,14 @@ class CWNano extends Nano {
throw UnimplementedError(); throw UnimplementedError();
} }
// @override
// WalletCredentials createNanoNewWalletCredentials({
// required String name,
// WalletInfo? walletInfo,
// }) =>
// NanoNewWalletCredentials(name: name, walletInfo: walletInfo);
@override @override
WalletCredentials createNanoNewWalletCredentials({ WalletCredentials createNanoNewWalletCredentials({
required String name, required String name,
required String language,
String? password, String? password,
}) { }) {
return NanoNewWalletCredentials( return NanoNewWalletCredentials(
name: name, name: name,
password: password, password: password,
language: language,
); );
} }

View file

@ -1,3 +1,4 @@
import 'package:cw_nano/nano_wallet_service.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
@ -43,7 +44,6 @@ abstract class Nano {
WalletCredentials createNanoNewWalletCredentials({ WalletCredentials createNanoNewWalletCredentials({
required String name, required String name,
required String language,
String password, String password,
}); });

View file

@ -602,7 +602,6 @@ abstract class Nano {
WalletCredentials createNanoNewWalletCredentials({ WalletCredentials createNanoNewWalletCredentials({
required String name, required String name,
required String language,
String password, String password,
}); });