Merge branch 'release-4.2.4' into CAKE-345-batch-sending

# Conflicts:
#	lib/bitcoin/electrum_wallet.dart
#	lib/src/screens/send/send_page.dart
#	lib/src/screens/send/send_template_page.dart
#	lib/view_model/send/send_view_model.dart
#	res/values/strings_de.arb
#	res/values/strings_en.arb
#	res/values/strings_es.arb
#	res/values/strings_hi.arb
#	res/values/strings_hr.arb
#	res/values/strings_it.arb
#	res/values/strings_ja.arb
#	res/values/strings_ko.arb
#	res/values/strings_nl.arb
#	res/values/strings_pl.arb
#	res/values/strings_pt.arb
#	res/values/strings_ru.arb
#	res/values/strings_uk.arb
#	res/values/strings_zh.arb
This commit is contained in:
OleksandrSobol
2021-07-22 17:52:48 +03:00
74 changed files with 1750 additions and 338 deletions
@@ -1,4 +1,10 @@
import 'package:cake_wallet/entities/crypto_currency.dart';
class BitcoinTransactionWrongBalanceException implements Exception {
BitcoinTransactionWrongBalanceException(this.currency);
final CryptoCurrency currency;
@override
String toString() => 'Wrong balance. Not enough BTC on your balance.';
String toString() => 'Wrong balance. Not enough ${currency.title} on your balance.';
}
+7 -1
View File
@@ -1,7 +1,10 @@
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
class BitcoinUnspent {
BitcoinUnspent(this.address, this.hash, this.value, this.vout);
BitcoinUnspent(this.address, this.hash, this.value, this.vout)
: isSending = true,
isFrozen = false,
note = '';
factory BitcoinUnspent.fromJSON(
BitcoinAddressRecord address, Map<String, dynamic> json) =>
@@ -15,4 +18,7 @@ class BitcoinUnspent {
bool get isP2wpkh =>
address.address.startsWith('bc') || address.address.startsWith('ltc');
bool isSending;
bool isFrozen;
String note;
}
+15 -7
View File
@@ -1,12 +1,14 @@
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:flutter/foundation.dart';
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:cake_wallet/bitcoin/utils.dart';
import 'package:cake_wallet/bitcoin/electrum_wallet_snapshot.dart';
import 'package:cake_wallet/bitcoin/electrum_wallet.dart';
import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
import 'package:cake_wallet/bitcoin/electrum_balance.dart';
import 'package:cake_wallet/bitcoin/bitcoin_wallet_addresses.dart';
part 'bitcoin_wallet.g.dart';
@@ -17,6 +19,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
{@required String mnemonic,
@required String password,
@required WalletInfo walletInfo,
@required Box<UnspentCoinsInfo> unspentCoinsInfo,
List<BitcoinAddressRecord> initialAddresses,
ElectrumBalance initialBalance,
int accountIndex = 0})
@@ -24,14 +27,22 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
mnemonic: mnemonic,
password: password,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo,
networkType: bitcoin.bitcoin,
initialAddresses: initialAddresses,
initialBalance: initialBalance,
accountIndex: accountIndex);
initialBalance: initialBalance) {
walletAddresses = BitcoinWalletAddresses(
walletInfo,
initialAddresses: initialAddresses,
accountIndex: accountIndex,
hd: hd,
networkType: networkType);
}
static Future<BitcoinWallet> open({
@required String name,
@required WalletInfo walletInfo,
@required Box<UnspentCoinsInfo> unspentCoinsInfo,
@required String password,
}) async {
final snp = ElectrumWallletSnapshot(name, walletInfo.type, password);
@@ -40,12 +51,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
mnemonic: snp.mnemonic,
password: password,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp.addresses,
initialBalance: snp.balance,
accountIndex: snp.accountIndex);
}
@override
String getAddress({@required int index, @required bitcoin.HDWallet hd}) =>
generateP2WPKHAddress(hd: hd, index: index, networkType: networkType);
}
+33
View File
@@ -0,0 +1,33 @@
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:cake_wallet/bitcoin/utils.dart';
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
import 'package:cake_wallet/bitcoin/electrum_wallet_addresses.dart';
import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart';
part 'bitcoin_wallet_addresses.g.dart';
class BitcoinWalletAddresses = BitcoinWalletAddressesBase
with _$BitcoinWalletAddresses;
abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses
with Store {
BitcoinWalletAddressesBase(
WalletInfo walletInfo,
{@required List<BitcoinAddressRecord> initialAddresses,
int accountIndex = 0,
@required bitcoin.HDWallet hd,
@required this.networkType})
: super(
walletInfo,
initialAddresses: initialAddresses,
accountIndex: accountIndex,
hd: hd);
bitcoin.NetworkType networkType;
@override
String getAddress({@required int index, @required bitcoin.HDWallet hd}) =>
generateP2WPKHAddress(hd: hd, index: index, networkType: networkType);
}
+9 -4
View File
@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart';
import 'package:cake_wallet/bitcoin/bitcoin_wallet_creation_credentials.dart';
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
import 'package:cake_wallet/core/wallet_base.dart';
import 'package:cake_wallet/core/wallet_service.dart';
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
@@ -14,9 +15,10 @@ class BitcoinWalletService extends WalletService<
BitcoinNewWalletCredentials,
BitcoinRestoreWalletFromSeedCredentials,
BitcoinRestoreWalletFromWIFCredentials> {
BitcoinWalletService(this.walletInfoSource);
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
@override
WalletType getType() => WalletType.bitcoin;
@@ -26,7 +28,8 @@ class BitcoinWalletService extends WalletService<
final wallet = BitcoinWallet(
mnemonic: await generateMnemonic(),
password: credentials.password,
walletInfo: credentials.walletInfo);
walletInfo: credentials.walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.save();
await wallet.init();
return wallet;
@@ -42,7 +45,8 @@ class BitcoinWalletService extends WalletService<
(info) => info.id == WalletBase.idFor(name, getType()),
orElse: () => null);
final wallet = await BitcoinWalletBase.open(
password: password, name: name, walletInfo: walletInfo);
password: password, name: name, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.init();
return wallet;
}
@@ -67,7 +71,8 @@ class BitcoinWalletService extends WalletService<
final wallet = BitcoinWallet(
password: credentials.password,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo);
walletInfo: credentials.walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.save();
await wallet.init();
return wallet;
+149 -95
View File
@@ -1,5 +1,8 @@
import 'dart:async';
import 'dart:convert';
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
import 'package:hive/hive.dart';
import 'package:cake_wallet/bitcoin/electrum_wallet_addresses.dart';
import 'package:mobx/mobx.dart';
import 'package:rxdart/subjects.dart';
import 'package:flutter/foundation.dart';
@@ -38,30 +41,28 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
ElectrumWalletBase(
{@required String password,
@required WalletInfo walletInfo,
@required Box<UnspentCoinsInfo> unspentCoinsInfo,
@required List<BitcoinAddressRecord> initialAddresses,
@required this.networkType,
@required this.mnemonic,
ElectrumClient electrumClient,
int accountIndex = 0,
ElectrumBalance initialBalance})
: balance = initialBalance ??
const ElectrumBalance(confirmed: 0, unconfirmed: 0),
hd = bitcoin.HDWallet.fromSeed(mnemonicToSeedBytes(mnemonic),
network: networkType)
.derivePath("m/0'/0"),
addresses = ObservableList<BitcoinAddressRecord>.of(
(initialAddresses ?? []).toSet()),
syncStatus = NotConnectedSyncStatus(),
_password = password,
_accountIndex = accountIndex,
_feeRates = <int>[],
_isTransactionUpdating = false,
super(walletInfo) {
this.electrumClient = electrumClient ?? ElectrumClient();
this.walletInfo = walletInfo;
this.unspentCoinsInfo = unspentCoinsInfo;
transactionHistory =
ElectrumTransactionHistory(walletInfo: walletInfo, password: password);
_unspent = [];
unspentCoins = [];
_scripthashesUpdateSubject = {};
}
@@ -72,10 +73,10 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
final String mnemonic;
ElectrumClient electrumClient;
Box<UnspentCoinsInfo> unspentCoinsInfo;
@override
@observable
String address;
ElectrumWalletAddresses walletAddresses;
@override
@observable
@@ -85,9 +86,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
@observable
SyncStatus syncStatus;
ObservableList<BitcoinAddressRecord> addresses;
List<String> get scriptHashes => addresses
List<String> get scriptHashes => walletAddresses.addresses
.map((addr) => scriptHash(addr.address, networkType: networkType))
.toList();
@@ -103,85 +102,26 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
wif: hd.wif, privateKey: hd.privKey, publicKey: hd.pubKey);
final String _password;
List<BitcoinUnspent> _unspent;
List<BitcoinUnspent> unspentCoins;
List<int> _feeRates;
int _accountIndex;
Map<String, BehaviorSubject<Object>> _scripthashesUpdateSubject;
bool _isTransactionUpdating;
Future<void> init() async {
await generateAddresses();
address = addresses[_accountIndex].address;
await walletAddresses.init();
await transactionHistory.init();
}
@action
Future<void> nextAddress() async {
_accountIndex += 1;
if (_accountIndex >= addresses.length) {
_accountIndex = 0;
}
address = addresses[_accountIndex].address;
await save();
}
Future<void> generateAddresses() async {
if (addresses.length < 33) {
final addressesCount = 33 - addresses.length;
await generateNewAddresses(addressesCount,
startIndex: addresses.length, hd: hd);
}
}
Future<BitcoinAddressRecord> generateNewAddress(
{bool isHidden = false, bitcoin.HDWallet hd}) async {
_accountIndex += 1;
final _hd = hd ?? this.hd;
final address = BitcoinAddressRecord(
getAddress(index: _accountIndex, hd: _hd),
index: _accountIndex,
isHidden: isHidden);
addresses.add(address);
await save();
return address;
}
Future<List<BitcoinAddressRecord>> generateNewAddresses(int count,
{int startIndex = 0, bitcoin.HDWallet hd, bool isHidden = false}) async {
final list = <BitcoinAddressRecord>[];
for (var i = startIndex; i < count + startIndex; i++) {
final address = BitcoinAddressRecord(getAddress(index: i, hd: hd),
index: i, isHidden: isHidden);
list.add(address);
}
addresses.addAll(list);
await save();
return list;
}
Future<void> updateAddress(String address) async {
for (final addr in addresses) {
if (addr.address == address) {
await save();
break;
}
}
}
@action
@override
Future<void> startSync() async {
try {
syncStatus = StartingSyncStatus();
updateTransactions();
await updateTransactions();
_subscribeForUpdates();
await _updateBalance();
await _updateUnspent();
await updateUnspent();
_feeRates = await electrumClient.feeRates();
Timer.periodic(const Duration(minutes: 1),
@@ -219,6 +159,23 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
final transactionCredentials = credentials as BitcoinTransactionCredentials;
final inputs = <BitcoinUnspent>[];
final sendItemList = transactionCredentials.sendItemList;
var allInputsAmount = 0;
if (unspentCoins.isEmpty) {
await updateUnspent();
}
for (final utx in unspentCoins) {
if (utx.isSending) {
allInputsAmount += utx.value;
inputs.add(utx);
}
}
if (inputs.isEmpty) {
throw BitcoinTransactionNoInputsException();
}
final allAmountFee =
calculateEstimatedFee(transactionCredentials.priority, null);
final allAmount = balance.confirmed - allAmountFee;
@@ -280,22 +237,48 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
throw BitcoinTransactionWrongBalanceException();
}
feeAmountForPriority(transactionCredentials.priority, inputs.length, 1);
final allAmount = allInputsAmount - allAmountFee;
final credentialsAmount = transactionCredentials.amount != null
? stringDoubleToBitcoinAmount(transactionCredentials.amount)
: 0;
final amount = transactionCredentials.amount == null ||
allAmount - credentialsAmount < minAmount
? allAmount
: credentialsAmount;
final fee = transactionCredentials.amount == null || amount == allAmount
? allAmountFee
: calculateEstimatedFee(transactionCredentials.priority, amount);
if (fee == 0) {
throw BitcoinTransactionWrongBalanceException(currency);
}
final totalAmount = amount + fee;
if (totalAmount > balance.confirmed || totalAmount > allInputsAmount) {
throw BitcoinTransactionWrongBalanceException(currency);
}
final txb = bitcoin.TransactionBuilder(network: networkType);
final changeAddress = walletAddresses.address;
var leftAmount = totalAmount;
final changeAddress = address;
var leftAmount = totalAmount;
var totalInputAmount = 0;
if (_unspent.isEmpty) {
await _updateUnspent();
}
inputs.clear();
for (final utx in _unspent) {
leftAmount = leftAmount - utx.value;
totalInputAmount += utx.value;
inputs.add(utx);
for (final utx in unspentCoins) {
if (utx.isSending) {
leftAmount = leftAmount - utx.value;
totalInputAmount += utx.value;
inputs.add(utx);
if (leftAmount <= 0) {
break;
if (leftAmount <= 0) {
break;
}
}
}
@@ -303,6 +286,8 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
throw BitcoinTransactionNoInputsException();
}
if (amount <= 0 || totalInputAmount < totalAmount) {
throw BitcoinTransactionWrongBalanceException(currency);
if (amount <= 0 || totalInputAmount < amount) {
throw BitcoinTransactionWrongBalanceException();
}
@@ -361,8 +346,8 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
String toJSON() => json.encode({
'mnemonic': mnemonic,
'account_index': _accountIndex.toString(),
'addresses': addresses.map((addr) => addr.toJSON()).toList(),
'account_index': walletAddresses.accountIndex.toString(),
'addresses': walletAddresses.addresses.map((addr) => addr.toJSON()).toList(),
'balance': balance?.toJSON()
});
@@ -387,17 +372,26 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
if (amount != null) {
int totalValue = 0;
for (final input in _unspent) {
for (final input in unspentCoins) {
if (totalValue >= amount) {
break;
}
totalValue += input.value;
inputsCount += 1;
if (input.isSending) {
totalValue += input.value;
inputsCount += 1;
}
}
if (totalValue < amount) return 0;
} else {
inputsCount = _unspent.length;
for (final input in unspentCoins) {
if (input.isSending) {
inputsCount += 1;
}
}
}
// If send all, then we have no change value
final _outputsCount = outputsCount ?? (amount != null ? 2 : 1);
@@ -428,24 +422,84 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
} catch (_) {}
}
String getAddress({@required int index, @required bitcoin.HDWallet hd}) => '';
Future<String> makePath() async =>
pathForWallet(name: walletInfo.name, type: walletInfo.type);
Future<void> _updateUnspent() async {
final unspent = await Future.wait(addresses.map((address) => electrumClient
Future<void> updateUnspent() async {
final unspent = await Future.wait(walletAddresses
.addresses.map((address) => electrumClient
.getListUnspentWithAddress(address.address, networkType)
.then((unspent) => unspent
.map((unspent) => BitcoinUnspent.fromJSON(address, unspent)))));
_unspent = unspent.expand((e) => e).toList();
unspentCoins = unspent.expand((e) => e).toList();
if (unspentCoinsInfo.isEmpty) {
unspentCoins.forEach((coin) => _addCoinInfo(coin));
return;
}
if (unspentCoins.isNotEmpty) {
unspentCoins.forEach((coin) {
final coinInfoList = unspentCoinsInfo.values.where((element) =>
element.walletId.contains(id) && element.hash.contains(coin.hash));
if (coinInfoList.isNotEmpty) {
final coinInfo = coinInfoList.first;
coin.isFrozen = coinInfo.isFrozen;
coin.isSending = coinInfo.isSending;
coin.note = coinInfo.note;
} else {
_addCoinInfo(coin);
}
});
}
await _refreshUnspentCoinsInfo();
}
Future<void> _addCoinInfo(BitcoinUnspent coin) async {
final newInfo = UnspentCoinsInfo(
walletId: id,
hash: coin.hash,
isFrozen: coin.isFrozen,
isSending: coin.isSending,
note: coin.note
);
await unspentCoinsInfo.add(newInfo);
}
Future<void> _refreshUnspentCoinsInfo() async {
try {
final List<dynamic> keys = <dynamic>[];
final currentWalletUnspentCoins = unspentCoinsInfo.values
.where((element) => element.walletId.contains(id));
if (currentWalletUnspentCoins.isNotEmpty) {
currentWalletUnspentCoins.forEach((element) {
final existUnspentCoins = unspentCoins
?.where((coin) => element.hash.contains(coin?.hash));
if (existUnspentCoins?.isEmpty ?? true) {
keys.add(element.key);
}
});
}
if (keys.isNotEmpty) {
await unspentCoinsInfo.deleteAll(keys);
}
} catch (e) {
print(e.toString());
}
}
Future<ElectrumTransactionInfo> fetchTransactionInfo(
{@required String hash, @required int height}) async {
final tx = await electrumClient.getTransactionExpanded(hash: hash);
return ElectrumTransactionInfo.fromElectrumVerbose(tx, walletInfo.type,
height: height, addresses: addresses);
height: height, addresses: walletAddresses.addresses);
}
@override
@@ -489,7 +543,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
_scripthashesUpdateSubject[sh].listen((event) async {
try {
await _updateBalance();
await _updateUnspent();
await updateUnspent();
await updateTransactions();
} catch (e) {
print(e.toString());
+111
View File
@@ -0,0 +1,111 @@
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
import 'package:cake_wallet/entities/wallet_addresses.dart';
import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart';
part 'electrum_wallet_addresses.g.dart';
class ElectrumWalletAddresses = ElectrumWalletAddressesBase
with _$ElectrumWalletAddresses;
abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
ElectrumWalletAddressesBase(WalletInfo walletInfo,
{@required List<BitcoinAddressRecord> initialAddresses,
int accountIndex = 0,
@required bitcoin.HDWallet hd})
: super(walletInfo) {
this.hd = hd;
this.accountIndex = accountIndex;
addresses = ObservableList<BitcoinAddressRecord>.of(
(initialAddresses ?? []).toSet());
}
@override
@observable
String address;
bitcoin.HDWallet hd;
ObservableList<BitcoinAddressRecord> addresses;
int accountIndex;
@override
Future<void> init() async {
await generateAddresses();
address = addresses[accountIndex].address;
await updateAddressesInBox();
}
@action
Future<void> nextAddress() async {
accountIndex += 1;
if (accountIndex >= addresses.length) {
accountIndex = 0;
}
address = addresses[accountIndex].address;
await updateAddressesInBox();
}
Future<void> generateAddresses() async {
if (addresses.length < 33) {
final addressesCount = 33 - addresses.length;
await generateNewAddresses(addressesCount,
startIndex: addresses.length, hd: hd);
}
}
Future<BitcoinAddressRecord> generateNewAddress(
{bool isHidden = false, bitcoin.HDWallet hd}) async {
accountIndex += 1;
final _hd = hd ?? this.hd;
final address = BitcoinAddressRecord(
getAddress(index: accountIndex, hd: _hd),
index: accountIndex,
isHidden: isHidden);
addresses.add(address);
return address;
}
Future<List<BitcoinAddressRecord>> generateNewAddresses(int count,
{int startIndex = 0, bitcoin.HDWallet hd, bool isHidden = false}) async {
final list = <BitcoinAddressRecord>[];
for (var i = startIndex; i < count + startIndex; i++) {
final address = BitcoinAddressRecord(getAddress(index: i, hd: hd),
index: i, isHidden: isHidden);
list.add(address);
}
addresses.addAll(list);
return list;
}
/*Future<void> updateAddress(String address) async {
for (final addr in addresses) {
if (addr.address == address) {
await save();
break;
}
}
}*/
String getAddress({@required int index, @required bitcoin.HDWallet hd}) => '';
@override
Future<void> updateAddressesInBox() async {
try {
addressesMap.clear();
addressesMap[address] = '';
await saveAddressesInBox();
} catch (e) {
print(e.toString());
}
}
}
+16 -26
View File
@@ -1,8 +1,9 @@
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
import 'package:cake_wallet/bitcoin/litecoin_wallet_addresses.dart';
import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:cake_wallet/bitcoin/electrum_wallet_snapshot.dart';
@@ -10,7 +11,6 @@ import 'package:cake_wallet/bitcoin/electrum_wallet.dart';
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
import 'package:cake_wallet/bitcoin/electrum_balance.dart';
import 'package:cake_wallet/bitcoin/litecoin_network.dart';
import 'package:cake_wallet/bitcoin/utils.dart';
part 'litecoin_wallet.g.dart';
@@ -21,6 +21,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
{@required String mnemonic,
@required String password,
@required WalletInfo walletInfo,
@required Box<UnspentCoinsInfo> unspentCoinsInfo,
List<BitcoinAddressRecord> initialAddresses,
ElectrumBalance initialBalance,
int accountIndex = 0})
@@ -28,14 +29,23 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
mnemonic: mnemonic,
password: password,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo,
networkType: litecoinNetwork,
initialAddresses: initialAddresses,
initialBalance: initialBalance,
accountIndex: accountIndex);
initialBalance: initialBalance) {
walletAddresses = LitecoinWalletAddresses(
walletInfo,
initialAddresses: initialAddresses,
accountIndex: accountIndex,
hd: hd,
networkType: networkType,
mnemonic: mnemonic);
}
static Future<LitecoinWallet> open({
@required String name,
@required WalletInfo walletInfo,
@required Box<UnspentCoinsInfo> unspentCoinsInfo,
@required String password,
}) async {
final snp = ElectrumWallletSnapshot(name, walletInfo.type, password);
@@ -44,32 +54,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
mnemonic: snp.mnemonic,
password: password,
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp.addresses,
initialBalance: snp.balance,
accountIndex: snp.accountIndex);
}
@override
String getAddress({@required int index, @required bitcoin.HDWallet hd}) =>
generateP2WPKHAddress(hd: hd, index: index, networkType: networkType);
@override
Future<void> generateAddresses() async {
if (addresses.length < 33) {
final addressesCount = 22 - addresses.length;
await generateNewAddresses(addressesCount,
hd: hd, startIndex: addresses.length);
final changeRoot = bitcoin.HDWallet.fromSeed(
mnemonicToSeedBytes(mnemonic),
network: networkType)
.derivePath("m/0'/1");
await generateNewAddresses(11,
startIndex: 0, hd: changeRoot, isHidden: true);
}
}
@override
int feeRate(TransactionPriority priority) {
if (priority is LitecoinTransactionPriority) {
@@ -0,0 +1,54 @@
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
import 'package:cake_wallet/bitcoin/utils.dart';
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
import 'package:cake_wallet/bitcoin/electrum_wallet_addresses.dart';
import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart';
part 'litecoin_wallet_addresses.g.dart';
class LitecoinWalletAddresses = LitecoinWalletAddressesBase
with _$LitecoinWalletAddresses;
abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses
with Store {
LitecoinWalletAddressesBase(
WalletInfo walletInfo,
{@required List<BitcoinAddressRecord> initialAddresses,
int accountIndex = 0,
@required bitcoin.HDWallet hd,
@required this.networkType,
@required this.mnemonic})
: super(
walletInfo,
initialAddresses: initialAddresses,
accountIndex: accountIndex,
hd: hd);
bitcoin.NetworkType networkType;
final String mnemonic;
@override
String getAddress({@required int index, @required bitcoin.HDWallet hd}) =>
generateP2WPKHAddress(hd: hd, index: index, networkType: networkType);
@override
Future<void> generateAddresses() async {
if (addresses.length < 33) {
final addressesCount = 22 - addresses.length;
await generateNewAddresses(addressesCount,
hd: hd, startIndex: addresses.length);
final changeRoot = bitcoin.HDWallet.fromSeed(
mnemonicToSeedBytes(mnemonic),
network: networkType)
.derivePath("m/0'/1");
await generateNewAddresses(11,
startIndex: 0, hd: changeRoot, isHidden: true);
}
}
}
+9 -4
View File
@@ -1,4 +1,5 @@
import 'dart:io';
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
import 'package:hive/hive.dart';
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart';
@@ -14,9 +15,10 @@ class LitecoinWalletService extends WalletService<
BitcoinNewWalletCredentials,
BitcoinRestoreWalletFromSeedCredentials,
BitcoinRestoreWalletFromWIFCredentials> {
LitecoinWalletService(this.walletInfoSource);
LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
@override
WalletType getType() => WalletType.litecoin;
@@ -26,7 +28,8 @@ class LitecoinWalletService extends WalletService<
final wallet = LitecoinWallet(
mnemonic: await generateMnemonic(),
password: credentials.password,
walletInfo: credentials.walletInfo);
walletInfo: credentials.walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.save();
await wallet.init();
@@ -43,7 +46,8 @@ class LitecoinWalletService extends WalletService<
(info) => info.id == WalletBase.idFor(name, getType()),
orElse: () => null);
final wallet = await LitecoinWalletBase.open(
password: password, name: name, walletInfo: walletInfo);
password: password, name: name, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.init();
return wallet;
}
@@ -68,7 +72,8 @@ class LitecoinWalletService extends WalletService<
final wallet = LitecoinWallet(
password: credentials.password,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo);
walletInfo: credentials.walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.save();
await wallet.init();
return wallet;
+32
View File
@@ -0,0 +1,32 @@
import 'package:hive/hive.dart';
part 'unspent_coins_info.g.dart';
@HiveType(typeId: UnspentCoinsInfo.typeId)
class UnspentCoinsInfo extends HiveObject {
UnspentCoinsInfo({
this.walletId,
this.hash,
this.isFrozen,
this.isSending,
this.note});
static const typeId = 9;
static const boxName = 'Unspent';
static const boxKey = 'unspentBoxKey';
@HiveField(0)
String walletId;
@HiveField(1)
String hash;
@HiveField(2)
bool isFrozen;
@HiveField(3)
bool isSending;
@HiveField(4)
String note;
}
+1 -1
View File
@@ -15,7 +15,7 @@ abstract class BuyProvider {
String get trackUrl;
WalletType get walletType => wallet.type;
String get walletAddress => wallet.address;
String get walletAddress => wallet.walletAddresses.address;
String get walletId => wallet.id;
@override
+5 -3
View File
@@ -1,12 +1,12 @@
import 'package:cake_wallet/entities/balance.dart';
import 'package:cake_wallet/entities/transaction_info.dart';
import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:cake_wallet/entities/wallet_addresses.dart';
import 'package:flutter/foundation.dart';
import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:cake_wallet/core/pending_transaction.dart';
import 'package:cake_wallet/core/transaction_history.dart';
import 'package:cake_wallet/entities/currency_for_wallet_type.dart';
import 'package:cake_wallet/entities/monero_transaction_priority.dart';
import 'package:cake_wallet/entities/crypto_currency.dart';
import 'package:cake_wallet/entities/sync_status.dart';
import 'package:cake_wallet/entities/node.dart';
@@ -31,9 +31,9 @@ abstract class WalletBase<
String get name => walletInfo.name;
String get address;
//String get address;
set address(String address);
//set address(String address);
BalanceType get balance;
@@ -45,6 +45,8 @@ abstract class WalletBase<
Object get keys;
WalletAddresses get walletAddresses;
HistoryType transactionHistory;
Future<void> connectToNode({@required Node node});
+46 -4
View File
@@ -1,5 +1,6 @@
import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart';
import 'package:cake_wallet/bitcoin/litecoin_wallet_service.dart';
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
import 'package:cake_wallet/core/backup_service.dart';
import 'package:cake_wallet/core/wallet_service.dart';
import 'package:cake_wallet/entities/biometric_auth.dart';
@@ -39,6 +40,8 @@ import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
import 'package:cake_wallet/src/screens/support/support_page.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
@@ -77,6 +80,9 @@ import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart';
import 'package:cake_wallet/view_model/support_view_model.dart';
import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
import 'package:cake_wallet/view_model/trade_details_view_model.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_details_view_model.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/auth_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
@@ -126,6 +132,7 @@ Box<Template> _templates;
Box<ExchangeTemplate> _exchangeTemplates;
Box<TransactionDescription> _transactionDescriptionBox;
Box<Order> _ordersSource;
Box<UnspentCoinsInfo> _unspentCoinsInfoSource;
Future setup(
{Box<WalletInfo> walletInfoSource,
@@ -135,7 +142,8 @@ Future setup(
Box<Template> templates,
Box<ExchangeTemplate> exchangeTemplates,
Box<TransactionDescription> transactionDescriptionBox,
Box<Order> ordersSource}) async {
Box<Order> ordersSource,
Box<UnspentCoinsInfo> unspentCoinsInfoSource}) async {
_walletInfoSource = walletInfoSource;
_nodeSource = nodeSource;
_contactSource = contactSource;
@@ -144,6 +152,7 @@ Future setup(
_exchangeTemplates = exchangeTemplates;
_transactionDescriptionBox = transactionDescriptionBox;
_ordersSource = ordersSource;
_unspentCoinsInfoSource = unspentCoinsInfoSource;
if (!_isSetupFinished) {
getIt.registerSingletonAsync<SharedPreferences>(
@@ -370,7 +379,8 @@ Future setup(
getIt.registerFactoryParam<MoneroAccountEditOrCreateViewModel,
AccountListItem, void>(
(AccountListItem account, _) => MoneroAccountEditOrCreateViewModel(
(getIt.get<AppStore>().wallet as MoneroWallet).accountList,
(getIt.get<AppStore>().wallet as MoneroWallet).walletAddresses.accountList,
wallet: getIt.get<AppStore>().wallet,
accountListItem: account));
getIt.registerFactoryParam<MoneroAccountEditOrCreatePage, AccountListItem,
@@ -454,9 +464,11 @@ Future setup(
getIt.registerFactory(() => MoneroWalletService(_walletInfoSource));
getIt.registerFactory(() => BitcoinWalletService(_walletInfoSource));
getIt.registerFactory(() =>
BitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource));
getIt.registerFactory(() => LitecoinWalletService(_walletInfoSource));
getIt.registerFactory(() =>
LitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource));
getIt.registerFactoryParam<WalletService, WalletType, void>(
(WalletType param1, __) {
@@ -592,5 +604,35 @@ Future setup(
getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>()));
getIt.registerFactory(() {
final wallet = getIt.get<AppStore>().wallet;
return UnspentCoinsListViewModel(
wallet: wallet,
unspentCoinsInfo: _unspentCoinsInfoSource);
});
getIt.registerFactory(() => UnspentCoinsListPage(
unspentCoinsListViewModel: getIt.get<UnspentCoinsListViewModel>()
));
getIt.registerFactoryParam<UnspentCoinsDetailsViewModel,
UnspentCoinsItem, UnspentCoinsListViewModel>((item, model) =>
UnspentCoinsDetailsViewModel(
unspentCoinsItem: item,
unspentCoinsListViewModel: model));
getIt.registerFactoryParam<UnspentCoinsDetailsPage, List, void>(
(List args, _) {
final item = args.first as UnspentCoinsItem;
final unspentCoinsListViewModel = args[1] as UnspentCoinsListViewModel;
return UnspentCoinsDetailsPage(
unspentCoinsDetailsViewModel:
getIt.get<UnspentCoinsDetailsViewModel>(
param1: item,
param2: unspentCoinsListViewModel));
});
_isSetupFinished = true;
}
@@ -0,0 +1,47 @@
import 'package:cake_wallet/entities/openalias_record.dart';
import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
const topLevelDomain = 'crypto';
Future<ParsedAddress> parseAddressFromDomain(
String domain, String ticker) async {
try {
final formattedName = OpenaliasRecord.formatDomainName(domain);
final domainParts = formattedName.split('.');
final name = domainParts.last;
if (domainParts.length <= 1 || domainParts.first.isEmpty || name.isEmpty) {
return ParsedAddress(address: domain);
}
if (name.contains(topLevelDomain)) {
final address =
await fetchUnstoppableDomainAddress(domain, ticker);
if (address?.isEmpty ?? true) {
return ParsedAddress(address: domain);
}
return ParsedAddress(
address: address,
name: domain,
parseFrom: ParseFrom.unstoppableDomains);
}
final record = await OpenaliasRecord.fetchAddressAndName(formattedName);
if (record == null || record.address.contains(formattedName)) {
return ParsedAddress(address: domain);
}
return ParsedAddress(
address: record.address,
name: record.name,
parseFrom: ParseFrom.openAlias);
} catch (e) {
print(e.toString());
}
return ParsedAddress(address: domain);
}
+12
View File
@@ -0,0 +1,12 @@
enum ParseFrom {unstoppableDomains, openAlias, notParsed}
class ParsedAddress {
ParsedAddress({
this.address = '',
this.name = '',
this.parseFrom = ParseFrom.notParsed});
final String address;
final String name;
final ParseFrom parseFrom;
}
@@ -0,0 +1,22 @@
import 'package:flutter/services.dart';
const channel = MethodChannel('com.cake_wallet/native_utils');
Future<String> fetchUnstoppableDomainAddress(String domain, String ticker) async {
var address = '';
try {
address = await channel.invokeMethod(
'getUnstoppableDomainAddress',
<String, String> {
'domain' : domain,
'ticker' : ticker
}
);
} catch (e) {
print('Unstoppable domain error: ${e.toString()}');
address = '';
}
return address;
}
+36
View File
@@ -0,0 +1,36 @@
import 'package:cake_wallet/entities/wallet_info.dart';
abstract class WalletAddresses {
WalletAddresses(this.walletInfo) {
addressesMap = {};
}
final WalletInfo walletInfo;
String get address;
set address(String address);
Map<String, String> addressesMap;
Future<void> init();
Future<void> updateAddressesInBox();
Future<void> saveAddressesInBox() async {
try {
if (walletInfo == null) {
return;
}
walletInfo.address = address;
walletInfo.addresses = addressesMap;
if (walletInfo.isInBox) {
await walletInfo.save();
}
} catch (e) {
print(e.toString());
}
}
}
+3
View File
@@ -53,5 +53,8 @@ class WalletInfo extends HiveObject {
@HiveField(8)
String address;
@HiveField(10)
Map<String, String> addresses;
DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp);
}
+11 -1
View File
@@ -1,3 +1,4 @@
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
import 'package:cake_wallet/entities/language_service.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:flutter/material.dart';
@@ -75,6 +76,10 @@ Future<void> main() async {
Hive.registerAdapter(OrderAdapter());
}
if (!Hive.isAdapterRegistered(UnspentCoinsInfo.typeId)) {
Hive.registerAdapter(UnspentCoinsInfoAdapter());
}
final secureStorage = FlutterSecureStorage();
final transactionDescriptionsBoxKey = await getEncryptionKey(
secureStorage: secureStorage, forKey: TransactionDescription.boxKey);
@@ -95,6 +100,8 @@ Future<void> main() async {
final templates = await Hive.openBox<Template>(Template.boxName);
final exchangeTemplates =
await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
final unspentCoinsInfoSource =
await Hive.openBox<UnspentCoinsInfo>(UnspentCoinsInfo.boxName);
await initialSetup(
sharedPreferences: await SharedPreferences.getInstance(),
nodes: nodes,
@@ -102,6 +109,7 @@ Future<void> main() async {
contactSource: contacts,
tradesSource: trades,
ordersSource: orders,
unspentCoinsInfoSource: unspentCoinsInfoSource,
// fiatConvertationService: fiatConvertationService,
templates: templates,
exchangeTemplates: exchangeTemplates,
@@ -134,6 +142,7 @@ Future<void> initialSetup(
@required Box<Template> templates,
@required Box<ExchangeTemplate> exchangeTemplates,
@required Box<TransactionDescription> transactionDescriptions,
@required Box<UnspentCoinsInfo> unspentCoinsInfoSource,
FlutterSecureStorage secureStorage,
int initialMigrationVersion = 15}) async {
LanguageService.loadLocaleList();
@@ -153,7 +162,8 @@ Future<void> initialSetup(
templates: templates,
exchangeTemplates: exchangeTemplates,
transactionDescriptionBox: transactionDescriptions,
ordersSource: ordersSource);
ordersSource: ordersSource,
unspentCoinsInfoSource: unspentCoinsInfoSource);
await bootstrap(navigatorKey);
monero_wallet.onStartup();
}
+22 -55
View File
@@ -3,6 +3,8 @@ import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:cake_wallet/monero/monero_amount_format.dart';
import 'package:cake_wallet/monero/monero_transaction_creation_exception.dart';
import 'package:cake_wallet/monero/monero_transaction_info.dart';
import 'package:cake_wallet/monero/monero_wallet_addresses.dart';
import 'package:cake_wallet/monero/monero_wallet_utils.dart';
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_monero/transaction_history.dart'
@@ -15,10 +17,7 @@ import 'package:cake_wallet/monero/pending_monero_transaction.dart';
import 'package:cake_wallet/monero/monero_wallet_keys.dart';
import 'package:cake_wallet/monero/monero_balance.dart';
import 'package:cake_wallet/monero/monero_transaction_history.dart';
import 'package:cake_wallet/monero/monero_subaddress_list.dart';
import 'package:cake_wallet/monero/monero_account_list.dart';
import 'package:cake_wallet/monero/account.dart';
import 'package:cake_wallet/monero/subaddress.dart';
import 'package:cake_wallet/core/pending_transaction.dart';
import 'package:cake_wallet/core/wallet_base.dart';
import 'package:cake_wallet/entities/sync_status.dart';
@@ -35,9 +34,7 @@ class MoneroWallet = MoneroWalletBase with _$MoneroWallet;
abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
MoneroTransactionHistory, MoneroTransactionInfo> with Store {
MoneroWalletBase({WalletInfo walletInfo})
: accountList = MoneroAccountList(),
subaddressList = MoneroSubaddressList(),
super(walletInfo) {
: super(walletInfo) {
transactionHistory = MoneroTransactionHistory();
balance = MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: 0),
@@ -47,33 +44,26 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
_isSavingAfterSync = false;
_isSavingAfterNewTransaction = false;
_isTransactionUpdating = false;
_onAccountChangeReaction = reaction((_) => account, (Account account) {
walletAddresses = MoneroWalletAddresses(walletInfo);
_onAccountChangeReaction = reaction((_) => walletAddresses.account,
(Account account) {
balance = MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: account.id),
unlockedBalance:
monero_wallet.getUnlockedBalance(accountIndex: account.id));
subaddressList.update(accountIndex: account.id);
subaddress = subaddressList.subaddresses.first;
address = subaddress.address;
walletAddresses.updateSubaddressList(accountIndex: account.id);
});
}
static const int _autoAfterSyncSaveInterval = 60000;
@observable
Account account;
@observable
Subaddress subaddress;
@override
MoneroWalletAddresses walletAddresses;
@override
@observable
SyncStatus syncStatus;
@override
@observable
String address;
@override
@observable
MoneroBalance balance;
@@ -88,10 +78,6 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
publicSpendKey: monero_wallet.getPublicSpendKey(),
publicViewKey: monero_wallet.getPublicViewKey());
final MoneroSubaddressList subaddressList;
final MoneroAccountList accountList;
SyncListener _listener;
ReactionDisposer _onAccountChangeReaction;
int _lastAutosaveTimestamp;
@@ -101,15 +87,11 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
int _lastSaveTimestamp;
Future<void> init() async {
accountList.update();
account = accountList.accounts.first;
subaddressList.update(accountIndex: account.id ?? 0);
subaddress = subaddressList.getAll().first;
await walletAddresses.init();
balance = MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: account.id),
fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account.id),
unlockedBalance:
monero_wallet.getUnlockedBalance(accountIndex: account.id));
address = subaddress.address;
monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id));
_setListeners();
await updateTransactions();
@@ -129,24 +111,6 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
_onAccountChangeReaction?.reaction?.dispose();
}
bool validate() {
accountList.update();
final accountListLength = accountList.accounts?.length ?? 0;
if (accountListLength <= 0) {
return false;
}
subaddressList.update(accountIndex: accountList.accounts.first.id);
final subaddressListLength = subaddressList.subaddresses?.length ?? 0;
if (subaddressListLength <= 0) {
return false;
}
return true;
}
@override
Future<void> connectToNode({@required Node node}) async {
try {
@@ -189,7 +153,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
? moneroParseAmount(amount: _credentials.amount)
: null;
final unlockedBalance =
monero_wallet.getUnlockedBalance(accountIndex: account.id);
monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id);
if ((amount != null && unlockedBalance < amount) ||
(amount == null && unlockedBalance <= 0)) {
@@ -209,7 +173,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
paymentId: _credentials.paymentId,
amount: _credentials.amount,
priorityRaw: _credentials.priority.serialize(),
accountIndex: account.id);
accountIndex: walletAddresses.account.id);
return PendingMoneroTransaction(pendingTransactionDescription);
}
@@ -238,12 +202,15 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
@override
Future<void> save() async {
await walletAddresses.updateAddressesInBox();
final now = DateTime.now().millisecondsSinceEpoch;
if (now - _lastSaveTimestamp < Duration(seconds: 10).inMilliseconds) {
return;
}
await backupWalletFiles(name);
_lastSaveTimestamp = now;
await monero_wallet.store();
}
@@ -265,7 +232,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
monero_wallet.rescanBlockchainAsync();
await startSync();
_askForUpdateBalance();
accountList.update();
walletAddresses.accountList.update();
await _askForUpdateTransactionHistory();
await save();
await walletInfo.save();
@@ -364,10 +331,10 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
await updateTransactions();
int _getFullBalance() =>
monero_wallet.getFullBalance(accountIndex: account.id);
monero_wallet.getFullBalance(accountIndex: walletAddresses.account.id);
int _getUnlockedBalance() =>
monero_wallet.getUnlockedBalance(accountIndex: account.id);
monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account.id);
Future<void> _afterSyncSave() async {
try {
@@ -414,13 +381,13 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
if (walletInfo.isRecovery) {
await _askForUpdateTransactionHistory();
_askForUpdateBalance();
accountList.update();
walletAddresses.accountList.update();
}
if (blocksLeft < 100) {
await _askForUpdateTransactionHistory();
_askForUpdateBalance();
accountList.update();
walletAddresses.accountList.update();
syncStatus = SyncedSyncStatus();
await _afterSyncSave();
+85
View File
@@ -0,0 +1,85 @@
import 'package:cake_wallet/entities/wallet_addresses.dart';
import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:cake_wallet/monero/account.dart';
import 'package:cake_wallet/monero/monero_account_list.dart';
import 'package:cake_wallet/monero/monero_subaddress_list.dart';
import 'package:cake_wallet/monero/subaddress.dart';
import 'package:mobx/mobx.dart';
part 'monero_wallet_addresses.g.dart';
class MoneroWalletAddresses = MoneroWalletAddressesBase
with _$MoneroWalletAddresses;
abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
MoneroWalletAddressesBase(WalletInfo walletInfo) : super(walletInfo) {
accountList = MoneroAccountList();
subaddressList = MoneroSubaddressList();
}
@override
@observable
String address;
@observable
Account account;
@observable
Subaddress subaddress;
MoneroSubaddressList subaddressList;
MoneroAccountList accountList;
@override
Future<void> init() async {
accountList.update();
account = accountList.accounts.first;
updateSubaddressList(accountIndex: account.id ?? 0);
await updateAddressesInBox();
}
@override
Future<void> updateAddressesInBox() async {
try {
final _subaddressList = MoneroSubaddressList();
addressesMap.clear();
accountList.accounts.forEach((account) {
_subaddressList.update(accountIndex: account.id);
_subaddressList.subaddresses.forEach((subaddress) {
addressesMap[subaddress.address] = subaddress.label;
});
});
await saveAddressesInBox();
} catch (e) {
print(e.toString());
}
}
bool validate() {
accountList.update();
final accountListLength = accountList.accounts?.length ?? 0;
if (accountListLength <= 0) {
return false;
}
subaddressList.update(accountIndex: accountList.accounts.first.id);
final subaddressListLength = subaddressList.subaddresses?.length ?? 0;
if (subaddressListLength <= 0) {
return false;
}
return true;
}
void updateSubaddressList({int accountIndex}) {
subaddressList.update(accountIndex: accountIndex);
subaddress = subaddressList.subaddresses.first;
address = subaddress.address;
}
}
+5 -13
View File
@@ -1,5 +1,6 @@
import 'dart:io';
import 'package:cake_wallet/core/wallet_base.dart';
import 'package:cake_wallet/monero/monero_wallet_utils.dart';
import 'package:hive/hive.dart';
import 'package:cw_monero/wallet_manager.dart' as monero_wallet_manager;
import 'package:cw_monero/wallet.dart' as monero_wallet;
@@ -55,16 +56,7 @@ class MoneroWalletService extends WalletService<
MoneroWalletService(this.walletInfoSource);
final Box<WalletInfo> walletInfoSource;
static Future<void> _removeCache(String name) async {
final path = await pathForWallet(name: name, type: WalletType.monero);
final cacheFile = File(path);
if (cacheFile.existsSync()) {
cacheFile.deleteSync();
}
}
static bool walletFilesExist(String path) =>
!File(path).existsSync() && !File('$path.keys').existsSync();
@@ -117,10 +109,10 @@ class MoneroWalletService extends WalletService<
(info) => info.id == WalletBase.idFor(name, getType()),
orElse: () => null);
final wallet = MoneroWallet(walletInfo: walletInfo);
final isValid = wallet.validate();
final isValid = wallet.walletAddresses.validate();
if (!isValid) {
await _removeCache(name);
await restoreOrResetWalletFiles(name);
wallet.close();
return openWallet(name, password);
}
@@ -135,7 +127,7 @@ class MoneroWalletService extends WalletService<
(e is WalletOpeningException &&
(e.message == 'std::bad_alloc' ||
e.message.contains('bad_alloc')))) {
await _removeCache(name);
await restoreOrResetWalletFiles(name);
return openWallet(name, password);
}
+88
View File
@@ -0,0 +1,88 @@
import 'dart:io';
import 'package:cake_wallet/entities/pathForWallet.dart';
import 'package:cake_wallet/entities/wallet_type.dart';
String backupFileName(String originalPath) {
final pathParts = originalPath.split('/');
final newName = '#_${pathParts.last}';
pathParts.removeLast();
pathParts.add(newName);
return pathParts.join('/');
}
Future<void> backupWalletFiles(String name) async {
final path = await pathForWallet(name: name, type: WalletType.monero);
final cacheFile = File(path);
final keysFile = File('$path.keys');
final addressListFile = File('$path.address.txt');
final newCacheFilePath = backupFileName(cacheFile.path);
final newKeysFilePath = backupFileName(keysFile.path);
final newAddressListFilePath = backupFileName(addressListFile.path);
if (cacheFile.existsSync()) {
await cacheFile.copy(newCacheFilePath);
}
if (keysFile.existsSync()) {
await keysFile.copy(newKeysFilePath);
}
if (addressListFile.existsSync()) {
await addressListFile.copy(newAddressListFilePath);
}
}
Future<void> restoreWalletFiles(String name) async {
final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero);
final cacheFilePath = '$walletDirPath/$name';
final keysFilePath = '$walletDirPath/$name.keys';
final addressListFilePath = '$walletDirPath/$name.address.txt';
final backupCacheFile = File(backupFileName(cacheFilePath));
final backupKeysFile = File(backupFileName(keysFilePath));
final backupAddressListFile = File(backupFileName(addressListFilePath));
if (backupCacheFile.existsSync()) {
await backupCacheFile.copy(cacheFilePath);
}
if (backupKeysFile.existsSync()) {
await backupKeysFile.copy(keysFilePath);
}
if (backupAddressListFile.existsSync()) {
await backupAddressListFile.copy(addressListFilePath);
}
}
Future<bool> backupWalletFilesExists(String name) async {
final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero);
final cacheFilePath = '$walletDirPath/$name';
final keysFilePath = '$walletDirPath/$name.keys';
final addressListFilePath = '$walletDirPath/$name.address.txt';
final backupCacheFile = File(backupFileName(cacheFilePath));
final backupKeysFile = File(backupFileName(keysFilePath));
final backupAddressListFile = File(backupFileName(addressListFilePath));
return backupCacheFile.existsSync()
&& backupKeysFile.existsSync()
&& backupAddressListFile.existsSync();
}
Future<void> removeCache(String name) async {
final path = await pathForWallet(name: name, type: WalletType.monero);
final cacheFile = File(path);
if (cacheFile.existsSync()) {
cacheFile.deleteSync();
}
}
Future<void> restoreOrResetWalletFiles(String name) async {
final backupsExists = await backupWalletFilesExists(name);
if (backupsExists) {
await restoreWalletFiles(name);
}
removeCache(name);
}
+1
View File
@@ -45,6 +45,7 @@ class Palette {
static const Color dullGray = Color.fromRGBO(98, 98, 98, 1.0);
static const Color protectiveBlue = Color.fromRGBO(33, 148, 255, 1.0);
static const Color darkBlue = Color.fromRGBO(109, 128, 178, 1.0);
static const Color paleCornflowerBlue = Color.fromRGBO(185, 196, 237, 1.0);
}
class PaletteDark {
+1 -1
View File
@@ -37,7 +37,7 @@ void startCurrentWalletChangeReaction(AppStore appStore,
await wallet.connectToNode(node: node);
if (wallet.walletInfo.address?.isEmpty ?? true) {
wallet.walletInfo.address = wallet.address;
wallet.walletInfo.address = wallet.walletAddresses.address;
if (wallet.walletInfo.isInBox) {
await wallet.walletInfo.save();
+15
View File
@@ -11,9 +11,12 @@ import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/src/screens/support/support_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
@@ -364,6 +367,18 @@ Route<dynamic> createRoute(RouteSettings settings) {
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SupportPage>());
case Routes.unspentCoinsList:
return MaterialPageRoute<void>(
builder: (_) => getIt.get<UnspentCoinsListPage>());
case Routes.unspentCoinsDetails:
final args = settings.arguments as List;
return MaterialPageRoute<void>(
builder: (_) =>
getIt.get<UnspentCoinsDetailsPage>(
param1: args));
default:
return MaterialPageRoute<void>(
builder: (_) => Scaffold(
+2
View File
@@ -55,4 +55,6 @@ class Routes {
static const orderDetails = '/order_details';
static const preOrder = '/pre_order';
static const buyWebView = '/buy_web_view';
static const unspentCoinsList = '/unspent_coins_list';
static const unspentCoinsDetails = '/unspent_coins_details';
}
+13 -11
View File
@@ -151,17 +151,19 @@ class ContactListPage extends BasePage {
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
image ?? Offstage(),
Padding(
padding: image != null
? EdgeInsets.only(left: 12)
: EdgeInsets.only(left: 0),
child: Text(
contact.name,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context).primaryTextTheme.title.color),
),
Expanded(
child: Padding(
padding: image != null
? EdgeInsets.only(left: 12)
: EdgeInsets.only(left: 0),
child: Text(
contact.name,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context).primaryTextTheme.title.color),
),
)
)
],
),
+90 -13
View File
@@ -1,7 +1,9 @@
import 'dart:ui';
import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/entities/sync_status.dart';
import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/exchange/changenow/changenow_exchange_provider.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_alert.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/cupertino.dart';
@@ -9,8 +11,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/exchange/exchange_provider.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/exchange/exchange_template.dart';
import 'package:cake_wallet/exchange/exchange_trade_state.dart';
import 'package:cake_wallet/exchange/limits_state.dart';
@@ -23,7 +23,6 @@ import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/entities/crypto_currency.dart';
import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
@@ -42,7 +41,9 @@ class ExchangePage extends BasePage {
final checkBoxKey = GlobalKey<StandardCheckboxState>();
final _formKey = GlobalKey<FormState>();
final _depositAmountFocus = FocusNode();
final _depositAddressFocus = FocusNode();
final _receiveAmountFocus = FocusNode();
final _receiveAddressFocus = FocusNode();
var _isReactionsSet = false;
@override
@@ -171,6 +172,7 @@ class ExchangePage extends BasePage {
.calculateDepositAllAmount()
: null,
amountFocusNode: _depositAmountFocus,
addressFocusNode: _depositAddressFocus,
key: depositKey,
title: S.of(context).you_will_send,
initialCurrency:
@@ -179,7 +181,7 @@ class ExchangePage extends BasePage {
initialAddress:
exchangeViewModel.depositCurrency ==
exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.address
? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.depositAddress,
initialIsAmountEditable: true,
initialIsAddressEditable:
@@ -223,6 +225,15 @@ class ExchangePage extends BasePage {
type: exchangeViewModel.wallet.type),
addressTextFieldValidator: AddressValidator(
type: exchangeViewModel.depositCurrency),
onPushPasteButton: (context) async {
final domain =
exchangeViewModel.depositAddress;
final ticker = exchangeViewModel
.depositCurrency.title.toLowerCase();
exchangeViewModel.depositAddress =
await applyOpenaliasOrUnstoppableDomains(
context, domain, ticker);
},
),
),
),
@@ -232,6 +243,7 @@ class ExchangePage extends BasePage {
child: Observer(
builder: (_) => ExchangeCard(
amountFocusNode: _receiveAmountFocus,
addressFocusNode: _receiveAddressFocus,
key: receiveKey,
title: S.of(context).you_will_get,
initialCurrency:
@@ -240,7 +252,7 @@ class ExchangePage extends BasePage {
initialAddress: exchangeViewModel
.receiveCurrency ==
exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.address
? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.receiveAddress,
initialIsAmountEditable: exchangeViewModel
.isReceiveAmountEditable,
@@ -268,6 +280,15 @@ class ExchangePage extends BasePage {
AddressValidator(
type: exchangeViewModel
.receiveCurrency),
onPushPasteButton: (context) async {
final domain =
exchangeViewModel.receiveAddress;
final ticker = exchangeViewModel
.receiveCurrency.title.toLowerCase();
exchangeViewModel.receiveAddress =
await applyOpenaliasOrUnstoppableDomains(
context, domain, ticker);
},
)),
)
],
@@ -371,7 +392,7 @@ class ExchangePage extends BasePage {
from: template.depositCurrency,
to: template.receiveCurrency,
onTap: () {
applyTemplate(
applyTemplate(context,
exchangeViewModel, template);
},
onRemove: () {
@@ -472,8 +493,8 @@ class ExchangePage extends BasePage {
));
}
void applyTemplate(
ExchangeViewModel exchangeViewModel, ExchangeTemplate template) {
void applyTemplate(BuildContext context,
ExchangeViewModel exchangeViewModel, ExchangeTemplate template) async {
exchangeViewModel.changeDepositCurrency(
currency: CryptoCurrency.fromString(template.depositCurrency));
exchangeViewModel.changeReceiveCurrency(
@@ -491,6 +512,16 @@ class ExchangePage extends BasePage {
exchangeViewModel.receiveAddress = template.receiveAddress;
exchangeViewModel.isReceiveAmountEntered = false;
exchangeViewModel.isFixedRateMode = false;
var domain = template.depositAddress;
var ticker = template.depositCurrency.toLowerCase();
exchangeViewModel.depositAddress =
await applyOpenaliasOrUnstoppableDomains(context, domain, ticker);
domain = template.receiveAddress;
ticker = template.receiveCurrency.toLowerCase();
exchangeViewModel.receiveAddress =
await applyOpenaliasOrUnstoppableDomains(context, domain, ticker);
}
void _setReactions(
@@ -646,7 +677,8 @@ class ExchangePage extends BasePage {
}
});
reaction((_) => exchangeViewModel.wallet.address, (String address) {
reaction((_) => exchangeViewModel.wallet.walletAddresses.address,
(String address) {
if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) {
depositKey.currentState.changeAddress(address: address);
}
@@ -656,6 +688,26 @@ class ExchangePage extends BasePage {
}
});
_depositAddressFocus.addListener(() async {
if (!_depositAddressFocus.hasFocus &&
depositAddressController.text.isNotEmpty) {
final domain = depositAddressController.text;
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
exchangeViewModel.depositAddress =
await applyOpenaliasOrUnstoppableDomains(context, domain, ticker);
}
});
_receiveAddressFocus.addListener(() async {
if (!_receiveAddressFocus.hasFocus &&
receiveAddressController.text.isNotEmpty) {
final domain = receiveAddressController.text;
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
exchangeViewModel.receiveAddress =
await applyOpenaliasOrUnstoppableDomains(context, domain, ticker);
}
});
_receiveAmountFocus.addListener(() {
if (_receiveAmountFocus.hasFocus && !exchangeViewModel.isFixedRateMode) {
showPopUp<void>(
@@ -707,7 +759,8 @@ class ExchangePage extends BasePage {
isCurrentTypeWallet ? exchangeViewModel.wallet.name : null);
key.currentState.changeAddress(
address: isCurrentTypeWallet ? exchangeViewModel.wallet.address : '');
address: isCurrentTypeWallet
? exchangeViewModel.wallet.walletAddresses.address : '');
key.currentState.changeAmount(amount: '');
}
@@ -719,11 +772,35 @@ class ExchangePage extends BasePage {
if (isCurrentTypeWallet) {
key.currentState.changeWalletName(exchangeViewModel.wallet.name);
key.currentState.addressController.text =
exchangeViewModel.wallet.address;
exchangeViewModel.wallet.walletAddresses.address;
} else if (key.currentState.addressController.text ==
exchangeViewModel.wallet.address) {
exchangeViewModel.wallet.walletAddresses.address) {
key.currentState.changeWalletName(null);
key.currentState.addressController.text = null;
}
}
Future<String> applyOpenaliasOrUnstoppableDomains(
BuildContext context, String domain, String ticker) async {
final parsedAddress = await parseAddressFromDomain(domain, ticker);
switch (parsedAddress.parseFrom) {
case ParseFrom.unstoppableDomains:
showAddressAlert(
context,
S.of(context).address_detected,
S.of(context).address_from_domain(parsedAddress.name));
break;
case ParseFrom.openAlias:
showAddressAlert(
context,
S.of(context).openalias_alert_title,
S.of(context).openalias_alert_content(parsedAddress.name));
break;
case ParseFrom.notParsed:
break;
}
return parsedAddress.address;
}
}
@@ -129,7 +129,7 @@ class ExchangeTemplatePage extends BasePage {
initialAddress: exchangeViewModel
.depositCurrency ==
exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.address
? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.depositAddress,
initialIsAmountEditable: true,
initialIsAddressEditable: exchangeViewModel
@@ -150,9 +150,8 @@ class ExchangeTemplatePage extends BasePage {
.color,
currencyValueValidator: AmountValidator(
type: exchangeViewModel.wallet.type),
addressTextFieldValidator: AddressValidator(
type:
exchangeViewModel.depositCurrency),
//addressTextFieldValidator: AddressValidator(
// type: exchangeViewModel.depositCurrency),
),
),
),
@@ -168,7 +167,7 @@ class ExchangeTemplatePage extends BasePage {
initialAddress:
exchangeViewModel.receiveCurrency ==
exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.address
? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.receiveAddress,
initialIsAmountEditable:
exchangeViewModel.provider is
@@ -190,8 +189,8 @@ class ExchangeTemplatePage extends BasePage {
.decorationColor,
currencyValueValidator: AmountValidator(
type: exchangeViewModel.wallet.type),
addressTextFieldValidator: AddressValidator(
type: exchangeViewModel.receiveCurrency),
//addressTextFieldValidator: AddressValidator(
// type: exchangeViewModel.receiveCurrency),
)),
)
],
@@ -383,7 +382,8 @@ class ExchangeTemplatePage extends BasePage {
}
});
reaction((_) => exchangeViewModel.wallet.address, (String address) {
reaction((_) => exchangeViewModel.wallet.walletAddresses.address,
(String address) {
if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) {
depositKey.currentState.changeAddress(address: address);
}
@@ -405,7 +405,8 @@ class ExchangeTemplatePage extends BasePage {
isCurrentTypeWallet ? exchangeViewModel.wallet.name : null);
key.currentState.changeAddress(
address: isCurrentTypeWallet ? exchangeViewModel.wallet.address : '');
address: isCurrentTypeWallet
? exchangeViewModel.wallet.walletAddresses.address : '');
key.currentState.changeAmount(amount: '');
}
@@ -417,9 +418,9 @@ class ExchangeTemplatePage extends BasePage {
if (isCurrentTypeWallet) {
key.currentState.changeWalletName(exchangeViewModel.wallet.name);
key.currentState.addressController.text =
exchangeViewModel.wallet.address;
exchangeViewModel.wallet.walletAddresses.address;
} else if (key.currentState.addressController.text ==
exchangeViewModel.wallet.address) {
exchangeViewModel.wallet.walletAddresses.address) {
key.currentState.changeWalletName(null);
key.currentState.addressController.text = null;
}
@@ -28,8 +28,10 @@ class ExchangeCard extends StatefulWidget {
this.currencyValueValidator,
this.addressTextFieldValidator,
this.amountFocusNode,
this.addressFocusNode,
this.hasAllAmount = false,
this.allAmount})
this.allAmount,
this.onPushPasteButton})
: super(key: key);
final List<CryptoCurrency> currencies;
@@ -49,8 +51,10 @@ class ExchangeCard extends StatefulWidget {
final FormFieldValidator<String> currencyValueValidator;
final FormFieldValidator<String> addressTextFieldValidator;
final FocusNode amountFocusNode;
final FocusNode addressFocusNode;
final bool hasAllAmount;
Function allAmount;
final Function allAmount;
final Function(BuildContext context) onPushPasteButton;
@override
ExchangeCardState createState() => ExchangeCardState();
@@ -288,6 +292,7 @@ class ExchangeCardState extends State<ExchangeCard> {
? Padding(
padding: EdgeInsets.only(top: 20),
child: AddressTextField(
focusNode: widget.addressFocusNode,
controller: addressController,
placeholder: widget.hasRefundAddress
? S.of(context).refund_address
@@ -311,6 +316,7 @@ class ExchangeCardState extends State<ExchangeCard> {
.decorationColor),
buttonColor: widget.addressButtonsColor,
validator: widget.addressTextFieldValidator,
onPushPasteButton: widget.onPushPasteButton,
),
)
: Padding(
+4
View File
@@ -1,4 +1,8 @@
import 'dart:ui';
import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/entities/transaction_priority.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_alert.dart';
import 'package:cake_wallet/src/screens/send/widgets/parse_address_from_domain_alert.dart';
import 'package:cake_wallet/src/screens/send/widgets/send_card.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
@@ -0,0 +1,55 @@
import 'package:cake_wallet/src/screens/transaction_details/textfield_list_item.dart';
import 'package:cake_wallet/src/screens/transaction_details/widgets/textfield_list_row.dart';
import 'package:cake_wallet/src/screens/unspent_coins/widgets/unspent_coins_switch_row.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_details_view_model.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_switch_item.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
class UnspentCoinsDetailsPage extends BasePage {
UnspentCoinsDetailsPage({this.unspentCoinsDetailsViewModel});
@override
String get title => S.current.unspent_coins_details_title;
final UnspentCoinsDetailsViewModel unspentCoinsDetailsViewModel;
@override
Widget body(BuildContext context) {
return SectionStandardList(
sectionCount: 1,
itemCounter: (int _) => unspentCoinsDetailsViewModel.items.length,
itemBuilder: (_, __, index) {
final item = unspentCoinsDetailsViewModel.items[index];
if (item is StandartListItem) {
return StandartListRow(
title: '${item.title}:',
value: item.value);
}
if (item is TextFieldListItem) {
return TextFieldListRow(
title: item.title,
value: item.value,
onSubmitted: item.onSubmitted,
);
}
if (item is UnspentCoinsSwitchItem) {
return Observer(builder: (_) => UnspentCoinsSwitchRow(
title: item.title,
switchValue: item.switchValue(),
onSwitchValueChange: item.onSwitchValueChange
));
}
return null;
});
}
}
@@ -0,0 +1,116 @@
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
class UnspentCoinsListPage extends BasePage {
UnspentCoinsListPage({this.unspentCoinsListViewModel});
@override
String get title => S.current.unspent_coins_title;
@override
Widget trailing(BuildContext context) {
final questionImage = Image.asset('assets/images/question_mark.png',
color: Theme.of(context).primaryTextTheme.title.color);
return SizedBox(
height: 20.0,
width: 20.0,
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: () => showUnspentCoinsAlert(context),
child: questionImage),
),
);
}
final UnspentCoinsListViewModel unspentCoinsListViewModel;
@override
Widget body(BuildContext context) =>
UnspentCoinsListForm(unspentCoinsListViewModel);
}
class UnspentCoinsListForm extends StatefulWidget {
UnspentCoinsListForm(this.unspentCoinsListViewModel);
final UnspentCoinsListViewModel unspentCoinsListViewModel;
@override
UnspentCoinsListFormState createState() =>
UnspentCoinsListFormState(unspentCoinsListViewModel);
}
class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
UnspentCoinsListFormState(this.unspentCoinsListViewModel);
final UnspentCoinsListViewModel unspentCoinsListViewModel;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
}
void afterLayout(dynamic _) {
showUnspentCoinsAlert(context);
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.fromLTRB(24, 12, 24, 24),
child: Observer(
builder: (_) => ListView.separated(
itemCount: unspentCoinsListViewModel.items.length,
separatorBuilder: (_, __) =>
SizedBox(height: 15),
itemBuilder: (_, int index) {
return Observer(builder: (_) {
final item = unspentCoinsListViewModel.items[index];
return GestureDetector(
onTap: () =>
Navigator.of(context)
.pushNamed(Routes.unspentCoinsDetails,
arguments: [item, unspentCoinsListViewModel]),
child: UnspentCoinsListItem(
address: item.address,
amount: item.amount,
isSending: item.isSending,
onCheckBoxTap: item.isFrozen
? null
: () async {
item.isSending = !item.isSending;
await unspentCoinsListViewModel
.saveUnspentCoinInfo(item);}));
});
}
)
)
);
}
}
void showUnspentCoinsAlert(BuildContext context) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: '',
alertContent: 'Information about unspent coins',
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
@@ -0,0 +1,94 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/palette.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
class UnspentCoinsListItem extends StatelessWidget {
UnspentCoinsListItem({
@required this.address,
@required this.amount,
@required this.isSending,
@required this.onCheckBoxTap,
});
static const amountColor = Palette.darkBlueCraiola;
static const addressColor = Palette.darkGray;
static const selectedItemColor = Palette.paleCornflowerBlue;
static const unselectedItemColor = Palette.moderateLavender;
final String address;
final String amount;
final bool isSending;
final Function() onCheckBoxTap;
@override
Widget build(BuildContext context) {
final itemColor = isSending? selectedItemColor : unselectedItemColor;
return Container(
height: 62,
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12)),
color: itemColor),
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(right: 12),
child: GestureDetector(
onTap: () => onCheckBoxTap?.call(),
child: Container(
height: 24.0,
width: 24.0,
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context)
.primaryTextTheme
.caption
.color,
width: 1.0),
borderRadius: BorderRadius.all(
Radius.circular(8.0)),
color: Theme.of(context).backgroundColor),
child: isSending
? Icon(
Icons.check,
color: Colors.blue,
size: 20.0,
)
: Offstage(),
)
)
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AutoSizeText(
amount,
style: TextStyle(
color: amountColor,
fontSize: 16,
fontWeight: FontWeight.w600
),
maxLines: 1,
),
AutoSizeText(
address,
style: TextStyle(
color: addressColor,
fontSize: 12,
),
maxLines: 1,
)
]
)
)
],
)
);
}
}
@@ -0,0 +1,45 @@
import 'package:cake_wallet/src/widgets/standart_switch.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class UnspentCoinsSwitchRow extends StatelessWidget {
UnspentCoinsSwitchRow(
{this.title,
this.titleFontSize = 14,
this.switchValue,
this.onSwitchValueChange});
final String title;
final double titleFontSize;
final bool switchValue;
final void Function(bool value) onSwitchValueChange;
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
color: Theme.of(context).backgroundColor,
child: Padding(
padding:
const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(title,
style: TextStyle(
fontSize: titleFontSize,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme.overline.color),
textAlign: TextAlign.left),
Padding(
padding: EdgeInsets.only(top: 12),
child: StandartSwitch(
value: switchValue,
onTaped: () => onSwitchValueChange(!switchValue))
)
]),
),
);
}
}
+5 -1
View File
@@ -24,7 +24,8 @@ class AddressTextField extends StatelessWidget {
this.iconColor,
this.textStyle,
this.hintStyle,
this.validator});
this.validator,
this.onPushPasteButton});
static const prefixIconWidth = 34.0;
static const prefixIconHeight = 34.0;
@@ -43,6 +44,7 @@ class AddressTextField extends StatelessWidget {
final TextStyle textStyle;
final TextStyle hintStyle;
final FocusNode focusNode;
final Function(BuildContext context) onPushPasteButton;
@override
Widget build(BuildContext context) {
@@ -225,5 +227,7 @@ class AddressTextField extends StatelessWidget {
if (address?.isNotEmpty ?? false) {
controller.text = address;
}
onPushPasteButton?.call(context);
}
}
+11 -11
View File
@@ -44,9 +44,6 @@ class StandardCheckboxState extends State<StandardCheckbox> {
Container(
height: 24.0,
width: 24.0,
margin: EdgeInsets.only(
right: 10.0,
),
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context)
@@ -65,14 +62,17 @@ class StandardCheckboxState extends State<StandardCheckbox> {
)
: Offstage(),
),
Text(
caption,
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
if (caption.isNotEmpty) Padding(
padding: EdgeInsets.only(left: 10),
child: Text(
caption,
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
)
)
],
),
-1
View File
@@ -1,4 +1,3 @@
import 'package:cake_wallet/palette.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@@ -16,11 +16,22 @@ class ContactListViewModel = ContactListViewModelBase
abstract class ContactListViewModelBase with Store {
ContactListViewModelBase(this.contactSource, this.walletInfoSource)
: contacts = ObservableList<ContactRecord>(),
walletContacts = walletInfoSource.values
.where((info) => info.address?.isNotEmpty ?? false)
.map((info) => WalletContact(
info.address, info.name, walletTypeToCryptoCurrency(info.type)))
.toList() {
walletContacts = [] {
walletInfoSource.values.forEach((info) {
if (info.addresses?.isNotEmpty ?? false) {
info.addresses?.forEach((address, label) {
final name = label.isNotEmpty
? info.name + ' ($label)'
: info.name;
walletContacts.add(WalletContact(
address,
name,
walletTypeToCryptoCurrency(info.type)));
});
}
});
_subscription = contactSource.bindToListWithTransform(
contacts, (Contact contact) => ContactRecord(contactSource, contact),
initialFire: true);
@@ -92,17 +92,17 @@ abstract class DashboardViewModelBase with Store {
final _wallet = wallet;
if (_wallet is MoneroWallet) {
subname = _wallet.account?.label;
subname = _wallet.walletAddresses.account?.label;
_onMoneroAccountChangeReaction = reaction((_) => _wallet.account,
(Account account) => _onMoneroAccountChange(_wallet));
_onMoneroAccountChangeReaction = reaction((_) => _wallet.walletAddresses
.account, (Account account) => _onMoneroAccountChange(_wallet));
_onMoneroBalanceChangeReaction = reaction((_) => _wallet.balance,
(MoneroBalance balance) => _onMoneroTransactionsUpdate(_wallet));
final _accountTransactions = _wallet
.transactionHistory.transactions.values
.where((tx) => tx.accountIndex == _wallet.account.id)
.where((tx) => tx.accountIndex == _wallet.walletAddresses.account.id)
.toList();
transactions = ObservableList.of(_accountTransactions.map((transaction) =>
@@ -131,7 +131,7 @@ abstract class DashboardViewModelBase with Store {
filter: (TransactionInfo tx) {
final wallet = _wallet;
if (tx is MoneroTransactionInfo && wallet is MoneroWallet) {
return tx.accountIndex == wallet.account.id;
return tx.accountIndex == wallet.walletAddresses.account.id;
}
return true;
@@ -153,7 +153,7 @@ abstract class DashboardViewModelBase with Store {
String subname;
@computed
String get address => wallet.address;
String get address => wallet.walletAddresses.address;
@computed
SyncStatus get status => wallet.syncStatus;
@@ -251,13 +251,13 @@ abstract class DashboardViewModelBase with Store {
wallet.type == WalletType.bitcoin && wallet.seed.split(' ').length < 24;
if (wallet is MoneroWallet) {
subname = wallet.account?.label;
subname = wallet.walletAddresses.account?.label;
_onMoneroAccountChangeReaction?.reaction?.dispose();
_onMoneroBalanceChangeReaction?.reaction?.dispose();
_onMoneroAccountChangeReaction = reaction((_) => wallet.account,
(Account account) => _onMoneroAccountChange(wallet));
_onMoneroAccountChangeReaction = reaction((_) => wallet.walletAddresses
.account, (Account account) => _onMoneroAccountChange(wallet));
_onMoneroBalanceChangeReaction = reaction((_) => wallet.balance,
(MoneroBalance balance) => _onMoneroTransactionsUpdate(wallet));
@@ -284,7 +284,7 @@ abstract class DashboardViewModelBase with Store {
settingsStore: appStore.settingsStore),
filter: (TransactionInfo tx) {
if (tx is MoneroTransactionInfo && wallet is MoneroWallet) {
return tx.accountIndex == wallet.account.id;
return tx.accountIndex == wallet.walletAddresses.account.id;
}
return true;
@@ -293,7 +293,7 @@ abstract class DashboardViewModelBase with Store {
@action
void _onMoneroAccountChange(MoneroWallet wallet) {
subname = wallet.account?.label;
subname = wallet.walletAddresses.account?.label;
_onMoneroTransactionsUpdate(wallet);
}
@@ -302,7 +302,7 @@ abstract class DashboardViewModelBase with Store {
transactions.clear();
final _accountTransactions = wallet.transactionHistory.transactions.values
.where((tx) => tx.accountIndex == wallet.account.id)
.where((tx) => tx.accountIndex == wallet.walletAddresses.account.id)
.toList();
transactions.addAll(_accountTransactions.map((transaction) =>
@@ -41,7 +41,8 @@ abstract class ExchangeViewModelBase with Store {
depositAmount = '';
receiveAmount = '';
receiveAddress = '';
depositAddress = depositCurrency == wallet.currency ? wallet.address : '';
depositAddress = depositCurrency == wallet.currency
? wallet.walletAddresses.address : '';
limitsState = LimitsInitialState();
tradeState = ExchangeTradeStateInitial();
_cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12;
@@ -308,8 +309,10 @@ abstract class ExchangeViewModelBase with Store {
isReceiveAmountEntered = false;
depositAmount = '';
receiveAmount = '';
depositAddress = depositCurrency == wallet.currency ? wallet.address : '';
receiveAddress = receiveCurrency == wallet.currency ? wallet.address : '';
depositAddress = depositCurrency == wallet.currency
? wallet.walletAddresses.address : '';
receiveAddress = receiveCurrency == wallet.currency
? wallet.walletAddresses.address : '';
isDepositAddressEnabled = !(depositCurrency == wallet.currency);
isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
isFixedRateMode = false;
@@ -1,3 +1,5 @@
import 'package:cake_wallet/core/wallet_base.dart';
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/monero/monero_account_list.dart';
@@ -10,11 +12,12 @@ class MoneroAccountEditOrCreateViewModel = MoneroAccountEditOrCreateViewModelBas
abstract class MoneroAccountEditOrCreateViewModelBase with Store {
MoneroAccountEditOrCreateViewModelBase(this._moneroAccountList,
{AccountListItem accountListItem})
{@required WalletBase wallet, AccountListItem accountListItem})
: state = InitialExecutionState(),
isEdit = accountListItem != null,
label = accountListItem?.label??'',
_accountListItem = accountListItem;
_accountListItem = accountListItem,
_wallet = wallet;
final bool isEdit;
@@ -26,6 +29,7 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store {
final MoneroAccountList _moneroAccountList;
final AccountListItem _accountListItem;
final WalletBase _wallet;
Future<void> save() async {
try {
@@ -38,6 +42,7 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store {
await _moneroAccountList.addAccount(label: label);
}
await _wallet.save();
state = ExecutedSuccessfullyState();
} catch (e) {
state = FailureState(e.toString());
@@ -20,15 +20,16 @@ abstract class MoneroAccountListViewModelBase with Store {
}
@computed
List<AccountListItem> get accounts => _moneroWallet.accountList.accounts
.map((acc) => AccountListItem(
List<AccountListItem> get accounts => _moneroWallet.walletAddresses
.accountList.accounts.map((acc) => AccountListItem(
label: acc.label,
id: acc.id,
isSelected: acc.id == _moneroWallet.account.id))
isSelected: acc.id == _moneroWallet.walletAddresses.account.id))
.toList();
final MoneroWallet _moneroWallet;
void select(AccountListItem item) =>
_moneroWallet.account = Account(id: item.id, label: item.label);
_moneroWallet.walletAddresses.account =
Account(id: item.id, label: item.label);
}
@@ -0,0 +1,65 @@
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cake_wallet/src/screens/transaction_details/textfield_list_item.dart';
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_switch_item.dart';
import 'package:mobx/mobx.dart';
part 'unspent_coins_details_view_model.g.dart';
class UnspentCoinsDetailsViewModel = UnspentCoinsDetailsViewModelBase
with _$UnspentCoinsDetailsViewModel;
abstract class UnspentCoinsDetailsViewModelBase with Store {
UnspentCoinsDetailsViewModelBase({
this.unspentCoinsItem, this.unspentCoinsListViewModel}) {
final amount = unspentCoinsItem.amount ?? '';
final address = unspentCoinsItem.address ?? '';
isFrozen = unspentCoinsItem.isFrozen ?? false;
note = unspentCoinsItem.note ?? '';
items = [
StandartListItem(
title: S.current.transaction_details_amount,
value: amount
),
StandartListItem(
title: S.current.widgets_address,
value: address
),
TextFieldListItem(
title: S.current.note_tap_to_change,
value: note,
onSubmitted: (value) {
unspentCoinsItem.note = value;
unspentCoinsListViewModel.saveUnspentCoinInfo(unspentCoinsItem);
}),
UnspentCoinsSwitchItem(
title: S.current.freeze,
value: '',
switchValue: () => isFrozen,
onSwitchValueChange: (value) async {
isFrozen = value;
unspentCoinsItem.isFrozen = value;
if (value) {
unspentCoinsItem.isSending = !value;
}
await unspentCoinsListViewModel.saveUnspentCoinInfo(unspentCoinsItem);
}
)
];
}
@observable
bool isFrozen;
@observable
String note;
final UnspentCoinsItem unspentCoinsItem;
final UnspentCoinsListViewModel unspentCoinsListViewModel;
List<TransactionDetailsListItem> items;
}
@@ -0,0 +1,33 @@
import 'package:mobx/mobx.dart';
part 'unspent_coins_item.g.dart';
class UnspentCoinsItem = UnspentCoinsItemBase with _$UnspentCoinsItem;
abstract class UnspentCoinsItemBase with Store {
UnspentCoinsItemBase({
this.address,
this.amount,
this.hash,
this.isFrozen,
this.note,
this.isSending});
@observable
String address;
@observable
String amount;
@observable
String hash;
@observable
bool isFrozen;
@observable
String note;
@observable
bool isSending;
}
@@ -0,0 +1,58 @@
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
import 'package:cake_wallet/bitcoin/electrum_wallet.dart';
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
import 'package:cake_wallet/core/wallet_base.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
part 'unspent_coins_list_view_model.g.dart';
class UnspentCoinsListViewModel = UnspentCoinsListViewModelBase with _$UnspentCoinsListViewModel;
abstract class UnspentCoinsListViewModelBase with Store {
UnspentCoinsListViewModelBase({
@required WalletBase wallet,
@required Box<UnspentCoinsInfo> unspentCoinsInfo}) {
_unspentCoinsInfo = unspentCoinsInfo;
_wallet = wallet as ElectrumWallet;
_wallet.updateUnspent();
}
ElectrumWallet _wallet;
Box<UnspentCoinsInfo> _unspentCoinsInfo;
@computed
ObservableList<UnspentCoinsItem> get items =>
ObservableList.of(_wallet.unspentCoins.map((elem) {
final amount = bitcoinAmountToString(amount: elem.value) +
' ${_wallet.currency.title}';
return UnspentCoinsItem(
address: elem.address.address,
amount: amount,
hash: elem.hash,
isFrozen: elem.isFrozen,
note: elem.note,
isSending: elem.isSending
);
}));
Future<void> saveUnspentCoinInfo(UnspentCoinsItem item) async {
try {
final info = _unspentCoinsInfo.values
.firstWhere((element) => element.walletId.contains(_wallet.id) &&
element.hash.contains(item.hash));
info.isFrozen = item.isFrozen;
info.isSending = item.isSending;
info.note = item.note;
await info.save();
await _wallet.updateUnspent();
} catch (e) {
print(e.toString());
}
}
}
@@ -0,0 +1,12 @@
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart';
class UnspentCoinsSwitchItem extends TransactionDetailsListItem {
UnspentCoinsSwitchItem({
String title,
String value,
this.switchValue,
this.onSwitchValueChange}) : super(title: title, value: value);
final bool Function() switchValue;
final void Function(bool value) onSwitchValueChange;
}
@@ -64,12 +64,15 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
final wallet = _wallet;
if (wallet is ElectrumWallet) {
await wallet.generateNewAddress();
await wallet.walletAddresses.generateNewAddress();
await wallet.save();
}
if (wallet is MoneroWallet) {
await wallet.subaddressList
.addSubaddress(accountIndex: wallet.account.id, label: label);
await wallet.walletAddresses.subaddressList
.addSubaddress(
accountIndex: wallet.walletAddresses.account.id,
label: label);
await wallet.save();
}
}
@@ -77,13 +80,14 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
Future<void> _update() async {
final wallet = _wallet;
if (wallet is BitcoinWallet) {
await wallet.updateAddress(_item.address as String);
}
/*if (wallet is BitcoinWallet) {
await wallet.walletAddresses.updateAddress(_item.address as String);
await wallet.save();
}*/
if (wallet is MoneroWallet) {
await wallet.subaddressList.setLabelSubaddress(
accountIndex: wallet.account.id,
await wallet.walletAddresses.subaddressList.setLabelSubaddress(
accountIndex: wallet.walletAddresses.account.id,
addressIndex: _item.id as int,
label: label);
await wallet.save();
@@ -80,7 +80,7 @@ abstract class WalletAddressListViewModelBase with Store {
@computed
WalletAddressListItem get address =>
WalletAddressListItem(address: _wallet.address);
WalletAddressListItem(address: _wallet.walletAddresses.address);
@computed
PaymentURI get uri {
@@ -105,8 +105,10 @@ abstract class WalletAddressListViewModelBase with Store {
final addressList = ObservableList<ListItem>();
if (wallet is MoneroWallet) {
final primaryAddress = wallet.subaddressList.subaddresses.first;
addressList.addAll(wallet.subaddressList.subaddresses.map((subaddress) {
final primaryAddress =
wallet.walletAddresses.subaddressList.subaddresses.first;
addressList.addAll(wallet.walletAddresses.subaddressList.subaddresses
.map((subaddress) {
final isPrimary = subaddress == primaryAddress;
return WalletAddressListItem(
@@ -118,8 +120,8 @@ abstract class WalletAddressListViewModelBase with Store {
}
if (wallet is BitcoinWallet) {
final primaryAddress = wallet.addresses.first;
final bitcoinAddresses = wallet.addresses.map((addr) {
final primaryAddress = wallet.walletAddresses.addresses.first;
final bitcoinAddresses = wallet.walletAddresses.addresses.map((addr) {
final isPrimary = addr == primaryAddress;
return WalletAddressListItem(
@@ -139,7 +141,7 @@ abstract class WalletAddressListViewModelBase with Store {
final wallet = _wallet;
if (wallet is MoneroWallet) {
return wallet.account.label;
return wallet.walletAddresses.account.label;
}
return null;
@@ -160,7 +162,7 @@ abstract class WalletAddressListViewModelBase with Store {
@action
void setAddress(WalletAddressListItem address) =>
_wallet.address = address.address;
_wallet.walletAddresses.address = address.address;
void _init() {
_baseItems = [];
@@ -177,7 +179,8 @@ abstract class WalletAddressListViewModelBase with Store {
final wallet = _wallet;
if (wallet is ElectrumWallet) {
wallet.nextAddress();
wallet.walletAddresses.nextAddress();
wallet.save();
}
}
}
+1 -1
View File
@@ -50,7 +50,7 @@ abstract class WalletCreationVMBase with Store {
dirPath: dirPath);
credentials.walletInfo = walletInfo;
final wallet = await process(credentials);
walletInfo.address = wallet.address;
walletInfo.address = wallet.walletAddresses.address;
await _walletInfoSource.add(walletInfo);
_appStore.changeCurrentWallet(wallet);
_appStore.authenticationStore.allowed();