diff --git a/cw_nano/lib/nano_util.dart b/cw_nano/lib/nano_util.dart index eb5953e5d..3cb66afaf 100644 --- a/cw_nano/lib/nano_util.dart +++ b/cw_nano/lib/nano_util.dart @@ -23,6 +23,10 @@ class NanoUtil { NanoAccountType.NANO, privateKeyToPublic(seedToPrivate(seed, index))); } + static String seedToMnemonic(String seed) { + return NanoMnemomics.seedToMnemonic(seed).join(" "); + } + // static String createPublicKey(String privateKey) { // return NanoHelpers.byteToHex(Ed25519Blake2b.getPubkey(NanoHelpers.hexToBytes(privateKey))!); // } @@ -207,5 +211,4 @@ class NanoUtil { final Decimal rawDecimal = Decimal.parse(rawPerCur.toString()); return (asDecimal * rawDecimal).toString(); } - } diff --git a/cw_nano/lib/nano_wallet.dart b/cw_nano/lib/nano_wallet.dart index 1403bb484..4ff372138 100644 --- a/cw_nano/lib/nano_wallet.dart +++ b/cw_nano/lib/nano_wallet.dart @@ -59,9 +59,10 @@ abstract class NanoWalletBase final String _password; final DerivationType _derivationType; - late final String _privateKey; - late final String _publicAddress; - late final String _seedKey; + String? _privateKey; + String? _publicAddress; + String? _seedKey; + String? _representativeAddress; Timer? _receiveTimer; @@ -82,11 +83,10 @@ abstract class NanoWalletBase // initialize the different forms of private / public key we'll need: Future init() async { final String type = (_derivationType == DerivationType.nano) ? "standard" : "hd"; - _seedKey = bip39.mnemonicToEntropy(_mnemonic).toUpperCase(); - _privateKey = await NanoUtil.uniSeedToPrivate(_seedKey, 0, type); - _publicAddress = await NanoUtil.uniSeedToAddress(_seedKey, 0, type); - this.walletInfo.address = _publicAddress; + _privateKey = await NanoUtil.uniSeedToPrivate(_seedKey!, 0, type); + _publicAddress = await NanoUtil.uniSeedToAddress(_seedKey!, 0, type); + this.walletInfo.address = _publicAddress!; await walletAddresses.init(); await transactionHistory.init(); @@ -158,7 +158,7 @@ abstract class NanoWalletBase final block = await _client.constructSendBlock( amountRaw: amt.toString(), destinationAddress: txOut.address, - privateKey: _privateKey, + privateKey: _privateKey!, balanceAfterTx: runningBalance, previousHash: previousHash, ); @@ -195,8 +195,8 @@ abstract class NanoWalletBase Future _receiveAll() async { await _updateBalance(); int blocksReceived = await this._client.confirmAllReceivable( - destinationAddress: _publicAddress, - privateKey: _privateKey, + destinationAddress: _publicAddress!, + privateKey: _privateKey!, ); if (blocksReceived > 0) { @@ -224,7 +224,7 @@ abstract class NanoWalletBase @override Future> fetchTransactions() async { - String address = _publicAddress; + String address = _publicAddress!; final transactions = await _client.fetchTransactions(address); @@ -249,7 +249,7 @@ abstract class NanoWalletBase @override NanoWalletKeys get keys { - return NanoWalletKeys(seedKey: _seedKey); + return NanoWalletKeys(seedKey: _seedKey!); } @override @@ -269,7 +269,7 @@ abstract class NanoWalletBase @override String get seed => _mnemonic; - + String get representative => _representativeAddress ?? ""; @action @@ -303,7 +303,8 @@ abstract class NanoWalletBase String toJSON() => json.encode({ 'seedKey': _seedKey, 'mnemonic': _mnemonic, - // 'balance': balance[currency]!.toJSON(), + 'currentBalance': balance[currency]?.currentBalance.toString() ?? "0", + 'receivableBalance': balance[currency]?.receivableBalance.toString() ?? "0", 'derivationType': _derivationType.toString() }); @@ -314,11 +315,12 @@ abstract class NanoWalletBase }) async { final path = await pathForWallet(name: name, type: walletInfo.type); final jsonSource = await read(path: path, password: password); + final data = json.decode(jsonSource) as Map; final mnemonic = data['mnemonic'] as String; final balance = NanoBalance.fromString( - formattedCurrentBalance: data['balance'] as String? ?? "0", - formattedReceivableBalance: "0"); + formattedCurrentBalance: data['currentBalance'] as String? ?? "0", + formattedReceivableBalance: data['receivableBalance'] as String? ?? "0"); DerivationType derivationType = DerivationType.bip39; if (data['derivationType'] == "DerivationType.nano") { @@ -336,16 +338,17 @@ abstract class NanoWalletBase mnemonic: mnemonic, initialBalance: balance, ); + // init() should always be run after this! } Future _updateBalance() async { - balance[currency] = await _client.getBalance(_publicAddress); + balance[currency] = await _client.getBalance(_publicAddress!); await save(); } Future _updateRep() async { try { - final accountInfo = await _client.getAccountInfo(_publicAddress); + final accountInfo = await _client.getAccountInfo(_publicAddress!); _representativeAddress = accountInfo["representative"] as String; } catch (e) { throw Exception("Failed to get representative address $e"); @@ -355,9 +358,9 @@ abstract class NanoWalletBase Future changeRep(String address) async { try { final String hash = await _client.changeRep( - privateKey: _privateKey, + privateKey: _privateKey!, repAddress: address, - ourAddress: _publicAddress, + ourAddress: _publicAddress!, ); if (hash.isNotEmpty) { _representativeAddress = address; diff --git a/cw_nano/lib/nano_wallet_service.dart b/cw_nano/lib/nano_wallet_service.dart index fca76bc1c..5b550387c 100644 --- a/cw_nano/lib/nano_wallet_service.dart +++ b/cw_nano/lib/nano_wallet_service.dart @@ -9,12 +9,13 @@ import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cw_nano/nano_balance.dart'; import 'package:cw_nano/nano_client.dart'; -import 'package:cw_nano/nano_mnemonic.dart'; +import 'package:cw_nano/nano_mnemonic.dart' as nm; import 'package:cw_nano/nano_util.dart'; import 'package:cw_nano/nano_wallet.dart'; import 'package:cw_nano/nano_wallet_info.dart'; import 'package:hive/hive.dart'; import 'package:bip39/bip39.dart' as bip39; +import 'package:nanodart/nanodart.dart'; class NanoNewWalletCredentials extends WalletCredentials { NanoNewWalletCredentials({required String name, String? password}) @@ -65,10 +66,18 @@ class NanoWalletService extends WalletService create(NanoNewWalletCredentials credentials) async { - final mnemonic = bip39.generateMnemonic(); + // nano standard: + DerivationType derivationType = DerivationType.nano; + String seedKey = NanoSeeds.generateSeed(); + String mnemonic = NanoUtil.seedToMnemonic(seedKey); + + // bip39: + // derivationType derivationType = DerivationType.bip39; + // String mnemonic = bip39.generateMnemonic(); + final nanoWalletInfo = NanoWalletInfo( walletInfo: credentials.walletInfo!, - derivationType: DerivationType.nano, + derivationType: derivationType, ); final wallet = NanoWallet( @@ -76,6 +85,7 @@ class NanoWalletService extends WalletService restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials) async { if (!bip39.validateMnemonic(credentials.mnemonic)) { - throw NanoMnemonicIsIncorrectException(); + throw nm.NanoMnemonicIsIncorrectException(); } if (!NanoMnemomics.validateMnemonic(credentials.mnemonic.split(' '))) { - throw NanoMnemonicIsIncorrectException(); + throw nm.NanoMnemonicIsIncorrectException(); } DerivationType derivationType = credentials.derivationType ?? diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index dc8f9dc65..c2de02646 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -44,42 +44,48 @@ part 'exchange_view_model.g.dart'; class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel; abstract class ExchangeViewModelBase with Store { - ExchangeViewModelBase(this.wallet, this.trades, this._exchangeTemplateStore, - this.tradesStore, this._settingsStore, this.sharedPreferences) - : _cryptoNumberFormat = NumberFormat(), - isFixedRateMode = false, - isReceiveAmountEntered = false, - depositAmount = '', - receiveAmount = '', - receiveAddress = '', - depositAddress = '', - isDepositAddressEnabled = false, - isReceiveAddressEnabled = false, - isReceiveAmountEditable = false, - _useTorOnly = false, - receiveCurrencies = [], - depositCurrencies = [], - limits = Limits(min: 0, max: 0), - tradeState = ExchangeTradeStateInitial(), - limitsState = LimitsInitialState(), - receiveCurrency = wallet.currency, - depositCurrency = wallet.currency, - providerList = [], - selectedProviders = ObservableList() { + ExchangeViewModelBase(this.wallet, this.trades, this._exchangeTemplateStore, this.tradesStore, + this._settingsStore, this.sharedPreferences) + : _cryptoNumberFormat = NumberFormat(), + isFixedRateMode = false, + isReceiveAmountEntered = false, + depositAmount = '', + receiveAmount = '', + receiveAddress = '', + depositAddress = '', + isDepositAddressEnabled = false, + isReceiveAddressEnabled = false, + isReceiveAmountEditable = false, + _useTorOnly = false, + receiveCurrencies = [], + depositCurrencies = [], + limits = Limits(min: 0, max: 0), + tradeState = ExchangeTradeStateInitial(), + limitsState = LimitsInitialState(), + receiveCurrency = wallet.currency, + depositCurrency = wallet.currency, + providerList = [], + selectedProviders = ObservableList() { _useTorOnly = _settingsStore.exchangeStatus == ExchangeApiMode.torOnly; _setProviders(); const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano]; - const excludeReceiveCurrencies = [CryptoCurrency.xlm, CryptoCurrency.xrp, - CryptoCurrency.bnb, CryptoCurrency.btt, CryptoCurrency.nano]; + const excludeReceiveCurrencies = [ + CryptoCurrency.xlm, + CryptoCurrency.xrp, + CryptoCurrency.bnb, + CryptoCurrency.btt, + CryptoCurrency.nano + ]; _initialPairBasedOnWallet(); - final Map exchangeProvidersSelection = json - .decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}") as Map; + final Map exchangeProvidersSelection = + json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}") + as Map; /// if the provider is not in the user settings (user's first time or newly added provider) /// then use its default value decided by us - selectedProviders = ObservableList.of(providersForCurrentPair().where( - (element) => exchangeProvidersSelection[element.title] == null + selectedProviders = ObservableList.of(providersForCurrentPair() + .where((element) => exchangeProvidersSelection[element.title] == null ? element.isEnabled : (exchangeProvidersSelection[element.title] as bool)) .toList()); @@ -94,33 +100,29 @@ abstract class ExchangeViewModelBase with Store { depositAmount = ''; receiveAmount = ''; receiveAddress = ''; - depositAddress = depositCurrency == wallet.currency - ? wallet.walletAddresses.address : ''; + depositAddress = depositCurrency == wallet.currency ? wallet.walletAddresses.address : ''; provider = providersForCurrentPair().first; final initialProvider = provider; provider!.checkIsAvailable().then((bool isAvailable) { if (!isAvailable && provider == initialProvider) { - provider = providerList.firstWhere( - (provider) => provider is ChangeNowExchangeProvider, + provider = providerList.firstWhere((provider) => provider is ChangeNowExchangeProvider, orElse: () => providerList.last); _onPairChange(); } }); receiveCurrencies = CryptoCurrency.all - .where((cryptoCurrency) => !excludeReceiveCurrencies.contains(cryptoCurrency)) - .toList(); + .where((cryptoCurrency) => !excludeReceiveCurrencies.contains(cryptoCurrency)) + .toList(); depositCurrencies = CryptoCurrency.all - .where((cryptoCurrency) => !excludeDepositCurrencies.contains(cryptoCurrency)) - .toList(); + .where((cryptoCurrency) => !excludeDepositCurrencies.contains(cryptoCurrency)) + .toList(); _defineIsReceiveAmountEditable(); loadLimits(); - reaction( - (_) => isFixedRateMode, - (Object _) { - loadLimits(); - _bestRate = 0; - _calculateBestRate(); - }); + reaction((_) => isFixedRateMode, (Object _) { + loadLimits(); + _bestRate = 0; + _calculateBestRate(); + }); } bool _useTorOnly; final WalletBase wallet; @@ -148,7 +150,7 @@ abstract class ExchangeViewModelBase with Store { /// initialize with descending comparator /// since we want largest rate first final SplayTreeMap _sortedAvailableProviders = - SplayTreeMap((double a, double b) => b.compareTo(a)); + SplayTreeMap((double a, double b) => b.compareTo(a)); final List _tradeAvailableProviders = []; @@ -204,9 +206,7 @@ abstract class ExchangeViewModelBase with Store { SyncStatus get status => wallet.syncStatus; @computed - ObservableList get templates => - _exchangeTemplateStore.templates; - + ObservableList get templates => _exchangeTemplateStore.templates; @computed TransactionPriority get transactionPriority { @@ -219,13 +219,13 @@ abstract class ExchangeViewModelBase with Store { return priority; } - bool get hasAllAmount => - (wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) && depositCurrency == wallet.currency; + (wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) && + depositCurrency == wallet.currency; - bool get isMoneroWallet => wallet.type == WalletType.monero; + bool get isMoneroWallet => wallet.type == WalletType.monero; - bool get isLowFee { + bool get isLowFee { switch (wallet.type) { case WalletType.monero: case WalletType.haven: @@ -340,20 +340,19 @@ abstract class ExchangeViewModelBase with Store { final amount = double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1; final _providers = _tradeAvailableProviders - .where((element) => !isFixedRateMode || element.supportsFixedRate).toList(); + .where((element) => !isFixedRateMode || element.supportsFixedRate) + .toList(); - final result = await Future.wait( - _providers.map((element) => element.fetchRate( - from: depositCurrency, - to: receiveCurrency, - amount: amount, - isFixedRateMode: isFixedRateMode, - isReceiveAmount: isFixedRateMode)) - ); + final result = await Future.wait(_providers.map((element) => element.fetchRate( + from: depositCurrency, + to: receiveCurrency, + amount: amount, + isFixedRateMode: isFixedRateMode, + isReceiveAmount: isFixedRateMode))); _sortedAvailableProviders.clear(); - for (int i=0;i _providersForPair( {required CryptoCurrency from, required CryptoCurrency to}) { final providers = providerList - .where((provider) => provider.pairList - .where((pair) => - pair.from == from && pair.to == to) - .isNotEmpty) + .where((provider) => + provider.pairList.where((pair) => pair.from == from && pair.to == to).isNotEmpty) .toList(); return providers; @@ -651,6 +641,10 @@ abstract class ExchangeViewModelBase with Store { depositCurrency = CryptoCurrency.eth; receiveCurrency = CryptoCurrency.xmr; break; + case WalletType.nano: + depositCurrency = CryptoCurrency.nano; + receiveCurrency = CryptoCurrency.xmr; + break; default: break; } @@ -694,8 +688,9 @@ abstract class ExchangeViewModelBase with Store { _bestRate = 0; _calculateBestRate(); - final Map exchangeProvidersSelection = json - .decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}") as Map; + final Map exchangeProvidersSelection = + json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}") + as Map; for (var provider in providerList) { exchangeProvidersSelection[provider.title] = selectedProviders.contains(provider); @@ -709,15 +704,15 @@ abstract class ExchangeViewModelBase with Store { bool get isAvailableInSelected { final providersForPair = providersForCurrentPair(); - return selectedProviders.any((element) => element.isAvailable && providersForPair.contains(element)); + return selectedProviders + .any((element) => element.isAvailable && providersForPair.contains(element)); } void _setAvailableProviders() { _tradeAvailableProviders.clear(); _tradeAvailableProviders.addAll( - selectedProviders - .where((provider) => providersForCurrentPair().contains(provider))); + selectedProviders.where((provider) => providersForCurrentPair().contains(provider))); } @action @@ -738,7 +733,7 @@ abstract class ExchangeViewModelBase with Store { } } - void _setProviders(){ + void _setProviders() { if (_settingsStore.exchangeStatus == ExchangeApiMode.torOnly) { providerList = _allProviders.where((provider) => provider.supportsOnionAddress).toList(); } else {