mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-01-10 12:54:38 +00:00
Merge branches 'CW-519-tor' and 'main' of https://github.com/cake-tech/cake_wallet into CW-519-tor
This commit is contained in:
commit
c1c5e4b1cf
48 changed files with 19443 additions and 19597 deletions
|
@ -28,7 +28,7 @@ class BitcoinAddressRecord {
|
||||||
}
|
}
|
||||||
|
|
||||||
final String address;
|
final String address;
|
||||||
final bool isHidden;
|
bool isHidden;
|
||||||
final int index;
|
final int index;
|
||||||
int _txCount;
|
int _txCount;
|
||||||
int _balance;
|
int _balance;
|
||||||
|
|
|
@ -12,10 +12,8 @@ import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
class BitcoinWalletService extends WalletService<
|
class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||||
BitcoinNewWalletCredentials,
|
BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
|
||||||
BitcoinRestoreWalletFromSeedCredentials,
|
|
||||||
BitcoinRestoreWalletFromWIFCredentials> {
|
|
||||||
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -42,28 +40,41 @@ class BitcoinWalletService extends WalletService<
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> openWallet(String name, String password) async {
|
Future<BitcoinWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
final walletInfo = walletInfoSource.values
|
||||||
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||||
final wallet = await BitcoinWalletBase.open(
|
try {
|
||||||
password: password, name: name, walletInfo: walletInfo,
|
final wallet = await BitcoinWalletBase.open(
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
password: password,
|
||||||
await wallet.init();
|
name: name,
|
||||||
return wallet;
|
walletInfo: walletInfo,
|
||||||
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
await wallet.init();
|
||||||
|
saveBackup(name);
|
||||||
|
return wallet;
|
||||||
|
} catch (_) {
|
||||||
|
await restoreWalletFilesFromBackup(name);
|
||||||
|
final wallet = await BitcoinWalletBase.open(
|
||||||
|
password: password,
|
||||||
|
name: name,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
await wallet.init();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> remove(String wallet) async {
|
Future<void> remove(String wallet) async {
|
||||||
File(await pathForWalletDir(name: wallet, type: getType()))
|
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
|
||||||
.delete(recursive: true);
|
final walletInfo = walletInfoSource.values
|
||||||
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
||||||
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
|
||||||
await walletInfoSource.delete(walletInfo.key);
|
await walletInfoSource.delete(walletInfo.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> rename(String currentName, String password, String newName) async {
|
Future<void> rename(String currentName, String password, String newName) async {
|
||||||
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
|
final currentWalletInfo = walletInfoSource.values
|
||||||
(info) => info.id == WalletBase.idFor(currentName, getType()))!;
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
|
||||||
final currentWallet = await BitcoinWalletBase.open(
|
final currentWallet = await BitcoinWalletBase.open(
|
||||||
password: password,
|
password: password,
|
||||||
name: currentName,
|
name: currentName,
|
||||||
|
@ -71,6 +82,7 @@ class BitcoinWalletService extends WalletService<
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
await saveBackup(newName);
|
||||||
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
@ -80,13 +92,11 @@ class BitcoinWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> restoreFromKeys(
|
Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials) async =>
|
||||||
BitcoinRestoreWalletFromWIFCredentials credentials) async =>
|
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> restoreFromSeed(
|
Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials) async {
|
||||||
BitcoinRestoreWalletFromSeedCredentials credentials) async {
|
|
||||||
if (!validateMnemonic(credentials.mnemonic)) {
|
if (!validateMnemonic(credentials.mnemonic)) {
|
||||||
throw BitcoinMnemonicIsIncorrectException();
|
throw BitcoinMnemonicIsIncorrectException();
|
||||||
}
|
}
|
||||||
|
@ -100,4 +110,4 @@ class BitcoinWalletService extends WalletService<
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
String get address {
|
String get address {
|
||||||
if (isEnabledAutoGenerateSubaddress) {
|
if (isEnabledAutoGenerateSubaddress) {
|
||||||
if (receiveAddresses.isEmpty) {
|
if (receiveAddresses.isEmpty) {
|
||||||
final newAddress = generateNewAddress().address;
|
final newAddress = generateNewAddress(hd: mainHd).address;
|
||||||
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(newAddress) : newAddress;
|
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(newAddress) : newAddress;
|
||||||
}
|
}
|
||||||
final receiveAddress = receiveAddresses.first.address;
|
final receiveAddress = receiveAddresses.first.address;
|
||||||
|
@ -215,6 +215,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
List<BitcoinAddressRecord> addrs;
|
List<BitcoinAddressRecord> addrs;
|
||||||
|
|
||||||
if (addresses.isNotEmpty) {
|
if (addresses.isNotEmpty) {
|
||||||
|
|
||||||
|
|
||||||
|
if(!isHidden) {
|
||||||
|
final receiveAddressesList = addresses.where((addr) => !addr.isHidden).toList();
|
||||||
|
validateSideHdAddresses(receiveAddressesList);
|
||||||
|
}
|
||||||
|
|
||||||
addrs = addresses.where((addr) => addr.isHidden == isHidden).toList();
|
addrs = addresses.where((addr) => addr.isHidden == isHidden).toList();
|
||||||
} else {
|
} else {
|
||||||
addrs = await _createNewAddresses(
|
addrs = await _createNewAddresses(
|
||||||
|
@ -296,4 +303,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
final transactionHistory = await electrumClient.getHistory(sh);
|
final transactionHistory = await electrumClient.getHistory(sh);
|
||||||
return transactionHistory.isNotEmpty;
|
return transactionHistory.isNotEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void validateSideHdAddresses(List<BitcoinAddressRecord> addrWithTransactions) {
|
||||||
|
addrWithTransactions.forEach((element) {
|
||||||
|
if (element.address != getAddress(index: element.index, hd: mainHd)) element.isHidden = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,11 +45,22 @@ class LitecoinWalletService extends WalletService<
|
||||||
Future<LitecoinWallet> openWallet(String name, String password) async {
|
Future<LitecoinWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||||
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||||
final wallet = await LitecoinWalletBase.open(
|
|
||||||
password: password, name: name, walletInfo: walletInfo,
|
try {
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
final wallet = await LitecoinWalletBase.open(
|
||||||
await wallet.init();
|
password: password, name: name, walletInfo: walletInfo,
|
||||||
return wallet;
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
await wallet.init();
|
||||||
|
saveBackup(name);
|
||||||
|
return wallet;
|
||||||
|
} catch (_) {
|
||||||
|
await restoreWalletFilesFromBackup(name);
|
||||||
|
final wallet = await LitecoinWalletBase.open(
|
||||||
|
password: password, name: name, walletInfo: walletInfo,
|
||||||
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
await wallet.init();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -72,6 +83,7 @@ class LitecoinWalletService extends WalletService<
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
await saveBackup(newName);
|
||||||
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
|
|
@ -51,11 +51,22 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
|
||||||
Future<BitcoinCashWallet> openWallet(String name, String password) async {
|
Future<BitcoinCashWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||||
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||||
final wallet = await BitcoinCashWalletBase.open(
|
|
||||||
password: password, name: name, walletInfo: walletInfo,
|
try {
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
final wallet = await BitcoinCashWalletBase.open(
|
||||||
await wallet.init();
|
password: password, name: name, walletInfo: walletInfo,
|
||||||
return wallet;
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
await wallet.init();
|
||||||
|
saveBackup(name);
|
||||||
|
return wallet;
|
||||||
|
} catch(_) {
|
||||||
|
await restoreWalletFilesFromBackup(name);
|
||||||
|
final wallet = await BitcoinCashWalletBase.open(
|
||||||
|
password: password, name: name, walletInfo: walletInfo,
|
||||||
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
await wallet.init();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -78,6 +89,7 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
await saveBackup(newName);
|
||||||
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
|
|
@ -54,6 +54,17 @@ Future<void> restoreWalletFiles(String name) async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> resetCache(String name) async {
|
||||||
|
await removeCache(name);
|
||||||
|
|
||||||
|
final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero);
|
||||||
|
final cacheFilePath = '$walletDirPath/$name';
|
||||||
|
final backupCacheFile = File(backupFileName(cacheFilePath));
|
||||||
|
if (backupCacheFile.existsSync()) {
|
||||||
|
await backupCacheFile.copy(cacheFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> backupWalletFilesExists(String name) async {
|
Future<bool> backupWalletFilesExists(String name) async {
|
||||||
final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero);
|
final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero);
|
||||||
final cacheFilePath = '$walletDirPath/$name';
|
final cacheFilePath = '$walletDirPath/$name';
|
||||||
|
@ -63,9 +74,9 @@ Future<bool> backupWalletFilesExists(String name) async {
|
||||||
final backupKeysFile = File(backupFileName(keysFilePath));
|
final backupKeysFile = File(backupFileName(keysFilePath));
|
||||||
final backupAddressListFile = File(backupFileName(addressListFilePath));
|
final backupAddressListFile = File(backupFileName(addressListFilePath));
|
||||||
|
|
||||||
return backupCacheFile.existsSync()
|
return backupCacheFile.existsSync() &&
|
||||||
&& backupKeysFile.existsSync()
|
backupKeysFile.existsSync() &&
|
||||||
&& backupAddressListFile.existsSync();
|
backupAddressListFile.existsSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeCache(String name) async {
|
Future<void> removeCache(String name) async {
|
||||||
|
@ -85,4 +96,4 @@ Future<void> restoreOrResetWalletFiles(String name) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
removeCache(name);
|
removeCache(name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import 'package:cw_core/node.dart';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_credentials.dart';
|
import 'package:cw_core/wallet_credentials.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
|
||||||
abstract class WalletService<N extends WalletCredentials, RFS extends WalletCredentials,
|
abstract class WalletService<N extends WalletCredentials, RFS extends WalletCredentials,
|
||||||
|
@ -21,4 +22,22 @@ abstract class WalletService<N extends WalletCredentials, RFS extends WalletCred
|
||||||
Future<void> remove(String wallet);
|
Future<void> remove(String wallet);
|
||||||
|
|
||||||
Future<void> rename(String currentName, String password, String newName);
|
Future<void> rename(String currentName, String password, String newName);
|
||||||
|
|
||||||
|
Future<void> restoreWalletFilesFromBackup(String name) async {
|
||||||
|
final backupWalletDirPath = await pathForWalletDir(name: "$name.backup", type: getType());
|
||||||
|
final walletDirPath = await pathForWalletDir(name: name, type: getType());
|
||||||
|
|
||||||
|
if (File(backupWalletDirPath).existsSync()) {
|
||||||
|
await File(backupWalletDirPath).copy(walletDirPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveBackup(String name) async {
|
||||||
|
final backupWalletDirPath = await pathForWalletDir(name: "$name.backup", type: getType());
|
||||||
|
final walletDirPath = await pathForWalletDir(name: name, type: getType());
|
||||||
|
|
||||||
|
if (File(walletDirPath).existsSync()) {
|
||||||
|
await File(walletDirPath).copy(backupWalletDirPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,16 +39,31 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
|
||||||
Future<EthereumWallet> openWallet(String name, String password) async {
|
Future<EthereumWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo =
|
final walletInfo =
|
||||||
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
||||||
final wallet = await EthereumWallet.open(
|
|
||||||
name: name,
|
|
||||||
password: password,
|
|
||||||
walletInfo: walletInfo,
|
|
||||||
);
|
|
||||||
|
|
||||||
await wallet.init();
|
try {
|
||||||
await wallet.save();
|
final wallet = await EthereumWallet.open(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
);
|
||||||
|
|
||||||
return wallet;
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
saveBackup(name);
|
||||||
|
return wallet;
|
||||||
|
} catch (_) {
|
||||||
|
|
||||||
|
await restoreWalletFilesFromBackup(name);
|
||||||
|
|
||||||
|
final wallet = await EthereumWallet.open(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
);
|
||||||
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -59,6 +74,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
|
||||||
password: password, name: currentName, walletInfo: currentWalletInfo);
|
password: password, name: currentName, walletInfo: currentWalletInfo);
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
await saveBackup(newName);
|
||||||
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
|
|
@ -163,6 +163,7 @@ class HavenWalletService extends WalletService<
|
||||||
final currentWallet = HavenWallet(walletInfo: currentWalletInfo);
|
final currentWallet = HavenWallet(walletInfo: currentWalletInfo);
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
await saveBackup(newName);
|
||||||
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
|
|
@ -36,6 +36,8 @@ import 'package:mobx/mobx.dart';
|
||||||
part 'monero_wallet.g.dart';
|
part 'monero_wallet.g.dart';
|
||||||
|
|
||||||
const moneroBlockSize = 1000;
|
const moneroBlockSize = 1000;
|
||||||
|
// not sure if this should just be 0 but setting it higher feels safer / should catch more cases:
|
||||||
|
const MIN_RESTORE_HEIGHT = 1000;
|
||||||
|
|
||||||
class MoneroWallet = MoneroWalletBase with _$MoneroWallet;
|
class MoneroWallet = MoneroWalletBase with _$MoneroWallet;
|
||||||
|
|
||||||
|
@ -79,7 +81,7 @@ abstract class MoneroWalletBase
|
||||||
|
|
||||||
Box<UnspentCoinsInfo> unspentCoinsInfo;
|
Box<UnspentCoinsInfo> unspentCoinsInfo;
|
||||||
|
|
||||||
void Function(FlutterErrorDetails)? _onError;
|
void Function(FlutterErrorDetails)? onError;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
late MoneroWalletAddresses walletAddresses;
|
late MoneroWalletAddresses walletAddresses;
|
||||||
|
@ -171,7 +173,26 @@ abstract class MoneroWalletBase
|
||||||
Future<void> startSync() async {
|
Future<void> startSync() async {
|
||||||
try {
|
try {
|
||||||
_setInitialHeight();
|
_setInitialHeight();
|
||||||
} catch (_) {}
|
} catch (_) {
|
||||||
|
// our restore height wasn't correct, so lets see if using the backup works:
|
||||||
|
try {
|
||||||
|
await resetCache(name);
|
||||||
|
_setInitialHeight();
|
||||||
|
} catch (e) {
|
||||||
|
// we still couldn't get a valid height from the backup?!:
|
||||||
|
// try to use the date instead:
|
||||||
|
try {
|
||||||
|
_setHeightFromDate();
|
||||||
|
} catch (e, s) {
|
||||||
|
// we still couldn't get a valid sync height :/
|
||||||
|
onError?.call(FlutterErrorDetails(
|
||||||
|
exception: e,
|
||||||
|
stack: s,
|
||||||
|
library: this.runtimeType.toString(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
syncStatus = AttemptingSyncStatus();
|
syncStatus = AttemptingSyncStatus();
|
||||||
|
@ -339,6 +360,8 @@ abstract class MoneroWalletBase
|
||||||
if (currentAddressListFile.existsSync()) {
|
if (currentAddressListFile.existsSync()) {
|
||||||
await currentAddressListFile.rename('$newWalletPath.address.txt');
|
await currentAddressListFile.rename('$newWalletPath.address.txt');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await backupWalletFiles(newWalletName);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final currentWalletPath = await pathForWallet(name: name, type: type);
|
final currentWalletPath = await pathForWallet(name: name, type: type);
|
||||||
|
|
||||||
|
@ -402,9 +425,7 @@ abstract class MoneroWalletBase
|
||||||
if (coin.spent == 0) {
|
if (coin.spent == 0) {
|
||||||
final unspent = MoneroUnspent.fromCoinsInfoRow(coin);
|
final unspent = MoneroUnspent.fromCoinsInfoRow(coin);
|
||||||
if (unspent.hash.isNotEmpty) {
|
if (unspent.hash.isNotEmpty) {
|
||||||
unspent.isChange = transaction_history
|
unspent.isChange = transaction_history.getTransaction(unspent.hash).direction == 1;
|
||||||
.getTransaction(unspent.hash)
|
|
||||||
.direction == 1;
|
|
||||||
}
|
}
|
||||||
unspentCoins.add(unspent);
|
unspentCoins.add(unspent);
|
||||||
}
|
}
|
||||||
|
@ -418,7 +439,7 @@ abstract class MoneroWalletBase
|
||||||
if (unspentCoins.isNotEmpty) {
|
if (unspentCoins.isNotEmpty) {
|
||||||
unspentCoins.forEach((coin) {
|
unspentCoins.forEach((coin) {
|
||||||
final coinInfoList = unspentCoinsInfo.values.where((element) =>
|
final coinInfoList = unspentCoinsInfo.values.where((element) =>
|
||||||
element.walletId.contains(id) &&
|
element.walletId.contains(id) &&
|
||||||
element.accountIndex == walletAddresses.account!.id &&
|
element.accountIndex == walletAddresses.account!.id &&
|
||||||
element.keyImage!.contains(coin.keyImage!));
|
element.keyImage!.contains(coin.keyImage!));
|
||||||
|
|
||||||
|
@ -438,7 +459,7 @@ abstract class MoneroWalletBase
|
||||||
_askForUpdateBalance();
|
_askForUpdateBalance();
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
_onError?.call(FlutterErrorDetails(
|
onError?.call(FlutterErrorDetails(
|
||||||
exception: e,
|
exception: e,
|
||||||
stack: s,
|
stack: s,
|
||||||
library: this.runtimeType.toString(),
|
library: this.runtimeType.toString(),
|
||||||
|
@ -534,18 +555,36 @@ abstract class MoneroWalletBase
|
||||||
_listener = monero_wallet.setListeners(_onNewBlock, _onNewTransaction);
|
_listener = monero_wallet.setListeners(_onNewBlock, _onNewTransaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if the height is correct:
|
||||||
void _setInitialHeight() {
|
void _setInitialHeight() {
|
||||||
if (walletInfo.isRecovery) {
|
if (walletInfo.isRecovery) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final currentHeight = monero_wallet.getCurrentHeight();
|
final height = monero_wallet.getCurrentHeight();
|
||||||
|
|
||||||
if (currentHeight <= 1) {
|
if (height > MIN_RESTORE_HEIGHT) {
|
||||||
final height = _getHeightByDate(walletInfo.date);
|
// the restore height is probably correct, so we do nothing:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("height isn't > $MIN_RESTORE_HEIGHT!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setHeightFromDate() {
|
||||||
|
if (walletInfo.isRecovery) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final height = _getHeightByDate(walletInfo.date);
|
||||||
|
|
||||||
|
if (height > MIN_RESTORE_HEIGHT) {
|
||||||
monero_wallet.setRecoveringFromSeed(isRecovery: true);
|
monero_wallet.setRecoveringFromSeed(isRecovery: true);
|
||||||
monero_wallet.setRefreshFromBlockHeight(height: height);
|
monero_wallet.setRefreshFromBlockHeight(height: height);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw Exception("height isn't > $MIN_RESTORE_HEIGHT!");
|
||||||
}
|
}
|
||||||
|
|
||||||
int _getHeightDistance(DateTime date) {
|
int _getHeightDistance(DateTime date) {
|
||||||
|
@ -561,7 +600,8 @@ abstract class MoneroWalletBase
|
||||||
final heightDistance = _getHeightDistance(date);
|
final heightDistance = _getHeightDistance(date);
|
||||||
|
|
||||||
if (nodeHeight <= 0) {
|
if (nodeHeight <= 0) {
|
||||||
return 0;
|
// the node returned 0 (an error state), so lets just restore from cache:
|
||||||
|
throw Exception("nodeHeight is <= 0!");
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeHeight - heightDistance;
|
return nodeHeight - heightDistance;
|
||||||
|
@ -650,7 +690,7 @@ abstract class MoneroWalletBase
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void setExceptionHandler(void Function(FlutterErrorDetails) onError) => _onError = onError;
|
void setExceptionHandler(void Function(FlutterErrorDetails) e) => onError = e;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String signMessage(String message, {String? address}) {
|
String signMessage(String message, {String? address}) {
|
||||||
|
|
|
@ -11,11 +11,13 @@ import 'package:cw_core/get_height_by_date.dart';
|
||||||
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
|
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
|
||||||
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
|
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
|
||||||
import 'package:cw_monero/monero_wallet.dart';
|
import 'package:cw_monero/monero_wallet.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:polyseed/polyseed.dart';
|
import 'package:polyseed/polyseed.dart';
|
||||||
|
|
||||||
class MoneroNewWalletCredentials extends WalletCredentials {
|
class MoneroNewWalletCredentials extends WalletCredentials {
|
||||||
MoneroNewWalletCredentials({required String name, required this.language, required this.isPolyseed, String? password})
|
MoneroNewWalletCredentials(
|
||||||
|
{required String name, required this.language, required this.isPolyseed, String? password})
|
||||||
: super(name: name, password: password);
|
: super(name: name, password: password);
|
||||||
|
|
||||||
final String language;
|
final String language;
|
||||||
|
@ -52,10 +54,8 @@ class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
|
||||||
final String spendKey;
|
final String spendKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MoneroWalletService extends WalletService<
|
class MoneroWalletService extends WalletService<MoneroNewWalletCredentials,
|
||||||
MoneroNewWalletCredentials,
|
MoneroRestoreWalletFromSeedCredentials, MoneroRestoreWalletFromKeysCredentials> {
|
||||||
MoneroRestoreWalletFromSeedCredentials,
|
|
||||||
MoneroRestoreWalletFromKeysCredentials> {
|
|
||||||
MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -112,6 +112,7 @@ class MoneroWalletService extends WalletService<
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MoneroWallet> openWallet(String name, String password) async {
|
Future<MoneroWallet> openWallet(String name, String password) async {
|
||||||
|
MoneroWallet? wallet;
|
||||||
try {
|
try {
|
||||||
final path = await pathForWallet(name: name, type: getType());
|
final path = await pathForWallet(name: name, type: getType());
|
||||||
|
|
||||||
|
@ -119,11 +120,10 @@ class MoneroWalletService extends WalletService<
|
||||||
await repairOldAndroidWallet(name);
|
await repairOldAndroidWallet(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
await monero_wallet_manager
|
await monero_wallet_manager.openWalletAsync({'path': path, 'password': password});
|
||||||
.openWalletAsync({'path': path, 'password': password});
|
final walletInfo = walletInfoSource.values
|
||||||
final walletInfo = walletInfoSource.values.firstWhere(
|
.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
||||||
(info) => info.id == WalletBase.idFor(name, getType()));
|
wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
final wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
|
|
||||||
final isValid = wallet.walletAddresses.validate();
|
final isValid = wallet.walletAddresses.validate();
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
|
@ -135,7 +135,7 @@ class MoneroWalletService extends WalletService<
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
} catch (e) {
|
} catch (e, s) {
|
||||||
// TODO: Implement Exception for wallet list service.
|
// TODO: Implement Exception for wallet list service.
|
||||||
|
|
||||||
final bool isBadAlloc = e.toString().contains('bad_alloc') ||
|
final bool isBadAlloc = e.toString().contains('bad_alloc') ||
|
||||||
|
@ -156,16 +156,18 @@ class MoneroWalletService extends WalletService<
|
||||||
final bool invalidSignature = e.toString().contains('invalid signature') ||
|
final bool invalidSignature = e.toString().contains('invalid signature') ||
|
||||||
(e is WalletOpeningException && e.message.contains('invalid signature'));
|
(e is WalletOpeningException && e.message.contains('invalid signature'));
|
||||||
|
|
||||||
if (isBadAlloc ||
|
if (!isBadAlloc &&
|
||||||
doesNotCorrespond ||
|
!doesNotCorrespond &&
|
||||||
isMissingCacheFilesIOS ||
|
!isMissingCacheFilesIOS &&
|
||||||
isMissingCacheFilesAndroid ||
|
!isMissingCacheFilesAndroid &&
|
||||||
invalidSignature) {
|
!invalidSignature &&
|
||||||
await restoreOrResetWalletFiles(name);
|
wallet != null &&
|
||||||
return openWallet(name, password);
|
wallet.onError != null) {
|
||||||
|
wallet.onError!(FlutterErrorDetails(exception: e, stack: s));
|
||||||
}
|
}
|
||||||
|
|
||||||
rethrow;
|
await restoreOrResetWalletFiles(name);
|
||||||
|
return openWallet(name, password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,10 +187,9 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> rename(
|
Future<void> rename(String currentName, String password, String newName) async {
|
||||||
String currentName, String password, String newName) async {
|
final currentWalletInfo = walletInfoSource.values
|
||||||
final currentWalletInfo = walletInfoSource.values.firstWhere(
|
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
|
||||||
(info) => info.id == WalletBase.idFor(currentName, getType()));
|
|
||||||
final currentWallet =
|
final currentWallet =
|
||||||
MoneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
|
MoneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
|
||||||
|
@ -202,8 +203,7 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MoneroWallet> restoreFromKeys(
|
Future<MoneroWallet> restoreFromKeys(MoneroRestoreWalletFromKeysCredentials credentials) async {
|
||||||
MoneroRestoreWalletFromKeysCredentials credentials) async {
|
|
||||||
try {
|
try {
|
||||||
final path = await pathForWallet(name: credentials.name, type: getType());
|
final path = await pathForWallet(name: credentials.name, type: getType());
|
||||||
await monero_wallet_manager.restoreFromKeys(
|
await monero_wallet_manager.restoreFromKeys(
|
||||||
|
@ -227,9 +227,7 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MoneroWallet> restoreFromSeed(
|
Future<MoneroWallet> restoreFromSeed(MoneroRestoreWalletFromSeedCredentials credentials) async {
|
||||||
MoneroRestoreWalletFromSeedCredentials credentials) async {
|
|
||||||
|
|
||||||
// Restore from Polyseed
|
// Restore from Polyseed
|
||||||
if (Polyseed.isValidSeed(credentials.mnemonic)) {
|
if (Polyseed.isValidSeed(credentials.mnemonic)) {
|
||||||
return restoreFromPolyseed(credentials);
|
return restoreFromPolyseed(credentials);
|
||||||
|
@ -254,14 +252,16 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<MoneroWallet> restoreFromPolyseed(MoneroRestoreWalletFromSeedCredentials credentials) async {
|
Future<MoneroWallet> restoreFromPolyseed(
|
||||||
|
MoneroRestoreWalletFromSeedCredentials credentials) async {
|
||||||
try {
|
try {
|
||||||
final path = await pathForWallet(name: credentials.name, type: getType());
|
final path = await pathForWallet(name: credentials.name, type: getType());
|
||||||
final polyseedCoin = PolyseedCoin.POLYSEED_MONERO;
|
final polyseedCoin = PolyseedCoin.POLYSEED_MONERO;
|
||||||
final lang = PolyseedLang.getByPhrase(credentials.mnemonic);
|
final lang = PolyseedLang.getByPhrase(credentials.mnemonic);
|
||||||
final polyseed = Polyseed.decode(credentials.mnemonic, lang, polyseedCoin);
|
final polyseed = Polyseed.decode(credentials.mnemonic, lang, polyseedCoin);
|
||||||
|
|
||||||
return _restoreFromPolyseed(path, credentials.password!, polyseed, credentials.walletInfo!, lang);
|
return _restoreFromPolyseed(
|
||||||
|
path, credentials.password!, polyseed, credentials.walletInfo!, lang);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// TODO: Implement Exception for wallet list service.
|
// TODO: Implement Exception for wallet list service.
|
||||||
print('MoneroWalletsManager Error: $e');
|
print('MoneroWalletsManager Error: $e');
|
||||||
|
@ -269,11 +269,11 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<MoneroWallet> _restoreFromPolyseed(String path, String password, Polyseed polyseed,
|
Future<MoneroWallet> _restoreFromPolyseed(
|
||||||
WalletInfo walletInfo, PolyseedLang lang,
|
String path, String password, Polyseed polyseed, WalletInfo walletInfo, PolyseedLang lang,
|
||||||
{PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO, int? overrideHeight}) async {
|
{PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO, int? overrideHeight}) async {
|
||||||
final height = overrideHeight ?? getMoneroHeigthByDate(
|
final height = overrideHeight ??
|
||||||
date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
|
getMoneroHeigthByDate(date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
|
||||||
final spendKey = polyseed.generateKey(coin, 32).toHexString();
|
final spendKey = polyseed.generateKey(coin, 32).toHexString();
|
||||||
final seed = polyseed.encode(lang, coin);
|
final seed = polyseed.encode(lang, coin);
|
||||||
|
|
||||||
|
@ -288,8 +288,7 @@ class MoneroWalletService extends WalletService<
|
||||||
restoreHeight: height,
|
restoreHeight: height,
|
||||||
spendKey: spendKey);
|
spendKey: spendKey);
|
||||||
|
|
||||||
final wallet = MoneroWallet(
|
final wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
|
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
|
@ -301,16 +300,14 @@ class MoneroWalletService extends WalletService<
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final oldAndroidWalletDirPath =
|
final oldAndroidWalletDirPath = await outdatedAndroidPathForWalletDir(name: name);
|
||||||
await outdatedAndroidPathForWalletDir(name: name);
|
|
||||||
final dir = Directory(oldAndroidWalletDirPath);
|
final dir = Directory(oldAndroidWalletDirPath);
|
||||||
|
|
||||||
if (!dir.existsSync()) {
|
if (!dir.existsSync()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final newWalletDirPath =
|
final newWalletDirPath = await pathForWalletDir(name: name, type: getType());
|
||||||
await pathForWalletDir(name: name, type: getType());
|
|
||||||
|
|
||||||
dir.listSync().forEach((f) {
|
dir.listSync().forEach((f) {
|
||||||
final file = File(f.path);
|
final file = File(f.path);
|
||||||
|
|
|
@ -69,6 +69,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
NanoWallet(walletInfo: currentWalletInfo, password: password, mnemonic: randomWords);
|
NanoWallet(walletInfo: currentWalletInfo, password: password, mnemonic: randomWords);
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
await saveBackup(newName);
|
||||||
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
@ -150,14 +151,29 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
Future<NanoWallet> openWallet(String name, String password) async {
|
Future<NanoWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo =
|
final walletInfo =
|
||||||
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
||||||
final wallet = await NanoWalletBase.open(
|
|
||||||
name: name,
|
|
||||||
password: password,
|
|
||||||
walletInfo: walletInfo,
|
|
||||||
);
|
|
||||||
|
|
||||||
await wallet.init();
|
try {
|
||||||
await wallet.save();
|
final wallet = await NanoWalletBase.open(
|
||||||
return wallet;
|
name: name,
|
||||||
|
password: password,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
);
|
||||||
|
|
||||||
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
saveBackup(name);
|
||||||
|
return wallet;
|
||||||
|
} catch (_) {
|
||||||
|
await restoreWalletFilesFromBackup(name);
|
||||||
|
final wallet = await NanoWalletBase.open(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
);
|
||||||
|
|
||||||
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,16 +42,31 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
|
||||||
Future<PolygonWallet> openWallet(String name, String password) async {
|
Future<PolygonWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo =
|
final walletInfo =
|
||||||
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
||||||
final wallet = await PolygonWallet.open(
|
|
||||||
name: name,
|
|
||||||
password: password,
|
|
||||||
walletInfo: walletInfo,
|
|
||||||
);
|
|
||||||
|
|
||||||
await wallet.init();
|
try {
|
||||||
await wallet.save();
|
final wallet = await PolygonWallet.open(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
);
|
||||||
|
|
||||||
return wallet;
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
saveBackup(name);
|
||||||
|
return wallet;
|
||||||
|
} catch (_) {
|
||||||
|
await restoreWalletFilesFromBackup(name);
|
||||||
|
|
||||||
|
final wallet = await PolygonWallet.open(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
);
|
||||||
|
|
||||||
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -100,6 +115,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
|
||||||
password: password, name: currentName, walletInfo: currentWalletInfo);
|
password: password, name: currentName, walletInfo: currentWalletInfo);
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
await saveBackup(newName);
|
||||||
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
|
|
@ -191,7 +191,7 @@
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>Used for scan QR code</string>
|
<string>Used for scanning QR code and can be used to capture images for identification purposes by third-party providers.</string>
|
||||||
<key>NSDocumentsFolderUsageDescription</key>
|
<key>NSDocumentsFolderUsageDescription</key>
|
||||||
<string>We need access to documents folder for getting access to open/save backup file</string>
|
<string>We need access to documents folder for getting access to open/save backup file</string>
|
||||||
<key>NSFaceIDUsageDescription</key>
|
<key>NSFaceIDUsageDescription</key>
|
||||||
|
|
|
@ -65,7 +65,7 @@ class CWBitcoin extends Bitcoin {
|
||||||
@override
|
@override
|
||||||
Future<void> generateNewAddress(Object wallet, String label) async {
|
Future<void> generateNewAddress(Object wallet, String label) async {
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
await bitcoinWallet.walletAddresses.generateNewAddress(label: label);
|
await bitcoinWallet.walletAddresses.generateNewAddress(label: label, hd: bitcoinWallet.hd);
|
||||||
await wallet.save();
|
await wallet.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
|
||||||
import 'package:cake_wallet/entities/qr_view_data.dart';
|
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/qr_code_theme.dart';
|
import 'package:cake_wallet/themes/extensions/qr_code_theme.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
@ -7,7 +6,6 @@ import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dar
|
||||||
import 'package:cake_wallet/utils/brightness_util.dart';
|
import 'package:cake_wallet/utils/brightness_util.dart';
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
@ -146,10 +144,9 @@ class QRWidget extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: AutoSizeText(
|
child: Text(
|
||||||
addressListViewModel.address.address,
|
addressListViewModel.address.address,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
maxLines: addressListViewModel.wallet.type == WalletType.monero ? 2 : 1,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
|
|
|
@ -189,7 +189,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
output.loadContact(contact);
|
output.loadContact(contact);
|
||||||
},
|
},
|
||||||
validator: validator,
|
validator: validator,
|
||||||
selectedCurrency: sendViewModel.currency,
|
selectedCurrency: sendViewModel.selectedCryptoCurrency,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
if (output.isParsedAddress)
|
if (output.isParsedAddress)
|
||||||
|
|
|
@ -69,40 +69,44 @@ class SendTemplateCard extends StatelessWidget {
|
||||||
validator: sendTemplateViewModel.templateValidator),
|
validator: sendTemplateViewModel.templateValidator),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 20),
|
padding: EdgeInsets.only(top: 20),
|
||||||
child: AddressTextField(
|
child: Observer(
|
||||||
selectedCurrency: sendTemplateViewModel.cryptoCurrency,
|
builder: (context) {
|
||||||
controller: _addressController,
|
return AddressTextField(
|
||||||
onURIScanned: (uri) {
|
selectedCurrency: template.selectedCurrency,
|
||||||
final paymentRequest = PaymentRequest.fromUri(uri);
|
controller: _addressController,
|
||||||
_addressController.text = paymentRequest.address;
|
onURIScanned: (uri) {
|
||||||
_cryptoAmountController.text = paymentRequest.amount;
|
final paymentRequest = PaymentRequest.fromUri(uri);
|
||||||
},
|
_addressController.text = paymentRequest.address;
|
||||||
options: [
|
_cryptoAmountController.text = paymentRequest.amount;
|
||||||
AddressTextFieldOption.paste,
|
},
|
||||||
AddressTextFieldOption.qrCode,
|
options: [
|
||||||
AddressTextFieldOption.addressBook
|
AddressTextFieldOption.paste,
|
||||||
],
|
AddressTextFieldOption.qrCode,
|
||||||
onPushPasteButton: (context) async {
|
AddressTextFieldOption.addressBook
|
||||||
template.output.resetParsedAddress();
|
],
|
||||||
await template.output.fetchParsedAddress(context);
|
onPushPasteButton: (context) async {
|
||||||
},
|
template.output.resetParsedAddress();
|
||||||
onPushAddressBookButton: (context) async {
|
await template.output.fetchParsedAddress(context);
|
||||||
template.output.resetParsedAddress();
|
},
|
||||||
await template.output.fetchParsedAddress(context);
|
onPushAddressBookButton: (context) async {
|
||||||
},
|
template.output.resetParsedAddress();
|
||||||
buttonColor: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
|
await template.output.fetchParsedAddress(context);
|
||||||
borderColor: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
|
},
|
||||||
textStyle: TextStyle(
|
buttonColor: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
|
||||||
fontSize: 14,
|
borderColor: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
|
||||||
fontWeight: FontWeight.w500,
|
textStyle: TextStyle(
|
||||||
color: Colors.white,
|
fontSize: 14,
|
||||||
),
|
fontWeight: FontWeight.w500,
|
||||||
hintStyle: TextStyle(
|
color: Colors.white,
|
||||||
fontSize: 14,
|
),
|
||||||
fontWeight: FontWeight.w500,
|
hintStyle: TextStyle(
|
||||||
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor,
|
fontSize: 14,
|
||||||
),
|
fontWeight: FontWeight.w500,
|
||||||
validator: sendTemplateViewModel.addressValidator,
|
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor,
|
||||||
|
),
|
||||||
|
validator: sendTemplateViewModel.addressValidator,
|
||||||
|
);
|
||||||
|
}
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
|
|
|
@ -16,11 +16,15 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||||
class ExceptionHandler {
|
class ExceptionHandler {
|
||||||
static bool _hasError = false;
|
static bool _hasError = false;
|
||||||
static const _coolDownDurationInDays = 7;
|
static const _coolDownDurationInDays = 7;
|
||||||
|
static File? _file;
|
||||||
|
|
||||||
static void _saveException(String? error, StackTrace? stackTrace, {String? library}) async {
|
static void _saveException(String? error, StackTrace? stackTrace, {String? library}) async {
|
||||||
final appDocDir = await getApplicationDocumentsDirectory();
|
if (_file == null) {
|
||||||
|
final appDocDir = await getApplicationDocumentsDirectory();
|
||||||
|
|
||||||
|
_file = File('${appDocDir.path}/error.txt');
|
||||||
|
}
|
||||||
|
|
||||||
final file = File('${appDocDir.path}/error.txt');
|
|
||||||
final exception = {
|
final exception = {
|
||||||
"${DateTime.now()}": {
|
"${DateTime.now()}": {
|
||||||
"Error": "$error\n\n",
|
"Error": "$error\n\n",
|
||||||
|
@ -33,14 +37,14 @@ class ExceptionHandler {
|
||||||
==========================================================\n\n''';
|
==========================================================\n\n''';
|
||||||
|
|
||||||
/// don't save existing errors
|
/// don't save existing errors
|
||||||
if (file.existsSync()) {
|
if (_file!.existsSync()) {
|
||||||
final String fileContent = await file.readAsString();
|
final String fileContent = await _file!.readAsString();
|
||||||
if (fileContent.contains("${exception.values.first}")) {
|
if (fileContent.contains("${exception.values.first}")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file.writeAsStringSync(
|
_file!.writeAsStringSync(
|
||||||
"$exception $separator",
|
"$exception $separator",
|
||||||
mode: FileMode.append,
|
mode: FileMode.append,
|
||||||
);
|
);
|
||||||
|
@ -48,16 +52,18 @@ class ExceptionHandler {
|
||||||
|
|
||||||
static void _sendExceptionFile() async {
|
static void _sendExceptionFile() async {
|
||||||
try {
|
try {
|
||||||
final appDocDir = await getApplicationDocumentsDirectory();
|
if (_file == null) {
|
||||||
|
final appDocDir = await getApplicationDocumentsDirectory();
|
||||||
|
|
||||||
final file = File('${appDocDir.path}/error.txt');
|
_file = File('${appDocDir.path}/error.txt');
|
||||||
|
}
|
||||||
|
|
||||||
await _addDeviceInfo(file);
|
await _addDeviceInfo(_file!);
|
||||||
|
|
||||||
final MailOptions mailOptions = MailOptions(
|
final MailOptions mailOptions = MailOptions(
|
||||||
subject: 'Mobile App Issue',
|
subject: 'Mobile App Issue',
|
||||||
recipients: ['support@cakewallet.com'],
|
recipients: ['support@cakewallet.com'],
|
||||||
attachments: [file.path],
|
attachments: [_file!.path],
|
||||||
);
|
);
|
||||||
|
|
||||||
final result = await FlutterMailer.send(mailOptions);
|
final result = await FlutterMailer.send(mailOptions);
|
||||||
|
@ -67,7 +73,7 @@ class ExceptionHandler {
|
||||||
if (result.name == MailerResponse.sent.name ||
|
if (result.name == MailerResponse.sent.name ||
|
||||||
result.name == MailerResponse.saved.name ||
|
result.name == MailerResponse.saved.name ||
|
||||||
result.name == MailerResponse.android.name) {
|
result.name == MailerResponse.android.name) {
|
||||||
file.writeAsString("", mode: FileMode.write);
|
_file!.writeAsString("", mode: FileMode.write);
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
_saveException(e.toString(), s);
|
_saveException(e.toString(), s);
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -13,10 +13,20 @@ void main(List<String> args) async {
|
||||||
|
|
||||||
print('Appending "$name": "$text"');
|
print('Appending "$name": "$text"');
|
||||||
|
|
||||||
|
// add translation to all languages:
|
||||||
for (var lang in langs) {
|
for (var lang in langs) {
|
||||||
final fileName = getArbFileName(lang);
|
final fileName = getArbFileName(lang);
|
||||||
final translation = await getTranslation(text, lang);
|
final translation = await getTranslation(text, lang);
|
||||||
|
|
||||||
appendStringToArbFile(fileName, name, translation);
|
appendStringToArbFile(fileName, name, translation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print('Alphabetizing all files...');
|
||||||
|
|
||||||
|
for (var lang in langs) {
|
||||||
|
final fileName = getArbFileName(lang);
|
||||||
|
alphabetizeArbFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
print('Done!');
|
||||||
}
|
}
|
|
@ -30,8 +30,10 @@ void main(List<String> args) async {
|
||||||
missingDefaults[key] = arbObj[key] as String;
|
missingDefaults[key] = arbObj[key] as String;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (missingDefaults.isNotEmpty)
|
if (missingDefaults.isNotEmpty) {
|
||||||
await appendTranslations(lang, missingDefaults);
|
await appendTranslations(lang, missingDefaults);
|
||||||
|
alphabetizeArbFile(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,9 +47,7 @@ Map<String, dynamic> readArbFile(File file) {
|
||||||
}
|
}
|
||||||
|
|
||||||
String getArbFileName(String lang) {
|
String getArbFileName(String lang) {
|
||||||
final shortLang = lang
|
final shortLang = lang.split("-").first;
|
||||||
.split("-")
|
|
||||||
.first;
|
|
||||||
return "./res/values/strings_$shortLang.arb";
|
return "./res/values/strings_$shortLang.arb";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,3 +64,25 @@ List<String> getMissingKeysInArbFile(String fileName, Iterable<String> langKeys)
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void alphabetizeArbFile(String fileName) {
|
||||||
|
final file = File(fileName);
|
||||||
|
final arbObj = readArbFile(file);
|
||||||
|
|
||||||
|
final sortedKeys = arbObj.keys.toList()
|
||||||
|
..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
|
||||||
|
final Map<String, dynamic> sortedArbObj = {};
|
||||||
|
for (var key in sortedKeys) {
|
||||||
|
sortedArbObj[key] = arbObj[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
final outputContent = json
|
||||||
|
.encode(sortedArbObj)
|
||||||
|
.replaceAll('","', '",\n "')
|
||||||
|
.replaceAll('{"', '{\n "')
|
||||||
|
.replaceAll('"}', '"\n}')
|
||||||
|
.replaceAll('":"', '": "')
|
||||||
|
.replaceAll('\$ {', '\${');
|
||||||
|
|
||||||
|
file.writeAsStringSync(outputContent);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue