feat: initial hive -> SQLite (#2502)

* feat: initial hive -> SQLite

Includes migration script and initial work done to
remove dependency on hive generators.

Also contains some refactoring to make sync code
async.

Features new dev screen that lists all SQLite tables

Aspects that need testing:
- backups (from this version, and from previous versions as well)
- wallet loading / wallet list
- almost everything else
- renaming
- deletion
- sorting in wallet list
- wallet groups
- wallet seed/keys
- wallet switching
- wallet creation / restore - all methods
- wallet address page
- hardware wallet connection (and prompts from mall screens)
- contacts
- advanced settings in wallet creation
- derivation paths
- some minor wownero fixes
- base, hardware wallets

* fix: build errors

* fix: Bad state: No element in backup restore

* address comments from review

* fix: HardwareWalletType
This commit is contained in:
cyan
2025-10-31 22:37:15 +01:00
committed by GitHub
parent 718525926e
commit 76876181e5
121 changed files with 2623 additions and 917 deletions
+2 -2
View File
@@ -308,8 +308,8 @@ jobs:
rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet
exec timeout --signal=SIGKILL 900 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites/restore_wallet_through_seeds_flow_test.dart
- name: Test [cw_monero]
timeout-minutes: 2
run: cd cw_monero && flutter test
timeout-minutes: 15
run: cd cw_monero && flutter test --verbose
- name: Stop screen recording, encrypt and upload
if: always()
run: |
+2
View File
@@ -59,6 +59,8 @@ RUN set -o xtrace \
ffmpeg network-manager x11-utils xvfb psmisc \
# extra linux dependencies so flutter doesn't complain
mesa-utils \
# database
libsqlite3-0 libsqlite3-dev \
# aarch64-linux-gnu dependencies
g++-aarch64-linux-gnu gcc-aarch64-linux-gnu \
# x86_64-linux-gnu dependencies
+2
View File
@@ -22,6 +22,7 @@ class BaseWallet extends EVMChainWallet {
BaseWallet({
required super.walletInfo,
required super.password,
required super.derivationInfo,
super.mnemonic,
super.initialBalance,
super.privateKey,
@@ -150,6 +151,7 @@ class BaseWallet extends EVMChainWallet {
return BaseWallet(
walletInfo: walletInfo,
derivationInfo: await walletInfo.getDerivationInfo(),
password: password,
mnemonic: keysData.mnemonic,
privateKey: keysData.privateKey,
+18 -9
View File
@@ -11,7 +11,6 @@ import 'package:cw_base/base_mnemonics_exception.dart';
class BaseWalletService extends EVMChainWalletService<BaseWallet> {
BaseWalletService(
super.walletInfoSource,
super.isDirect, {
required this.client,
});
@@ -29,6 +28,7 @@ class BaseWalletService extends EVMChainWalletService<BaseWallet> {
final wallet = BaseWallet(
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
mnemonic: mnemonic,
password: credentials.password!,
passphrase: credentials.passphrase,
@@ -44,8 +44,10 @@ class BaseWalletService extends EVMChainWalletService<BaseWallet> {
@override
Future<BaseWallet> openWallet(String name, String password) async {
final walletInfo =
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
try {
final wallet = await BaseWallet.open(
@@ -84,6 +86,7 @@ class BaseWalletService extends EVMChainWalletService<BaseWallet> {
password: credentials.password!,
privateKey: credentials.privateKey,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@@ -97,14 +100,17 @@ class BaseWalletService extends EVMChainWalletService<BaseWallet> {
@override
Future<BaseWallet> restoreFromHardwareWallet(
EVMChainRestoreWalletFromHardware credentials) async {
credentials.walletInfo!.derivationInfo = DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0");
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
derivationInfo.derivationType = DerivationType.bip39;
derivationInfo.derivationPath = "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0";
await derivationInfo.save();
credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
credentials.walletInfo!.address = credentials.hwAccountData.address;
await credentials.walletInfo!.save();
final wallet = BaseWallet(
walletInfo: credentials.walletInfo!,
derivationInfo: derivationInfo,
password: credentials.password!,
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -128,6 +134,7 @@ class BaseWalletService extends EVMChainWalletService<BaseWallet> {
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
passphrase: credentials.passphrase,
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -142,8 +149,10 @@ class BaseWalletService extends EVMChainWalletService<BaseWallet> {
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final currentWallet = await BaseWallet.open(
password: password,
name: currentName,
@@ -158,6 +167,6 @@ class BaseWalletService extends EVMChainWalletService<BaseWallet> {
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
newWalletInfo.save();
}
}
+14 -6
View File
@@ -47,6 +47,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
BitcoinWalletBase({
required String password,
required WalletInfo walletInfo,
required DerivationInfo derivationInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Box<PayjoinSession> payjoinBox,
required EncryptionFileUtils encryptionFileUtils,
@@ -69,6 +70,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
xpub: xpub,
password: password,
walletInfo: walletInfo,
derivationInfo: derivationInfo,
unspentCoinsInfo: unspentCoinsInfo,
network: networkParam == null
? BitcoinNetwork.mainnet
@@ -133,7 +135,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
}) async {
late Uint8List seedBytes;
switch (walletInfo.derivationInfo?.derivationType) {
final derivationInfo = await walletInfo.getDerivationInfo();
switch (derivationInfo.derivationType) {
case DerivationType.bip39:
seedBytes = await bip39.mnemonicToSeed(
mnemonic,
@@ -152,6 +156,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
passphrase: passphrase ?? "",
password: password,
walletInfo: walletInfo,
derivationInfo: derivationInfo,
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses,
initialSilentAddresses: initialSilentAddresses,
@@ -212,20 +217,21 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
);
}
walletInfo.derivationInfo ??= DerivationInfo();
final derivationInfo = await walletInfo.getDerivationInfo();
// set the default if not present:
walletInfo.derivationInfo!.derivationPath ??=
derivationInfo.derivationPath ??=
snp?.derivationPath ?? electrum_path;
walletInfo.derivationInfo!.derivationType ??=
derivationInfo.derivationType ??=
snp?.derivationType ?? DerivationType.electrum;
await derivationInfo.save();
Uint8List? seedBytes = null;
final mnemonic = keysData.mnemonic;
final passphrase = keysData.passphrase;
if (mnemonic != null) {
switch (walletInfo.derivationInfo!.derivationType) {
switch (derivationInfo.derivationType) {
case DerivationType.electrum:
seedBytes =
await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
@@ -246,6 +252,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
password: password,
passphrase: passphrase,
walletInfo: walletInfo,
derivationInfo: derivationInfo,
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp?.addresses,
initialSilentAddresses: snp?.silentAddresses,
@@ -484,7 +491,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
: null;
final index = addressEntry?.index ?? 0;
final isChange = addressEntry?.isHidden == true ? 1 : 0;
final accountPath = walletInfo.derivationInfo?.derivationPath;
final derivationInfo = await walletInfo.getDerivationInfo();
final accountPath = derivationInfo.derivationPath;
final derivationPath =
accountPath != null ? "$accountPath/$isChange/$index" : null;
@@ -16,6 +16,10 @@ class BitcoinNewWalletCredentials extends WalletCredentials {
walletInfo: walletInfo,
password: password,
passphrase: passphrase,
derivationInfo: DerivationInfo(
derivationType: derivationType,
derivationPath: derivationPath,
),
);
final String? mnemonic;
+28 -12
View File
@@ -23,10 +23,9 @@ class BitcoinWalletService extends WalletService<
BitcoinRestoreWalletFromSeedCredentials,
BitcoinWalletFromKeysCredentials,
BitcoinRestoreWalletFromHardware> {
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource,
BitcoinWalletService(this.unspentCoinsInfoSource,
this.payjoinSessionSource, this.isDirect);
final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final Box<PayjoinSession> payjoinSessionSource;
final bool isDirect;
@@ -40,7 +39,13 @@ class BitcoinWalletService extends WalletService<
credentials.walletInfo?.network = network.value;
final String mnemonic;
switch ( credentials.walletInfo?.derivationInfo?.derivationType) {
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
derivationInfo.derivationType = credentials.derivationInfo?.derivationType ?? derivationInfo.derivationType;
derivationInfo.derivationPath = credentials.derivationInfo?.derivationPath ?? derivationInfo.derivationPath;
derivationInfo.description = credentials.derivationInfo?.description ?? derivationInfo.description;
derivationInfo.scriptType = credentials.derivationInfo?.scriptType ?? derivationInfo.scriptType;
await derivationInfo.save();
switch (derivationInfo.derivationType) {
case DerivationType.bip39:
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
@@ -51,6 +56,7 @@ class BitcoinWalletService extends WalletService<
mnemonic = await generateElectrumMnemonic();
break;
}
await derivationInfo.save();
final wallet = await BitcoinWalletBase.create(
mnemonic: mnemonic,
@@ -75,8 +81,10 @@ class BitcoinWalletService extends WalletService<
@override
Future<BitcoinWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
try {
final wallet = await BitcoinWalletBase.open(
password: password,
@@ -107,9 +115,11 @@ class BitcoinWalletService extends WalletService<
@override
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
@@ -123,8 +133,10 @@ class BitcoinWalletService extends WalletService<
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final currentWallet = await BitcoinWalletBase.open(
password: password,
name: currentName,
@@ -141,7 +153,7 @@ class BitcoinWalletService extends WalletService<
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
await newWalletInfo.save();
}
@override
@@ -149,15 +161,18 @@ class BitcoinWalletService extends WalletService<
{bool? isTestnet}) async {
final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
credentials.walletInfo?.network = network.value;
credentials.walletInfo?.derivationInfo?.derivationPath =
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
derivationInfo.derivationPath =
credentials.hwAccountData.derivationPath;
final xpub = convertZpubToXpub(credentials.hwAccountData.xpub!);
await credentials.walletInfo!.save();
final wallet = await BitcoinWallet(
password: credentials.password!,
xpub: xpub,
walletInfo: credentials.walletInfo!,
derivationInfo: derivationInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
networkParam: network,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -180,6 +195,7 @@ class BitcoinWalletService extends WalletService<
password: credentials.password!,
xpub: xpub,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
networkParam: network,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+10 -8
View File
@@ -63,6 +63,7 @@ abstract class ElectrumWalletBase
ElectrumWalletBase({
required String password,
required WalletInfo walletInfo,
required DerivationInfo derivationInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required this.network,
required this.encryptionFileUtils,
@@ -75,9 +76,8 @@ abstract class ElectrumWalletBase
ElectrumBalance? initialBalance,
CryptoCurrency? currency,
bool? alwaysScan,
})
: accountHD = getAccountHDWallet(currency, network, seedBytes, xpub,
walletInfo.derivationInfo, walletInfo.hardwareWalletType),
}) : accountHD =
getAccountHDWallet(currency, network, seedBytes, xpub, derivationInfo, walletInfo.hardwareWalletType),
syncStatus = NotConnectedSyncStatus(),
_password = password,
_feeRates = <int>[],
@@ -100,9 +100,10 @@ abstract class ElectrumWalletBase
this.unspentCoinsInfo = unspentCoinsInfo,
this.isTestnet = !network.isMainnet,
this._mnemonic = mnemonic,
super(walletInfo) {
super(walletInfo, derivationInfo) {
this.electrumClient = electrumClient ?? electrum.ElectrumClient();
this.walletInfo = walletInfo;
this.derivationInfo = derivationInfo;
transactionHistory = ElectrumTransactionHistory(
walletInfo: walletInfo,
password: password,
@@ -771,8 +772,9 @@ abstract class ElectrumWalletBase
pubKeyHex = hd.childKey(Bip32KeyIndex(utx.bitcoinAddressRecord.index)).publicKey.toHex();
}
final derivationPath =
"${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? electrum_path)}"
"${_hardenedDerivationPath(derivationInfo.derivationPath ?? electrum_path)}"
"/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
"/${utx.bitcoinAddressRecord.index}";
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
@@ -981,7 +983,7 @@ abstract class ElectrumWalletBase
// Get Derivation path for change Address since it is needed in Litecoin and BitcoinCash hardware Wallets
final changeDerivationPath =
"${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
"${_hardenedDerivationPath(derivationInfo.derivationPath ?? "m/0'")}"
"/${changeAddress.isHidden ? "1" : "0"}"
"/${changeAddress.index}";
utxoDetails.publicKeys[address.pubKeyHash()] =
@@ -1447,8 +1449,8 @@ abstract class ElectrumWalletBase
? SegwitAddresType.p2wpkh.toString()
: walletInfo.addressPageType.toString(),
'balance': balance[currency]?.toJSON(),
'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index,
'derivationPath': walletInfo.derivationInfo?.derivationPath,
'derivationTypeIndex': derivationInfo.derivationType?.index,
'derivationPath': derivationInfo.derivationPath,
'silent_addresses': walletAddresses.silentAddresses.map((addr) => addr.toJSON()).toList(),
'silent_address_index': walletAddresses.currentSilentAddressIndex.toString(),
'mweb_addresses': walletAddresses.mwebAddresses.map((addr) => addr.toJSON()).toList(),
+11 -6
View File
@@ -60,6 +60,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
LitecoinWalletBase({
required String password,
required WalletInfo walletInfo,
required DerivationInfo derivationInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required EncryptionFileUtils encryptionFileUtils,
Uint8List? seedBytes,
@@ -80,6 +81,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
passphrase: passphrase,
xpub: xpub,
walletInfo: walletInfo,
derivationInfo: derivationInfo,
unspentCoinsInfo: unspentCoinsInfo,
network: LitecoinNetwork.mainnet,
initialAddresses: initialAddresses,
@@ -168,6 +170,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
{required String mnemonic,
required String password,
required WalletInfo walletInfo,
required DerivationInfo derivationInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required EncryptionFileUtils encryptionFileUtils,
String? passphrase,
@@ -179,7 +182,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
Map<String, int>? initialChangeAddressIndex}) async {
late Uint8List seedBytes;
switch (walletInfo.derivationInfo?.derivationType) {
switch (derivationInfo.derivationType) {
case DerivationType.bip39:
seedBytes = await bip39.mnemonicToSeed(
mnemonic,
@@ -195,6 +198,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
mnemonic: mnemonic,
password: password,
walletInfo: walletInfo,
derivationInfo: derivationInfo,
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses,
initialMwebAddresses: initialMwebAddresses,
@@ -245,18 +249,17 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
);
}
walletInfo.derivationInfo ??= DerivationInfo();
final derivationInfo = await walletInfo.getDerivationInfo();
// set the default if not present:
walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path;
walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
derivationInfo.derivationPath ??= snp?.derivationPath ?? electrum_path;
derivationInfo.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
Uint8List? seedBytes = null;
final mnemonic = keysData.mnemonic;
final passphrase = keysData.passphrase;
if (mnemonic != null) {
switch (walletInfo.derivationInfo?.derivationType) {
switch (derivationInfo.derivationType) {
case DerivationType.bip39:
seedBytes = await bip39.mnemonicToSeed(
mnemonic,
@@ -269,12 +272,14 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
break;
}
}
await derivationInfo.save();
return LitecoinWallet(
mnemonic: keysData.mnemonic,
xpub: keysData.xPub,
password: password,
walletInfo: walletInfo,
derivationInfo: derivationInfo,
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp?.addresses,
initialMwebAddresses: snp?.mwebAddresses,
+24 -14
View File
@@ -22,10 +22,8 @@ class LitecoinWalletService extends WalletService<
BitcoinRestoreWalletFromSeedCredentials,
BitcoinRestoreWalletFromWIFCredentials,
BitcoinRestoreWalletFromHardware> {
LitecoinWalletService(
this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
LitecoinWalletService(this.unspentCoinsInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final bool isDirect;
@@ -35,7 +33,7 @@ class LitecoinWalletService extends WalletService<
@override
Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
final String mnemonic;
switch (credentials.walletInfo?.derivationInfo?.derivationType) {
switch (credentials.derivationInfo?.derivationType) {
case DerivationType.bip39:
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
@@ -52,6 +50,7 @@ class LitecoinWalletService extends WalletService<
password: credentials.password!,
passphrase: credentials.passphrase,
walletInfo: credentials.walletInfo!,
derivationInfo: credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@@ -68,8 +67,10 @@ class LitecoinWalletService extends WalletService<
@override
Future<LitecoinWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
try {
final wallet = await LitecoinWalletBase.open(
@@ -99,12 +100,14 @@ class LitecoinWalletService extends WalletService<
@override
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
// if there are no more litecoin wallets left, cleanup the neutrino db and other files created by mwebd:
if (walletInfoSource.values.where((info) => info.type == WalletType.litecoin).isEmpty) {
if ((await WalletInfo.selectList('type = ?', [WalletType.litecoin.index])).isEmpty) {
final appDirPath = (await getApplicationSupportDirectory()).path;
File neturinoDb = File('$appDirPath/neutrino.db');
File blockHeaders = File('$appDirPath/block_headers.bin');
@@ -136,8 +139,10 @@ class LitecoinWalletService extends WalletService<
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final currentWallet = await LitecoinWalletBase.open(
password: password,
name: currentName,
@@ -153,7 +158,7 @@ class LitecoinWalletService extends WalletService<
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
await newWalletInfo.save();
}
@override
@@ -161,13 +166,17 @@ class LitecoinWalletService extends WalletService<
{bool? isTestnet}) async {
final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet;
credentials.walletInfo?.network = network.value;
credentials.walletInfo?.derivationInfo?.derivationPath =
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
derivationInfo.derivationPath =
credentials.hwAccountData.derivationPath;
await derivationInfo.save();
credentials.walletInfo!.save();
final wallet = await LitecoinWallet(
password: credentials.password!,
xpub: credentials.hwAccountData.xpub,
walletInfo: credentials.walletInfo!,
derivationInfo: derivationInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@@ -193,6 +202,7 @@ class LitecoinWalletService extends WalletService<
passphrase: credentials.passphrase,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
derivationInfo: credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
+64
View File
@@ -1028,6 +1028,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.0"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
url: "https://pub.dev"
source: hosted
version: "2.5.4+6"
sqflite_common_ffi:
dependency: transitive
description:
name: sqflite_common_ffi
sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
url: "https://pub.dev"
source: hosted
version: "2.3.4+4"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
url: "https://pub.dev"
source: hosted
version: "2.4.1+1"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqlite3:
dependency: transitive
description:
name: sqlite3
sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
url: "https://pub.dev"
source: hosted
version: "2.9.0"
stack_trace:
dependency: transitive
description:
@@ -1060,6 +1116,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
url: "https://pub.dev"
source: hosted
version: "3.3.0+3"
term_glyph:
dependency: transitive
description:
@@ -28,6 +28,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
required String mnemonic,
required String password,
required WalletInfo walletInfo,
required DerivationInfo derivationInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Uint8List seedBytes,
required EncryptionFileUtils encryptionFileUtils,
@@ -41,6 +42,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
mnemonic: mnemonic,
password: password,
walletInfo: walletInfo,
derivationInfo: derivationInfo,
unspentCoinsInfo: unspentCoinsInfo,
network: BitcoinCashNetwork.mainnet,
initialAddresses: initialAddresses,
@@ -81,6 +83,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
mnemonic: mnemonic,
password: password,
walletInfo: walletInfo,
derivationInfo: await walletInfo.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses,
initialBalance: initialBalance,
@@ -134,6 +137,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
mnemonic: keysData.mnemonic!,
password: password,
walletInfo: walletInfo,
derivationInfo: await walletInfo.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp?.addresses.map((addr) {
try {
@@ -18,9 +18,8 @@ class BitcoinCashWalletService extends WalletService<
BitcoinCashRestoreWalletFromSeedCredentials,
BitcoinCashRestoreWalletFromWIFCredentials,
BitcoinCashNewWalletCredentials> {
BitcoinCashWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
BitcoinCashWalletService(this.unspentCoinsInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final bool isDirect;
@@ -51,8 +50,10 @@ class BitcoinCashWalletService extends WalletService<
@override
Future<BitcoinCashWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
try {
final wallet = await BitcoinCashWalletBase.open(
@@ -82,9 +83,11 @@ class BitcoinCashWalletService extends WalletService<
@override
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
@@ -98,8 +101,10 @@ class BitcoinCashWalletService extends WalletService<
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final currentWallet = await BitcoinCashWalletBase.open(
password: password,
name: currentName,
@@ -114,7 +119,7 @@ class BitcoinCashWalletService extends WalletService<
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
await newWalletInfo.save();
}
@override
+114
View File
@@ -0,0 +1,114 @@
import 'package:sqflite/sqflite.dart';
late Database db;
Future<void> initDb({String? pathOverride}) async {
db = await openDatabase(
pathOverride ?? "cake.db",
version: 1,
onCreate: (Database db, int version) async {
await db.execute(
'''
CREATE TABLE WalletInfo (
walletInfoId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
id TEXT NOT NULL,
name TEXT NOT NULL,
"type" INTEGER NOT NULL,
isRecovery INTEGER DEFAULT (0) NOT NULL,
walletInfoDerivationInfoId INTEGER NOT NULL,
restoreHeight INTEGER DEFAULT (0) NOT NULL,
"timestamp" INTEGER DEFAULT (0) NOT NULL,
dirPath TEXT NOT NULL,
"path" TEXT NOT NULL,
address TEXT NOT NULL,
yatEid TEXT,
yatLastUsedAddressRaw TEXT,
showIntroCakePayCard INTEGER DEFAULT (1),
addressPageType TEXT,
network TEXT,
hardwareWalletType INTEGER,
parentAddress TEXT,
hashedWalletIdentifier TEXT,
isNonSeedWallet INTEGER DEFAULT (0) NOT NULL,
sortOrder INTEGER DEFAULT (0) NOT NULL
);
''');
await db.execute(
'''
CREATE TABLE WalletInfoDerivationInfo (
walletInfoDerivationInfoId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
address TEXT NOT NULL,
balance TEXT NOT NULL,
transactionsCount INTEGER DEFAULT (0) NOT NULL,
derivationType INTEGER NOT NULL,
derivationPath TEXT,
scriptType TEXT,
description TEXT
);
''');
await db.execute(
'''
CREATE TABLE WalletInfoAddress (
walletInfoAddressId INTEGER PRIMARY KEY AUTOINCREMENT,
walletInfoId INTEGER,
"type" INTEGER NOT NULL,
address TEXT NOT NULL,
CONSTRAINT WalletInfoAddress_WalletInfo_FK FOREIGN KEY (walletInfoId) REFERENCES WalletInfo(walletInfoId)
);
''');
await db.execute(
'''
CREATE TABLE WalletInfoAddressInfo (
walletInfoAddressInfoId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
walletInfoId INTEGER NOT NULL,
mapKey INTEGER NOT NULL,
mapValueAccountIndex INTEGER NOT NULL,
mapValueAddress TEXT NOT NULL,
mapValueLabel TEXT NOT NULL,
CONSTRAINT WalletInfoAddressInfo_WalletInfo_FK FOREIGN KEY (walletInfoId) REFERENCES WalletInfo(walletInfoId)
);
''');
await db.execute(
'''
CREATE TABLE "WalletInfoAddressMap" (
walletInfoAddressMapId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
walletInfoId INTEGER NOT NULL,
addressKey TEXT NOT NULL,
addressValue TEXT NOT NULL,
CONSTRAINT WalletInfoAddress_WalletInfo_FK FOREIGN KEY (walletInfoId) REFERENCES WalletInfo(walletInfoId)
);
'''
);
}
);
}
Future<Map<String, dynamic>> dumpDb() async {
try {
return await _dumpDb();
} catch (e) {
return {
"error": e.toString(),
"stackTrace": StackTrace.current.toString(),
};
}
}
Future<List<String>> _getTableNames() async {
final tableNames = await db.rawQuery('SELECT name FROM sqlite_master WHERE type = "table"');
return tableNames.map((e) => (e["name"]).toString()).toList();
}
Future<Map<String, dynamic>> _dumpDb() async {
final tableNames = await _getTableNames();
final ret = <String, dynamic>{};
for (final tableName in tableNames) {
ret[tableName] = await db.query(tableName);
}
return ret;
}
+13 -12
View File
@@ -1,4 +1,3 @@
import 'package:cw_core/address_info.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
@@ -9,8 +8,12 @@ abstract class WalletAddresses {
allAddressesMap = {},
addressInfos = {},
usedAddresses = {},
hiddenAddresses = walletInfo.hiddenAddresses?.toSet() ?? {},
manualAddresses = walletInfo.manualAddresses?.toSet() ?? {};
hiddenAddresses = {},
manualAddresses = {} {
walletInfo.getUsedAddresses().then((value) => usedAddresses = value);
walletInfo.getHiddenAddresses().then((value) => hiddenAddresses = value);
walletInfo.getManualAddresses().then((value) => manualAddresses = value);
}
final WalletInfo walletInfo;
@@ -47,7 +50,7 @@ abstract class WalletAddresses {
return tmp;
}
Map<int, List<AddressInfo>> addressInfos;
Map<int, List<WalletInfoAddressInfo>> addressInfos;
Set<String> usedAddresses;
@@ -62,15 +65,13 @@ abstract class WalletAddresses {
Future<void> saveAddressesInBox() async {
try {
walletInfo.address = address;
walletInfo.addresses = addressesMap;
walletInfo.addressInfos = addressInfos;
walletInfo.usedAddresses = usedAddresses.toList();
walletInfo.hiddenAddresses = hiddenAddresses.toList();
walletInfo.manualAddresses = manualAddresses.toList();
walletInfo.setAddresses(addressesMap);
walletInfo.setAddressInfos(addressInfos);
walletInfo.setUsedAddresses(usedAddresses.toList());
walletInfo.setHiddenAddresses(hiddenAddresses.toList());
walletInfo.setManualAddresses(manualAddresses.toList());
if (walletInfo.isInBox) {
await walletInfo.save();
}
await walletInfo.save();
} catch (e) {
printV(e.toString());
}
@@ -1,13 +0,0 @@
import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/account_list.dart';
import 'package:cw_core/wallet_info.dart';
abstract class WalletAddressesWithAccount<T> extends WalletAddresses {
WalletAddressesWithAccount(WalletInfo walletInfo) : super(walletInfo);
// T get account;
// set account(T account);
AccountList<T> get accountList;
}
+2 -1
View File
@@ -15,12 +15,13 @@ import 'package:cw_core/wallet_type.dart';
abstract class WalletBase<BalanceType extends Balance, HistoryType extends TransactionHistoryBase,
TransactionType extends TransactionInfo> {
WalletBase(this.walletInfo);
WalletBase(this.walletInfo, this.derivationInfo);
static String idFor(String name, WalletType type) =>
walletTypeToString(type).toLowerCase() + '_' + name;
WalletInfo walletInfo;
DerivationInfo derivationInfo;
WalletType get type => walletInfo.type;
+1 -5
View File
@@ -10,11 +10,7 @@ abstract class WalletCredentials {
this.passphrase,
this.derivationInfo,
this.hardwareWalletType,
}) {
if (this.walletInfo != null && derivationInfo != null) {
this.walletInfo!.derivationInfo = derivationInfo;
}
}
});
final String name;
final int? height;
+456 -94
View File
@@ -1,47 +1,239 @@
import 'dart:async';
import 'package:cw_core/address_info.dart';
import 'package:cw_core/db/sqlite.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart';
import 'package:sqflite/sqflite.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/wallet_info_legacy.dart' as wiLegacy;
part 'wallet_info.g.dart';
Future<void> performHiveMigration() async {
try {
if (!CakeHive.isAdapterRegistered(wiLegacy.WalletInfo.typeId)) {
CakeHive.registerAdapter(wiLegacy.WalletInfoAdapter());
}
if (!CakeHive.isAdapterRegistered(DERIVATION_TYPE_TYPE_ID)) {
CakeHive.registerAdapter(wiLegacy.DerivationTypeAdapter());
}
if (!CakeHive.isAdapterRegistered(wiLegacy.DerivationInfo.typeId)) {
CakeHive.registerAdapter(wiLegacy.DerivationInfoAdapter());
}
if (!CakeHive.isAdapterRegistered(HARDWARE_WALLET_TYPE_TYPE_ID)) {
CakeHive.registerAdapter(wiLegacy.HardwareWalletTypeAdapter());
}
final walletInfoBox = await CakeHive.openBox<wiLegacy.WalletInfo>(wiLegacy.WalletInfo.boxName);
await wiLegacy.WalletInfo.migrateAllToSqlite(walletInfoBox);
} catch (e) {
printV('Error performing Hive migration: $e, continuing anyway');
}
}
@HiveType(typeId: DERIVATION_TYPE_TYPE_ID)
enum DerivationType {
@HiveField(0)
unknown,
@HiveField(1)
def, // default is a reserved word
@HiveField(2)
nano,
@HiveField(3)
bip39,
@HiveField(4)
electrum,
}
@HiveType(typeId: HARDWARE_WALLET_TYPE_TYPE_ID)
enum HardwareWalletType {
@HiveField(0)
ledger,
@HiveField(1)
bitbox,
@HiveField(2)
cupcake,
@HiveField(3)
coldcard,
@HiveField(4)
seedsigner,
@HiveField(5)
keystone,
@HiveField(6)
trezor,
}
@HiveType(typeId: DerivationInfo.typeId)
class DerivationInfo extends HiveObject {
enum WalletInfoAddressType {
used,
hidden,
manual,
}
class WalletInfoAddressInfo {
WalletInfoAddressInfo({
this.id = 0,
required this.walletInfoId,
required this.mapKey,
required this.accountIndex,
required this.address,
required this.label,
});
int id;
int walletInfoId;
int mapKey;
int accountIndex;
String address;
String label;
static String get tableName => 'walletInfoAddressInfo';
static String get selfIdColumn => "${tableName}Id";
static Future<List<WalletInfoAddressInfo>> selectList(int walletInfoId) async {
final query = await db.query(tableName, where: 'walletInfoId = ?', whereArgs: [walletInfoId]);
return List.generate(query.length, (index) => WalletInfoAddressInfo.fromJson(query[index]));
}
static Future<int> deleteByWalletInfoId(int walletInfoId) async {
return await db.delete(tableName, where: 'walletInfoId = ?', whereArgs: [walletInfoId]);
}
static Future<int> insert({
required int walletInfoId,
required int mapKey,
required int accountIndex,
required String address,
required String label,
}) async {
return await db.insert(tableName, {
"walletInfoId": walletInfoId,
"mapKey": mapKey,
"mapValueAccountIndex": accountIndex,
"mapValueAddress": address,
"mapValueLabel": label,
});
}
Map<String, dynamic> toJson() {
return {
selfIdColumn: id,
"walletInfoId": walletInfoId,
"mapKey": mapKey,
"mapValueAccountIndex": accountIndex,
"mapValueAddress": address,
"mapValueLabel": label,
};
}
factory WalletInfoAddressInfo.fromJson(Map<String, dynamic> json) {
return WalletInfoAddressInfo(
id: json[selfIdColumn] as int,
walletInfoId: json['walletInfoId'] as int,
mapKey: json['mapKey'] as int,
accountIndex: json['mapValueAccountIndex'] as int,
address: json['mapValueAddress'] as String,
label: json['mapValueLabel'] as String,
);
}
}
class WalletInfoAddressMap {
WalletInfoAddressMap({
required this.id,
required this.walletInfoId,
required this.addressKey,
required this.addressValue,
});
int id;
int walletInfoId;
String addressKey;
String addressValue;
static String get tableName => 'walletInfoAddressMap';
static String get selfIdColumn => "${tableName}Id";
static Future<List<WalletInfoAddressMap>> selectList(int walletInfoId) async {
final query = await db.query(tableName, where: 'walletInfoId = ?', whereArgs: [walletInfoId]);
return List.generate(query.length, (index) => WalletInfoAddressMap.fromJson(query[index]));
}
static Future<int> deleteByWalletInfoId(int walletInfoId) async {
return await db.delete(tableName, where: 'walletInfoId = ?', whereArgs: [walletInfoId]);
}
static Future<int> insert(int walletInfoId, String addressKey, String addressValue) async {
return await db.insert(tableName, {
"walletInfoId": walletInfoId,
"addressKey": addressKey,
"addressValue": addressValue,
});
}
Map<String, dynamic> toJson() {
return {
selfIdColumn: id,
"walletInfoId": walletInfoId,
"addressKey": addressKey,
"addressValue": addressValue,
};
}
factory WalletInfoAddressMap.fromJson(Map<String, dynamic> json) {
return WalletInfoAddressMap(
id: json[selfIdColumn] as int,
walletInfoId: json['walletInfoId'] as int,
addressKey: json['addressKey'] as String,
addressValue: json['addressValue'] as String,
);
}
}
class WalletInfoAddress {
WalletInfoAddress({
this.id = 0,
required this.walletInfoId,
required this.type,
required this.address,
});
int id;
int walletInfoId;
WalletInfoAddressType type;
String address;
static String get tableName => 'walletInfoAddress';
static String get selfIdColumn => "${tableName}Id";
static Future<List<WalletInfoAddress>> selectList(int walletInfoId, WalletInfoAddressType type) async {
final query = await db.query(tableName, where: 'walletInfoId = ? AND type = ?', whereArgs: [walletInfoId, type.index]);
return List.generate(query.length, (index) => WalletInfoAddress.fromJson(query[index]));
}
static Future<int> deleteByAddress(int walletInfoId, WalletInfoAddressType type, String address) async {
return await db.delete(tableName, where: 'walletInfoId = ? AND type = ? AND address = ?', whereArgs: [walletInfoId, type.index, address]);
}
static Future<int> deleteByType(int walletInfoId, WalletInfoAddressType type) async {
return await db.delete(tableName, where: 'walletInfoId = ? AND type = ?', whereArgs: [walletInfoId, type.index]);
}
static Future<int> insert(int walletInfoId, WalletInfoAddressType type, String address) async {
final select = await db.query(tableName, where: 'walletInfoId = ? AND type = ? AND address = ?', whereArgs: [walletInfoId, type.index, address]);
if (select.isNotEmpty) {
return select[0][selfIdColumn] as int;
}
return await db.insert(tableName, {
"walletInfoId": walletInfoId,
"type": type.index,
"address": address,
});
}
Map<String, dynamic> toJson() {
return {
selfIdColumn: id,
"walletInfoId": walletInfoId,
"type": type.index,
"address": address,
};
}
factory WalletInfoAddress.fromJson(Map<String, dynamic> json) {
return WalletInfoAddress(
id: json[selfIdColumn] as int,
walletInfoId: json['walletInfoId'] as int,
type: WalletInfoAddressType.values[json['type'] as int],
address: json['address'] as String,
);
}
}
class DerivationInfo {
DerivationInfo({
this.id = 0,
this.derivationType,
this.derivationPath,
this.balance = "",
@@ -51,33 +243,77 @@ class DerivationInfo extends HiveObject {
this.description,
});
static const typeId = DERIVATION_INFO_TYPE_ID;
int id;
static String get tableName => 'walletInfoDerivationInfo';
static String get selfIdColumn => "${tableName}Id";
@HiveField(0, defaultValue: '')
String address;
@HiveField(1, defaultValue: '')
String balance;
@HiveField(2, defaultValue: 0)
int transactionsCount;
@HiveField(3)
DerivationType? derivationType;
@HiveField(4)
String? derivationPath;
String? scriptType;
String? description;
@HiveField(5)
final String? scriptType;
static Future<List<DerivationInfo>> selectList(String where, List<dynamic> whereArgs) async {
final query = await db.query(
tableName,
columns: [
selfIdColumn,
'address',
'balance',
'transactionsCount',
'derivationType',
'derivationPath',
'scriptType',
'description',
],
where: where.isNotEmpty ? where : "1 = 1",
whereArgs: whereArgs.isNotEmpty ? whereArgs : null,
);
return List.generate(query.length, (index) => DerivationInfo.fromJson(query[index]));
}
@HiveField(6)
final String? description;
Map<String, dynamic> toJson() {
return {
selfIdColumn: id,
"address": address,
"balance": balance,
"transactionsCount": transactionsCount,
"derivationType": derivationType?.index,
"derivationPath": derivationPath,
"scriptType": scriptType,
"description": description,
};
}
factory DerivationInfo.fromJson(Map<String, dynamic> json ) {
return DerivationInfo(
id: json[selfIdColumn] as int,
derivationType: DerivationType.values[json['derivationType'] as int? ?? 0],
derivationPath: json['derivationPath'] as String?,
balance: json['balance'] as String? ?? "",
address: json['address'] as String? ?? "",
transactionsCount: json['transactionsCount'] as int? ?? 0,
scriptType: json['scriptType'] as String?,
description: json['description'] as String?,
);
}
Future<int> save() async {
final json = toJson();
if (json[selfIdColumn] == 0) {
json[selfIdColumn] = null;
}
id = await db.insert(tableName, json, conflictAlgorithm: ConflictAlgorithm.replace);
return id;
}
}
@HiveType(typeId: WalletInfo.typeId)
class WalletInfo extends HiveObject {
class WalletInfo {
WalletInfo(
this.internalId,
this.id,
this.name,
this.type,
@@ -90,11 +326,12 @@ class WalletInfo extends HiveObject {
this.yatEid,
this.yatLastUsedAddressRaw,
this.showIntroCakePayCard,
this.derivationInfo,
this.derivationInfoId,
this.hardwareWalletType,
this.parentAddress,
this.hashedWalletIdentifier,
this.isNonSeedWallet,
this.sortOrder,
) : _yatLastUsedAddressController = StreamController<String>.broadcast();
factory WalletInfo.external({
@@ -110,13 +347,15 @@ class WalletInfo extends HiveObject {
bool? showIntroCakePayCard,
String yatEid = '',
String yatLastUsedAddressRaw = '',
DerivationInfo? derivationInfo,
int? derivationInfoId,
HardwareWalletType? hardwareWalletType,
String? parentAddress,
String? hashedWalletIdentifier,
bool? isNonSeedWallet,
int? sortOrder,
}) {
return WalletInfo(
0,
id,
name,
type,
@@ -129,96 +368,135 @@ class WalletInfo extends HiveObject {
yatEid,
yatLastUsedAddressRaw,
showIntroCakePayCard,
derivationInfo,
derivationInfoId ?? -1,
hardwareWalletType,
parentAddress,
hashedWalletIdentifier,
isNonSeedWallet ?? false,
sortOrder ?? 0,
);
}
static const typeId = WALLET_INFO_TYPE_ID;
static const boxName = 'WalletInfo';
static String get tableName => 'walletInfo';
static String get selfIdColumn => "${tableName}Id";
int internalId;
@HiveField(0, defaultValue: '')
String id;
@HiveField(1, defaultValue: '')
String name;
@HiveField(2)
WalletType type;
@HiveField(3, defaultValue: false)
bool isRecovery;
@HiveField(4, defaultValue: 0)
int restoreHeight;
@HiveField(5, defaultValue: 0)
int timestamp;
@HiveField(6, defaultValue: '')
String dirPath;
@HiveField(7, defaultValue: '')
String path;
@HiveField(8, defaultValue: '')
String address;
Future<Map<String, String>> getAddresses() async {
final list = await WalletInfoAddressMap.selectList(internalId);
return Map.fromEntries(list.map((e) => MapEntry(e.addressKey, e.addressValue)));
}
@HiveField(10)
Map<String, String>? addresses;
Future<void> setAddresses(Map<String, String> addresses) async {
await WalletInfoAddressMap.deleteByWalletInfoId(internalId);
final keys = addresses.keys.toList();
for (final address in keys) {
await WalletInfoAddressMap.insert(internalId, address, addresses[address]!);
}
}
@HiveField(11)
String? yatEid;
@HiveField(12)
String? yatLastUsedAddressRaw;
@HiveField(13)
bool? showIntroCakePayCard;
Future<Map<int, List<WalletInfoAddressInfo>>> getAddressInfos() async {
final list = await WalletInfoAddressInfo.selectList(internalId);
final ret = <int, List<WalletInfoAddressInfo>>{};
for (final e in list) {
ret[e.mapKey] ??= [];
ret[e.mapKey]!.add(e);
}
return ret;
}
@HiveField(14)
Map<int, List<AddressInfo>>? addressInfos;
Future<void> setAddressInfos(Map<int, List<WalletInfoAddressInfo>> addressInfos) async {
await WalletInfoAddressInfo.deleteByWalletInfoId(internalId);
final entries = addressInfos.entries.toList();
for (final addressInfo in entries) {
for (final info in addressInfo.value) {
await WalletInfoAddressInfo.insert(
walletInfoId: internalId,
mapKey: addressInfo.key,
accountIndex: info.accountIndex,
address: info.address,
label: info.label,
);
}
}
}
@HiveField(15)
List<String>? usedAddresses;
Future<Set<String>> getUsedAddresses() async {
final list = await WalletInfoAddress.selectList(internalId, WalletInfoAddressType.used);
return list.map((e) => e.address).toSet();
}
Future<void> setUsedAddresses(List<String> addresses) async {
await WalletInfoAddress.deleteByType(internalId, WalletInfoAddressType.used);
for (final address in addresses) {
await WalletInfoAddress.insert(internalId, WalletInfoAddressType.used, address);
}
}
@deprecated
@HiveField(16)
DerivationType? derivationType; // no longer used
Future<Set<String>> getHiddenAddresses() async {
final list = await WalletInfoAddress.selectList(internalId, WalletInfoAddressType.hidden);
return list.map((e) => e.address).toSet();
}
Future<void> setHiddenAddresses(List<String> addresses) async {
await WalletInfoAddress.deleteByType(internalId, WalletInfoAddressType.hidden);
for (final address in addresses) {
await WalletInfoAddress.insert(internalId, WalletInfoAddressType.hidden, address);
}
}
@deprecated
@HiveField(17)
String? derivationPath; // no longer used
Future<Set<String>> getManualAddresses() async {
final list = await WalletInfoAddress.selectList(internalId, WalletInfoAddressType.manual);
return list.map((e) => e.address).toSet();
}
Future<void> setManualAddresses(List<String> addresses) async {
await WalletInfoAddress.deleteByType(internalId, WalletInfoAddressType.manual);
for (final address in addresses) {
await WalletInfoAddress.insert(internalId, WalletInfoAddressType.manual, address);
}
}
Future<void> addAddress(String address, WalletInfoAddressType type) async {
await WalletInfoAddress.insert(internalId, type, address);
}
@HiveField(18)
String? addressPageType;
@HiveField(19)
String? network;
@HiveField(20)
DerivationInfo? derivationInfo;
@HiveField(21)
int derivationInfoId;
DerivationInfo? _derivationInfo;
Future<DerivationInfo> getDerivationInfo() async {
if (_derivationInfo != null) {
return _derivationInfo!;
}
final list = await DerivationInfo.selectList('walletInfoDerivationInfoId = ?', [derivationInfoId]);
if (list.isEmpty) {
final di = DerivationInfo(
id: 0,
derivationType: DerivationType.unknown,
);
derivationInfoId = await di.save();
_derivationInfo = di;
return di;
}
_derivationInfo = list[0];
return _derivationInfo!;
}
HardwareWalletType? hardwareWalletType;
@HiveField(22)
String? parentAddress;
@HiveField(23)
List<String>? hiddenAddresses;
@HiveField(24)
List<String>? manualAddresses;
@HiveField(25)
String? hashedWalletIdentifier;
@HiveField(26, defaultValue: false)
bool isNonSeedWallet;
int sortOrder;
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
@@ -248,6 +526,90 @@ class WalletInfo extends HiveObject {
StreamController<String> _yatLastUsedAddressController;
Map<String, dynamic> toJson() => {
selfIdColumn: internalId,
"id": id,
"name": name,
"type": type.index,
"isRecovery": isRecovery ? 1 : 0,
"restoreHeight": restoreHeight,
"timestamp": timestamp,
"dirPath": dirPath,
"path": path,
"address": address,
"yatEid": yatEid,
"yatLastUsedAddressRaw": yatLastUsedAddressRaw,
"showIntroCakePayCard": showIntroCakePayCard == true ? 1 : 0, // SQL regression: null -> false
"walletInfoDerivationInfoId": derivationInfoId,
"hardwareWalletType": hardwareWalletType?.index,
"parentAddress": parentAddress,
"hashedWalletIdentifier": hashedWalletIdentifier,
"isNonSeedWallet": isNonSeedWallet ? 1 : 0,
"sortOrder": sortOrder,
};
factory WalletInfo.fromJson(Map<String, dynamic> json) {
return WalletInfo(
json[selfIdColumn] as int,
json['id'] as String,
json['name'] as String,
WalletType.values[json['type'] as int],
(json['isRecovery'] as int) == 1,
json['restoreHeight'] as int,
json['timestamp'] as int,
json['dirPath'] as String,
json['path'] as String,
json['address'] as String,
json['yatEid'] as String?,
json['yatLastUsedAddressRaw'] as String?,
(json['showIntroCakePayCard'] as int) == 1,
json['walletInfoDerivationInfoId'] as int,
json['hardwareWalletType'] == null ? null : HardwareWalletType.values[json['hardwareWalletType'] as int],
json['parentAddress'] as String?,
json['hashedWalletIdentifier'] as String?,
(json['isNonSeedWallet'] as int) == 1,
json['sortOrder'] as int? ?? 0,
);
}
Future<int> save() async {
final json = toJson();
if (json[selfIdColumn] == 0) {
json[selfIdColumn] = null;
}
if (_derivationInfo != null) {
derivationInfoId = await _derivationInfo!.save();
}
internalId = await db.insert(tableName, json, conflictAlgorithm: ConflictAlgorithm.replace);
return internalId;
}
static Future<int> delete(WalletInfo walletInfo) async {
return await db.delete(tableName, where: 'id = ?', whereArgs: [walletInfo.id]);
}
static Future<List<WalletInfo>> selectList(String where, List<dynamic> whereArgs, {String orderBy = 'sortOrder'}) async {
final list = await db.query(
tableName,
where: where.isNotEmpty ? where : "1 = 1",
whereArgs: whereArgs.isNotEmpty ? whereArgs : null,
orderBy: orderBy,
);
return List.generate(list.length, (index) => WalletInfo.fromJson(list[index]));
}
static Future<List<WalletInfo>> getAll() async {
return selectList('', []);
}
static Future<WalletInfo?> get(String name, WalletType type) async {
final list = await selectList('name = ? AND type = ?', [name, type.index]);
if (list.isEmpty) {
return null;
}
return list[0];
}
Future<void> updateRestoreHeight(int height) async {
restoreHeight = height;
await save();
+308
View File
@@ -0,0 +1,308 @@
// NOTE: This code was generated by Hive, but since Hive is on the way out, this
// code no longer uses hive generators, so when we get rid of hive we also will
// be able to get rid of
import 'dart:async';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_info.dart' as newWi;
import 'package:cw_core/address_info.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart';
part 'wallet_info_legacy.part.dart';
// // @HiveType(typeId: DERIVATION_TYPE_TYPE_ID)
// enum DerivationType {
// // @HiveField(0)
// unknown,
// // @HiveField(1)
// def, // default is a reserved word
// // @HiveField(2)
// nano,
// // @HiveField(3)
// bip39,
// // @HiveField(4)
// electrum,
// }
// // @HiveType(typeId: HARDWARE_WALLET_TYPE_TYPE_ID)
// enum HardwareWalletType {
// // @HiveField(0)
// ledger,
// }
// @HiveType(typeId: DerivationInfo.typeId)
class DerivationInfo extends HiveObject {
DerivationInfo({
this.derivationType,
this.derivationPath,
this.balance = "",
this.address = "",
this.transactionsCount = 0,
this.scriptType,
this.description,
});
static const typeId = DERIVATION_INFO_TYPE_ID;
// @HiveField(0, defaultValue: '')
String address;
// @HiveField(1, defaultValue: '')
String balance;
// @HiveField(2, defaultValue: 0)
int transactionsCount;
// @HiveField(3)
newWi.DerivationType? derivationType;
// @HiveField(4)
String? derivationPath;
// @HiveField(5)
final String? scriptType;
// @HiveField(6)
final String? description;
}
// @HiveType(typeId: WalletInfo.typeId)
class WalletInfo extends HiveObject {
WalletInfo(
this.id,
this.name,
this.type,
this.isRecovery,
this.restoreHeight,
this.timestamp,
this.dirPath,
this.path,
this.address,
this.yatEid,
this.yatLastUsedAddressRaw,
this.showIntroCakePayCard,
this.derivationInfo,
this.hardwareWalletType,
this.parentAddress,
this.hashedWalletIdentifier,
this.isNonSeedWallet,
) : _yatLastUsedAddressController = StreamController<String>.broadcast();
factory WalletInfo.external({
required String id,
required String name,
required WalletType type,
required bool isRecovery,
required int restoreHeight,
required DateTime date,
required String dirPath,
required String path,
required String address,
bool? showIntroCakePayCard,
String yatEid = '',
String yatLastUsedAddressRaw = '',
DerivationInfo? derivationInfo,
newWi.HardwareWalletType? hardwareWalletType,
String? parentAddress,
String? hashedWalletIdentifier,
bool? isNonSeedWallet,
}) {
return WalletInfo(
id,
name,
type,
isRecovery,
restoreHeight,
date.millisecondsSinceEpoch,
dirPath,
path,
address,
yatEid,
yatLastUsedAddressRaw,
showIntroCakePayCard,
derivationInfo,
hardwareWalletType,
parentAddress,
hashedWalletIdentifier,
isNonSeedWallet ?? false,
);
}
static const typeId = WALLET_INFO_TYPE_ID;
static const boxName = 'WalletInfo';
// @HiveField(0, defaultValue: '')
String id;
// @HiveField(1, defaultValue: '')
String name;
// @HiveField(2)
WalletType type;
// @HiveField(3, defaultValue: false)
bool isRecovery;
// @HiveField(4, defaultValue: 0)
int restoreHeight;
// @HiveField(5, defaultValue: 0)
int timestamp;
// @HiveField(6, defaultValue: '')
String dirPath;
// @HiveField(7, defaultValue: '')
String path;
// @HiveField(8, defaultValue: '')
String address;
// @HiveField(10)
Map<String, String>? addresses;
// @HiveField(11)
String? yatEid;
// @HiveField(12)
String? yatLastUsedAddressRaw;
// @HiveField(13)
bool? showIntroCakePayCard;
// @HiveField(14)
Map<int, List<AddressInfo>>? addressInfos;
// @HiveField(15)
List<String>? usedAddresses;
@deprecated
// @HiveField(16)
newWi.DerivationType? derivationType; // no longer used
@deprecated
// @HiveField(17)
String? derivationPath; // no longer used
// @HiveField(18)
String? addressPageType;
// @HiveField(19)
String? network;
// @HiveField(20)
DerivationInfo? derivationInfo;
// @HiveField(21)
newWi.HardwareWalletType? hardwareWalletType;
// @HiveField(22)
String? parentAddress;
// @HiveField(23)
List<String>? hiddenAddresses;
// @HiveField(24)
List<String>? manualAddresses;
// @HiveField(25)
String? hashedWalletIdentifier;
// @HiveField(26, defaultValue: false)
bool isNonSeedWallet;
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
set yatLastUsedAddress(String address) {
yatLastUsedAddressRaw = address;
_yatLastUsedAddressController.add(address);
}
String get yatEmojiId => yatEid ?? '';
bool get isShowIntroCakePayCard {
if (showIntroCakePayCard == null) {
return type != WalletType.haven;
}
return showIntroCakePayCard!;
}
bool get isHardwareWallet => hardwareWalletType != null;
DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp);
Stream<String> get yatLastUsedAddressStream => _yatLastUsedAddressController.stream;
StreamController<String> _yatLastUsedAddressController;
Future<void> updateRestoreHeight(int height) async {
restoreHeight = height;
await save();
}
Future<void> migrateToSqlite() async {
final di = newWi.DerivationInfo(
id: 0,
derivationType: derivationInfo?.derivationType ?? derivationType ?? newWi.DerivationType.unknown,
derivationPath: derivationInfo?.derivationPath ?? derivationPath ?? '',
);
final derivationInfoId = await di.save();
final walletInfo = newWi.WalletInfo(
0,
id,
name,
type,
isRecovery,
restoreHeight,
timestamp,
dirPath,
path,
address,
yatEid,
yatLastUsedAddressRaw,
showIntroCakePayCard,
derivationInfoId,
hardwareWalletType,
parentAddress,
hashedWalletIdentifier,
isNonSeedWallet,
0,
);
final wiId = await walletInfo.save();
for (final address in usedAddresses ?? <String>[]) {
await newWi.WalletInfoAddress.insert(wiId, newWi.WalletInfoAddressType.used, address);
}
for (final address in hiddenAddresses ?? <String>[]) {
await newWi.WalletInfoAddress.insert(wiId, newWi.WalletInfoAddressType.hidden, address);
}
for (final address in manualAddresses ?? <String>[]) {
await newWi.WalletInfoAddress.insert(wiId, newWi.WalletInfoAddressType.manual, address);
}
for (int i = 0; i < (addressInfos?.length ?? 0); i++) {
for (final address in addressInfos![i] ?? <AddressInfo>[]) {
await newWi.WalletInfoAddressInfo.insert(
walletInfoId: wiId,
mapKey: i,
accountIndex: address.accountIndex??0,
address: address.address,
label: address.label,
);
}
}
await walletInfo.setAddresses(addresses ?? <String, String>{});
}
static Future<void> migrateAllToSqlite(final Box<WalletInfo> box) async {
printV('Migrating WalletInfo to SQLite: start');
final sw = Stopwatch()..start();
final list = box.values.toList();
for (final wi in list) {
await wi.migrateToSqlite();
await wi.delete();
}
printV('Migrating WalletInfo to SQLite: end (${sw.elapsedMilliseconds}ms)');
}
}
+287
View File
@@ -0,0 +1,287 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'wallet_info_legacy.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class DerivationInfoAdapter extends TypeAdapter<DerivationInfo> {
@override
final int typeId = 17;
@override
DerivationInfo read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return DerivationInfo(
derivationType: fields[3] as newWi.DerivationType?,
derivationPath: fields[4] as String?,
balance: fields[1] == null ? '' : fields[1] as String,
address: fields[0] == null ? '' : fields[0] as String,
transactionsCount: fields[2] == null ? 0 : fields[2] as int,
scriptType: fields[5] as String?,
description: fields[6] as String?,
);
}
@override
void write(BinaryWriter writer, DerivationInfo obj) {
writer
..writeByte(7)
..writeByte(0)
..write(obj.address)
..writeByte(1)
..write(obj.balance)
..writeByte(2)
..write(obj.transactionsCount)
..writeByte(3)
..write(obj.derivationType)
..writeByte(4)
..write(obj.derivationPath)
..writeByte(5)
..write(obj.scriptType)
..writeByte(6)
..write(obj.description);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is DerivationInfoAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class WalletInfoAdapter extends TypeAdapter<WalletInfo> {
@override
final int typeId = 4;
@override
WalletInfo read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return WalletInfo(
fields[0] == null ? '' : fields[0] as String,
fields[1] == null ? '' : fields[1] as String,
fields[2] as WalletType,
fields[3] == null ? false : fields[3] as bool,
fields[4] == null ? 0 : fields[4] as int,
fields[5] == null ? 0 : fields[5] as int,
fields[6] == null ? '' : fields[6] as String,
fields[7] == null ? '' : fields[7] as String,
fields[8] == null ? '' : fields[8] as String,
fields[11] as String?,
fields[12] as String?,
fields[13] as bool?,
fields[20] as DerivationInfo?,
fields[21] as newWi.HardwareWalletType?,
fields[22] as String?,
fields[25] as String?,
fields[26] == null ? false : fields[26] as bool,
)
..addresses = (fields[10] as Map?)?.cast<String, String>()
..addressInfos = (fields[14] as Map?)?.map((dynamic k, dynamic v) =>
MapEntry(k as int, (v as List).cast<AddressInfo>()))
..usedAddresses = (fields[15] as List?)?.cast<String>()
..derivationType = fields[16] as newWi.DerivationType?
..derivationPath = fields[17] as String?
..addressPageType = fields[18] as String?
..network = fields[19] as String?
..hiddenAddresses = (fields[23] as List?)?.cast<String>()
..manualAddresses = (fields[24] as List?)?.cast<String>();
}
@override
void write(BinaryWriter writer, WalletInfo obj) {
writer
..writeByte(26)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.name)
..writeByte(2)
..write(obj.type)
..writeByte(3)
..write(obj.isRecovery)
..writeByte(4)
..write(obj.restoreHeight)
..writeByte(5)
..write(obj.timestamp)
..writeByte(6)
..write(obj.dirPath)
..writeByte(7)
..write(obj.path)
..writeByte(8)
..write(obj.address)
..writeByte(10)
..write(obj.addresses)
..writeByte(11)
..write(obj.yatEid)
..writeByte(12)
..write(obj.yatLastUsedAddressRaw)
..writeByte(13)
..write(obj.showIntroCakePayCard)
..writeByte(14)
..write(obj.addressInfos)
..writeByte(15)
..write(obj.usedAddresses)
..writeByte(16)
..write(obj.derivationType)
..writeByte(17)
..write(obj.derivationPath)
..writeByte(18)
..write(obj.addressPageType)
..writeByte(19)
..write(obj.network)
..writeByte(20)
..write(obj.derivationInfo)
..writeByte(21)
..write(obj.hardwareWalletType)
..writeByte(22)
..write(obj.parentAddress)
..writeByte(23)
..write(obj.hiddenAddresses)
..writeByte(24)
..write(obj.manualAddresses)
..writeByte(25)
..write(obj.hashedWalletIdentifier)
..writeByte(26)
..write(obj.isNonSeedWallet);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is WalletInfoAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class DerivationTypeAdapter extends TypeAdapter<newWi.DerivationType> {
@override
final int typeId = 15;
@override
newWi.DerivationType read(BinaryReader reader) {
switch (reader.readByte()) {
case 0:
return newWi.DerivationType.unknown;
case 1:
return newWi.DerivationType.def;
case 2:
return newWi.DerivationType.nano;
case 3:
return newWi.DerivationType.bip39;
case 4:
return newWi.DerivationType.electrum;
default:
return newWi.DerivationType.unknown;
}
}
@override
void write(BinaryWriter writer, newWi.DerivationType obj) {
switch (obj) {
case newWi.DerivationType.unknown:
writer.writeByte(0);
break;
case newWi.DerivationType.def:
writer.writeByte(1);
break;
case newWi.DerivationType.nano:
writer.writeByte(2);
break;
case newWi.DerivationType.bip39:
writer.writeByte(3);
break;
case newWi.DerivationType.electrum:
writer.writeByte(4);
break;
}
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is DerivationTypeAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class HardwareWalletTypeAdapter extends TypeAdapter<newWi.HardwareWalletType> {
@override
final int typeId = 19;
@override
newWi.HardwareWalletType read(BinaryReader reader) {
switch (reader.readByte()) {
case 0:
return newWi.HardwareWalletType.ledger;
case 1:
return newWi.HardwareWalletType.bitbox;
case 2:
return newWi.HardwareWalletType.cupcake;
case 3:
return newWi.HardwareWalletType.coldcard;
case 4:
return newWi.HardwareWalletType.seedsigner;
case 5:
return newWi.HardwareWalletType.keystone;
case 6:
return newWi.HardwareWalletType.trezor;
default:
return newWi.HardwareWalletType.ledger;
}
}
@override
void write(BinaryWriter writer, newWi.HardwareWalletType obj) {
switch (obj) {
case newWi.HardwareWalletType.ledger:
writer.writeByte(0);
break;
case newWi.HardwareWalletType.bitbox:
writer.writeByte(1);
break;
case newWi.HardwareWalletType.cupcake:
writer.writeByte(2);
break;
case newWi.HardwareWalletType.coldcard:
writer.writeByte(3);
break;
case newWi.HardwareWalletType.seedsigner:
writer.writeByte(4);
break;
case newWi.HardwareWalletType.keystone:
writer.writeByte(5);
break;
case newWi.HardwareWalletType.trezor:
writer.writeByte(6);
break;
}
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is HardwareWalletTypeAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
+1 -1
View File
@@ -64,5 +64,5 @@ abstract class WalletService<N extends WalletCredentials, RFS extends WalletCred
/// Check if the Wallet requires a hardware wallet to be connected during
/// the opening flow. (Currently only the case for Monero)
bool requireHardwareWalletConnection(String name) => false;
Future<bool> requireHardwareWalletConnection(String name) async => false;
}
+64
View File
@@ -674,6 +674,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.1"
sqflite:
dependency: "direct main"
description:
name: sqflite
sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
url: "https://pub.dev"
source: hosted
version: "2.5.4+6"
sqflite_common_ffi:
dependency: "direct main"
description:
name: sqflite_common_ffi
sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
url: "https://pub.dev"
source: hosted
version: "2.3.4+4"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
url: "https://pub.dev"
source: hosted
version: "2.4.1+1"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqlite3:
dependency: transitive
description:
name: sqlite3
sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
url: "https://pub.dev"
source: hosted
version: "2.9.0"
stack_trace:
dependency: transitive
description:
@@ -706,6 +762,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
url: "https://pub.dev"
source: hosted
version: "3.3.0+3"
term_glyph:
dependency: transitive
description:
+3 -1
View File
@@ -14,7 +14,7 @@ dependencies:
sdk: flutter
http: ^1.1.0
file: ^7.0.0
path_provider: ^2.0.11
path_provider: ^2.1.5
mobx: ^2.0.7+4
flutter_mobx: ^2.0.6+1
intl: any
@@ -44,6 +44,8 @@ dependencies:
git:
url: https://github.com/cake-tech/blockchain_utils
ref: cake-update-v2
sqflite: ^2.4.1
sqflite_common_ffi: ^2.3.4+4
dev_dependencies:
flutter_test:
+6 -6
View File
@@ -37,7 +37,7 @@ class DecredWallet = DecredWalletBase with _$DecredWallet;
abstract class DecredWalletBase
extends WalletBase<DecredBalance, DecredTransactionHistory, DecredTransactionInfo> with Store {
DecredWalletBase(WalletInfo walletInfo, String password, Box<UnspentCoinsInfo> unspentCoinsInfo,
DecredWalletBase(WalletInfo walletInfo, DerivationInfo derivationInfo, String password, Box<UnspentCoinsInfo> unspentCoinsInfo,
Libwallet libwallet, Function() closeLibwallet)
: _password = password,
_libwallet = libwallet,
@@ -45,15 +45,15 @@ abstract class DecredWalletBase
this.syncStatus = NotConnectedSyncStatus(),
this.unspentCoinsInfo = unspentCoinsInfo,
this.watchingOnly =
walletInfo.derivationInfo?.derivationPath == DecredWalletService.pubkeyRestorePath ||
walletInfo.derivationInfo?.derivationPath ==
derivationInfo.derivationPath == DecredWalletService.pubkeyRestorePath ||
derivationInfo.derivationPath ==
DecredWalletService.pubkeyRestorePathTestnet,
this.balance = ObservableMap.of({CryptoCurrency.dcr: DecredBalance.zero()}),
this.isTestnet = walletInfo.derivationInfo?.derivationPath ==
this.isTestnet = derivationInfo.derivationPath ==
DecredWalletService.seedRestorePathTestnet ||
walletInfo.derivationInfo?.derivationPath ==
derivationInfo.derivationPath ==
DecredWalletService.pubkeyRestorePathTestnet,
super(walletInfo) {
super(walletInfo, derivationInfo) {
walletAddresses = DecredWalletAddresses(walletInfo, libwallet);
transactionHistory = DecredTransactionHistory();
+25 -13
View File
@@ -37,15 +37,11 @@ abstract class DecredWalletAddressesBase extends WalletAddresses with Store {
@override
Future<void> init() async {
if (walletInfo.addresses != null) {
addressesMap = walletInfo.addresses!;
}
if (walletInfo.addressInfos != null) {
addressInfos = walletInfo.addressInfos!;
}
if (walletInfo.usedAddresses != null) {
usedAddresses = {...walletInfo.usedAddresses!};
}
addressesMap = await walletInfo.getAddresses();
addressInfos = await walletInfo.getAddressInfos();
usedAddresses = await walletInfo.getUsedAddresses();
manualAddresses = await walletInfo.getManualAddresses();
hiddenAddresses = await walletInfo.getHiddenAddresses();
await updateAddressesInBox();
}
@@ -61,7 +57,15 @@ abstract class DecredWalletAddressesBase extends WalletAddresses with Store {
}
addressesMap[addr] = "";
addressInfos[0] ??= [];
addressInfos[0]?.add(AddressInfo(address: addr, label: "", accountIndex: 0));
addressInfos[0]?.add(
WalletInfoAddressInfo(
walletInfoId: walletInfo.internalId,
mapKey: 0,
address: addr,
label: "",
accountIndex: 0,
),
);
});
// Add used addresses.
@@ -79,11 +83,11 @@ abstract class DecredWalletAddressesBase extends WalletAddresses with Store {
await saveAddressesInBox();
}
List<AddressInfo> getAddressInfos() {
List<WalletInfoAddressInfo> getAddressInfos() {
if (addressInfos.containsKey(0)) {
return addressInfos[0]!;
}
return <AddressInfo>[];
return <WalletInfoAddressInfo>[];
}
Future<void> updateAddress(String address, String label) async {
@@ -128,7 +132,15 @@ abstract class DecredWalletAddressesBase extends WalletAddresses with Store {
if (!addressesMap.containsKey(addr)) {
addressesMap[addr] = "";
addressInfos[0] ??= [];
addressInfos[0]?.add(AddressInfo(address: addr, label: label, accountIndex: 0));
addressInfos[0]?.add(
WalletInfoAddressInfo(
walletInfoId: walletInfo.internalId,
mapKey: 0,
address: addr,
label: label,
accountIndex: 0,
),
);
}
selectedAddr = addr;
await saveAddressesInBox();
+39 -28
View File
@@ -18,9 +18,8 @@ class DecredWalletService extends WalletService<
DecredRestoreWalletFromSeedCredentials,
DecredRestoreWalletFromPubkeyCredentials,
DecredRestoreWalletFromHardwareCredentials> {
DecredWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
DecredWalletService(this.unspentCoinsInfoSource);
final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final seedRestorePath = "m/44'/42'";
static final seedRestorePathTestnet = "m/44'/1'";
@@ -68,9 +67,10 @@ class DecredWalletService extends WalletService<
"unsyncedaddrs": true,
};
await libwallet!.createWallet(jsonEncode(config));
final di = DerivationInfo(
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
credentials.walletInfo!.derivationInfo = di;
final di = await credentials.walletInfo!.getDerivationInfo();
di.derivationPath = isTestnet == true ? seedRestorePathTestnet : seedRestorePath;
await di.save();
credentials.walletInfo!.save();
credentials.walletInfo!.network = network;
// ios will move our wallet directory when updating. Since we must
// recalculate the new path every time we open the wallet, ensure this path
@@ -79,7 +79,7 @@ class DecredWalletService extends WalletService<
// going forward.
credentials.walletInfo!.dirPath = "";
credentials.walletInfo!.path = "";
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
final wallet = DecredWallet(credentials.walletInfo!, di, credentials.password!,
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
await wallet.init();
return wallet;
@@ -113,13 +113,17 @@ class DecredWalletService extends WalletService<
@override
Future<DecredWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
final di = await walletInfo.getDerivationInfo();
if (walletInfo.network == null || walletInfo.network == "") {
walletInfo.network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
walletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
walletInfo.network = di.derivationPath == seedRestorePathTestnet ||
di.derivationPath == pubkeyRestorePathTestnet
? testnet
: mainnet;
walletInfo.save();
}
await this.init();
@@ -149,7 +153,7 @@ class DecredWalletService extends WalletService<
};
await libwallet!.loadWallet(jsonEncode(config));
final wallet =
DecredWallet(walletInfo, password, this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
DecredWallet(walletInfo, di, password, this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
await wallet.init();
return wallet;
}
@@ -157,25 +161,32 @@ class DecredWalletService extends WalletService<
@override
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
final network = currentWalletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
currentWalletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final di = await currentWalletInfo.getDerivationInfo();
final network = di.derivationPath == seedRestorePathTestnet ||
di.derivationPath == pubkeyRestorePathTestnet
? testnet
: mainnet;
currentWalletInfo.network = network;
currentWalletInfo.save();
if (libwallet == null) {
libwallet = await Libwallet.spawn();
libwallet!.initLibdcrwallet("", "err");
}
final currentWallet = DecredWallet(
currentWalletInfo, password, this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
currentWalletInfo, di, password, this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
await currentWallet.renameWalletFiles(newName);
@@ -185,7 +196,7 @@ class DecredWalletService extends WalletService<
newWalletInfo.dirPath = "";
newWalletInfo.path = "";
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
await newWalletInfo.save();
}
@override
@@ -203,13 +214,13 @@ class DecredWalletService extends WalletService<
"unsyncedaddrs": true,
};
await libwallet!.createWallet(jsonEncode(config));
final di = DerivationInfo(
derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
credentials.walletInfo!.derivationInfo = di;
final di = await credentials.walletInfo!.getDerivationInfo();
di.derivationPath = isTestnet == true ? seedRestorePathTestnet : seedRestorePath;
await di.save();
credentials.walletInfo!.network = network;
credentials.walletInfo!.dirPath = "";
credentials.walletInfo!.path = "";
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
final wallet = DecredWallet(credentials.walletInfo!, di, credentials.password!,
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
await wallet.init();
return wallet;
@@ -231,13 +242,13 @@ class DecredWalletService extends WalletService<
"unsyncedaddrs": true,
};
await libwallet!.createWatchOnlyWallet(jsonEncode(config));
final di = DerivationInfo(
derivationPath: isTestnet == true ? pubkeyRestorePathTestnet : pubkeyRestorePath);
credentials.walletInfo!.derivationInfo = di;
final di = await credentials.walletInfo!.getDerivationInfo();
di.derivationPath = isTestnet == true ? pubkeyRestorePathTestnet : pubkeyRestorePath;
await di.save();
credentials.walletInfo!.network = network;
credentials.walletInfo!.dirPath = "";
credentials.walletInfo!.path = "";
final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
final wallet = DecredWallet(credentials.walletInfo!, di, credentials.password!,
this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
await wallet.init();
return wallet;
+64
View File
@@ -705,6 +705,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.1"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
url: "https://pub.dev"
source: hosted
version: "2.5.4+6"
sqflite_common_ffi:
dependency: transitive
description:
name: sqflite_common_ffi
sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
url: "https://pub.dev"
source: hosted
version: "2.3.4+4"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
url: "https://pub.dev"
source: hosted
version: "2.4.1+1"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqlite3:
dependency: transitive
description:
name: sqlite3
sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
url: "https://pub.dev"
source: hosted
version: "2.9.0"
stack_trace:
dependency: transitive
description:
@@ -737,6 +793,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
url: "https://pub.dev"
source: hosted
version: "3.3.0+3"
term_glyph:
dependency: transitive
description:
+5
View File
@@ -26,6 +26,7 @@ abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
required String mnemonic,
required String password,
required WalletInfo walletInfo,
required DerivationInfo derivationInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Uint8List seedBytes,
required EncryptionFileUtils encryptionFileUtils,
@@ -39,6 +40,7 @@ abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
mnemonic: mnemonic,
password: password,
walletInfo: walletInfo,
derivationInfo: derivationInfo,
unspentCoinsInfo: unspentCoinsInfo,
network: DogecoinNetwork.mainnet,
initialAddresses: initialAddresses,
@@ -83,6 +85,7 @@ abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
{required String mnemonic,
required String password,
required WalletInfo walletInfo,
required DerivationInfo derivationInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required EncryptionFileUtils encryptionFileUtils,
String? passphrase,
@@ -95,6 +98,7 @@ abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
mnemonic: mnemonic,
password: password,
walletInfo: walletInfo,
derivationInfo: derivationInfo,
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses,
initialBalance: initialBalance,
@@ -148,6 +152,7 @@ abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
mnemonic: keysData.mnemonic!,
password: password,
walletInfo: walletInfo,
derivationInfo: await walletInfo.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp?.addresses,
initialBalance: snp?.balance,
@@ -18,9 +18,8 @@ class DogeCoinWalletService extends WalletService<
DogeCoinRestoreWalletFromSeedCredentials,
DogeCoinRestoreWalletFromWIFCredentials,
DogeCoinNewWalletCredentials> {
DogeCoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
DogeCoinWalletService(this.unspentCoinsInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
final bool isDirect;
@@ -39,6 +38,7 @@ class DogeCoinWalletService extends WalletService<
mnemonic: credentials.mnemonic ?? MnemonicBip39.generate(strength: strength),
password: credentials.password!,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
passphrase: credentials.passphrase,
@@ -51,9 +51,10 @@ class DogeCoinWalletService extends WalletService<
@override
Future<DogeCoinWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
try {
final wallet = await DogeCoinWalletBase.open(
password: password,
@@ -82,9 +83,11 @@ class DogeCoinWalletService extends WalletService<
@override
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
final unspentCoinsToDelete = unspentCoinsInfoSource.values
.where((unspentCoin) => unspentCoin.walletId == walletInfo.id)
@@ -99,8 +102,10 @@ class DogeCoinWalletService extends WalletService<
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final currentWallet = await DogeCoinWalletBase.open(
password: password,
name: currentName,
@@ -115,7 +120,7 @@ class DogeCoinWalletService extends WalletService<
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
await newWalletInfo.save();
}
@override
@@ -141,6 +146,7 @@ class DogeCoinWalletService extends WalletService<
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
passphrase: credentials.passphrase);
+2
View File
@@ -23,6 +23,7 @@ class EthereumWallet extends EVMChainWallet {
required super.client,
required super.password,
required super.walletInfo,
required super.derivationInfo,
super.mnemonic,
super.initialBalance,
super.privateKey,
@@ -176,6 +177,7 @@ class EthereumWallet extends EVMChainWallet {
return EthereumWallet(
walletInfo: walletInfo,
derivationInfo: await walletInfo.getDerivationInfo(),
password: password,
mnemonic: keysData.mnemonic,
privateKey: keysData.privateKey,
+19 -10
View File
@@ -10,7 +10,7 @@ import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
import 'package:cw_evm/evm_chain_wallet_service.dart';
class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
EthereumWalletService(super.walletInfoSource, super.isDirect, {required this.client});
EthereumWalletService(super.isDirect, {required this.client});
late EthereumClient client;
@@ -25,6 +25,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
final wallet = EthereumWallet(
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
mnemonic: mnemonic,
password: credentials.password!,
passphrase: credentials.passphrase,
@@ -41,8 +42,10 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
@override
Future<EthereumWallet> openWallet(String name, String password) async {
final walletInfo =
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
try {
final wallet = await EthereumWallet.open(
@@ -75,8 +78,10 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final currentWallet = await EthereumWallet.open(
password: password,
name: currentName,
@@ -91,21 +96,23 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
await newWalletInfo.save();
}
@override
Future<EthereumWallet> restoreFromHardwareWallet(
EVMChainRestoreWalletFromHardware credentials) async {
credentials.walletInfo!.derivationInfo = DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0"
);
final di = await credentials.walletInfo!.getDerivationInfo();
di.derivationType = DerivationType.bip39;
di.derivationPath = "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0";
await di.save();
credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
credentials.walletInfo!.address = credentials.hwAccountData.address;
credentials.walletInfo!.save();
final wallet = EthereumWallet(
walletInfo: credentials.walletInfo!,
derivationInfo: di,
password: credentials.password!,
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -125,6 +132,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
password: credentials.password!,
privateKey: credentials.privateKey,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@@ -147,6 +155,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
passphrase: credentials.passphrase,
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
+2 -1
View File
@@ -67,6 +67,7 @@ abstract class EVMChainWalletBase
with Store, WalletKeysFile {
EVMChainWalletBase({
required WalletInfo walletInfo,
required DerivationInfo derivationInfo,
required EVMChainClient client,
required CryptoCurrency nativeCurrency,
String? mnemonic,
@@ -88,7 +89,7 @@ abstract class EVMChainWalletBase
nativeCurrency: initialBalance ?? EVMChainERC20Balance(BigInt.zero),
},
),
super(walletInfo) {
super(walletInfo, derivationInfo) {
this.walletInfo = walletInfo;
transactionHistory = setUpTransactionHistory(walletInfo, password, encryptionFileUtils);
+6 -5
View File
@@ -15,9 +15,8 @@ abstract class EVMChainWalletService<T extends EVMChainWallet> extends WalletSer
EVMChainRestoreWalletFromSeedCredentials,
EVMChainRestoreWalletFromPrivateKey,
EVMChainRestoreWalletFromHardware> {
EVMChainWalletService(this.walletInfoSource, this.isDirect);
EVMChainWalletService(this.isDirect);
final Box<WalletInfo> walletInfoSource;
final bool isDirect;
@override
@@ -48,8 +47,10 @@ abstract class EVMChainWalletService<T extends EVMChainWallet> extends WalletSer
@override
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
}
}
@@ -19,8 +19,8 @@ class EvmLedgerCredentials extends CredentialsWithKnownAddress {
@override
EthereumAddress get address => EthereumAddress.fromHex(_address);
void setLedgerConnection(LedgerConnection connection,
[String? derivationPath]) {
Future<void> setLedgerConnection(LedgerConnection connection,
[String? derivationPath]) async {
ethereumLedgerApp = EthereumLedgerApp(connection,
derivationPath: derivationPath ?? "m/44'/60'/0'/0/0");
}
+1 -1
View File
@@ -15,7 +15,7 @@ String? latestLedgerCommand;
typedef LedgerCallback = Void Function(Pointer<UnsignedChar>, UnsignedInt);
NativeCallable<LedgerCallback>? callable;
void enableLedgerExchange(LedgerConnection connection) {
Future<void> enableLedgerExchange(LedgerConnection connection) async {
callable?.close();
void callback(Pointer<UnsignedChar> request, int requestLength) async {
+4 -3
View File
@@ -55,6 +55,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
MoneroTransactionHistory, MoneroTransactionInfo> with Store {
MoneroWalletBase(
{required WalletInfo walletInfo,
required DerivationInfo derivationInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo,
required String password})
: balance = ObservableMap<CryptoCurrency, MoneroBalance>.of({
@@ -70,7 +71,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
syncStatus = NotConnectedSyncStatus(),
unspentCoins = [],
this.unspentCoinsInfo = unspentCoinsInfo,
super(walletInfo) {
super(walletInfo, derivationInfo) {
transactionHistory = MoneroTransactionHistory();
walletAddresses = MoneroWalletAddresses(walletInfo, transactionHistory);
@@ -976,8 +977,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
return monero_wallet.verifyMessage(message, address, signature);
}
void setLedgerConnection(LedgerConnection connection) {
enableLedgerExchange(connection);
Future<void> setLedgerConnection(LedgerConnection connection) async {
await enableLedgerExchange(connection);
}
@override
+6 -3
View File
@@ -5,7 +5,6 @@ import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_monero/api/subaddress_list.dart' as subaddress_list;
import 'package:cw_monero/api/transaction_history.dart';
import 'package:cw_monero/api/wallet.dart';
import 'package:cw_monero/monero_account_list.dart';
import 'package:cw_monero/monero_subaddress_list.dart';
@@ -90,8 +89,12 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
_subaddressList.subaddresses.forEach((subaddress) {
addressesMap[subaddress.address] = subaddress.label;
addressInfos[account.id] ??= [];
addressInfos[account.id]?.add(AddressInfo(
address: subaddress.address, label: subaddress.label, accountIndex: account.id));
addressInfos[account.id]?.add(WalletInfoAddressInfo(
walletInfoId: walletInfo.internalId,
mapKey: account.id,
accountIndex: account.id,
address: subaddress.address,
label: subaddress.label));
});
});
+40 -22
View File
@@ -99,9 +99,8 @@ class MoneroWalletService extends WalletService<
MoneroRestoreWalletFromSeedCredentials,
MoneroRestoreWalletFromKeysCredentials,
MoneroRestoreWalletFromHardwareCredentials> {
MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
MoneroWalletService(this.unspentCoinsInfoSource);
final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
static bool walletFilesExist(String path) =>
@@ -148,6 +147,7 @@ class MoneroWalletService extends WalletService<
passphrase: credentials.passphrase ?? "");
final wallet = MoneroWallet(
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
password: credentials.password!);
await wallet.init();
@@ -182,10 +182,13 @@ class MoneroWalletService extends WalletService<
await monero_wallet_manager
.openWallet(path: path, password: password);
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
final wallet = MoneroWallet(
walletInfo: walletInfo,
derivationInfo: await walletInfo.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
password: password);
@@ -234,17 +237,22 @@ class MoneroWalletService extends WalletService<
await file.delete(recursive: true);
}
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
await walletInfoSource.delete(walletInfo.key);
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhere(
(info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final currentWallet = MoneroWallet(
walletInfo: currentWalletInfo,
derivationInfo: await currentWalletInfo.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
password: password,
);
@@ -255,7 +263,7 @@ class MoneroWalletService extends WalletService<
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
await newWalletInfo.save();
}
@override
@@ -273,6 +281,7 @@ class MoneroWalletService extends WalletService<
spendKey: credentials.spendKey);
final wallet = MoneroWallet(
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
password: credentials.password!);
await wallet.init();
@@ -303,6 +312,7 @@ class MoneroWalletService extends WalletService<
final wallet = MoneroWallet(
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
password: credentials.password!);
await wallet.init();
@@ -359,6 +369,7 @@ class MoneroWalletService extends WalletService<
restoreHeight: credentials.height!);
final wallet = MoneroWallet(
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
password: credentials.password!);
await wallet.init();
@@ -379,10 +390,11 @@ class MoneroWalletService extends WalletService<
String? passphrase,
int? overrideHeight,
}) async {
walletInfo.derivationInfo = DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/44'/128'/0'/0/0",
);
final derivationInfo = await walletInfo.getDerivationInfo();
derivationInfo.derivationType = DerivationType.bip39;
derivationInfo.derivationPath = "m/44'/128'/0'/0/0";
await derivationInfo.save();
walletInfo.save();
final legacyMnemonic =
getLegacySeedFromBip39(mnemonic, passphrase: passphrase ?? "");
@@ -409,6 +421,7 @@ class MoneroWalletService extends WalletService<
final wallet = MoneroWallet(
walletInfo: walletInfo,
derivationInfo: derivationInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
password: password,
);
@@ -453,6 +466,7 @@ class MoneroWalletService extends WalletService<
final wallet = MoneroWallet(
walletInfo: walletInfo,
derivationInfo: await walletInfo.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
password: password,
);
@@ -485,6 +499,7 @@ class MoneroWalletService extends WalletService<
final wallet = MoneroWallet(
walletInfo: walletInfo,
derivationInfo: await walletInfo.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
password: password,
);
@@ -529,10 +544,13 @@ class MoneroWalletService extends WalletService<
await monero_wallet_manager
.openWallet(path: path, password: password);
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
final wallet = MoneroWallet(
walletInfo: walletInfo,
derivationInfo: await walletInfo.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
password: password,
);
@@ -544,12 +562,12 @@ class MoneroWalletService extends WalletService<
}
@override
bool requireHardwareWalletConnection(String name) {
return walletInfoSource.values
.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(name, getType()))
?.isHardwareWallet ??
false;
Future<bool> requireHardwareWalletConnection(String name) async {
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
return walletInfo.isHardwareWallet;
}
}
+64
View File
@@ -818,6 +818,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.1"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
url: "https://pub.dev"
source: hosted
version: "2.5.4+6"
sqflite_common_ffi:
dependency: "direct dev"
description:
name: sqflite_common_ffi
sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
url: "https://pub.dev"
source: hosted
version: "2.3.4+4"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
url: "https://pub.dev"
source: hosted
version: "2.4.1+1"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqlite3:
dependency: transitive
description:
name: sqlite3
sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
url: "https://pub.dev"
source: hosted
version: "2.9.0"
stack_trace:
dependency: transitive
description:
@@ -850,6 +906,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
url: "https://pub.dev"
source: hosted
version: "3.3.0+3"
term_glyph:
dependency: transitive
description:
+1
View File
@@ -39,6 +39,7 @@ dev_dependencies:
mobx_codegen: ^2.0.7
mockito: ^5.4.5
hive_generator: ^2.0.1
sqflite_common_ffi: ^2.3.4+4
dependency_overrides:
watcher: ^1.1.0
@@ -1,5 +1,6 @@
import 'dart:io';
import 'package:cw_core/db/sqlite.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
@@ -7,26 +8,28 @@ import 'package:cw_core/wallet_type.dart';
import 'package:cw_monero/monero_wallet_service.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hive/hive.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'mock/path_provider.dart';
import 'utils/setup_monero_c.dart';
Future<void> main() async {
group("MoneroWalletService Tests", () {
Hive.init('./test/data/db');
late MoneroWalletService walletService;
late File moneroCBinary;
setUpAll(() async {
databaseFactory = databaseFactoryFfi;
await initDb(pathOverride: './test/data/db');
Hive.init('./test/data/db');
PathProviderPlatform.instance = MockPathProviderPlatform();
final Box<WalletInfo> walletInfoSource =
await Hive.openBox('testWalletInfo');
final Box<UnspentCoinsInfo> unspentCoinsInfoSource =
await Hive.openBox('testUnspentCoinsInfo');
walletService = MoneroWalletService(walletInfoSource, unspentCoinsInfoSource);
walletService = MoneroWalletService(unspentCoinsInfoSource);
moneroCBinary = getMoneroCBinary().copySync(moneroCBinaryName);
});
+7 -4
View File
@@ -39,6 +39,7 @@ abstract class NanoWalletBase
with Store, WalletKeysFile {
NanoWalletBase({
required WalletInfo walletInfo,
required DerivationInfo derivationInfo,
required String mnemonic,
required String password,
NanoBalance? initialBalance,
@@ -47,7 +48,7 @@ abstract class NanoWalletBase
}) : syncStatus = NotConnectedSyncStatus(),
_password = password,
_mnemonic = mnemonic,
_derivationType = walletInfo.derivationInfo!.derivationType!,
_derivationType = derivationInfo.derivationType!,
_isTransactionUpdating = false,
_encryptionFileUtils = encryptionFileUtils,
_client = NanoClient(),
@@ -56,7 +57,7 @@ abstract class NanoWalletBase
CryptoCurrency.nano: initialBalance ??
NanoBalance(currentBalance: BigInt.zero, receivableBalance: BigInt.zero)
}),
super(walletInfo) {
super(walletInfo, derivationInfo) {
this.walletInfo = walletInfo;
transactionHistory = NanoTransactionHistory(
walletInfo: walletInfo,
@@ -433,11 +434,13 @@ abstract class NanoWalletBase
derivationType = DerivationType.bip39;
}
walletInfo.derivationInfo ??= DerivationInfo(derivationType: derivationType);
walletInfo.derivationInfo!.derivationType ??= derivationType;
final derivationInfo = await walletInfo.getDerivationInfo();
derivationInfo.derivationType ??= derivationType;
derivationInfo.save();
return NanoWallet(
walletInfo: walletInfo,
derivationInfo: derivationInfo,
password: password,
mnemonic: keysData.mnemonic!,
initialBalance: balance,
@@ -14,6 +14,7 @@ class NanoNewWalletCredentials extends WalletCredentials {
password: password,
walletInfo: walletInfo,
passphrase: passphrase,
derivationInfo: DerivationInfo(derivationType: derivationType),
);
final String? mnemonic;
+28 -19
View File
@@ -19,9 +19,8 @@ class NanoWalletService extends WalletService<
NanoRestoreWalletFromSeedCredentials,
NanoRestoreWalletFromKeysCredentials,
NanoNewWalletCredentials> {
NanoWalletService(this.walletInfoSource, this.isDirect);
NanoWalletService(this.isDirect);
final Box<WalletInfo> walletInfoSource;
final bool isDirect;
static bool walletFilesExist(String path) =>
@@ -33,7 +32,8 @@ class NanoWalletService extends WalletService<
@override
Future<WalletBase> create(NanoNewWalletCredentials credentials, {bool? isTestnet}) async {
final String mnemonic;
switch (credentials.walletInfo?.derivationInfo?.derivationType) {
final derivationInfo = credentials.derivationInfo ?? await credentials.walletInfo!.getDerivationInfo();
switch (derivationInfo.derivationType) {
case DerivationType.nano:
String seedKey = NanoSeeds.generateSeed();
mnemonic = credentials.mnemonic ?? NanoDerivations.standardSeedToMnemonic(seedKey);
@@ -47,6 +47,7 @@ class NanoWalletService extends WalletService<
final wallet = NanoWallet(
walletInfo: credentials.walletInfo!,
derivationInfo: derivationInfo,
mnemonic: mnemonic,
password: credentials.password!,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -65,20 +66,25 @@ class NanoWalletService extends WalletService<
await file.delete(recursive: true);
}
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
await walletInfoSource.delete(walletInfo.key);
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
String randomWords =
(List<String>.from(nm.NanoMnemomics.WORDLIST)..shuffle()).take(24).join(' ');
final currentWallet = NanoWallet(
walletInfo: currentWalletInfo,
derivationInfo: await currentWalletInfo.getDerivationInfo(),
password: password,
mnemonic: randomWords,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -91,7 +97,7 @@ class NanoWalletService extends WalletService<
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
await newWalletInfo.save();
}
@override
@@ -115,18 +121,17 @@ class NanoWalletService extends WalletService<
throw Exception("Wasn't a valid nano style seed!");
}
}
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
// should never happen but just in case:
if (credentials.walletInfo!.derivationInfo == null) {
credentials.walletInfo!.derivationInfo = DerivationInfo(derivationType: DerivationType.nano);
} else if (credentials.walletInfo!.derivationInfo!.derivationType == null) {
credentials.walletInfo!.derivationInfo!.derivationType = DerivationType.nano;
if (derivationInfo.derivationType == null) {
derivationInfo.derivationType = DerivationType.nano;
}
final wallet = await NanoWallet(
password: credentials.password!,
mnemonic: mnemonic ?? credentials.seedKey,
walletInfo: credentials.walletInfo!,
derivationInfo: derivationInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.init();
@@ -157,15 +162,17 @@ class NanoWalletService extends WalletService<
}
}
DerivationType derivationType =
credentials.walletInfo?.derivationInfo?.derivationType ?? DerivationType.nano;
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
DerivationType derivationType = derivationInfo.derivationType ?? DerivationType.nano;
credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: derivationType);
derivationInfo.derivationType = derivationType;
derivationInfo.save();
final wallet = await NanoWallet(
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
derivationInfo: derivationInfo,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@@ -180,8 +187,10 @@ class NanoWalletService extends WalletService<
@override
Future<NanoWallet> openWallet(String name, String password) async {
final walletInfo =
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
try {
final wallet = await NanoWalletBase.open(
+64
View File
@@ -823,6 +823,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.1"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
url: "https://pub.dev"
source: hosted
version: "2.5.4+6"
sqflite_common_ffi:
dependency: transitive
description:
name: sqflite_common_ffi
sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
url: "https://pub.dev"
source: hosted
version: "2.3.4+4"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
url: "https://pub.dev"
source: hosted
version: "2.4.1+1"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqlite3:
dependency: transitive
description:
name: sqlite3
sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
url: "https://pub.dev"
source: hosted
version: "2.9.0"
stack_trace:
dependency: transitive
description:
@@ -855,6 +911,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
url: "https://pub.dev"
source: hosted
version: "3.3.0+3"
term_glyph:
dependency: transitive
description:
+4
View File
@@ -21,6 +21,7 @@ import 'package:cw_polygon/polygon_transaction_info.dart';
class PolygonWallet extends EVMChainWallet {
PolygonWallet({
required super.walletInfo,
required super.derivationInfo,
required super.password,
super.mnemonic,
super.initialBalance,
@@ -152,8 +153,11 @@ class PolygonWallet extends EVMChainWallet {
);
}
final derivationInfo = await walletInfo.getDerivationInfo();
return PolygonWallet(
walletInfo: walletInfo,
derivationInfo: derivationInfo,
password: password,
mnemonic: keysData.mnemonic,
privateKey: keysData.privateKey,
+18 -11
View File
@@ -10,8 +10,7 @@ import 'package:cw_polygon/polygon_mnemonics_exception.dart';
import 'package:cw_polygon/polygon_wallet.dart';
class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
PolygonWalletService(
super.walletInfoSource, super.isDirect, {
PolygonWalletService(super.isDirect, {
required this.client,
});
@@ -28,6 +27,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
final wallet = PolygonWallet(
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
mnemonic: mnemonic,
password: credentials.password!,
passphrase: credentials.passphrase,
@@ -43,8 +43,10 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
@override
Future<PolygonWallet> openWallet(String name, String password) async {
final walletInfo =
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
try {
final wallet = await PolygonWallet.open(
@@ -82,6 +84,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
password: credentials.password!,
privateKey: credentials.privateKey,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@@ -95,15 +98,16 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
@override
Future<PolygonWallet> restoreFromHardwareWallet(
EVMChainRestoreWalletFromHardware credentials) async {
credentials.walletInfo!.derivationInfo = DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0"
);
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
derivationInfo.derivationType = DerivationType.bip39;
derivationInfo.derivationPath = "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0";
derivationInfo.save();
credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
credentials.walletInfo!.address = credentials.hwAccountData.address;
final wallet = PolygonWallet(
walletInfo: credentials.walletInfo!,
derivationInfo: derivationInfo,
password: credentials.password!,
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -127,6 +131,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
passphrase: credentials.passphrase,
client: client,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -141,8 +146,10 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final currentWallet = await PolygonWallet.open(
password: password,
name: currentName,
@@ -157,6 +164,6 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
await newWalletInfo.save();
}
}
@@ -8,7 +8,7 @@ class CwSharedExternal {
const MethodChannel('cw_shared_external');
static Future<String> get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
final String version = (await _channel.invokeMethod('getPlatformVersion')).toString();
return version;
}
}
+5 -1
View File
@@ -43,6 +43,7 @@ abstract class SolanaWalletBase
with Store, WalletKeysFile {
SolanaWalletBase({
required WalletInfo walletInfo,
required DerivationInfo derivationInfo,
String? mnemonic,
String? privateKey,
required String password,
@@ -57,7 +58,7 @@ abstract class SolanaWalletBase
walletAddresses = SolanaWalletAddresses(walletInfo),
balance = ObservableMap<CryptoCurrency, SolanaBalance>.of(
{CryptoCurrency.sol: initialBalance ?? SolanaBalance(BigInt.zero.toDouble())}),
super(walletInfo) {
super(walletInfo, derivationInfo) {
this.walletInfo = walletInfo;
transactionHistory = SolanaTransactionHistory(
walletInfo: walletInfo,
@@ -449,8 +450,11 @@ abstract class SolanaWalletBase
);
}
final derivationInfo = await walletInfo.getDerivationInfo();
return SolanaWallet(
walletInfo: walletInfo,
derivationInfo: derivationInfo,
password: password,
passphrase: keysData.passphrase,
mnemonic: keysData.mnemonic,
+18 -10
View File
@@ -18,9 +18,8 @@ import 'package:hive/hive.dart';
class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
SolanaRestoreWalletFromSeedCredentials, SolanaRestoreWalletFromPrivateKey, SolanaNewWalletCredentials> {
SolanaWalletService(this.walletInfoSource, this.isDirect);
SolanaWalletService(this.isDirect);
final Box<WalletInfo> walletInfoSource;
final bool isDirect;
@override
@@ -31,6 +30,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
final wallet = SolanaWallet(
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
mnemonic: mnemonic,
password: credentials.password!,
passphrase: credentials.passphrase,
@@ -52,8 +52,10 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
@override
Future<SolanaWallet> openWallet(String name, String password) async {
final walletInfo =
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
try {
final wallet = await SolanaWalletBase.open(
@@ -88,9 +90,11 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
@override
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
}
@override
@@ -100,6 +104,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
password: credentials.password!,
privateKey: credentials.privateKey,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@@ -121,6 +126,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
passphrase: credentials.passphrase,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@@ -134,8 +140,10 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final currentWallet = await SolanaWalletBase.open(
password: password,
name: currentName,
@@ -150,7 +158,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
await newWalletInfo.save();
}
@override
+5 -1
View File
@@ -42,6 +42,7 @@ abstract class TronWalletBase
with Store, WalletKeysFile {
TronWalletBase({
required WalletInfo walletInfo,
required DerivationInfo derivationInfo,
String? mnemonic,
String? privateKey,
required String password,
@@ -57,7 +58,7 @@ abstract class TronWalletBase
balance = ObservableMap<CryptoCurrency, TronBalance>.of(
{CryptoCurrency.trx: initialBalance ?? TronBalance(BigInt.zero)},
),
super(walletInfo) {
super(walletInfo, derivationInfo) {
this.walletInfo = walletInfo;
transactionHistory = TronTransactionHistory(
walletInfo: walletInfo, password: password, encryptionFileUtils: encryptionFileUtils);
@@ -163,8 +164,11 @@ abstract class TronWalletBase
);
}
final derivationInfo = await walletInfo.getDerivationInfo();
return TronWallet(
walletInfo: walletInfo,
derivationInfo: derivationInfo,
password: password,
mnemonic: keysData.mnemonic,
privateKey: keysData.privateKey,
+18 -11
View File
@@ -1,7 +1,6 @@
import 'dart:io';
import 'package:bip39/bip39.dart' as bip39;
import 'package:collection/collection.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/pathForWallet.dart';
@@ -22,11 +21,10 @@ class TronWalletService extends WalletService<
TronRestoreWalletFromSeedCredentials,
TronRestoreWalletFromPrivateKey,
TronNewWalletCredentials> {
TronWalletService(this.walletInfoSource, {required this.client, required this.isDirect});
TronWalletService({required this.client, required this.isDirect});
late TronClient client;
final Box<WalletInfo> walletInfoSource;
final bool isDirect;
@override
@@ -40,6 +38,7 @@ class TronWalletService extends WalletService<
final wallet = TronWallet(
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
mnemonic: mnemonic,
password: credentials.password!,
passphrase: credentials.passphrase,
@@ -55,8 +54,10 @@ class TronWalletService extends WalletService<
@override
Future<TronWallet> openWallet(String name, String password) async {
final walletInfo =
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
try {
final wallet = await TronWalletBase.open(
@@ -97,6 +98,7 @@ class TronWalletService extends WalletService<
password: credentials.password!,
privateKey: credentials.privateKey,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@@ -120,6 +122,7 @@ class TronWalletService extends WalletService<
password: credentials.password!,
mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
passphrase: credentials.passphrase,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
@@ -133,8 +136,10 @@ class TronWalletService extends WalletService<
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final currentWallet = await TronWalletBase.open(
password: password,
name: currentName,
@@ -149,7 +154,7 @@ class TronWalletService extends WalletService<
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
await newWalletInfo.save();
}
@override
@@ -159,9 +164,11 @@ class TronWalletService extends WalletService<
@override
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
}
@override
+17 -8
View File
@@ -102,16 +102,22 @@ String getAddress({int accountIndex = 0, int addressIndex = 1}) {
return addressCache[wptr!.address]![accountIndex]![addressIndex]!;
}
int getFullBalance({int accountIndex = 0}) =>
wownero.Wallet_balance(wptr!, accountIndex: accountIndex);
int getUnlockedBalance({int accountIndex = 0}) =>
wownero.Wallet_unlockedBalance(wptr!, accountIndex: accountIndex);
int getCurrentHeight() => wownero.Wallet_blockChainHeight(wptr!);
int getFullBalance({int accountIndex = 0}) {
if (wptr == null) return 0;
return wownero.Wallet_balance(wptr!, accountIndex: accountIndex);
}
int getUnlockedBalance({int accountIndex = 0}) {
if (wptr == null) return 0;
return wownero.Wallet_unlockedBalance(wptr!, accountIndex: accountIndex);
}
int getCurrentHeight() {
if (wptr == null) return 0;
return wownero.Wallet_blockChainHeight(wptr!);
}
int cachedNodeHeight = 0;
int getNodeHeightSync() {
if (wptr == null) return 0;
(() async {
final wptrAddress = wptr!.address;
cachedNodeHeight = await Isolate.run(() async {
@@ -121,7 +127,10 @@ int getNodeHeightSync() {
return cachedNodeHeight;
}
bool isConnectedSync() => wownero.Wallet_connected(wptr!) != 0;
bool isConnectedSync() {
if (wptr == null) return false;
return wownero.Wallet_connected(wptr!) != 0;
}
Future<bool> setupNodeSync(
{required String address,
+2 -2
View File
@@ -53,7 +53,7 @@ abstract class WowneroWalletBase
extends WalletBase<WowneroBalance, WowneroTransactionHistory, WowneroTransactionInfo>
with Store {
WowneroWalletBase(
{required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required String password})
{required WalletInfo walletInfo, required DerivationInfo derivationInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required String password})
: balance = ObservableMap<CryptoCurrency, WowneroBalance>.of({
CryptoCurrency.wow: WowneroBalance(
fullBalance: wownero_wallet.getFullBalance(accountIndex: 0),
@@ -66,7 +66,7 @@ abstract class WowneroWalletBase
syncStatus = NotConnectedSyncStatus(),
unspentCoins = [],
this.unspentCoinsInfo = unspentCoinsInfo,
super(walletInfo) {
super(walletInfo, derivationInfo) {
transactionHistory = WowneroTransactionHistory();
walletAddresses = WowneroWalletAddresses(walletInfo, transactionHistory);
+6 -2
View File
@@ -88,8 +88,12 @@ abstract class WowneroWalletAddressesBase extends WalletAddresses with Store {
_subaddressList.subaddresses.forEach((subaddress) {
addressesMap[subaddress.address] = subaddress.label;
addressInfos[account.id] ??= [];
addressInfos[account.id]?.add(AddressInfo(
address: subaddress.address, label: subaddress.label, accountIndex: account.id));
addressInfos[account.id]?.add(WalletInfoAddressInfo(
walletInfoId: walletInfo.internalId,
mapKey: account.id,
accountIndex: account.id,
address: subaddress.address,
label: subaddress.label));
});
});
+22 -17
View File
@@ -66,9 +66,8 @@ class WowneroWalletService extends WalletService<
WowneroRestoreWalletFromSeedCredentials,
WowneroRestoreWalletFromKeysCredentials,
WowneroNewWalletCredentials> {
WowneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
WowneroWalletService(this.unspentCoinsInfoSource);
final Box<WalletInfo> walletInfoSource;
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
static bool walletFilesExist(String path) =>
@@ -99,7 +98,7 @@ class WowneroWalletService extends WalletService<
await wownero_wallet_manager.createWallet(
path: path, password: credentials.password!, language: credentials.language, passphrase: credentials.passphrase??'');
final wallet = WowneroWallet(
walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!);
walletInfo: credentials.walletInfo!, derivationInfo: await credentials.walletInfo!.getDerivationInfo(), unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!);
await wallet.init();
return wallet;
@@ -133,9 +132,11 @@ class WowneroWalletService extends WalletService<
}
await wownero_wallet_manager.openWalletAsync({'path': path, 'password': password});
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
wallet = WowneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource, password: password);
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
wallet = WowneroWallet(walletInfo: walletInfo, derivationInfo: await walletInfo.getDerivationInfo(), unspentCoinsInfo: unspentCoinsInfoSource, password: password);
final isValid = wallet.walletAddresses.validate();
if (!isValid) {
@@ -206,17 +207,20 @@ class WowneroWalletService extends WalletService<
await file.delete(recursive: true);
}
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
await walletInfoSource.delete(walletInfo.key);
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet =
WowneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource, password: password);
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final currentWallet = WowneroWallet(walletInfo: currentWalletInfo, derivationInfo: await currentWalletInfo.getDerivationInfo(), unspentCoinsInfo: unspentCoinsInfoSource, password: password);
await currentWallet.renameWalletFiles(newName);
@@ -224,7 +228,7 @@ class WowneroWalletService extends WalletService<
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
await newWalletInfo.save();
}
@override
@@ -241,7 +245,7 @@ class WowneroWalletService extends WalletService<
viewKey: credentials.viewKey,
spendKey: credentials.spendKey);
final wallet = WowneroWallet(
walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!);
walletInfo: credentials.walletInfo!, derivationInfo: await credentials.walletInfo!.getDerivationInfo(), unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!);
await wallet.init();
return wallet;
@@ -275,7 +279,7 @@ class WowneroWalletService extends WalletService<
seed: credentials.mnemonic,
restoreHeight: credentials.height!);
final wallet = WowneroWallet(
walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!);
walletInfo: credentials.walletInfo!, derivationInfo: await credentials.walletInfo!.getDerivationInfo(), unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!);
await wallet.init();
return wallet;
@@ -321,6 +325,7 @@ class WowneroWalletService extends WalletService<
final wallet = WowneroWallet(
walletInfo: walletInfo,
derivationInfo: await walletInfo.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
password: password,
);
@@ -350,7 +355,7 @@ class WowneroWalletService extends WalletService<
wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase??'');
final wallet = WowneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource, password: password);
final wallet = WowneroWallet(walletInfo: walletInfo, derivationInfo: await walletInfo.getDerivationInfo(), unspentCoinsInfo: unspentCoinsInfoSource, password: password);
await wallet.init();
return wallet;
+64
View File
@@ -722,6 +722,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.1"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
url: "https://pub.dev"
source: hosted
version: "2.5.4+6"
sqflite_common_ffi:
dependency: transitive
description:
name: sqflite_common_ffi
sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
url: "https://pub.dev"
source: hosted
version: "2.3.4+4"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
url: "https://pub.dev"
source: hosted
version: "2.4.1+1"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqlite3:
dependency: transitive
description:
name: sqlite3
sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
url: "https://pub.dev"
source: hosted
version: "2.9.0"
stack_trace:
dependency: transitive
description:
@@ -754,6 +810,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
url: "https://pub.dev"
source: hosted
version: "3.3.0+3"
term_glyph:
dependency: transitive
description:
+6 -6
View File
@@ -105,14 +105,14 @@ abstract class ZanoWalletBase
/// number of transactions in each request
static final int _txChunkSize = (pow(2, 32) - 1).toInt();
ZanoWalletBase(WalletInfo walletInfo, String password)
ZanoWalletBase(WalletInfo walletInfo, DerivationInfo derivationInfo, String password)
: balance = ObservableMap.of({CryptoCurrency.zano: ZanoBalance.empty()}),
_isTransactionUpdating = false,
_hasSyncAfterStartup = false,
walletAddresses = ZanoWalletAddresses(walletInfo),
syncStatus = NotConnectedSyncStatus(),
_password = password,
super(walletInfo) {
super(walletInfo, derivationInfo) {
transactionHistory = ZanoTransactionHistory();
if (!CakeHive.isAdapterRegistered(ZanoAsset.typeId)) {
CakeHive.registerAdapter(ZanoAssetAdapter());
@@ -129,7 +129,7 @@ abstract class ZanoWalletBase
}
static Future<ZanoWallet> create({required WalletCredentials credentials}) async {
final wallet = ZanoWallet(credentials.walletInfo!, credentials.password!);
final wallet = ZanoWallet(credentials.walletInfo!, await credentials.walletInfo!.getDerivationInfo(), credentials.password!);
await wallet.initWallet();
final path = await pathForWallet(name: credentials.name, type: credentials.walletInfo!.type);
final createWalletResult = await wallet.createWallet(path, credentials.password!);
@@ -146,7 +146,7 @@ abstract class ZanoWalletBase
static Future<ZanoWallet> restore(
{required ZanoRestoreWalletFromSeedCredentials credentials}) async {
final wallet = ZanoWallet(credentials.walletInfo!, credentials.password!);
final wallet = ZanoWallet(credentials.walletInfo!, await credentials.walletInfo!.getDerivationInfo(), credentials.password!);
await wallet.initWallet();
final path = await pathForWallet(name: credentials.name, type: credentials.walletInfo!.type);
final createWalletResult = await wallet.restoreWalletFromSeed(
@@ -166,13 +166,13 @@ abstract class ZanoWalletBase
{required String name, required String password, required WalletInfo walletInfo}) async {
final path = await pathForWallet(name: name, type: walletInfo.type);
if (ZanoWalletApi.openWalletCache[path] != null) {
final wallet = ZanoWallet(walletInfo, password);
final wallet = ZanoWallet(walletInfo, await walletInfo.getDerivationInfo(), password);
await wallet.parseCreateWalletResult(ZanoWalletApi.openWalletCache[path]!).then((_) {
unawaited(wallet.init(ZanoWalletApi.openWalletCache[path]!.wi.address));
});
return wallet;
} else {
final wallet = ZanoWallet(walletInfo, password);
final wallet = ZanoWallet(walletInfo, await walletInfo.getDerivationInfo(), password);
await wallet.initWallet();
final createWalletResult = await wallet.loadWallet(path, password);
await wallet.parseCreateWalletResult(createWalletResult).then((_) {
+16 -9
View File
@@ -43,9 +43,7 @@ class ZanoRestoreWalletFromKeysCredentials extends WalletCredentials {
class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
ZanoRestoreWalletFromSeedCredentials, ZanoRestoreWalletFromKeysCredentials, ZanoNewWalletCredentials> {
ZanoWalletService(this.walletInfoSource);
final Box<WalletInfo> walletInfoSource;
ZanoWalletService();
static bool walletFilesExist(String path) => !File(path).existsSync() && !File('$path.keys').existsSync();
@@ -68,7 +66,10 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
@override
Future<ZanoWallet> openWallet(String name, String password) async {
final walletInfo = walletInfoSource.values.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final walletInfo = await WalletInfo.get(name, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
try {
final wallet = await ZanoWalletBase.open(name: name, password: password, walletInfo: walletInfo);
saveBackup(name);
@@ -89,14 +90,20 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
await file.delete(recursive: true);
}
final walletInfo = walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
await walletInfoSource.delete(walletInfo.key);
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = ZanoWallet(currentWalletInfo, password);
final currentWalletInfo = await WalletInfo.get(currentName, getType());
if (currentWalletInfo == null) {
throw Exception('Wallet not found');
}
final currentWallet = ZanoWallet(currentWalletInfo, await currentWalletInfo.getDerivationInfo(), password);
await currentWallet.renameWalletFiles(newName);
@@ -104,7 +111,7 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
await newWalletInfo.save();
}
@override
+64
View File
@@ -719,6 +719,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.1"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
url: "https://pub.dev"
source: hosted
version: "2.5.4+6"
sqflite_common_ffi:
dependency: transitive
description:
name: sqflite_common_ffi
sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
url: "https://pub.dev"
source: hosted
version: "2.3.4+4"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
url: "https://pub.dev"
source: hosted
version: "2.4.1+1"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqlite3:
dependency: transitive
description:
name: sqlite3
sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
url: "https://pub.dev"
source: hosted
version: "2.9.0"
stack_trace:
dependency: transitive
description:
@@ -751,6 +807,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
url: "https://pub.dev"
source: hosted
version: "3.3.0+3"
term_glyph:
dependency: transitive
description:
+6 -5
View File
@@ -4,8 +4,8 @@ class CWBase extends Base {
@override
List<String> getBaseWordList(String language) => EVMChainMnemonics.englishWordlist;
WalletService createBaseWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
BaseWalletService(walletInfoSource, isDirect, client: BaseClient());
WalletService createBaseWalletService(bool isDirect) =>
BaseWalletService(isDirect, client: BaseClient());
@override
WalletCredentials createBaseNewWalletCredentials({
@@ -216,14 +216,15 @@ class CWBase extends Base {
@override
void setHardwareWalletService(WalletBase wallet, HardwareWalletService service) {
Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service) async {
if (service is EVMChainLedgerService) {
((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials).setLedgerConnection(
service.ledgerConnection, wallet.walletInfo.derivationInfo?.derivationPath);
service.ledgerConnection, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
} else if (service is EVMChainBitboxService) {
((wallet as EVMChainWallet).evmChainPrivateKey as EvmBitboxCredentials)
.setBitbox(service.manager, wallet.walletInfo.derivationInfo?.derivationPath);
.setBitbox(service.manager, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
}
return Future.value();
}
@override
+4 -7
View File
@@ -260,17 +260,14 @@ class CWBitcoin extends Bitcoin {
}
WalletService createBitcoinWalletService(
Box<WalletInfo> walletInfoSource,
Box<UnspentCoinsInfo> unspentCoinSource,
Box<PayjoinSession> payjoinSessionSource,
bool isDirect) {
return BitcoinWalletService(
walletInfoSource, unspentCoinSource, payjoinSessionSource, isDirect);
return BitcoinWalletService(unspentCoinSource, payjoinSessionSource, isDirect);
}
WalletService createLitecoinWalletService(
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
return LitecoinWalletService(walletInfoSource, unspentCoinSource, isDirect);
WalletService createLitecoinWalletService(Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
return LitecoinWalletService(unspentCoinSource, isDirect);
}
@override
@@ -537,7 +534,7 @@ class CWBitcoin extends Bitcoin {
}
@override
void setHardwareWalletService(WalletBase wallet, HardwareWalletService service) {
Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service) async {
(wallet as ElectrumWallet).hardwareWalletService = service;
}
+2 -3
View File
@@ -5,9 +5,8 @@ class CWBitcoinCash extends BitcoinCash {
String getCashAddrFormat(String address) => AddressUtils.getCashAddrFormat(address);
@override
WalletService createBitcoinCashWalletService(
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
return BitcoinCashWalletService(walletInfoSource, unspentCoinSource, isDirect);
WalletService createBitcoinCashWalletService(Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
return BitcoinCashWalletService(unspentCoinSource, isDirect);
}
@override
+13 -23
View File
@@ -24,7 +24,7 @@ import 'package:cake_wallet/wallet_types.g.dart';
import 'package:cake_backup/backup.dart' as cake_backup;
class $BackupService {
$BackupService(this._secureStorage, this.walletInfoSource, this.transactionDescriptionBox,
$BackupService(this._secureStorage, this.transactionDescriptionBox,
this.keyService, this.sharedPreferences)
: cipher = Cryptography.instance.chacha20Poly1305Aead(),
correctWallets = <WalletInfo>[];
@@ -37,7 +37,6 @@ class $BackupService {
final Cipher cipher;
final SecureStorage _secureStorage;
final SharedPreferences sharedPreferences;
final Box<WalletInfo> walletInfoSource;
final Box<TransactionDescription> transactionDescriptionBox;
final KeyService keyService;
List<WalletInfo> correctWallets;
@@ -110,27 +109,14 @@ class $BackupService {
}
Future<void> verifyWallets() async {
final walletInfoSource = await reloadHiveWalletInfoBox();
correctWallets =
walletInfoSource.values.where((info) => availableWalletTypes.contains(info.type)).toList();
await performHiveMigration(); // for backups made before sqlite migration
correctWallets = (await WalletInfo.getAll()).where((info) => availableWalletTypes.contains(info.type)).toList();
if (correctWallets.isEmpty) {
throw Exception('Correct wallets not detected');
printV('Correct wallets not detected');
}
}
Future<Box<WalletInfo>> reloadHiveWalletInfoBox() async {
final appDir = await getAppDir();
await CakeHive.close();
CakeHive.init(appDir.path);
if (!CakeHive.isAdapterRegistered(WalletInfo.typeId)) {
CakeHive.registerAdapter(WalletInfoAdapter());
}
return await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
}
Future<void> importTransactionDescriptionDump() async {
final appDir = await getAppDir();
final transactionDescriptionFile = File('${appDir.path}/~_transaction_descriptions_dump');
@@ -191,12 +177,16 @@ class $BackupService {
String currentWalletName = data[PreferencesKey.currentWalletName] as String;
int currentWalletType = data[PreferencesKey.currentWalletType] as int;
final isCorrentCurrentWallet = correctWallets
final isCorrectCurrentWallet = correctWallets
.any((info) => info.name == currentWalletName && info.type.index == currentWalletType);
if (!isCorrentCurrentWallet) {
currentWalletName = correctWallets.first.name;
currentWalletType = serializeToInt(correctWallets.first.type);
try {
if (!isCorrectCurrentWallet) {
currentWalletName = correctWallets.first.name;
currentWalletType = serializeToInt(correctWallets.first.type);
}
} catch (e) {
}
if (DeviceInfo.instance.isDesktop) {
@@ -283,7 +273,7 @@ class $BackupService {
Future<Uint8List> exportKeychainDumpV2(String password,
{String keychainSalt = secrets.backupKeychainSalt}) async {
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
final wallets = await Future.wait(walletInfoSource.values.map((walletInfo) async {
final wallets = await Future.wait((await WalletInfo.getAll()).map((walletInfo) async {
try {
return {
'name': walletInfo.name,
+3 -6
View File
@@ -11,6 +11,7 @@ import 'package:crypto/crypto.dart';
import 'package:cw_core/root_dir.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart';
enum BackupVersion {
@@ -143,7 +144,7 @@ class BackupMetadata {
}
class BackupServiceV3 extends $BackupService {
BackupServiceV3(super.secureStorage, super.walletInfoSource, super.transactionDescriptionBox, super.keyService, super.sharedPreferences);
BackupServiceV3(super.secureStorage, super.transactionDescriptionBox, super.keyService, super.sharedPreferences);
static BackupVersion get currentVersion => BackupVersion.v3;
@@ -317,7 +318,6 @@ class BackupServiceV3 extends $BackupService {
Future<void> verifyHardwareWallets(String password,
{String keychainSalt = secrets.backupKeychainSalt}) async {
final walletInfoSource = await reloadHiveWalletInfoBox();
final appDir = await getAppDir();
final keychainDumpFile = File('${appDir.path}/~_keychain_dump');
final decryptedKeychainDumpFileData = await decryptV2(
@@ -334,10 +334,7 @@ class BackupServiceV3 extends $BackupService {
for (final expectedHardwareWallet in expectedHardwareWallets) {
final info = expectedHardwareWallet as Map<String, dynamic>;
final actualWalletInfo = walletInfoSource.values
.where((e) =>
e.name == info['name'] && e.type.toString() == info['type'])
.firstOrNull;
final actualWalletInfo = await WalletInfo.get(info['name'] as String, WalletType.values.firstWhere((e) => e.toString() == info['type'] as String));
if (actualWalletInfo != null &&
info["hardwareWalletType"] !=
actualWalletInfo.hardwareWalletType?.index) {
+9 -11
View File
@@ -16,8 +16,7 @@ class WalletCreationService {
{required WalletType initialType,
required this.keyService,
required this.sharedPreferences,
required this.settingsStore,
required this.walletInfoSource})
required this.settingsStore})
: type = initialType {
changeWalletType(type: type);
}
@@ -26,7 +25,6 @@ class WalletCreationService {
final SharedPreferences sharedPreferences;
final SettingsStore settingsStore;
final KeyService keyService;
final Box<WalletInfo> walletInfoSource;
WalletService? _service;
static const _isNewMoneroWalletPasswordUpdated = true;
@@ -36,23 +34,23 @@ class WalletCreationService {
_service = getIt.get<WalletService>(param1: type);
}
bool exists(String name) {
Future<bool> exists(String name) async {
final walletName = name.toLowerCase();
return walletInfoSource.values.any((walletInfo) => walletInfo.name.toLowerCase() == walletName);
return (await WalletInfo.getAll()).any((walletInfo) => walletInfo.name.toLowerCase() == walletName);
}
bool typeExists(WalletType type) {
return walletInfoSource.values.any((walletInfo) => walletInfo.type == type);
Future<bool> typeExists(WalletType type) async {
return (await WalletInfo.getAll()).any((walletInfo) => walletInfo.type == type);
}
void checkIfExists(String name) {
if (exists(name)) {
Future<void> checkIfExists(String name) async {
if (await exists(name)) {
throw Exception('Wallet with name ${name} already exists!');
}
}
Future<WalletBase> create(WalletCredentials credentials, {bool? isTestnet}) async {
checkIfExists(credentials.name);
await checkIfExists(credentials.name);
if (credentials.password == null) {
credentials.password = generateWalletPassword();
@@ -84,12 +82,12 @@ class WalletCreationService {
case WalletType.solana:
case WalletType.tron:
case WalletType.dogecoin:
case WalletType.nano:
return true;
case WalletType.monero:
case WalletType.wownero:
case WalletType.none:
case WalletType.haven:
case WalletType.nano:
case WalletType.banano:
case WalletType.zano:
case WalletType.decred:
+4 -5
View File
@@ -74,7 +74,7 @@ class WalletLoadingService {
} catch (error, stack) {
await ExceptionHandler.resetLastPopupDate();
final isLedgerError = await ExceptionHandler.isLedgerError(error);
if (isLedgerError || requireHardwareWalletConnection(type, name)) rethrow;
if (isLedgerError || await requireHardwareWalletConnection(type, name)) rethrow;
await ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack));
@@ -87,9 +87,8 @@ class WalletLoadingService {
}
// try opening another wallet that is not corrupted to give user access to the app
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
WalletBase? wallet;
for (var walletInfo in walletInfoSource.values) {
for (var walletInfo in await WalletInfo.getAll()) {
try {
final walletService = walletServiceFactory.call(walletInfo.type);
final walletPassword = await keyService.getWalletPassword(walletName: walletInfo.name);
@@ -195,8 +194,8 @@ class WalletLoadingService {
return "\n\n$type ($name): ${await walletService.getSeeds(name, password, type)}";
}
bool requireHardwareWalletConnection(WalletType type, String name) {
Future<bool> requireHardwareWalletConnection(WalletType type, String name) async {
final walletService = walletServiceFactory.call(type);
return walletService.requireHardwareWalletConnection(name);
return await walletService.requireHardwareWalletConnection(name);
}
}
+3 -4
View File
@@ -19,9 +19,8 @@ class CWDecred extends Decred {
DecredRestoreWalletFromPubkeyCredentials(name: name, pubkey: pubkey, password: password);
@override
WalletService createDecredWalletService(
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
return DecredWalletService(walletInfoSource, unspentCoinSource);
WalletService createDecredWalletService(Box<UnspentCoinsInfo> unspentCoinSource) {
return DecredWalletService(unspentCoinSource);
}
@override
@@ -53,7 +52,7 @@ class CWDecred extends Decred {
.toList(),
priority: priority as DecredTransactionPriority);
List<AddressInfo> getAddressInfos(Object wallet) {
List<WalletInfoAddressInfo> getAddressInfos(Object wallet) {
final decredWallet = wallet as DecredWallet;
return decredWallet.walletAddresses.getAddressInfos();
}
+24 -43
View File
@@ -2,7 +2,6 @@ import 'dart:async' show Timer;
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/anonpay/anonpay_api.dart';
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
import 'package:cake_wallet/anypay/anypay_api.dart';
import 'package:cake_wallet/base/base.dart';
@@ -301,7 +300,6 @@ import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
final getIt = GetIt.instance;
var _isSetupFinished = false;
late Box<WalletInfo> _walletInfoSource;
late Box<Node> _nodeSource;
late Box<Node> _powNodeSource;
late Box<Contact> _contactSource;
@@ -315,7 +313,6 @@ late Box<PayjoinSession> _payjoinSessionSource;
late Box<AnonpayInvoiceInfo> _anonpayInvoiceInfoSource;
Future<void> setup({
required Box<WalletInfo> walletInfoSource,
required Box<Node> nodeSource,
required Box<Node> powNodeSource,
required Box<Contact> contactSource,
@@ -330,7 +327,6 @@ Future<void> setup({
required SecureStorage secureStorage,
required GlobalKey<NavigatorState> navigatorKey,
}) async {
_walletInfoSource = walletInfoSource;
_nodeSource = nodeSource;
_powNodeSource = powNodeSource;
_contactSource = contactSource;
@@ -432,7 +428,7 @@ Future<void> setup({
keyService: getIt.get<KeyService>(),
sharedPreferences: getIt.get<SharedPreferences>(),
settingsStore: getIt.get<SettingsStore>(),
walletInfoSource: _walletInfoSource));
));
getIt.registerFactoryParam<AdvancedPrivacySettingsViewModel, WalletType, void>(
(type, _) => AdvancedPrivacySettingsViewModel(type, getIt.get<SettingsStore>()));
@@ -446,17 +442,17 @@ Future<void> setup({
(newWalletArgs, _) => WalletNewVM(
getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1:newWalletArgs.type),
_walletInfoSource,
getIt.get<AdvancedPrivacySettingsViewModel>(param1: newWalletArgs.type),
getIt.get<SeedSettingsViewModel>(),
newWalletArguments: newWalletArgs,));
newWalletArguments: newWalletArgs,
));
getIt.registerFactory<NewWalletTypeViewModel>(() => NewWalletTypeViewModel(_walletInfoSource));
final walletList = await WalletInfo.getAll();
getIt.registerFactory<NewWalletTypeViewModel>(() => NewWalletTypeViewModel(walletList.isNotEmpty));
getIt.registerFactory<WalletManager>(
() => WalletManager(
_walletInfoSource,
getIt.get<SharedPreferences>(),
),
);
@@ -535,7 +531,6 @@ Future<void> setup({
hardwareWalletVM,
getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type),
_walletInfoSource,
getIt.get<SeedSettingsViewModel>(),
type: type));
@@ -560,7 +555,6 @@ Future<void> setup({
getIt.get<ContactListViewModel>(),
getIt.get<UnspentCoinsListViewModel>(),
getIt.get<FeesViewModel>(),
_walletInfoSource,
getIt.get<FiatConversionStore>(),
),
);
@@ -639,7 +633,7 @@ Future<void> setup({
getIt.registerFactory<AuthPage>(instanceName: 'login', () {
return AuthPage(getIt.get<AuthViewModel>(), closable: false,
onAuthenticationFinished: (isAuthenticated, AuthPageState authPageState) {
onAuthenticationFinished: (isAuthenticated, AuthPageState authPageState) async {
if (!isAuthenticated) {
return;
}
@@ -680,7 +674,7 @@ Future<void> setup({
);
} else {
// wallet is already loaded:
if (appStore.wallet != null || requireHardwareWalletConnection()) {
if (appStore.wallet != null || await requireHardwareWalletConnection()) {
// goes to the dashboard:
authStore.allowed();
// trigger any deep links:
@@ -847,8 +841,7 @@ Future<void> setup({
: null,
coinTypeToSpendFrom: coinTypeToSpendFrom ?? UnspentCoinType.nonMweb,
getIt.get<UnspentCoinsListViewModel>(param1: coinTypeToSpendFrom),
getIt.get<FeesViewModel>(),
_walletInfoSource,
getIt.get<FeesViewModel>()
),
);
@@ -867,7 +860,6 @@ Future<void> setup({
if (DeviceInfo.instance.isMobile) {
getIt.registerFactory(
() => WalletListViewModel(
_walletInfoSource,
getIt.get<AppStore>(),
getIt.get<WalletLoadingService>(),
getIt.get<WalletManager>(),
@@ -878,7 +870,6 @@ Future<void> setup({
// from multiple places at the same time (Wallets DropDown, Wallets List in settings)
getIt.registerLazySingleton(
() => WalletListViewModel(
_walletInfoSource,
getIt.get<AppStore>(),
getIt.get<WalletLoadingService>(),
getIt.get<WalletManager>(),
@@ -890,7 +881,7 @@ Future<void> setup({
(Function(BuildContext)? onWalletLoaded, _) => WalletListPage(
walletListViewModel: getIt.get<WalletListViewModel>(),
authService: getIt.get<AuthService>(),
onWalletLoaded: onWalletLoaded,
onWalletLoaded: onWalletLoaded as Future<void> Function(BuildContext)?,
));
getIt.registerFactoryParam<WalletEditViewModel, WalletListViewModel, void>(
@@ -1034,7 +1025,7 @@ Future<void> setup({
getIt.registerFactoryParam<ContactListViewModel, CryptoCurrency?, void>(
(CryptoCurrency? cur, _) =>
ContactListViewModel(_contactSource, _walletInfoSource, cur, getIt.get<SettingsStore>()));
ContactListViewModel(_contactSource, walletList, cur, getIt.get<SettingsStore>()));
getIt.registerFactoryParam<ContactListPage, CryptoCurrency?, void>((CryptoCurrency? cur, _) =>
ContactListPage(getIt.get<ContactListViewModel>(param1: cur), getIt.get<AuthService>()));
@@ -1190,62 +1181,53 @@ Future<void> setup({
getIt.registerFactory(() => PaymentViewModel(
appStore: getIt.get<AppStore>(),
walletInfoSource: _walletInfoSource,
));
getIt.registerFactory(() => WalletSwitcherViewModel(
appStore: getIt.get<AppStore>(),
walletLoadingService: getIt.get<WalletLoadingService>(),
walletInfoSource: _walletInfoSource,
));
getIt.registerFactoryParam<WalletService, WalletType, void>((WalletType param1, __) {
switch (param1) {
case WalletType.monero:
return monero!.createMoneroWalletService(_walletInfoSource, _unspentCoinsInfoSource);
return monero!.createMoneroWalletService(_unspentCoinsInfoSource);
case WalletType.bitcoin:
return bitcoin!.createBitcoinWalletService(
_walletInfoSource,
_unspentCoinsInfoSource,
_payjoinSessionSource,
SettingsStoreBase.walletPasswordDirectInput,
);
case WalletType.litecoin:
return bitcoin!.createLitecoinWalletService(
_walletInfoSource,
_unspentCoinsInfoSource,
SettingsStoreBase.walletPasswordDirectInput,
);
case WalletType.ethereum:
return ethereum!.createEthereumWalletService(
_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
return ethereum!.createEthereumWalletService(SettingsStoreBase.walletPasswordDirectInput);
case WalletType.bitcoinCash:
return bitcoinCash!.createBitcoinCashWalletService(_walletInfoSource,
_unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput);
return bitcoinCash!.createBitcoinCashWalletService(_unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput);
case WalletType.dogecoin:
return dogecoin!.createDogeCoinWalletService(_walletInfoSource,
_unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput);
return dogecoin!.createDogeCoinWalletService(_unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput);
case WalletType.nano:
case WalletType.banano:
return nano!.createNanoWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
return nano!.createNanoWalletService(SettingsStoreBase.walletPasswordDirectInput);
case WalletType.polygon:
return polygon!.createPolygonWalletService(
_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
return polygon!.createPolygonWalletService(SettingsStoreBase.walletPasswordDirectInput);
case WalletType.solana:
return solana!.createSolanaWalletService(
_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
return solana!.createSolanaWalletService(SettingsStoreBase.walletPasswordDirectInput);
case WalletType.tron:
return tron!.createTronWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
return tron!.createTronWalletService(SettingsStoreBase.walletPasswordDirectInput);
case WalletType.wownero:
return wownero!.createWowneroWalletService(_walletInfoSource, _unspentCoinsInfoSource);
return wownero!.createWowneroWalletService(_unspentCoinsInfoSource);
case WalletType.zano:
return zano!.createZanoWalletService(_walletInfoSource);
return zano!.createZanoWalletService();
case WalletType.decred:
return decred!.createDecredWalletService(_walletInfoSource, _unspentCoinsInfoSource);
return decred!.createDecredWalletService(_unspentCoinsInfoSource);
case WalletType.base:
return base!.createBaseWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
return base!.createBaseWalletService(SettingsStoreBase.walletPasswordDirectInput);
case WalletType.haven:
return HavenWalletService(_walletInfoSource);
return HavenWalletService();
case WalletType.none:
throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService');
}
@@ -1275,7 +1257,6 @@ Future<void> setup({
return WalletRestoreViewModel(
getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type),
_walletInfoSource,
getIt.get<SeedSettingsViewModel>(),
type: type,
restoredWallet: restoredWallet,
@@ -1351,7 +1332,7 @@ Future<void> setup({
getIt.registerFactory(() => CakeFeaturesViewModel(getIt.get<CakePayService>()));
getIt.registerFactory(() => BackupServiceV3(getIt.get<SecureStorage>(), _walletInfoSource,
getIt.registerFactory(() => BackupServiceV3(getIt.get<SecureStorage>(),
_transactionDescriptionBox,
getIt.get<KeyService>(), getIt.get<SharedPreferences>()));
+2 -3
View File
@@ -4,9 +4,8 @@ part of 'dogecoin.dart';
class CWDogeCoin extends DogeCoin {
@override
WalletService createDogeCoinWalletService(
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
return DogeCoinWalletService(walletInfoSource, unspentCoinSource, isDirect);
WalletService createDogeCoinWalletService(Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
return DogeCoinWalletService(unspentCoinSource, isDirect);
}
@override
+32 -29
View File
@@ -5,6 +5,7 @@ import 'package:cake_wallet/core/secure_storage.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/haven_seed_store.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cake_wallet/entities/secret_store_key.dart';
import 'package:cw_core/root_dir.dart';
@@ -21,6 +22,7 @@ import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/entities/fs_migration.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_info_legacy.dart' as wiLegacy;
import 'package:cake_wallet/exchange/trade.dart';
import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:collection/collection.dart';
@@ -55,12 +57,11 @@ Future<void> defaultSettingsMigration(
required SecureStorage secureStorage,
required Box<Node> nodes,
required Box<Node> powNodes,
required Box<WalletInfo> walletInfoSource,
required Box<Trade> tradeSource,
required Box<Contact> contactSource,
required Box<HavenSeedStore> havenSeedStore}) async {
if (Platform.isIOS) {
await ios_migrate_v1(walletInfoSource, tradeSource, contactSource);
await ios_migrate_v1(tradeSource, contactSource);
}
// check current nodes for nullability regardless of the version
@@ -69,7 +70,7 @@ Future<void> defaultSettingsMigration(
final isNewInstall =
sharedPreferences.getInt(PreferencesKey.currentDefaultSettingsMigrationVersion) == null;
await _validateWalletInfoBoxData(walletInfoSource);
await _validateWalletInfoBoxData();
await sharedPreferences.setBool(PreferencesKey.isNewInstall, isNewInstall);
@@ -162,7 +163,7 @@ Future<void> defaultSettingsMigration(
break;
case 5:
await addAddressesForMoneroWallets(walletInfoSource);
await addAddressesForMoneroWallets();
break;
case 6:
@@ -323,7 +324,7 @@ Future<void> defaultSettingsMigration(
await updateNanoNodeList(nodes: nodes);
break;
case 32:
await updateBtcNanoWalletInfos(walletInfoSource);
await updateBtcNanoWalletInfos();
break;
case 33:
await addWalletNodeList(nodes: nodes, type: WalletType.tron);
@@ -361,7 +362,7 @@ Future<void> defaultSettingsMigration(
// await replaceTronDefaultNode(sharedPreferences: sharedPreferences, nodes: nodes);
break;
case 38:
await fixBtcDerivationPaths(walletInfoSource);
await fixBtcDerivationPaths();
break;
case 39:
_fixNodesUseSSLFlag(nodes);
@@ -738,7 +739,7 @@ Future<void> _updateMoneroPriority(SharedPreferences sharedPreferences) async {
}
}
Future<void> _validateWalletInfoBoxData(Box<WalletInfo> walletInfoSource) async {
Future<void> _validateWalletInfoBoxData() async {
try {
final root = await getAppDir();
@@ -780,7 +781,7 @@ Future<void> _validateWalletInfoBoxData(Box<WalletInfo> walletInfoSource) async
}
final id = prefix + '_' + name;
final exist = walletInfoSource.values.any((el) => el.id == id);
final exist = (await WalletInfo.getAll()).any((el) => el.id == id);
if (exist) {
continue;
@@ -799,7 +800,7 @@ Future<void> _validateWalletInfoBoxData(Box<WalletInfo> walletInfoSource) async
showIntroCakePayCard: false,
);
walletInfoSource.add(walletInfo);
await walletInfo.save();
}
}
} catch (_) {}
@@ -967,8 +968,8 @@ Future<void> updateNodeTypes({required Box<Node> nodes}) async {
});
}
Future<void> addAddressesForMoneroWallets(Box<WalletInfo> walletInfoSource) async {
final moneroWalletsInfo = walletInfoSource.values.where((info) => info.type == WalletType.monero);
Future<void> addAddressesForMoneroWallets() async {
final moneroWalletsInfo = (await WalletInfo.getAll()).where((info) => info.type == WalletType.monero);
moneroWalletsInfo.forEach((info) async {
try {
final walletPath = await pathForWallet(name: info.name, type: WalletType.monero);
@@ -1016,32 +1017,34 @@ Future<void> changeTransactionPriorityAndFeeRateKeys(SharedPreferences sharedPre
bitcoin!.getMediumTransactionPriority().serialize());
}
Future<void> fixBtcDerivationPaths(Box<WalletInfo> walletsInfoSource) async {
for (WalletInfo walletInfo in walletsInfoSource.values) {
Future<void> fixBtcDerivationPaths() async {
for (WalletInfo walletInfo in await WalletInfo.getAll()) {
if (walletInfo.type == WalletType.bitcoin ||
walletInfo.type == WalletType.bitcoinCash ||
walletInfo.type == WalletType.litecoin) {
if (walletInfo.derivationInfo?.derivationPath == "m/0'/0") {
walletInfo.derivationInfo!.derivationPath = "m/0'";
final derivationInfo = await walletInfo.getDerivationInfo();
if (derivationInfo?.derivationPath == "m/0'/0") {
derivationInfo!.derivationPath = "m/0'";
await walletInfo.save();
}
}
}
}
Future<void> updateBtcNanoWalletInfos(Box<WalletInfo> walletsInfoSource) async {
for (WalletInfo walletInfo in walletsInfoSource.values) {
if (walletInfo.type == WalletType.nano || walletInfo.type == WalletType.bitcoin) {
walletInfo.derivationInfo = DerivationInfo(
derivationPath: walletInfo.derivationPath,
derivationType: walletInfo.derivationType,
address: walletInfo.address,
transactionsCount: walletInfo.restoreHeight,
);
await walletInfo.save();
}
}
}
Future<void> updateBtcNanoWalletInfos() async {}
// Future<void> updateBtcNanoWalletInfos() async {
// for (WalletInfo walletInfo in await WalletInfo.getAll()) {
// if (walletInfo.type == WalletType.nano || walletInfo.type == WalletType.bitcoin) {
// final derivationInfo = await walletInfo.getDerivationInfo();
// derivationInfo = DerivationInfo(
// derivationPath: derivationInfo?.derivationPath,
// derivationType: derivationInfo?.derivationType,
// address: walletInfo.address,
// transactionsCount: walletInfo.restoreHeight,
// );
// await walletInfo.save();
// }
// }
// }
Future<void> checkCurrentNodes(
Box<Node> nodeSource, Box<Node> powNodeSource, SharedPreferences sharedPreferences) async {
+8 -6
View File
@@ -29,8 +29,7 @@ Future<void> migrate_android_v1() async {
await android_migrate_wallets(appDocDir: appDocDir);
}
Future<void> ios_migrate_v1(
Box<WalletInfo> walletInfoSource, Box<Trade> tradeSource, Box<Contact> contactSource) async {
Future<void> ios_migrate_v1(Box<Trade> tradeSource, Box<Contact> contactSource) async {
final prefs = await SharedPreferences.getInstance();
if (prefs.getBool('ios_migration_v1_completed') ?? false) {
@@ -40,7 +39,7 @@ Future<void> ios_migrate_v1(
await ios_migrate_user_defaults();
await ios_migrate_pin();
await ios_migrate_wallet_passwords();
await ios_migrate_wallet_info(walletInfoSource);
await ios_migrate_wallet_info();
await ios_migrate_trades_list(tradeSource);
await ios_migrate_address_book(contactSource);
@@ -278,7 +277,7 @@ Future<void> android_migrate_wallets({required Directory appDocDir}) async {
});
}
Future<void> ios_migrate_wallet_info(Box<WalletInfo> walletsInfoSource) async {
Future<void> ios_migrate_wallet_info() async {
final prefs = await SharedPreferences.getInstance();
if (prefs.getBool('ios_migration_wallet_info_completed') ?? false) {
@@ -289,6 +288,7 @@ Future<void> ios_migrate_wallet_info(Box<WalletInfo> walletsInfoSource) async {
final appDocDir = await getApplicationDocumentsDirectory();
final walletsDir = Directory('${appDocDir.path}/wallets');
final moneroWalletsDir = Directory('${walletsDir.path}/monero');
final walletsInfo = await WalletInfo.getAll();
final infoRecords = moneroWalletsDir
.listSync()
.map((item) {
@@ -307,7 +307,7 @@ Future<void> ios_migrate_wallet_info(Box<WalletInfo> walletsInfoSource) async {
final timestamp = dateAsDouble.toInt() * 1000;
final date = DateTime.fromMillisecondsSinceEpoch(timestamp);
final id = walletTypeToString(WalletType.monero).toLowerCase() + '_' + name;
final exist = walletsInfoSource.values.firstWhereOrNull((el) => el.id == id) != null;
final exist = walletsInfo.firstWhereOrNull((el) => el.id == id) != null;
if (exist) {
return null;
@@ -334,7 +334,9 @@ Future<void> ios_migrate_wallet_info(Box<WalletInfo> walletsInfoSource) async {
.where((el) => el != null)
.whereType<WalletInfo>()
.toList();
await walletsInfoSource.addAll(infoRecords);
for (final info in infoRecords) {
await info.save();
}
await prefs.setBool('ios_migration_wallet_info_completed', true);
} catch (e) {
printV(e.toString());
@@ -4,7 +4,7 @@ import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:shared_preferences/shared_preferences.dart';
bool requireHardwareWalletConnection() {
Future<bool> requireHardwareWalletConnection() async {
final name = getIt
.get<SharedPreferences>()
.getString(PreferencesKey.currentWalletName);
@@ -21,5 +21,5 @@ bool requireHardwareWalletConnection() {
final type = deserializeFromInt(typeRaw);
final walletLoadingService = getIt.get<WalletLoadingService>();
return walletLoadingService.requireHardwareWalletConnection(type, name);
return await walletLoadingService.requireHardwareWalletConnection(type, name);
}
+6 -7
View File
@@ -8,17 +8,16 @@ import 'package:hive/hive.dart';
import 'package:shared_preferences/shared_preferences.dart';
class WalletManager {
WalletManager(this._walletInfoSource, this._sharedPreferences);
WalletManager(this._sharedPreferences);
final Box<WalletInfo> _walletInfoSource;
final SharedPreferences _sharedPreferences;
final List<WalletGroup> walletGroups = [];
void updateWalletGroups() {
Future<void> updateWalletGroups() async {
walletGroups.clear();
for (final walletInfo in _walletInfoSource.values) {
for (final walletInfo in await WalletInfo.getAll()) {
final groupKey = _resolveGroupKey(walletInfo);
final group = _getOrCreateGroup(groupKey);
group.wallets.add(walletInfo);
@@ -115,7 +114,7 @@ class WalletManager {
// If the openedWallet already has an hash, then there is nothing to do
if (walletInfo.hashedWalletIdentifier != null &&
walletInfo.hashedWalletIdentifier!.isNotEmpty) {
updateWalletGroups(); // Still skeptical of calling this here. Looking for a better spot.
await updateWalletGroups(); // Still skeptical of calling this here. Looking for a better spot.
return;
}
@@ -123,7 +122,7 @@ class WalletManager {
final oldGroupKey = _resolveGroupKey(walletInfo); // parentAddress fallback
// Find all wallets that share this old group key (i.e the old group)
final oldGroupWallets = _walletInfoSource.values.where((w) {
final oldGroupWallets = (await WalletInfo.getAll()).where((w) {
final key = w.hashedWalletIdentifier != null && w.hashedWalletIdentifier!.isNotEmpty
? w.hashedWalletIdentifier
: (w.parentAddress ?? w.address);
@@ -150,7 +149,7 @@ class WalletManager {
}
// Finally, we rebuild the groups so that these wallets are now in the new group
updateWalletGroups();
await updateWalletGroups();
}
/// Copy an old group name to the new group key, then remove the old key.
+7 -6
View File
@@ -4,8 +4,8 @@ class CWEthereum extends Ethereum {
@override
List<String> getEthereumWordList(String language) => EVMChainMnemonics.englishWordlist;
WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
EthereumWalletService(walletInfoSource, isDirect, client: EthereumClient());
WalletService createEthereumWalletService(bool isDirect) =>
EthereumWalletService(isDirect, client: EthereumClient());
@override
WalletCredentials createEthereumNewWalletCredentials({
@@ -181,18 +181,19 @@ class CWEthereum extends Ethereum {
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
@override
void setHardwareWalletService(WalletBase wallet, HardwareWalletService service) {
Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service) async {
if (service is EVMChainLedgerService) {
((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials).setLedgerConnection(
service.ledgerConnection, wallet.walletInfo.derivationInfo?.derivationPath);
service.ledgerConnection, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
} else if (service is EVMChainBitboxService) {
((wallet as EVMChainWallet).evmChainPrivateKey as EvmBitboxCredentials)
.setBitbox(service.manager, wallet.walletInfo.derivationInfo?.derivationPath);
.setBitbox(service.manager, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
} else if (service is EVMChainTrezorService) {
((wallet as EVMChainWallet).evmChainPrivateKey as EvmTrezorCredentials)
.setTrezorConnect(service.connect, wallet.walletInfo.derivationInfo?.derivationPath);
.setTrezorConnect(service.connect, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
}
}
@override
HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection) =>
EVMChainLedgerService(connection);
+7 -6
View File
@@ -12,16 +12,15 @@ import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart';
class HavenWalletService extends WalletService {
final Box<WalletInfo> walletInfoSource;
HavenWalletService(this.walletInfoSource);
HavenWalletService();
@override
WalletType getType() => WalletType.haven;
@override
Future<void> remove(String wallet) async {
final path = await pathForWalletDir(name: wallet, type: WalletType.haven);
final path = await pathForWalletDir(name: wallet, type: getType());
final file = Directory(path);
final isExist = file.existsSync();
@@ -30,9 +29,11 @@ class HavenWalletService extends WalletService {
await file.delete(recursive: true);
}
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
await walletInfoSource.delete(walletInfo.key);
final walletInfo = await WalletInfo.get(wallet, getType());
if (walletInfo == null) {
throw Exception('Wallet not found');
}
await WalletInfo.delete(walletInfo);
}
@override
+1 -1
View File
@@ -1032,7 +1032,7 @@ class YoCupertinoLocalizations extends GlobalCupertinoLocalizations {
@override
// TODO: implement backButtonLabel
String get backButtonLabel => "backButtonLabel";
@override
// TODO: implement cancelButtonLabel
String get cancelButtonLabel => "cancelButtonLabel";
+8 -23
View File
@@ -47,8 +47,9 @@ import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/proxy_logger/memory_proxy_logger.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_core/utils/tor/abstract.dart';
import 'package:cw_core/db/sqlite.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/utils/tor/abstract.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@@ -86,12 +87,16 @@ Future<void> runAppWithZone({Key? topLevelKey}) async {
return true;
};
await FlutterDaemon().unmarkBackgroundSync();
await initDb();
try {
CakeTor.instance = await CakeTorInstance.getInstance();
} catch (e) {
printV("Failed to initialize tor: $e");
}
await initializeAppAtRoot();
if (kDebugMode) {
@@ -164,22 +169,6 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
CakeHive.registerAdapter(AddressInfoAdapter());
}
if (!CakeHive.isAdapterRegistered(WalletInfo.typeId)) {
CakeHive.registerAdapter(WalletInfoAdapter());
}
if (!CakeHive.isAdapterRegistered(DERIVATION_TYPE_TYPE_ID)) {
CakeHive.registerAdapter(DerivationTypeAdapter());
}
if (!CakeHive.isAdapterRegistered(DERIVATION_INFO_TYPE_ID)) {
CakeHive.registerAdapter(DerivationInfoAdapter());
}
if (!CakeHive.isAdapterRegistered(HARDWARE_WALLET_TYPE_TYPE_ID)) {
CakeHive.registerAdapter(HardwareWalletTypeAdapter());
}
if (!CakeHive.isAdapterRegistered(WALLET_TYPE_TYPE_ID)) {
CakeHive.registerAdapter(WalletTypeAdapter());
}
@@ -227,6 +216,7 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
if (!CakeHive.isAdapterRegistered(TronToken.typeId)) {
CakeHive.registerAdapter(TronTokenAdapter());
}
await performHiveMigration();
final secureStorage = secureStorageShared;
final transactionDescriptionsBoxKey =
@@ -242,7 +232,6 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
encryptionKey: transactionDescriptionsBoxKey);
final trades = await CakeHive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
final orders = await CakeHive.openBox<Order>(Order.boxName, encryptionKey: ordersBoxKey);
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
final templates = await CakeHive.openBox<Template>(Template.boxName);
final exchangeTemplates = await CakeHive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
final anonpayInvoiceInfo = await CakeHive.openBox<AnonpayInvoiceInfo>(AnonpayInvoiceInfo.boxName);
@@ -259,7 +248,6 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
sharedPreferences: await SharedPreferences.getInstance(),
nodes: nodes,
powNodes: powNodes,
walletInfoSource: walletInfoSource,
contactSource: contacts,
tradesSource: trades,
ordersSource: orders,
@@ -281,7 +269,6 @@ Future<void> initialSetup({
required SharedPreferences sharedPreferences,
required Box<Node> nodes,
required Box<Node> powNodes,
required Box<WalletInfo> walletInfoSource,
required Box<Contact> contactSource,
required Box<Trade> tradesSource,
required Box<Order> ordersSource,
@@ -294,21 +281,19 @@ Future<void> initialSetup({
required Box<UnspentCoinsInfo> unspentCoinsInfoSource,
required Box<PayjoinSession> payjoinSessionSource,
required Box<HavenSeedStore> havenSeedStore,
int initialMigrationVersion = 15,
required int initialMigrationVersion,
}) async {
LanguageService.loadLocaleList();
await defaultSettingsMigration(
secureStorage: secureStorage,
version: initialMigrationVersion,
sharedPreferences: sharedPreferences,
walletInfoSource: walletInfoSource,
contactSource: contactSource,
tradeSource: tradesSource,
nodes: nodes,
powNodes: powNodes,
havenSeedStore: havenSeedStore);
await setup(
walletInfoSource: walletInfoSource,
nodeSource: nodes,
powNodeSource: powNodes,
contactSource: contactSource,
+4 -5
View File
@@ -346,9 +346,8 @@ class CWMonero extends Monero {
}
@override
WalletService createMoneroWalletService(
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) =>
MoneroWalletService(walletInfoSource, unspentCoinSource);
WalletService createMoneroWalletService(Box<UnspentCoinsInfo> unspentCoinSource) =>
MoneroWalletService(unspentCoinSource);
@override
String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) {
@@ -416,9 +415,9 @@ class CWMonero extends Monero {
}
@override
void setLedgerConnection(Object wallet, ledger.LedgerConnection connection) {
Future<void> setLedgerConnection(Object wallet, ledger.LedgerConnection connection) async {
final moneroWallet = wallet as MoneroWallet;
moneroWallet.setLedgerConnection(connection);
await moneroWallet.setLedgerConnection(connection);
}
@override
+2 -2
View File
@@ -75,8 +75,8 @@ class CWNano extends Nano {
}
@override
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) {
return NanoWalletService(walletInfoSource, isDirect);
WalletService createNanoWalletService(bool isDirect) {
return NanoWalletService(isDirect);
}
@override
+6 -6
View File
@@ -4,8 +4,8 @@ class CWPolygon extends Polygon {
@override
List<String> getPolygonWordList(String language) => EVMChainMnemonics.englishWordlist;
WalletService createPolygonWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
PolygonWalletService(walletInfoSource, isDirect, client: PolygonClient());
WalletService createPolygonWalletService(bool isDirect) =>
PolygonWalletService(isDirect, client: PolygonClient());
@override
WalletCredentials createPolygonNewWalletCredentials({
@@ -206,16 +206,16 @@ class CWPolygon extends Polygon {
);
@override
void setHardwareWalletService(WalletBase wallet, HardwareWalletService service) {
Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service) async {
if (service is EVMChainLedgerService) {
((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials).setLedgerConnection(
service.ledgerConnection, wallet.walletInfo.derivationInfo?.derivationPath);
service.ledgerConnection, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
} else if (service is EVMChainBitboxService) {
((wallet as EVMChainWallet).evmChainPrivateKey as EvmBitboxCredentials)
.setBitbox(service.manager, wallet.walletInfo.derivationInfo?.derivationPath);
.setBitbox(service.manager, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
} else if (service is EVMChainTrezorService) {
((wallet as EVMChainWallet).evmChainPrivateKey as EvmTrezorCredentials)
.setTrezorConnect(service.connect, wallet.walletInfo.derivationInfo?.derivationPath);
.setTrezorConnect(service.connect, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
}
}
@@ -43,7 +43,7 @@ void startAuthenticationStateChange(
if (state == AuthenticationState.installed &&
!SettingsStoreBase.walletPasswordDirectInput) {
try {
if (!requireHardwareWalletConnection()) await loadCurrentWallet();
if (!(await requireHardwareWalletConnection())) await loadCurrentWallet();
} catch (error, stack) {
loginError = error;
await ExceptionHandler.resetLastPopupDate();
@@ -56,7 +56,7 @@ void startAuthenticationStateChange(
if ([AuthenticationState.allowed, AuthenticationState.allowedCreate]
.contains(state)) {
if (state == AuthenticationState.allowed &&
requireHardwareWalletConnection()) {
(await requireHardwareWalletConnection())) {
await navigatorKey.currentState!.pushNamedAndRemoveUntil(
Routes.connectDevices,
(route) => false,
+1 -3
View File
@@ -99,9 +99,7 @@ void startCurrentWalletChangeReaction(
if (wallet.walletInfo.address.isEmpty) {
wallet.walletInfo.address = wallet.walletAddresses.address;
if (wallet.walletInfo.isInBox) {
await wallet.walletInfo.save();
}
await wallet.walletInfo.save();
}
} catch (e) {
printV(e.toString());
+2 -2
View File
@@ -4,8 +4,8 @@ class CWSolana extends Solana {
@override
List<String> getSolanaWordList(String language) => SolanaMnemonics.englishWordlist;
WalletService createSolanaWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
SolanaWalletService(walletInfoSource, isDirect);
WalletService createSolanaWalletService(bool isDirect) =>
SolanaWalletService(isDirect);
@override
WalletCredentials createSolanaNewWalletCredentials({
@@ -17,6 +17,7 @@ import 'package:cw_core/wallet_type.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
class ContactListPage extends BasePage {
@@ -150,7 +151,9 @@ class _ContactPageBodyState extends State<ContactPageBody> with SingleTickerProv
child: TabBarView(
controller: _tabController,
children: [
_buildWalletContacts(context),
Observer(
builder: (final BuildContext context) => _buildWalletContacts(context),
),
ContactListBody(
contactListViewModel: widget.contactListViewModel,
tabController: _tabController,
@@ -351,7 +351,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
_formProcessing = false;
return;
}
if (_walletNewVM.nameExists(_walletNewVM.name)) {
if (await _walletNewVM.nameExists(_walletNewVM.name)) {
await showPopUp<void>(
context: context,
builder: (_) {
@@ -258,7 +258,7 @@ class WalletRestorePage extends BasePage {
return;
}
if (walletRestoreViewModel.nameExists(name)) {
if (await walletRestoreViewModel.nameExists(name)) {
showNameExistsAlert(formContext!);
_formProcessing = false;
return;
@@ -3,6 +3,7 @@ import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/dev/moneroc_cache_debug.dart';
import 'package:cake_wallet/src/screens/settings/widgets/setting_priority_picker_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
@@ -11,6 +12,7 @@ import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_core/db/sqlite.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -144,6 +146,16 @@ class OtherSettingsPage extends BasePage {
handler: (BuildContext context) =>
Navigator.of(context).pushNamed(Routes.devExchangeProviderLogs),
),
if (FeatureFlag.hasDevOptions)
SettingsCellWithArrow(
title: '[dev] browse sqlite db',
handler: (BuildContext context) async {
final data = await dumpDb();
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => JsonExplorerPage(data: data, title: 'sqlite db')),
);
}
),
Spacer(),
SettingsVersionCell(
title: S.of(context).version(_otherSettingsViewModel.currentVersion),
+11 -5
View File
@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:another_flushbar/flushbar.dart';
import 'package:cake_wallet/core/wallet_name_validator.dart';
import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
@@ -10,6 +12,7 @@ import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
@@ -86,7 +89,7 @@ class WalletEditPage extends BasePage {
onPressed: () async {
if (_formKey.currentState?.validate() ?? false) {
if (!pageArguments.isWalletGroup &&
pageArguments.walletNewVM!
await pageArguments.walletNewVM!
.nameExists(walletEditViewModel.newName)) {
showPopUp<void>(
context: context,
@@ -217,9 +220,12 @@ class WalletEditPage extends BasePage {
}
Future<void> hideProgressText() async {
await Future.delayed(Duration(milliseconds: 50), () {
_progressBar?.dismiss();
_progressBar = null;
});
try {
await Future.delayed(Duration(milliseconds: 250));
await _progressBar?.dismiss();
} catch (e) {
printV(e);
}
_progressBar = null;
}
}
@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:another_flushbar/flushbar.dart';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/core/new_wallet_arguments.dart';
@@ -27,6 +29,7 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -40,17 +43,26 @@ class WalletListPage extends BasePage {
final WalletListViewModel walletListViewModel;
final AuthService authService;
final Function(BuildContext)? onWalletLoaded;
final Future<void> Function(BuildContext)? onWalletLoaded;
@override
String get title => S.current.wallets;
@override
Widget body(BuildContext context) => WalletListBody(
Widget body(BuildContext context) => Observer(
builder: (_) {
if (walletListViewModel.singleWalletsList.isEmpty && walletListViewModel.multiWalletGroups.isEmpty) {
return Center(
child: CircularProgressIndicator(),
);
}
return WalletListBody(
walletListViewModel: walletListViewModel,
authService: authService,
onWalletLoaded: onWalletLoaded ?? (context) => Navigator.of(context).pop(),
);
}
);
@override
Widget trailing(BuildContext context) {
@@ -459,7 +471,7 @@ class WalletListBodyState extends State<WalletListBody> {
try {
final requireHardwareWalletConnection =
widget.walletListViewModel.requireHardwareWalletConnection(wallet);
await widget.walletListViewModel.requireHardwareWalletConnection(wallet);
if (requireHardwareWalletConnection) {
bool didConnect = false;
await Navigator.of(context).pushNamed(
@@ -489,22 +501,20 @@ class WalletListBodyState extends State<WalletListBody> {
buttonAction: () => Navigator.of(context).pop()),
);
}
changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
await widget.walletListViewModel.loadWallet(wallet);
await hideProgressText();
// only pop the wallets route in mobile as it will go back to dashboard page
// in desktop platforms the navigation tree is different
if (responsiveLayoutUtil.shouldRenderMobileUI) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (this.mounted) {
if (requireHardwareWalletConnection) {
Navigator.of(context).pop();
}
widget.onWalletLoaded.call(context);
}
});
// await Future.delayed(Duration(seconds: 1));
// if (!this.mounted) return;
if (!context.mounted) return;
if (requireHardwareWalletConnection) {
Navigator.of(context).pop();
}
await widget.onWalletLoaded.call(context);
}
unawaited(hideProgressText());
} catch (e) {
await ExceptionHandler.resetLastPopupDate();
final err = e.toString();
@@ -52,76 +52,79 @@ class _WalletSwitcherContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Observer(
builder: (_) {
final wallets = viewModel.getWallets(filterWalletType);
if (viewModel.isProcessing) {
return Container(
height: 200,
child: Center(
child: CircularProgressIndicator(
color: Theme.of(context).colorScheme.primary,
),
),
);
}
return Container(
height: 400,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: StandardList(
itemCount: wallets.length,
itemBuilder: (context, index) {
final wallet = wallets[index];
return InkWell(
onTap: () {
viewModel.selectWallet(wallet);
Navigator.of(context).pop();
},
borderRadius: BorderRadius.circular(12),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(16),
),
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
Image.asset(
walletTypeToCryptoCurrency(wallet.type).iconPath!,
width: 32,
height: 32,
),
const SizedBox(width: 16),
Text(
wallet.name,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
fontSize: 18,
fontWeight: FontWeight.w700,
color: Theme.of(context).colorScheme.onSurface,
letterSpacing: 0.0,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
);
},
return FutureBuilder(
future: viewModel.getWallets(filterWalletType),
builder: (context, snapshot) => Observer(
builder: (_) {
final List<WalletInfo> wallets = (snapshot.data ?? []);
if (viewModel.isProcessing) {
return Container(
height: 200,
child: Center(
child: CircularProgressIndicator(
color: Theme.of(context).colorScheme.primary,
),
),
],
),
);
},
);
}
return Container(
height: 400,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: StandardList(
itemCount: wallets.length,
itemBuilder: (context, index) {
final wallet = wallets[index];
return InkWell(
onTap: () {
viewModel.selectWallet(wallet);
Navigator.of(context).pop();
},
borderRadius: BorderRadius.circular(12),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(16),
),
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
Image.asset(
walletTypeToCryptoCurrency(wallet.type).iconPath!,
width: 32,
height: 32,
),
const SizedBox(width: 16),
Text(
wallet.name,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
fontSize: 18,
fontWeight: FontWeight.w700,
color: Theme.of(context).colorScheme.onSurface,
letterSpacing: 0.0,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
);
},
),
),
],
),
);
},
),
);
}
}
+2 -2
View File
@@ -55,8 +55,8 @@ abstract class AppStoreBase with Store {
getIt.get<WalletKitService>().create();
await getIt.get<WalletKitService>().init();
}
getIt.get<SharedPreferences>().setString(PreferencesKey.currentWalletName, wallet.name);
getIt
await getIt.get<SharedPreferences>().setString(PreferencesKey.currentWalletName, wallet.name);
await getIt
.get<SharedPreferences>()
.setInt(PreferencesKey.currentWalletType, serializeToInt(wallet.type));
}
+1 -3
View File
@@ -243,9 +243,7 @@ abstract class YatStoreBase with Store {
walletInfo!.yatEid = emoji;
if (walletInfo!.isInBox) {
walletInfo!.save();
}
walletInfo!.save();
} catch (e) {
printV(e.toString());
}
+2 -2
View File
@@ -5,8 +5,8 @@ class CWTron extends Tron {
List<String> getTronWordList(String language) => EVMChainMnemonics.englishWordlist;
@override
WalletService createTronWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
TronWalletService(walletInfoSource, client: TronClient(), isDirect: isDirect);
WalletService createTronWalletService(bool isDirect) =>
TronWalletService(client: TronClient(), isDirect: isDirect);
@override
WalletCredentials createTronNewWalletCredentials({
+2
View File
@@ -290,6 +290,8 @@ class ExceptionHandler {
"core/key_service.dart:14",
"Wallet is null",
"Wrong Device Status: 0x5515 (UNKNOWN)",
"FocusScopeNode was used after being disposed",
];
static Future<void> _addDeviceInfo(File file) async {
+13 -17
View File
@@ -10,11 +10,10 @@ import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart';
class TokenUtilities {
static Future<List<Erc20Token>> loadAllUniqueEvmTokens(
Box<WalletInfo> walletInfoSource,
) async {
final evmWallets = walletInfoSource.values.where(
(w) => isEVMCompatibleChain(w.type),
static Future<List<Erc20Token>> loadAllUniqueEvmTokens() async {
final allWi = await WalletInfo.getAll();
final evmWallets = allWi.where(
(w) => isEVMCompatibleChain(w.type),
);
final seen = <String>{};
@@ -35,10 +34,9 @@ class TokenUtilities {
return unique;
}
static Future<List<SPLToken>> loadAllUniqueSolTokens(
Box<WalletInfo> walletInfoSource,
) async {
final solWallets = walletInfoSource.values.where(
static Future<List<SPLToken>> loadAllUniqueSolTokens() async {
final allWi = await WalletInfo.getAll();
final solWallets = allWi.where(
(w) => w.type == WalletType.solana,
);
@@ -57,10 +55,9 @@ class TokenUtilities {
return unique;
}
static Future<List<TronToken>> loadAllUniqueTronTokens(
Box<WalletInfo> walletInfoSource,
) async {
final tronWallets = walletInfoSource.values.where(
static Future<List<TronToken>> loadAllUniqueTronTokens() async {
final allWi = await WalletInfo.getAll();
final tronWallets = allWi.where(
(w) => w.type == WalletType.tron,
);
@@ -82,7 +79,6 @@ class TokenUtilities {
/// - Tron: match by contractAddress
static Future<CryptoCurrency?> findTokenByAddress({
required WalletType walletType,
required Box<WalletInfo> walletInfoSource,
required String address,
}) async {
final lower = address.toLowerCase();
@@ -90,19 +86,19 @@ class TokenUtilities {
case WalletType.ethereum:
case WalletType.polygon:
case WalletType.base:
final tokens = await loadAllUniqueEvmTokens(walletInfoSource);
final tokens = await loadAllUniqueEvmTokens();
for (final t in tokens) {
if (t.contractAddress.toLowerCase() == lower) return t;
}
return null;
case WalletType.solana:
final solTokens = await loadAllUniqueSolTokens(walletInfoSource);
final solTokens = await loadAllUniqueSolTokens();
for (final t in solTokens) {
if (t.mintAddress.toLowerCase() == lower) return t;
}
return null;
case WalletType.tron:
final tronTokens = await loadAllUniqueTronTokens(walletInfoSource);
final tronTokens = await loadAllUniqueTronTokens();
for (final t in tronTokens) {
if (t.contractAddress.toLowerCase() == lower) return t;
}
@@ -22,16 +22,22 @@ class ContactListViewModel = ContactListViewModelBase with _$ContactListViewMode
abstract class ContactListViewModelBase with Store {
ContactListViewModelBase(
this.contactSource, this.walletInfoSource, this._currency, this.settingsStore)
this.contactSource, List<WalletInfo> wallets, this._currency, this.settingsStore)
: contacts = ObservableList<ContactRecord>(),
walletContacts = [],
isAutoGenerateEnabled =
settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.enabled {
walletInfoSource.values.forEach((info) {
unawaited(_init());
}
Future<void> _init() async {
final walletInfos = await WalletInfo.getAll();
for (final info in walletInfos) {
final addressInfos = await info.getAddressInfos();
final addresses = await info.getAddresses();
if ([WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type) &&
info.addressInfos != null) {
for (var key in info.addressInfos!.keys) {
final value = info.addressInfos![key];
addressInfos.isNotEmpty) {
for (var key in addressInfos.keys) {
final value = addressInfos[key];
final address = value?.first;
if (address != null) {
final name = _createName(info.name, address.label, key: key);
@@ -42,7 +48,7 @@ abstract class ContactListViewModelBase with Store {
));
}
}
} else if (info.addresses?.isNotEmpty == true && info.addresses!.length > 1) {
} else if (addresses.isNotEmpty == true && addresses.length > 1) {
if ([WalletType.monero, WalletType.wownero, WalletType.haven, WalletType.decred]
.contains(info.type)) {
final address = info.address;
@@ -53,7 +59,7 @@ abstract class ContactListViewModelBase with Store {
walletTypeToCryptoCurrency(info.type),
));
} else {
info.addresses!.forEach((address, label) {
addresses.forEach((address, label) {
if (label.isEmpty) {
return;
}
@@ -78,13 +84,14 @@ abstract class ContactListViewModelBase with Store {
walletTypeToCryptoCurrency(info.type),
));
}
});
}
_subscription = contactSource.bindToListWithTransform(
contacts, (Contact contact) => ContactRecord(contactSource, contact),
initialFire: true);
setOrderType(settingsStore.contactListOrder);
walletContacts = walletContacts.toList(); // rebuild
}
String _createName(String walletName, String label, {int? key = null}) {
@@ -97,9 +104,9 @@ abstract class ContactListViewModelBase with Store {
final bool isAutoGenerateEnabled;
final Box<Contact> contactSource;
final Box<WalletInfo> walletInfoSource;
final ObservableList<ContactRecord> contacts;
final List<WalletContact> walletContacts;
@observable
List<WalletContact> walletContacts = [];
final CryptoCurrency? _currency;
StreamSubscription<BoxEvent>? _subscription;
final SettingsStore settingsStore;
@@ -1098,8 +1098,8 @@ abstract class DashboardViewModelBase with Store {
void setSyncAll(bool value) => settingsStore.currentSyncAll = value;
Future<List<String>> checkForHavenWallets() async {
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
return walletInfoSource.values
final walletInfos = await WalletInfo.getAll();
return walletInfos
.where((element) => element.type == WalletType.haven)
.map((e) => e.name)
.toList();
@@ -1112,10 +1112,9 @@ abstract class DashboardViewModelBase with Store {
.loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt');
final vulnerableSeeds = vulnerableSeedsString.split("\n");
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
List<String> affectedWallets = [];
for (var walletInfo in walletInfoSource.values) {
final walletInfos = await WalletInfo.getAll();
for (var walletInfo in walletInfos) {
if (walletInfo.type == WalletType.bitcoin) {
final password = await keyService.getWalletPassword(walletName: walletInfo.name);
final path = await pathForWallet(name: walletInfo.name, type: walletInfo.type);
@@ -44,6 +44,7 @@ import 'package:cake_wallet/utils/token_utilities.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cake_wallet/view_model/send/fees_view_model.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/crypto_amount_format.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/erc20_token.dart';
@@ -82,7 +83,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
this.contactListViewModel,
this.unspentCoinsListViewModel,
this.feesViewModel,
this.walletInfoSource,
this.fiatConversionStore,
) : _cryptoNumberFormat = NumberFormat(),
isSendAllEnabled = false,
@@ -369,8 +369,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
final FeesViewModel feesViewModel;
final Box<WalletInfo> walletInfoSource;
@observable
double bestRate = 0.0;
@@ -1096,7 +1094,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
@action
Future<void> _injectUserEthTokensIntoCurrencyLists() async {
final userTokens = await TokenUtilities.loadAllUniqueEvmTokens(walletInfoSource);
final userTokens = await TokenUtilities.loadAllUniqueEvmTokens();
final toAddReceive = <CryptoCurrency>[];
final toAddDeposit = <CryptoCurrency>[];
@@ -1134,7 +1132,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
@action
Future<void> _injectUserSplTokensIntoCurrencyLists() async {
final userTokens = await TokenUtilities.loadAllUniqueSolTokens(walletInfoSource);
final userTokens = await TokenUtilities.loadAllUniqueSolTokens();
final toAddReceive = <CryptoCurrency>[];
final toAddDeposit = <CryptoCurrency>[];
@@ -1162,7 +1160,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
@action
Future<void> _injectUserTronTokensIntoCurrencyLists() async {
final userTokens = await TokenUtilities.loadAllUniqueTronTokens(walletInfoSource);
final userTokens = await TokenUtilities.loadAllUniqueTronTokens();
final toAddReceive = <CryptoCurrency>[];
final toAddDeposit = <CryptoCurrency>[];

Some files were not shown because too many files have changed in this diff Show More