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

cyan committed Oct 31, 2025 at 22:37 UTC 76876181e593f8c5ae4cfa3af5215cc3fbe4028f
121 files changed +2624 -918
.github/workflows/pr_test_build_linux.yml
+2 -2
@@ -308,8 +308,8 @@ jobs:
308 rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet
309 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
310 - name: Test [cw_monero]
311 - timeout-minutes: 2
312 - run: cd cw_monero && flutter test
311 + timeout-minutes: 15
312 + run: cd cw_monero && flutter test --verbose
313 - name: Stop screen recording, encrypt and upload
314 if: always()
315 run: |
Dockerfile
+2
@@ -59,6 +59,8 @@ RUN set -o xtrace \
59 ffmpeg network-manager x11-utils xvfb psmisc \
60 # extra linux dependencies so flutter doesn't complain
61 mesa-utils \
62 + # database
63 + libsqlite3-0 libsqlite3-dev \
64 # aarch64-linux-gnu dependencies
65 g++-aarch64-linux-gnu gcc-aarch64-linux-gnu \
66 # x86_64-linux-gnu dependencies
cw_base/lib/base_wallet.dart
+2
@@ -22,6 +22,7 @@ class BaseWallet extends EVMChainWallet {
22 BaseWallet({
23 required super.walletInfo,
24 required super.password,
25 + required super.derivationInfo,
26 super.mnemonic,
27 super.initialBalance,
28 super.privateKey,
@@ -150,6 +151,7 @@ class BaseWallet extends EVMChainWallet {
151
152 return BaseWallet(
153 walletInfo: walletInfo,
154 + derivationInfo: await walletInfo.getDerivationInfo(),
155 password: password,
156 mnemonic: keysData.mnemonic,
157 privateKey: keysData.privateKey,
cw_base/lib/base_wallet_service.dart
+18 -9
@@ -11,7 +11,6 @@ import 'package:cw_base/base_mnemonics_exception.dart';
11
12 class BaseWalletService extends EVMChainWalletService<BaseWallet> {
13 BaseWalletService(
14 - super.walletInfoSource,
14 super.isDirect, {
15 required this.client,
16 });
@@ -29,6 +28,7 @@ class BaseWalletService extends EVMChainWalletService<BaseWallet> {
28
29 final wallet = BaseWallet(
30 walletInfo: credentials.walletInfo!,
31 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
32 mnemonic: mnemonic,
33 password: credentials.password!,
34 passphrase: credentials.passphrase,
@@ -44,8 +44,10 @@ class BaseWalletService extends EVMChainWalletService<BaseWallet> {
44
45 @override
46 Future<BaseWallet> openWallet(String name, String password) async {
47 - final walletInfo =
48 - walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
47 + final walletInfo = await WalletInfo.get(name, getType());
48 + if (walletInfo == null) {
49 + throw Exception('Wallet not found');
50 + }
51
52 try {
53 final wallet = await BaseWallet.open(
@@ -84,6 +86,7 @@ class BaseWalletService extends EVMChainWalletService<BaseWallet> {
86 password: credentials.password!,
87 privateKey: credentials.privateKey,
88 walletInfo: credentials.walletInfo!,
89 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
90 client: client,
91 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
92 );
@@ -97,14 +100,17 @@ class BaseWalletService extends EVMChainWalletService<BaseWallet> {
100 @override
101 Future<BaseWallet> restoreFromHardwareWallet(
102 EVMChainRestoreWalletFromHardware credentials) async {
100 - credentials.walletInfo!.derivationInfo = DerivationInfo(
101 - derivationType: DerivationType.bip39,
102 - derivationPath: "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0");
103 + final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
104 + derivationInfo.derivationType = DerivationType.bip39;
105 + derivationInfo.derivationPath = "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0";
106 + await derivationInfo.save();
107 credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
108 credentials.walletInfo!.address = credentials.hwAccountData.address;
109 + await credentials.walletInfo!.save();
110
111 final wallet = BaseWallet(
112 walletInfo: credentials.walletInfo!,
113 + derivationInfo: derivationInfo,
114 password: credentials.password!,
115 client: client,
116 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -128,6 +134,7 @@ class BaseWalletService extends EVMChainWalletService<BaseWallet> {
134 password: credentials.password!,
135 mnemonic: credentials.mnemonic,
136 walletInfo: credentials.walletInfo!,
137 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
138 passphrase: credentials.passphrase,
139 client: client,
140 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -142,8 +149,10 @@ class BaseWalletService extends EVMChainWalletService<BaseWallet> {
149
150 @override
151 Future<void> rename(String currentName, String password, String newName) async {
145 - final currentWalletInfo = walletInfoSource.values
146 - .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
152 + final currentWalletInfo = await WalletInfo.get(currentName, getType());
153 + if (currentWalletInfo == null) {
154 + throw Exception('Wallet not found');
155 + }
156 final currentWallet = await BaseWallet.open(
157 password: password,
158 name: currentName,
@@ -158,6 +167,6 @@ class BaseWalletService extends EVMChainWalletService<BaseWallet> {
167 newWalletInfo.id = WalletBase.idFor(newName, getType());
168 newWalletInfo.name = newName;
169
161 - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
170 + newWalletInfo.save();
171 }
172 }
cw_bitcoin/lib/bitcoin_wallet.dart
+14 -6
@@ -47,6 +47,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
47 BitcoinWalletBase({
48 required String password,
49 required WalletInfo walletInfo,
50 + required DerivationInfo derivationInfo,
51 required Box<UnspentCoinsInfo> unspentCoinsInfo,
52 required Box<PayjoinSession> payjoinBox,
53 required EncryptionFileUtils encryptionFileUtils,
@@ -69,6 +70,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
70 xpub: xpub,
71 password: password,
72 walletInfo: walletInfo,
73 + derivationInfo: derivationInfo,
74 unspentCoinsInfo: unspentCoinsInfo,
75 network: networkParam == null
76 ? BitcoinNetwork.mainnet
@@ -133,7 +135,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
135 }) async {
136 late Uint8List seedBytes;
137
136 - switch (walletInfo.derivationInfo?.derivationType) {
138 + final derivationInfo = await walletInfo.getDerivationInfo();
139 +
140 + switch (derivationInfo.derivationType) {
141 case DerivationType.bip39:
142 seedBytes = await bip39.mnemonicToSeed(
143 mnemonic,
@@ -152,6 +156,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
156 passphrase: passphrase ?? "",
157 password: password,
158 walletInfo: walletInfo,
159 + derivationInfo: derivationInfo,
160 unspentCoinsInfo: unspentCoinsInfo,
161 initialAddresses: initialAddresses,
162 initialSilentAddresses: initialSilentAddresses,
@@ -212,20 +217,21 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
217 );
218 }
219
215 - walletInfo.derivationInfo ??= DerivationInfo();
220 + final derivationInfo = await walletInfo.getDerivationInfo();
221
222 // set the default if not present:
218 - walletInfo.derivationInfo!.derivationPath ??=
223 + derivationInfo.derivationPath ??=
224 snp?.derivationPath ?? electrum_path;
220 - walletInfo.derivationInfo!.derivationType ??=
225 + derivationInfo.derivationType ??=
226 snp?.derivationType ?? DerivationType.electrum;
227 + await derivationInfo.save();
228
229 Uint8List? seedBytes = null;
230 final mnemonic = keysData.mnemonic;
231 final passphrase = keysData.passphrase;
232
233 if (mnemonic != null) {
228 - switch (walletInfo.derivationInfo!.derivationType) {
234 + switch (derivationInfo.derivationType) {
235 case DerivationType.electrum:
236 seedBytes =
237 await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
@@ -246,6 +252,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
252 password: password,
253 passphrase: passphrase,
254 walletInfo: walletInfo,
255 + derivationInfo: derivationInfo,
256 unspentCoinsInfo: unspentCoinsInfo,
257 initialAddresses: snp?.addresses,
258 initialSilentAddresses: snp?.silentAddresses,
@@ -484,7 +491,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
491 : null;
492 final index = addressEntry?.index ?? 0;
493 final isChange = addressEntry?.isHidden == true ? 1 : 0;
487 - final accountPath = walletInfo.derivationInfo?.derivationPath;
494 + final derivationInfo = await walletInfo.getDerivationInfo();
495 + final accountPath = derivationInfo.derivationPath;
496 final derivationPath =
497 accountPath != null ? "$accountPath/$isChange/$index" : null;
498
cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart
+4
@@ -16,6 +16,10 @@ class BitcoinNewWalletCredentials extends WalletCredentials {
16 walletInfo: walletInfo,
17 password: password,
18 passphrase: passphrase,
19 + derivationInfo: DerivationInfo(
20 + derivationType: derivationType,
21 + derivationPath: derivationPath,
22 + ),
23 );
24
25 final String? mnemonic;
cw_bitcoin/lib/bitcoin_wallet_service.dart
+28 -12
@@ -23,10 +23,9 @@ class BitcoinWalletService extends WalletService<
23 BitcoinRestoreWalletFromSeedCredentials,
24 BitcoinWalletFromKeysCredentials,
25 BitcoinRestoreWalletFromHardware> {
26 - BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource,
26 + BitcoinWalletService(this.unspentCoinsInfoSource,
27 this.payjoinSessionSource, this.isDirect);
28
29 - final Box<WalletInfo> walletInfoSource;
29 final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
30 final Box<PayjoinSession> payjoinSessionSource;
31 final bool isDirect;
@@ -40,7 +39,13 @@ class BitcoinWalletService extends WalletService<
39 credentials.walletInfo?.network = network.value;
40
41 final String mnemonic;
43 - switch ( credentials.walletInfo?.derivationInfo?.derivationType) {
42 + final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
43 + derivationInfo.derivationType = credentials.derivationInfo?.derivationType ?? derivationInfo.derivationType;
44 + derivationInfo.derivationPath = credentials.derivationInfo?.derivationPath ?? derivationInfo.derivationPath;
45 + derivationInfo.description = credentials.derivationInfo?.description ?? derivationInfo.description;
46 + derivationInfo.scriptType = credentials.derivationInfo?.scriptType ?? derivationInfo.scriptType;
47 + await derivationInfo.save();
48 + switch (derivationInfo.derivationType) {
49 case DerivationType.bip39:
50 final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
51
@@ -51,6 +56,7 @@ class BitcoinWalletService extends WalletService<
56 mnemonic = await generateElectrumMnemonic();
57 break;
58 }
59 + await derivationInfo.save();
60
61 final wallet = await BitcoinWalletBase.create(
62 mnemonic: mnemonic,
@@ -75,8 +81,10 @@ class BitcoinWalletService extends WalletService<
81
82 @override
83 Future<BitcoinWallet> openWallet(String name, String password) async {
78 - final walletInfo = walletInfoSource.values
79 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
84 + final walletInfo = await WalletInfo.get(name, getType());
85 + if (walletInfo == null) {
86 + throw Exception('Wallet not found');
87 + }
88 try {
89 final wallet = await BitcoinWalletBase.open(
90 password: password,
@@ -107,9 +115,11 @@ class BitcoinWalletService extends WalletService<
115 @override
116 Future<void> remove(String wallet) async {
117 File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
110 - final walletInfo = walletInfoSource.values
111 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
112 - await walletInfoSource.delete(walletInfo.key);
118 + final walletInfo = await WalletInfo.get(wallet, getType());
119 + if (walletInfo == null) {
120 + throw Exception('Wallet not found');
121 + }
122 + await WalletInfo.delete(walletInfo);
123
124 final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
125 (unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
@@ -123,8 +133,10 @@ class BitcoinWalletService extends WalletService<
133
134 @override
135 Future<void> rename(String currentName, String password, String newName) async {
126 - final currentWalletInfo = walletInfoSource.values
127 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
136 + final currentWalletInfo = await WalletInfo.get(currentName, getType());
137 + if (currentWalletInfo == null) {
138 + throw Exception('Wallet not found');
139 + }
140 final currentWallet = await BitcoinWalletBase.open(
141 password: password,
142 name: currentName,
@@ -141,7 +153,7 @@ class BitcoinWalletService extends WalletService<
153 newWalletInfo.id = WalletBase.idFor(newName, getType());
154 newWalletInfo.name = newName;
155
144 - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
156 + await newWalletInfo.save();
157 }
158
159 @override
@@ -149,15 +161,18 @@ class BitcoinWalletService extends WalletService<
161 {bool? isTestnet}) async {
162 final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
163 credentials.walletInfo?.network = network.value;
152 - credentials.walletInfo?.derivationInfo?.derivationPath =
164 + final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
165 + derivationInfo.derivationPath =
166 credentials.hwAccountData.derivationPath;
167
168 final xpub = convertZpubToXpub(credentials.hwAccountData.xpub!);
169
170 + await credentials.walletInfo!.save();
171 final wallet = await BitcoinWallet(
172 password: credentials.password!,
173 xpub: xpub,
174 walletInfo: credentials.walletInfo!,
175 + derivationInfo: derivationInfo,
176 unspentCoinsInfo: unspentCoinsInfoSource,
177 networkParam: network,
178 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -180,6 +195,7 @@ class BitcoinWalletService extends WalletService<
195 password: credentials.password!,
196 xpub: xpub,
197 walletInfo: credentials.walletInfo!,
198 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
199 unspentCoinsInfo: unspentCoinsInfoSource,
200 networkParam: network,
201 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
cw_bitcoin/lib/electrum_wallet.dart
+10 -8
@@ -63,6 +63,7 @@ abstract class ElectrumWalletBase
63 ElectrumWalletBase({
64 required String password,
65 required WalletInfo walletInfo,
66 + required DerivationInfo derivationInfo,
67 required Box<UnspentCoinsInfo> unspentCoinsInfo,
68 required this.network,
69 required this.encryptionFileUtils,
@@ -75,9 +76,8 @@ abstract class ElectrumWalletBase
76 ElectrumBalance? initialBalance,
77 CryptoCurrency? currency,
78 bool? alwaysScan,
78 - })
79 - : accountHD = getAccountHDWallet(currency, network, seedBytes, xpub,
80 - walletInfo.derivationInfo, walletInfo.hardwareWalletType),
79 + }) : accountHD =
80 + getAccountHDWallet(currency, network, seedBytes, xpub, derivationInfo, walletInfo.hardwareWalletType),
81 syncStatus = NotConnectedSyncStatus(),
82 _password = password,
83 _feeRates = <int>[],
@@ -100,9 +100,10 @@ abstract class ElectrumWalletBase
100 this.unspentCoinsInfo = unspentCoinsInfo,
101 this.isTestnet = !network.isMainnet,
102 this._mnemonic = mnemonic,
103 - super(walletInfo) {
103 + super(walletInfo, derivationInfo) {
104 this.electrumClient = electrumClient ?? electrum.ElectrumClient();
105 this.walletInfo = walletInfo;
106 + this.derivationInfo = derivationInfo;
107 transactionHistory = ElectrumTransactionHistory(
108 walletInfo: walletInfo,
109 password: password,
@@ -771,8 +772,9 @@ abstract class ElectrumWalletBase
772 pubKeyHex = hd.childKey(Bip32KeyIndex(utx.bitcoinAddressRecord.index)).publicKey.toHex();
773 }
774
775 +
776 final derivationPath =
775 - "${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? electrum_path)}"
777 + "${_hardenedDerivationPath(derivationInfo.derivationPath ?? electrum_path)}"
778 "/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
779 "/${utx.bitcoinAddressRecord.index}";
780 publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
@@ -981,7 +983,7 @@ abstract class ElectrumWalletBase
983
984 // Get Derivation path for change Address since it is needed in Litecoin and BitcoinCash hardware Wallets
985 final changeDerivationPath =
984 - "${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
986 + "${_hardenedDerivationPath(derivationInfo.derivationPath ?? "m/0'")}"
987 "/${changeAddress.isHidden ? "1" : "0"}"
988 "/${changeAddress.index}";
989 utxoDetails.publicKeys[address.pubKeyHash()] =
@@ -1447,8 +1449,8 @@ abstract class ElectrumWalletBase
1449 ? SegwitAddresType.p2wpkh.toString()
1450 : walletInfo.addressPageType.toString(),
1451 'balance': balance[currency]?.toJSON(),
1450 - 'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index,
1451 - 'derivationPath': walletInfo.derivationInfo?.derivationPath,
1452 + 'derivationTypeIndex': derivationInfo.derivationType?.index,
1453 + 'derivationPath': derivationInfo.derivationPath,
1454 'silent_addresses': walletAddresses.silentAddresses.map((addr) => addr.toJSON()).toList(),
1455 'silent_address_index': walletAddresses.currentSilentAddressIndex.toString(),
1456 'mweb_addresses': walletAddresses.mwebAddresses.map((addr) => addr.toJSON()).toList(),
cw_bitcoin/lib/litecoin_wallet.dart
+11 -6
@@ -60,6 +60,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
60 LitecoinWalletBase({
61 required String password,
62 required WalletInfo walletInfo,
63 + required DerivationInfo derivationInfo,
64 required Box<UnspentCoinsInfo> unspentCoinsInfo,
65 required EncryptionFileUtils encryptionFileUtils,
66 Uint8List? seedBytes,
@@ -80,6 +81,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
81 passphrase: passphrase,
82 xpub: xpub,
83 walletInfo: walletInfo,
84 + derivationInfo: derivationInfo,
85 unspentCoinsInfo: unspentCoinsInfo,
86 network: LitecoinNetwork.mainnet,
87 initialAddresses: initialAddresses,
@@ -168,6 +170,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
170 {required String mnemonic,
171 required String password,
172 required WalletInfo walletInfo,
173 + required DerivationInfo derivationInfo,
174 required Box<UnspentCoinsInfo> unspentCoinsInfo,
175 required EncryptionFileUtils encryptionFileUtils,
176 String? passphrase,
@@ -179,7 +182,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
182 Map<String, int>? initialChangeAddressIndex}) async {
183 late Uint8List seedBytes;
184
182 - switch (walletInfo.derivationInfo?.derivationType) {
185 + switch (derivationInfo.derivationType) {
186 case DerivationType.bip39:
187 seedBytes = await bip39.mnemonicToSeed(
188 mnemonic,
@@ -195,6 +198,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
198 mnemonic: mnemonic,
199 password: password,
200 walletInfo: walletInfo,
201 + derivationInfo: derivationInfo,
202 unspentCoinsInfo: unspentCoinsInfo,
203 initialAddresses: initialAddresses,
204 initialMwebAddresses: initialMwebAddresses,
@@ -245,18 +249,17 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
249 );
250 }
251
248 - walletInfo.derivationInfo ??= DerivationInfo();
249 -
252 + final derivationInfo = await walletInfo.getDerivationInfo();
253 // set the default if not present:
251 - walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path;
252 - walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
254 + derivationInfo.derivationPath ??= snp?.derivationPath ?? electrum_path;
255 + derivationInfo.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
256
257 Uint8List? seedBytes = null;
258 final mnemonic = keysData.mnemonic;
259 final passphrase = keysData.passphrase;
260
261 if (mnemonic != null) {
259 - switch (walletInfo.derivationInfo?.derivationType) {
262 + switch (derivationInfo.derivationType) {
263 case DerivationType.bip39:
264 seedBytes = await bip39.mnemonicToSeed(
265 mnemonic,
@@ -269,12 +272,14 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
272 break;
273 }
274 }
275 + await derivationInfo.save();
276
277 return LitecoinWallet(
278 mnemonic: keysData.mnemonic,
279 xpub: keysData.xPub,
280 password: password,
281 walletInfo: walletInfo,
282 + derivationInfo: derivationInfo,
283 unspentCoinsInfo: unspentCoinsInfo,
284 initialAddresses: snp?.addresses,
285 initialMwebAddresses: snp?.mwebAddresses,
cw_bitcoin/lib/litecoin_wallet_service.dart
+24 -14
@@ -22,10 +22,8 @@ class LitecoinWalletService extends WalletService<
22 BitcoinRestoreWalletFromSeedCredentials,
23 BitcoinRestoreWalletFromWIFCredentials,
24 BitcoinRestoreWalletFromHardware> {
25 - LitecoinWalletService(
26 - this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
25 + LitecoinWalletService(this.unspentCoinsInfoSource, this.isDirect);
26
28 - final Box<WalletInfo> walletInfoSource;
27 final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
28 final bool isDirect;
29
@@ -35,7 +33,7 @@ class LitecoinWalletService extends WalletService<
33 @override
34 Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
35 final String mnemonic;
38 - switch (credentials.walletInfo?.derivationInfo?.derivationType) {
36 + switch (credentials.derivationInfo?.derivationType) {
37 case DerivationType.bip39:
38 final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
39
@@ -52,6 +50,7 @@ class LitecoinWalletService extends WalletService<
50 password: credentials.password!,
51 passphrase: credentials.passphrase,
52 walletInfo: credentials.walletInfo!,
53 + derivationInfo: credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
54 unspentCoinsInfo: unspentCoinsInfoSource,
55 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
56 );
@@ -68,8 +67,10 @@ class LitecoinWalletService extends WalletService<
67 @override
68 Future<LitecoinWallet> openWallet(String name, String password) async {
69
71 - final walletInfo = walletInfoSource.values
72 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
70 + final walletInfo = await WalletInfo.get(name, getType());
71 + if (walletInfo == null) {
72 + throw Exception('Wallet not found');
73 + }
74
75 try {
76 final wallet = await LitecoinWalletBase.open(
@@ -99,12 +100,14 @@ class LitecoinWalletService extends WalletService<
100 @override
101 Future<void> remove(String wallet) async {
102 File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
102 - final walletInfo = walletInfoSource.values
103 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
104 - await walletInfoSource.delete(walletInfo.key);
103 + final walletInfo = await WalletInfo.get(wallet, getType());
104 + if (walletInfo == null) {
105 + throw Exception('Wallet not found');
106 + }
107 + await WalletInfo.delete(walletInfo);
108
109 // if there are no more litecoin wallets left, cleanup the neutrino db and other files created by mwebd:
107 - if (walletInfoSource.values.where((info) => info.type == WalletType.litecoin).isEmpty) {
110 + if ((await WalletInfo.selectList('type = ?', [WalletType.litecoin.index])).isEmpty) {
111 final appDirPath = (await getApplicationSupportDirectory()).path;
112 File neturinoDb = File('$appDirPath/neutrino.db');
113 File blockHeaders = File('$appDirPath/block_headers.bin');
@@ -136,8 +139,10 @@ class LitecoinWalletService extends WalletService<
139
140 @override
141 Future<void> rename(String currentName, String password, String newName) async {
139 - final currentWalletInfo = walletInfoSource.values
140 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
142 + final currentWalletInfo = await WalletInfo.get(currentName, getType());
143 + if (currentWalletInfo == null) {
144 + throw Exception('Wallet not found');
145 + }
146 final currentWallet = await LitecoinWalletBase.open(
147 password: password,
148 name: currentName,
@@ -153,7 +158,7 @@ class LitecoinWalletService extends WalletService<
158 newWalletInfo.id = WalletBase.idFor(newName, getType());
159 newWalletInfo.name = newName;
160
156 - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
161 + await newWalletInfo.save();
162 }
163
164 @override
@@ -161,13 +166,17 @@ class LitecoinWalletService extends WalletService<
166 {bool? isTestnet}) async {
167 final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet;
168 credentials.walletInfo?.network = network.value;
164 - credentials.walletInfo?.derivationInfo?.derivationPath =
169 + final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
170 + derivationInfo.derivationPath =
171 credentials.hwAccountData.derivationPath;
172 + await derivationInfo.save();
173 + credentials.walletInfo!.save();
174
175 final wallet = await LitecoinWallet(
176 password: credentials.password!,
177 xpub: credentials.hwAccountData.xpub,
178 walletInfo: credentials.walletInfo!,
179 + derivationInfo: derivationInfo,
180 unspentCoinsInfo: unspentCoinsInfoSource,
181 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
182 );
@@ -193,6 +202,7 @@ class LitecoinWalletService extends WalletService<
202 passphrase: credentials.passphrase,
203 mnemonic: credentials.mnemonic,
204 walletInfo: credentials.walletInfo!,
205 + derivationInfo: credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
206 unspentCoinsInfo: unspentCoinsInfoSource,
207 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
208 );
cw_bitcoin/pubspec.lock
+64
@@ -1028,6 +1028,62 @@ packages:
1028 url: "https://pub.dev"
1029 source: hosted
1030 version: "7.0.0"
1031 + sqflite:
1032 + dependency: transitive
1033 + description:
1034 + name: sqflite
1035 + sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
1036 + url: "https://pub.dev"
1037 + source: hosted
1038 + version: "2.4.1"
1039 + sqflite_android:
1040 + dependency: transitive
1041 + description:
1042 + name: sqflite_android
1043 + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
1044 + url: "https://pub.dev"
1045 + source: hosted
1046 + version: "2.4.0"
1047 + sqflite_common:
1048 + dependency: transitive
1049 + description:
1050 + name: sqflite_common
1051 + sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
1052 + url: "https://pub.dev"
1053 + source: hosted
1054 + version: "2.5.4+6"
1055 + sqflite_common_ffi:
1056 + dependency: transitive
1057 + description:
1058 + name: sqflite_common_ffi
1059 + sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
1060 + url: "https://pub.dev"
1061 + source: hosted
1062 + version: "2.3.4+4"
1063 + sqflite_darwin:
1064 + dependency: transitive
1065 + description:
1066 + name: sqflite_darwin
1067 + sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
1068 + url: "https://pub.dev"
1069 + source: hosted
1070 + version: "2.4.1+1"
1071 + sqflite_platform_interface:
1072 + dependency: transitive
1073 + description:
1074 + name: sqflite_platform_interface
1075 + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
1076 + url: "https://pub.dev"
1077 + source: hosted
1078 + version: "2.4.0"
1079 + sqlite3:
1080 + dependency: transitive
1081 + description:
1082 + name: sqlite3
1083 + sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
1084 + url: "https://pub.dev"
1085 + source: hosted
1086 + version: "2.9.0"
1087 stack_trace:
1088 dependency: transitive
1089 description:
@@ -1060,6 +1116,14 @@ packages:
1116 url: "https://pub.dev"
1117 source: hosted
1118 version: "1.4.1"
1119 + synchronized:
1120 + dependency: transitive
1121 + description:
1122 + name: synchronized
1123 + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
1124 + url: "https://pub.dev"
1125 + source: hosted
1126 + version: "3.3.0+3"
1127 term_glyph:
1128 dependency: transitive
1129 description:
cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart
+4
@@ -28,6 +28,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
28 required String mnemonic,
29 required String password,
30 required WalletInfo walletInfo,
31 + required DerivationInfo derivationInfo,
32 required Box<UnspentCoinsInfo> unspentCoinsInfo,
33 required Uint8List seedBytes,
34 required EncryptionFileUtils encryptionFileUtils,
@@ -41,6 +42,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
42 mnemonic: mnemonic,
43 password: password,
44 walletInfo: walletInfo,
45 + derivationInfo: derivationInfo,
46 unspentCoinsInfo: unspentCoinsInfo,
47 network: BitcoinCashNetwork.mainnet,
48 initialAddresses: initialAddresses,
@@ -81,6 +83,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
83 mnemonic: mnemonic,
84 password: password,
85 walletInfo: walletInfo,
86 + derivationInfo: await walletInfo.getDerivationInfo(),
87 unspentCoinsInfo: unspentCoinsInfo,
88 initialAddresses: initialAddresses,
89 initialBalance: initialBalance,
@@ -134,6 +137,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
137 mnemonic: keysData.mnemonic!,
138 password: password,
139 walletInfo: walletInfo,
140 + derivationInfo: await walletInfo.getDerivationInfo(),
141 unspentCoinsInfo: unspentCoinsInfo,
142 initialAddresses: snp?.addresses.map((addr) {
143 try {
cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_service.dart
+15 -10
@@ -18,9 +18,8 @@ class BitcoinCashWalletService extends WalletService<
18 BitcoinCashRestoreWalletFromSeedCredentials,
19 BitcoinCashRestoreWalletFromWIFCredentials,
20 BitcoinCashNewWalletCredentials> {
21 - BitcoinCashWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
21 + BitcoinCashWalletService(this.unspentCoinsInfoSource, this.isDirect);
22
23 - final Box<WalletInfo> walletInfoSource;
23 final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
24 final bool isDirect;
25
@@ -51,8 +50,10 @@ class BitcoinCashWalletService extends WalletService<
50
51 @override
52 Future<BitcoinCashWallet> openWallet(String name, String password) async {
54 - final walletInfo = walletInfoSource.values
55 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
53 + final walletInfo = await WalletInfo.get(name, getType());
54 + if (walletInfo == null) {
55 + throw Exception('Wallet not found');
56 + }
57
58 try {
59 final wallet = await BitcoinCashWalletBase.open(
@@ -82,9 +83,11 @@ class BitcoinCashWalletService extends WalletService<
83 @override
84 Future<void> remove(String wallet) async {
85 File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
85 - final walletInfo = walletInfoSource.values
86 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
87 - await walletInfoSource.delete(walletInfo.key);
86 + final walletInfo = await WalletInfo.get(wallet, getType());
87 + if (walletInfo == null) {
88 + throw Exception('Wallet not found');
89 + }
90 + await WalletInfo.delete(walletInfo);
91
92 final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
93 (unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
@@ -98,8 +101,10 @@ class BitcoinCashWalletService extends WalletService<
101
102 @override
103 Future<void> rename(String currentName, String password, String newName) async {
101 - final currentWalletInfo = walletInfoSource.values
102 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
104 + final currentWalletInfo = await WalletInfo.get(currentName, getType());
105 + if (currentWalletInfo == null) {
106 + throw Exception('Wallet not found');
107 + }
108 final currentWallet = await BitcoinCashWalletBase.open(
109 password: password,
110 name: currentName,
@@ -114,7 +119,7 @@ class BitcoinCashWalletService extends WalletService<
119 newWalletInfo.id = WalletBase.idFor(newName, getType());
120 newWalletInfo.name = newName;
121
117 - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
122 + await newWalletInfo.save();
123 }
124
125 @override
cw_core/lib/db/sqlite.dart new
+114
@@ -0,0 +1,114 @@
1 +
2 +import 'package:sqflite/sqflite.dart';
3 +
4 +late Database db;
5 +
6 +Future<void> initDb({String? pathOverride}) async {
7 + db = await openDatabase(
8 + pathOverride ?? "cake.db",
9 + version: 1,
10 + onCreate: (Database db, int version) async {
11 + await db.execute(
12 + '''
13 +CREATE TABLE WalletInfo (
14 + walletInfoId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
15 + id TEXT NOT NULL,
16 + name TEXT NOT NULL,
17 + "type" INTEGER NOT NULL,
18 + isRecovery INTEGER DEFAULT (0) NOT NULL,
19 + walletInfoDerivationInfoId INTEGER NOT NULL,
20 + restoreHeight INTEGER DEFAULT (0) NOT NULL,
21 + "timestamp" INTEGER DEFAULT (0) NOT NULL,
22 + dirPath TEXT NOT NULL,
23 + "path" TEXT NOT NULL,
24 + address TEXT NOT NULL,
25 + yatEid TEXT,
26 + yatLastUsedAddressRaw TEXT,
27 + showIntroCakePayCard INTEGER DEFAULT (1),
28 + addressPageType TEXT,
29 + network TEXT,
30 + hardwareWalletType INTEGER,
31 + parentAddress TEXT,
32 + hashedWalletIdentifier TEXT,
33 + isNonSeedWallet INTEGER DEFAULT (0) NOT NULL,
34 + sortOrder INTEGER DEFAULT (0) NOT NULL
35 +);
36 +''');
37 +
38 + await db.execute(
39 + '''
40 +CREATE TABLE WalletInfoDerivationInfo (
41 + walletInfoDerivationInfoId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
42 + address TEXT NOT NULL,
43 + balance TEXT NOT NULL,
44 + transactionsCount INTEGER DEFAULT (0) NOT NULL,
45 + derivationType INTEGER NOT NULL,
46 + derivationPath TEXT,
47 + scriptType TEXT,
48 + description TEXT
49 +);
50 +''');
51 +
52 + await db.execute(
53 + '''
54 +CREATE TABLE WalletInfoAddress (
55 + walletInfoAddressId INTEGER PRIMARY KEY AUTOINCREMENT,
56 + walletInfoId INTEGER,
57 + "type" INTEGER NOT NULL,
58 + address TEXT NOT NULL,
59 + CONSTRAINT WalletInfoAddress_WalletInfo_FK FOREIGN KEY (walletInfoId) REFERENCES WalletInfo(walletInfoId)
60 +);
61 +''');
62 +
63 + await db.execute(
64 + '''
65 +CREATE TABLE WalletInfoAddressInfo (
66 + walletInfoAddressInfoId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
67 + walletInfoId INTEGER NOT NULL,
68 + mapKey INTEGER NOT NULL,
69 + mapValueAccountIndex INTEGER NOT NULL,
70 + mapValueAddress TEXT NOT NULL,
71 + mapValueLabel TEXT NOT NULL,
72 + CONSTRAINT WalletInfoAddressInfo_WalletInfo_FK FOREIGN KEY (walletInfoId) REFERENCES WalletInfo(walletInfoId)
73 +);
74 +''');
75 +
76 + await db.execute(
77 + '''
78 +CREATE TABLE "WalletInfoAddressMap" (
79 + walletInfoAddressMapId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
80 + walletInfoId INTEGER NOT NULL,
81 + addressKey TEXT NOT NULL,
82 + addressValue TEXT NOT NULL,
83 + CONSTRAINT WalletInfoAddress_WalletInfo_FK FOREIGN KEY (walletInfoId) REFERENCES WalletInfo(walletInfoId)
84 +);
85 + '''
86 + );
87 + }
88 + );
89 +}
90 +
91 +Future<Map<String, dynamic>> dumpDb() async {
92 + try {
93 + return await _dumpDb();
94 + } catch (e) {
95 + return {
96 + "error": e.toString(),
97 + "stackTrace": StackTrace.current.toString(),
98 + };
99 + }
100 +}
101 +
102 +Future<List<String>> _getTableNames() async {
103 + final tableNames = await db.rawQuery('SELECT name FROM sqlite_master WHERE type = "table"');
104 + return tableNames.map((e) => (e["name"]).toString()).toList();
105 +}
106 +
107 +Future<Map<String, dynamic>> _dumpDb() async {
108 + final tableNames = await _getTableNames();
109 + final ret = <String, dynamic>{};
110 + for (final tableName in tableNames) {
111 + ret[tableName] = await db.query(tableName);
112 + }
113 + return ret;
114 +}
\ No newline at end of file
cw_core/lib/wallet_addresses.dart
+14 -13
@@ -1,4 +1,3 @@
1 -import 'package:cw_core/address_info.dart';
1 import 'package:cw_core/utils/print_verbose.dart';
2 import 'package:cw_core/wallet_info.dart';
3 import 'package:cw_core/wallet_type.dart';
@@ -9,8 +8,12 @@ abstract class WalletAddresses {
8 allAddressesMap = {},
9 addressInfos = {},
10 usedAddresses = {},
12 - hiddenAddresses = walletInfo.hiddenAddresses?.toSet() ?? {},
13 - manualAddresses = walletInfo.manualAddresses?.toSet() ?? {};
11 + hiddenAddresses = {},
12 + manualAddresses = {} {
13 + walletInfo.getUsedAddresses().then((value) => usedAddresses = value);
14 + walletInfo.getHiddenAddresses().then((value) => hiddenAddresses = value);
15 + walletInfo.getManualAddresses().then((value) => manualAddresses = value);
16 + }
17
18 final WalletInfo walletInfo;
19
@@ -47,7 +50,7 @@ abstract class WalletAddresses {
50 return tmp;
51 }
52
50 - Map<int, List<AddressInfo>> addressInfos;
53 + Map<int, List<WalletInfoAddressInfo>> addressInfos;
54
55 Set<String> usedAddresses;
56
@@ -62,15 +65,13 @@ abstract class WalletAddresses {
65 Future<void> saveAddressesInBox() async {
66 try {
67 walletInfo.address = address;
65 - walletInfo.addresses = addressesMap;
66 - walletInfo.addressInfos = addressInfos;
67 - walletInfo.usedAddresses = usedAddresses.toList();
68 - walletInfo.hiddenAddresses = hiddenAddresses.toList();
69 - walletInfo.manualAddresses = manualAddresses.toList();
70 -
71 - if (walletInfo.isInBox) {
72 - await walletInfo.save();
73 - }
68 + walletInfo.setAddresses(addressesMap);
69 + walletInfo.setAddressInfos(addressInfos);
70 + walletInfo.setUsedAddresses(usedAddresses.toList());
71 + walletInfo.setHiddenAddresses(hiddenAddresses.toList());
72 + walletInfo.setManualAddresses(manualAddresses.toList());
73 +
74 + await walletInfo.save();
75 } catch (e) {
76 printV(e.toString());
77 }
cw_core/lib/wallet_addresses_with_account.dart deleted
-13
@@ -1,13 +0,0 @@
1 -import 'package:cw_core/wallet_addresses.dart';
2 -import 'package:cw_core/account_list.dart';
3 -import 'package:cw_core/wallet_info.dart';
4 -
5 -abstract class WalletAddressesWithAccount<T> extends WalletAddresses {
6 - WalletAddressesWithAccount(WalletInfo walletInfo) : super(walletInfo);
7 -
8 - // T get account;
9 -
10 - // set account(T account);
11 -
12 - AccountList<T> get accountList;
13 -}
\ No newline at end of file
cw_core/lib/wallet_base.dart
+2 -1
@@ -15,12 +15,13 @@ import 'package:cw_core/wallet_type.dart';
15
16 abstract class WalletBase<BalanceType extends Balance, HistoryType extends TransactionHistoryBase,
17 TransactionType extends TransactionInfo> {
18 - WalletBase(this.walletInfo);
18 + WalletBase(this.walletInfo, this.derivationInfo);
19
20 static String idFor(String name, WalletType type) =>
21 walletTypeToString(type).toLowerCase() + '_' + name;
22
23 WalletInfo walletInfo;
24 + DerivationInfo derivationInfo;
25
26 WalletType get type => walletInfo.type;
27
cw_core/lib/wallet_credentials.dart
+1 -5
@@ -10,11 +10,7 @@ abstract class WalletCredentials {
10 this.passphrase,
11 this.derivationInfo,
12 this.hardwareWalletType,
13 - }) {
14 - if (this.walletInfo != null && derivationInfo != null) {
15 - this.walletInfo!.derivationInfo = derivationInfo;
16 - }
17 - }
13 + });
14
15 final String name;
16 final int? height;
cw_core/lib/wallet_info.dart
+457 -95
@@ -1,47 +1,239 @@
1 import 'dart:async';
2
3 -import 'package:cw_core/address_info.dart';
3 +import 'package:cw_core/db/sqlite.dart';
4 import 'package:cw_core/hive_type_ids.dart';
5 +import 'package:cw_core/utils/print_verbose.dart';
6 import 'package:cw_core/wallet_type.dart';
6 -import 'package:hive/hive.dart';
7 -
8 -part 'wallet_info.g.dart';
7 +import 'package:sqflite/sqflite.dart';
8 +import 'package:cw_core/cake_hive.dart';
9 +import 'package:cw_core/wallet_info_legacy.dart' as wiLegacy;
10 +
11 +Future<void> performHiveMigration() async {
12 + try {
13 + if (!CakeHive.isAdapterRegistered(wiLegacy.WalletInfo.typeId)) {
14 + CakeHive.registerAdapter(wiLegacy.WalletInfoAdapter());
15 + }
16 + if (!CakeHive.isAdapterRegistered(DERIVATION_TYPE_TYPE_ID)) {
17 + CakeHive.registerAdapter(wiLegacy.DerivationTypeAdapter());
18 + }
19 + if (!CakeHive.isAdapterRegistered(wiLegacy.DerivationInfo.typeId)) {
20 + CakeHive.registerAdapter(wiLegacy.DerivationInfoAdapter());
21 + }
22 + if (!CakeHive.isAdapterRegistered(HARDWARE_WALLET_TYPE_TYPE_ID)) {
23 + CakeHive.registerAdapter(wiLegacy.HardwareWalletTypeAdapter());
24 + }
25 + final walletInfoBox = await CakeHive.openBox<wiLegacy.WalletInfo>(wiLegacy.WalletInfo.boxName);
26 + await wiLegacy.WalletInfo.migrateAllToSqlite(walletInfoBox);
27 + } catch (e) {
28 + printV('Error performing Hive migration: $e, continuing anyway');
29 + }
30 +}
31
10 -@HiveType(typeId: DERIVATION_TYPE_TYPE_ID)
32 enum DerivationType {
12 - @HiveField(0)
33 unknown,
14 - @HiveField(1)
34 def, // default is a reserved word
16 - @HiveField(2)
35 nano,
18 - @HiveField(3)
36 bip39,
20 - @HiveField(4)
37 electrum,
38 }
39
24 -@HiveType(typeId: HARDWARE_WALLET_TYPE_TYPE_ID)
40 enum HardwareWalletType {
26 - @HiveField(0)
41 ledger,
28 - @HiveField(1)
42 bitbox,
30 - @HiveField(2)
43 cupcake,
32 - @HiveField(3)
44 coldcard,
34 - @HiveField(4)
45 seedsigner,
36 - @HiveField(5)
46 keystone,
38 - @HiveField(6)
47 trezor,
48 }
49
42 -@HiveType(typeId: DerivationInfo.typeId)
43 -class DerivationInfo extends HiveObject {
50 +enum WalletInfoAddressType {
51 + used,
52 + hidden,
53 + manual,
54 +}
55 +
56 +class WalletInfoAddressInfo {
57 + WalletInfoAddressInfo({
58 + this.id = 0,
59 + required this.walletInfoId,
60 + required this.mapKey,
61 + required this.accountIndex,
62 + required this.address,
63 + required this.label,
64 + });
65 +
66 + int id;
67 + int walletInfoId;
68 + int mapKey;
69 + int accountIndex;
70 + String address;
71 + String label;
72 +
73 + static String get tableName => 'walletInfoAddressInfo';
74 + static String get selfIdColumn => "${tableName}Id";
75 +
76 + static Future<List<WalletInfoAddressInfo>> selectList(int walletInfoId) async {
77 + final query = await db.query(tableName, where: 'walletInfoId = ?', whereArgs: [walletInfoId]);
78 + return List.generate(query.length, (index) => WalletInfoAddressInfo.fromJson(query[index]));
79 + }
80 +
81 + static Future<int> deleteByWalletInfoId(int walletInfoId) async {
82 + return await db.delete(tableName, where: 'walletInfoId = ?', whereArgs: [walletInfoId]);
83 + }
84 + static Future<int> insert({
85 + required int walletInfoId,
86 + required int mapKey,
87 + required int accountIndex,
88 + required String address,
89 + required String label,
90 + }) async {
91 + return await db.insert(tableName, {
92 + "walletInfoId": walletInfoId,
93 + "mapKey": mapKey,
94 + "mapValueAccountIndex": accountIndex,
95 + "mapValueAddress": address,
96 + "mapValueLabel": label,
97 + });
98 + }
99 +
100 + Map<String, dynamic> toJson() {
101 + return {
102 + selfIdColumn: id,
103 + "walletInfoId": walletInfoId,
104 + "mapKey": mapKey,
105 + "mapValueAccountIndex": accountIndex,
106 + "mapValueAddress": address,
107 + "mapValueLabel": label,
108 + };
109 + }
110 +
111 + factory WalletInfoAddressInfo.fromJson(Map<String, dynamic> json) {
112 + return WalletInfoAddressInfo(
113 + id: json[selfIdColumn] as int,
114 + walletInfoId: json['walletInfoId'] as int,
115 + mapKey: json['mapKey'] as int,
116 + accountIndex: json['mapValueAccountIndex'] as int,
117 + address: json['mapValueAddress'] as String,
118 + label: json['mapValueLabel'] as String,
119 + );
120 + }
121 +}
122 +
123 +class WalletInfoAddressMap {
124 + WalletInfoAddressMap({
125 + required this.id,
126 + required this.walletInfoId,
127 + required this.addressKey,
128 + required this.addressValue,
129 + });
130 +
131 +
132 + int id;
133 + int walletInfoId;
134 + String addressKey;
135 + String addressValue;
136 +
137 + static String get tableName => 'walletInfoAddressMap';
138 + static String get selfIdColumn => "${tableName}Id";
139 +
140 + static Future<List<WalletInfoAddressMap>> selectList(int walletInfoId) async {
141 + final query = await db.query(tableName, where: 'walletInfoId = ?', whereArgs: [walletInfoId]);
142 + return List.generate(query.length, (index) => WalletInfoAddressMap.fromJson(query[index]));
143 + }
144 + static Future<int> deleteByWalletInfoId(int walletInfoId) async {
145 + return await db.delete(tableName, where: 'walletInfoId = ?', whereArgs: [walletInfoId]);
146 + }
147 + static Future<int> insert(int walletInfoId, String addressKey, String addressValue) async {
148 + return await db.insert(tableName, {
149 + "walletInfoId": walletInfoId,
150 + "addressKey": addressKey,
151 + "addressValue": addressValue,
152 + });
153 + }
154 +
155 + Map<String, dynamic> toJson() {
156 + return {
157 + selfIdColumn: id,
158 + "walletInfoId": walletInfoId,
159 + "addressKey": addressKey,
160 + "addressValue": addressValue,
161 + };
162 + }
163 +
164 + factory WalletInfoAddressMap.fromJson(Map<String, dynamic> json) {
165 + return WalletInfoAddressMap(
166 + id: json[selfIdColumn] as int,
167 + walletInfoId: json['walletInfoId'] as int,
168 + addressKey: json['addressKey'] as String,
169 + addressValue: json['addressValue'] as String,
170 + );
171 + }
172 +}
173 +
174 +class WalletInfoAddress {
175 + WalletInfoAddress({
176 + this.id = 0,
177 + required this.walletInfoId,
178 + required this.type,
179 + required this.address,
180 + });
181 +
182 + int id;
183 + int walletInfoId;
184 + WalletInfoAddressType type;
185 + String address;
186 +
187 + static String get tableName => 'walletInfoAddress';
188 + static String get selfIdColumn => "${tableName}Id";
189 +
190 + static Future<List<WalletInfoAddress>> selectList(int walletInfoId, WalletInfoAddressType type) async {
191 + final query = await db.query(tableName, where: 'walletInfoId = ? AND type = ?', whereArgs: [walletInfoId, type.index]);
192 + return List.generate(query.length, (index) => WalletInfoAddress.fromJson(query[index]));
193 + }
194 +
195 + static Future<int> deleteByAddress(int walletInfoId, WalletInfoAddressType type, String address) async {
196 + return await db.delete(tableName, where: 'walletInfoId = ? AND type = ? AND address = ?', whereArgs: [walletInfoId, type.index, address]);
197 + }
198 +
199 + static Future<int> deleteByType(int walletInfoId, WalletInfoAddressType type) async {
200 + return await db.delete(tableName, where: 'walletInfoId = ? AND type = ?', whereArgs: [walletInfoId, type.index]);
201 + }
202 +
203 + static Future<int> insert(int walletInfoId, WalletInfoAddressType type, String address) async {
204 + final select = await db.query(tableName, where: 'walletInfoId = ? AND type = ? AND address = ?', whereArgs: [walletInfoId, type.index, address]);
205 + if (select.isNotEmpty) {
206 + return select[0][selfIdColumn] as int;
207 + }
208 + return await db.insert(tableName, {
209 + "walletInfoId": walletInfoId,
210 + "type": type.index,
211 + "address": address,
212 + });
213 + }
214 +
215 + Map<String, dynamic> toJson() {
216 + return {
217 + selfIdColumn: id,
218 + "walletInfoId": walletInfoId,
219 + "type": type.index,
220 + "address": address,
221 + };
222 + }
223 +
224 + factory WalletInfoAddress.fromJson(Map<String, dynamic> json) {
225 + return WalletInfoAddress(
226 + id: json[selfIdColumn] as int,
227 + walletInfoId: json['walletInfoId'] as int,
228 + type: WalletInfoAddressType.values[json['type'] as int],
229 + address: json['address'] as String,
230 + );
231 + }
232 +}
233 +
234 +class DerivationInfo {
235 DerivationInfo({
236 + this.id = 0,
237 this.derivationType,
238 this.derivationPath,
239 this.balance = "",
@@ -51,33 +243,77 @@ class DerivationInfo extends HiveObject {
243 this.description,
244 });
245
54 - static const typeId = DERIVATION_INFO_TYPE_ID;
246 + int id;
247
56 - @HiveField(0, defaultValue: '')
57 - String address;
248 + static String get tableName => 'walletInfoDerivationInfo';
249 + static String get selfIdColumn => "${tableName}Id";
250
59 - @HiveField(1, defaultValue: '')
251 + String address;
252 String balance;
61 -
62 - @HiveField(2, defaultValue: 0)
253 int transactionsCount;
64 -
65 - @HiveField(3)
254 DerivationType? derivationType;
67 -
68 - @HiveField(4)
255 String? derivationPath;
256 + String? scriptType;
257 + String? description;
258 +
259 + static Future<List<DerivationInfo>> selectList(String where, List<dynamic> whereArgs) async {
260 + final query = await db.query(
261 + tableName,
262 + columns: [
263 + selfIdColumn,
264 + 'address',
265 + 'balance',
266 + 'transactionsCount',
267 + 'derivationType',
268 + 'derivationPath',
269 + 'scriptType',
270 + 'description',
271 + ],
272 + where: where.isNotEmpty ? where : "1 = 1",
273 + whereArgs: whereArgs.isNotEmpty ? whereArgs : null,
274 + );
275 + return List.generate(query.length, (index) => DerivationInfo.fromJson(query[index]));
276 + }
277
71 - @HiveField(5)
72 - final String? scriptType;
278 + Map<String, dynamic> toJson() {
279 + return {
280 + selfIdColumn: id,
281 + "address": address,
282 + "balance": balance,
283 + "transactionsCount": transactionsCount,
284 + "derivationType": derivationType?.index,
285 + "derivationPath": derivationPath,
286 + "scriptType": scriptType,
287 + "description": description,
288 + };
289 + }
290
74 - @HiveField(6)
75 - final String? description;
291 + factory DerivationInfo.fromJson(Map<String, dynamic> json ) {
292 + return DerivationInfo(
293 + id: json[selfIdColumn] as int,
294 + derivationType: DerivationType.values[json['derivationType'] as int? ?? 0],
295 + derivationPath: json['derivationPath'] as String?,
296 + balance: json['balance'] as String? ?? "",
297 + address: json['address'] as String? ?? "",
298 + transactionsCount: json['transactionsCount'] as int? ?? 0,
299 + scriptType: json['scriptType'] as String?,
300 + description: json['description'] as String?,
301 + );
302 + }
303 +
304 + Future<int> save() async {
305 + final json = toJson();
306 + if (json[selfIdColumn] == 0) {
307 + json[selfIdColumn] = null;
308 + }
309 + id = await db.insert(tableName, json, conflictAlgorithm: ConflictAlgorithm.replace);
310 + return id;
311 + }
312 }
313
78 -@HiveType(typeId: WalletInfo.typeId)
79 -class WalletInfo extends HiveObject {
314 +class WalletInfo {
315 WalletInfo(
316 + this.internalId,
317 this.id,
318 this.name,
319 this.type,
@@ -90,11 +326,12 @@ class WalletInfo extends HiveObject {
326 this.yatEid,
327 this.yatLastUsedAddressRaw,
328 this.showIntroCakePayCard,
93 - this.derivationInfo,
329 + this.derivationInfoId,
330 this.hardwareWalletType,
331 this.parentAddress,
332 this.hashedWalletIdentifier,
333 this.isNonSeedWallet,
334 + this.sortOrder,
335 ) : _yatLastUsedAddressController = StreamController<String>.broadcast();
336
337 factory WalletInfo.external({
@@ -110,13 +347,15 @@ class WalletInfo extends HiveObject {
347 bool? showIntroCakePayCard,
348 String yatEid = '',
349 String yatLastUsedAddressRaw = '',
113 - DerivationInfo? derivationInfo,
350 + int? derivationInfoId,
351 HardwareWalletType? hardwareWalletType,
352 String? parentAddress,
353 String? hashedWalletIdentifier,
354 bool? isNonSeedWallet,
355 + int? sortOrder,
356 }) {
357 return WalletInfo(
358 + 0,
359 id,
360 name,
361 type,
@@ -129,96 +368,135 @@ class WalletInfo extends HiveObject {
368 yatEid,
369 yatLastUsedAddressRaw,
370 showIntroCakePayCard,
132 - derivationInfo,
371 + derivationInfoId ?? -1,
372 hardwareWalletType,
373 parentAddress,
374 hashedWalletIdentifier,
375 isNonSeedWallet ?? false,
376 + sortOrder ?? 0,
377 );
378 }
379
140 - static const typeId = WALLET_INFO_TYPE_ID;
141 - static const boxName = 'WalletInfo';
380 + static String get tableName => 'walletInfo';
381 + static String get selfIdColumn => "${tableName}Id";
382
143 - @HiveField(0, defaultValue: '')
144 - String id;
383 + int internalId;
384
146 - @HiveField(1, defaultValue: '')
385 + String id;
386 String name;
148 -
149 - @HiveField(2)
387 WalletType type;
151 -
152 - @HiveField(3, defaultValue: false)
388 bool isRecovery;
154 -
155 - @HiveField(4, defaultValue: 0)
389 int restoreHeight;
157 -
158 - @HiveField(5, defaultValue: 0)
390 int timestamp;
160 -
161 - @HiveField(6, defaultValue: '')
391 String dirPath;
163 -
164 - @HiveField(7, defaultValue: '')
392 String path;
166 -
167 - @HiveField(8, defaultValue: '')
393 String address;
394 + Future<Map<String, String>> getAddresses() async {
395 + final list = await WalletInfoAddressMap.selectList(internalId);
396 + return Map.fromEntries(list.map((e) => MapEntry(e.addressKey, e.addressValue)));
397 + }
398
170 - @HiveField(10)
171 - Map<String, String>? addresses;
399 + Future<void> setAddresses(Map<String, String> addresses) async {
400 + await WalletInfoAddressMap.deleteByWalletInfoId(internalId);
401 + final keys = addresses.keys.toList();
402 + for (final address in keys) {
403 + await WalletInfoAddressMap.insert(internalId, address, addresses[address]!);
404 + }
405 + }
406
173 - @HiveField(11)
407 String? yatEid;
175 -
176 - @HiveField(12)
408 String? yatLastUsedAddressRaw;
178 -
179 - @HiveField(13)
409 bool? showIntroCakePayCard;
410 + Future<Map<int, List<WalletInfoAddressInfo>>> getAddressInfos() async {
411 + final list = await WalletInfoAddressInfo.selectList(internalId);
412 + final ret = <int, List<WalletInfoAddressInfo>>{};
413 + for (final e in list) {
414 + ret[e.mapKey] ??= [];
415 + ret[e.mapKey]!.add(e);
416 + }
417 + return ret;
418 + }
419
182 - @HiveField(14)
183 - Map<int, List<AddressInfo>>? addressInfos;
420 + Future<void> setAddressInfos(Map<int, List<WalletInfoAddressInfo>> addressInfos) async {
421 + await WalletInfoAddressInfo.deleteByWalletInfoId(internalId);
422 + final entries = addressInfos.entries.toList();
423 + for (final addressInfo in entries) {
424 + for (final info in addressInfo.value) {
425 + await WalletInfoAddressInfo.insert(
426 + walletInfoId: internalId,
427 + mapKey: addressInfo.key,
428 + accountIndex: info.accountIndex,
429 + address: info.address,
430 + label: info.label,
431 + );
432 + }
433 + }
434 + }
435
185 - @HiveField(15)
186 - List<String>? usedAddresses;
436 + Future<Set<String>> getUsedAddresses() async {
437 + final list = await WalletInfoAddress.selectList(internalId, WalletInfoAddressType.used);
438 + return list.map((e) => e.address).toSet();
439 + }
440 + Future<void> setUsedAddresses(List<String> addresses) async {
441 + await WalletInfoAddress.deleteByType(internalId, WalletInfoAddressType.used);
442 + for (final address in addresses) {
443 + await WalletInfoAddress.insert(internalId, WalletInfoAddressType.used, address);
444 + }
445 + }
446
188 - @deprecated
189 - @HiveField(16)
190 - DerivationType? derivationType; // no longer used
447 + Future<Set<String>> getHiddenAddresses() async {
448 + final list = await WalletInfoAddress.selectList(internalId, WalletInfoAddressType.hidden);
449 + return list.map((e) => e.address).toSet();
450 + }
451 + Future<void> setHiddenAddresses(List<String> addresses) async {
452 + await WalletInfoAddress.deleteByType(internalId, WalletInfoAddressType.hidden);
453 + for (final address in addresses) {
454 + await WalletInfoAddress.insert(internalId, WalletInfoAddressType.hidden, address);
455 + }
456 + }
457
192 - @deprecated
193 - @HiveField(17)
194 - String? derivationPath; // no longer used
458 + Future<Set<String>> getManualAddresses() async {
459 + final list = await WalletInfoAddress.selectList(internalId, WalletInfoAddressType.manual);
460 + return list.map((e) => e.address).toSet();
461 + }
462 + Future<void> setManualAddresses(List<String> addresses) async {
463 + await WalletInfoAddress.deleteByType(internalId, WalletInfoAddressType.manual);
464 + for (final address in addresses) {
465 + await WalletInfoAddress.insert(internalId, WalletInfoAddressType.manual, address);
466 + }
467 + }
468
196 - @HiveField(18)
197 - String? addressPageType;
469 + Future<void> addAddress(String address, WalletInfoAddressType type) async {
470 + await WalletInfoAddress.insert(internalId, type, address);
471 + }
472
199 - @HiveField(19)
473 + String? addressPageType;
474 String? network;
201 -
202 - @HiveField(20)
203 - DerivationInfo? derivationInfo;
204 -
205 - @HiveField(21)
475 + int derivationInfoId;
476 + DerivationInfo? _derivationInfo;
477 + Future<DerivationInfo> getDerivationInfo() async {
478 + if (_derivationInfo != null) {
479 + return _derivationInfo!;
480 + }
481 + final list = await DerivationInfo.selectList('walletInfoDerivationInfoId = ?', [derivationInfoId]);
482 + if (list.isEmpty) {
483 + final di = DerivationInfo(
484 + id: 0,
485 + derivationType: DerivationType.unknown,
486 + );
487 + derivationInfoId = await di.save();
488 + _derivationInfo = di;
489 + return di;
490 + }
491 + _derivationInfo = list[0];
492 + return _derivationInfo!;
493 + }
494 HardwareWalletType? hardwareWalletType;
207 -
208 - @HiveField(22)
495 String? parentAddress;
210 -
211 - @HiveField(23)
212 - List<String>? hiddenAddresses;
213 -
214 - @HiveField(24)
215 - List<String>? manualAddresses;
216 -
217 - @HiveField(25)
496 String? hashedWalletIdentifier;
219 -
220 - @HiveField(26, defaultValue: false)
497 bool isNonSeedWallet;
498 +
499 + int sortOrder;
500
501 String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
502
@@ -248,6 +526,90 @@ class WalletInfo extends HiveObject {
526
527 StreamController<String> _yatLastUsedAddressController;
528
529 + Map<String, dynamic> toJson() => {
530 + selfIdColumn: internalId,
531 + "id": id,
532 + "name": name,
533 + "type": type.index,
534 + "isRecovery": isRecovery ? 1 : 0,
535 + "restoreHeight": restoreHeight,
536 + "timestamp": timestamp,
537 + "dirPath": dirPath,
538 + "path": path,
539 + "address": address,
540 + "yatEid": yatEid,
541 + "yatLastUsedAddressRaw": yatLastUsedAddressRaw,
542 + "showIntroCakePayCard": showIntroCakePayCard == true ? 1 : 0, // SQL regression: null -> false
543 + "walletInfoDerivationInfoId": derivationInfoId,
544 + "hardwareWalletType": hardwareWalletType?.index,
545 + "parentAddress": parentAddress,
546 + "hashedWalletIdentifier": hashedWalletIdentifier,
547 + "isNonSeedWallet": isNonSeedWallet ? 1 : 0,
548 + "sortOrder": sortOrder,
549 + };
550 +
551 + factory WalletInfo.fromJson(Map<String, dynamic> json) {
552 + return WalletInfo(
553 + json[selfIdColumn] as int,
554 + json['id'] as String,
555 + json['name'] as String,
556 + WalletType.values[json['type'] as int],
557 + (json['isRecovery'] as int) == 1,
558 + json['restoreHeight'] as int,
559 + json['timestamp'] as int,
560 + json['dirPath'] as String,
561 + json['path'] as String,
562 + json['address'] as String,
563 + json['yatEid'] as String?,
564 + json['yatLastUsedAddressRaw'] as String?,
565 + (json['showIntroCakePayCard'] as int) == 1,
566 + json['walletInfoDerivationInfoId'] as int,
567 + json['hardwareWalletType'] == null ? null : HardwareWalletType.values[json['hardwareWalletType'] as int],
568 + json['parentAddress'] as String?,
569 + json['hashedWalletIdentifier'] as String?,
570 + (json['isNonSeedWallet'] as int) == 1,
571 + json['sortOrder'] as int? ?? 0,
572 + );
573 + }
574 +
575 + Future<int> save() async {
576 + final json = toJson();
577 + if (json[selfIdColumn] == 0) {
578 + json[selfIdColumn] = null;
579 + }
580 + if (_derivationInfo != null) {
581 + derivationInfoId = await _derivationInfo!.save();
582 + }
583 + internalId = await db.insert(tableName, json, conflictAlgorithm: ConflictAlgorithm.replace);
584 + return internalId;
585 + }
586 +
587 + static Future<int> delete(WalletInfo walletInfo) async {
588 + return await db.delete(tableName, where: 'id = ?', whereArgs: [walletInfo.id]);
589 + }
590 +
591 + static Future<List<WalletInfo>> selectList(String where, List<dynamic> whereArgs, {String orderBy = 'sortOrder'}) async {
592 + final list = await db.query(
593 + tableName,
594 + where: where.isNotEmpty ? where : "1 = 1",
595 + whereArgs: whereArgs.isNotEmpty ? whereArgs : null,
596 + orderBy: orderBy,
597 + );
598 + return List.generate(list.length, (index) => WalletInfo.fromJson(list[index]));
599 + }
600 +
601 + static Future<List<WalletInfo>> getAll() async {
602 + return selectList('', []);
603 + }
604 +
605 + static Future<WalletInfo?> get(String name, WalletType type) async {
606 + final list = await selectList('name = ? AND type = ?', [name, type.index]);
607 + if (list.isEmpty) {
608 + return null;
609 + }
610 + return list[0];
611 + }
612 +
613 Future<void> updateRestoreHeight(int height) async {
614 restoreHeight = height;
615 await save();
cw_core/lib/wallet_info_legacy.dart new
+308
@@ -0,0 +1,308 @@
1 +// NOTE: This code was generated by Hive, but since Hive is on the way out, this
2 +// code no longer uses hive generators, so when we get rid of hive we also will
3 +// be able to get rid of
4 +
5 +import 'dart:async';
6 +
7 +import 'package:cw_core/utils/print_verbose.dart';
8 +import 'package:cw_core/wallet_info.dart' as newWi;
9 +import 'package:cw_core/address_info.dart';
10 +import 'package:cw_core/hive_type_ids.dart';
11 +import 'package:cw_core/wallet_type.dart';
12 +import 'package:hive/hive.dart';
13 +
14 +part 'wallet_info_legacy.part.dart';
15 +
16 +// // @HiveType(typeId: DERIVATION_TYPE_TYPE_ID)
17 +// enum DerivationType {
18 +// // @HiveField(0)
19 +// unknown,
20 +// // @HiveField(1)
21 +// def, // default is a reserved word
22 +// // @HiveField(2)
23 +// nano,
24 +// // @HiveField(3)
25 +// bip39,
26 +// // @HiveField(4)
27 +// electrum,
28 +// }
29 +
30 +// // @HiveType(typeId: HARDWARE_WALLET_TYPE_TYPE_ID)
31 +// enum HardwareWalletType {
32 +// // @HiveField(0)
33 +// ledger,
34 +// }
35 +
36 +// @HiveType(typeId: DerivationInfo.typeId)
37 +class DerivationInfo extends HiveObject {
38 + DerivationInfo({
39 + this.derivationType,
40 + this.derivationPath,
41 + this.balance = "",
42 + this.address = "",
43 + this.transactionsCount = 0,
44 + this.scriptType,
45 + this.description,
46 + });
47 +
48 + static const typeId = DERIVATION_INFO_TYPE_ID;
49 +
50 + // @HiveField(0, defaultValue: '')
51 + String address;
52 +
53 + // @HiveField(1, defaultValue: '')
54 + String balance;
55 +
56 + // @HiveField(2, defaultValue: 0)
57 + int transactionsCount;
58 +
59 + // @HiveField(3)
60 + newWi.DerivationType? derivationType;
61 +
62 + // @HiveField(4)
63 + String? derivationPath;
64 +
65 + // @HiveField(5)
66 + final String? scriptType;
67 +
68 + // @HiveField(6)
69 + final String? description;
70 +}
71 +
72 +// @HiveType(typeId: WalletInfo.typeId)
73 +class WalletInfo extends HiveObject {
74 + WalletInfo(
75 + this.id,
76 + this.name,
77 + this.type,
78 + this.isRecovery,
79 + this.restoreHeight,
80 + this.timestamp,
81 + this.dirPath,
82 + this.path,
83 + this.address,
84 + this.yatEid,
85 + this.yatLastUsedAddressRaw,
86 + this.showIntroCakePayCard,
87 + this.derivationInfo,
88 + this.hardwareWalletType,
89 + this.parentAddress,
90 + this.hashedWalletIdentifier,
91 + this.isNonSeedWallet,
92 + ) : _yatLastUsedAddressController = StreamController<String>.broadcast();
93 +
94 + factory WalletInfo.external({
95 + required String id,
96 + required String name,
97 + required WalletType type,
98 + required bool isRecovery,
99 + required int restoreHeight,
100 + required DateTime date,
101 + required String dirPath,
102 + required String path,
103 + required String address,
104 + bool? showIntroCakePayCard,
105 + String yatEid = '',
106 + String yatLastUsedAddressRaw = '',
107 + DerivationInfo? derivationInfo,
108 + newWi.HardwareWalletType? hardwareWalletType,
109 + String? parentAddress,
110 + String? hashedWalletIdentifier,
111 + bool? isNonSeedWallet,
112 + }) {
113 + return WalletInfo(
114 + id,
115 + name,
116 + type,
117 + isRecovery,
118 + restoreHeight,
119 + date.millisecondsSinceEpoch,
120 + dirPath,
121 + path,
122 + address,
123 + yatEid,
124 + yatLastUsedAddressRaw,
125 + showIntroCakePayCard,
126 + derivationInfo,
127 + hardwareWalletType,
128 + parentAddress,
129 + hashedWalletIdentifier,
130 + isNonSeedWallet ?? false,
131 + );
132 + }
133 +
134 + static const typeId = WALLET_INFO_TYPE_ID;
135 + static const boxName = 'WalletInfo';
136 +
137 + // @HiveField(0, defaultValue: '')
138 + String id;
139 +
140 + // @HiveField(1, defaultValue: '')
141 + String name;
142 +
143 + // @HiveField(2)
144 + WalletType type;
145 +
146 + // @HiveField(3, defaultValue: false)
147 + bool isRecovery;
148 +
149 + // @HiveField(4, defaultValue: 0)
150 + int restoreHeight;
151 +
152 + // @HiveField(5, defaultValue: 0)
153 + int timestamp;
154 +
155 + // @HiveField(6, defaultValue: '')
156 + String dirPath;
157 +
158 + // @HiveField(7, defaultValue: '')
159 + String path;
160 +
161 + // @HiveField(8, defaultValue: '')
162 + String address;
163 +
164 + // @HiveField(10)
165 + Map<String, String>? addresses;
166 +
167 + // @HiveField(11)
168 + String? yatEid;
169 +
170 + // @HiveField(12)
171 + String? yatLastUsedAddressRaw;
172 +
173 + // @HiveField(13)
174 + bool? showIntroCakePayCard;
175 +
176 + // @HiveField(14)
177 + Map<int, List<AddressInfo>>? addressInfos;
178 +
179 + // @HiveField(15)
180 + List<String>? usedAddresses;
181 +
182 + @deprecated
183 + // @HiveField(16)
184 + newWi.DerivationType? derivationType; // no longer used
185 +
186 + @deprecated
187 + // @HiveField(17)
188 + String? derivationPath; // no longer used
189 +
190 + // @HiveField(18)
191 + String? addressPageType;
192 +
193 + // @HiveField(19)
194 + String? network;
195 +
196 + // @HiveField(20)
197 + DerivationInfo? derivationInfo;
198 +
199 + // @HiveField(21)
200 + newWi.HardwareWalletType? hardwareWalletType;
201 +
202 + // @HiveField(22)
203 + String? parentAddress;
204 +
205 + // @HiveField(23)
206 + List<String>? hiddenAddresses;
207 +
208 + // @HiveField(24)
209 + List<String>? manualAddresses;
210 +
211 + // @HiveField(25)
212 + String? hashedWalletIdentifier;
213 +
214 + // @HiveField(26, defaultValue: false)
215 + bool isNonSeedWallet;
216 +
217 + String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
218 +
219 + set yatLastUsedAddress(String address) {
220 + yatLastUsedAddressRaw = address;
221 + _yatLastUsedAddressController.add(address);
222 + }
223 +
224 + String get yatEmojiId => yatEid ?? '';
225 +
226 + bool get isShowIntroCakePayCard {
227 + if (showIntroCakePayCard == null) {
228 + return type != WalletType.haven;
229 + }
230 + return showIntroCakePayCard!;
231 + }
232 +
233 + bool get isHardwareWallet => hardwareWalletType != null;
234 +
235 + DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp);
236 +
237 + Stream<String> get yatLastUsedAddressStream => _yatLastUsedAddressController.stream;
238 +
239 + StreamController<String> _yatLastUsedAddressController;
240 +
241 + Future<void> updateRestoreHeight(int height) async {
242 + restoreHeight = height;
243 + await save();
244 + }
245 +
246 + Future<void> migrateToSqlite() async {
247 + final di = newWi.DerivationInfo(
248 + id: 0,
249 + derivationType: derivationInfo?.derivationType ?? derivationType ?? newWi.DerivationType.unknown,
250 + derivationPath: derivationInfo?.derivationPath ?? derivationPath ?? '',
251 + );
252 + final derivationInfoId = await di.save();
253 + final walletInfo = newWi.WalletInfo(
254 + 0,
255 + id,
256 + name,
257 + type,
258 + isRecovery,
259 + restoreHeight,
260 + timestamp,
261 + dirPath,
262 + path,
263 + address,
264 + yatEid,
265 + yatLastUsedAddressRaw,
266 + showIntroCakePayCard,
267 + derivationInfoId,
268 + hardwareWalletType,
269 + parentAddress,
270 + hashedWalletIdentifier,
271 + isNonSeedWallet,
272 + 0,
273 + );
274 + final wiId = await walletInfo.save();
275 + for (final address in usedAddresses ?? <String>[]) {
276 + await newWi.WalletInfoAddress.insert(wiId, newWi.WalletInfoAddressType.used, address);
277 + }
278 + for (final address in hiddenAddresses ?? <String>[]) {
279 + await newWi.WalletInfoAddress.insert(wiId, newWi.WalletInfoAddressType.hidden, address);
280 + }
281 + for (final address in manualAddresses ?? <String>[]) {
282 + await newWi.WalletInfoAddress.insert(wiId, newWi.WalletInfoAddressType.manual, address);
283 + }
284 + for (int i = 0; i < (addressInfos?.length ?? 0); i++) {
285 + for (final address in addressInfos![i] ?? <AddressInfo>[]) {
286 + await newWi.WalletInfoAddressInfo.insert(
287 + walletInfoId: wiId,
288 + mapKey: i,
289 + accountIndex: address.accountIndex??0,
290 + address: address.address,
291 + label: address.label,
292 + );
293 + }
294 + }
295 + await walletInfo.setAddresses(addresses ?? <String, String>{});
296 + }
297 +
298 + static Future<void> migrateAllToSqlite(final Box<WalletInfo> box) async {
299 + printV('Migrating WalletInfo to SQLite: start');
300 + final sw = Stopwatch()..start();
301 + final list = box.values.toList();
302 + for (final wi in list) {
303 + await wi.migrateToSqlite();
304 + await wi.delete();
305 + }
306 + printV('Migrating WalletInfo to SQLite: end (${sw.elapsedMilliseconds}ms)');
307 + }
308 +}
cw_core/lib/wallet_info_legacy.part.dart new
+287
@@ -0,0 +1,287 @@
1 +// GENERATED CODE - DO NOT MODIFY BY HAND
2 +
3 +part of 'wallet_info_legacy.dart';
4 +
5 +// **************************************************************************
6 +// TypeAdapterGenerator
7 +// **************************************************************************
8 +
9 +class DerivationInfoAdapter extends TypeAdapter<DerivationInfo> {
10 + @override
11 + final int typeId = 17;
12 +
13 + @override
14 + DerivationInfo read(BinaryReader reader) {
15 + final numOfFields = reader.readByte();
16 + final fields = <int, dynamic>{
17 + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
18 + };
19 + return DerivationInfo(
20 + derivationType: fields[3] as newWi.DerivationType?,
21 + derivationPath: fields[4] as String?,
22 + balance: fields[1] == null ? '' : fields[1] as String,
23 + address: fields[0] == null ? '' : fields[0] as String,
24 + transactionsCount: fields[2] == null ? 0 : fields[2] as int,
25 + scriptType: fields[5] as String?,
26 + description: fields[6] as String?,
27 + );
28 + }
29 +
30 + @override
31 + void write(BinaryWriter writer, DerivationInfo obj) {
32 + writer
33 + ..writeByte(7)
34 + ..writeByte(0)
35 + ..write(obj.address)
36 + ..writeByte(1)
37 + ..write(obj.balance)
38 + ..writeByte(2)
39 + ..write(obj.transactionsCount)
40 + ..writeByte(3)
41 + ..write(obj.derivationType)
42 + ..writeByte(4)
43 + ..write(obj.derivationPath)
44 + ..writeByte(5)
45 + ..write(obj.scriptType)
46 + ..writeByte(6)
47 + ..write(obj.description);
48 + }
49 +
50 + @override
51 + int get hashCode => typeId.hashCode;
52 +
53 + @override
54 + bool operator ==(Object other) =>
55 + identical(this, other) ||
56 + other is DerivationInfoAdapter &&
57 + runtimeType == other.runtimeType &&
58 + typeId == other.typeId;
59 +}
60 +
61 +class WalletInfoAdapter extends TypeAdapter<WalletInfo> {
62 + @override
63 + final int typeId = 4;
64 +
65 + @override
66 + WalletInfo read(BinaryReader reader) {
67 + final numOfFields = reader.readByte();
68 + final fields = <int, dynamic>{
69 + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
70 + };
71 + return WalletInfo(
72 + fields[0] == null ? '' : fields[0] as String,
73 + fields[1] == null ? '' : fields[1] as String,
74 + fields[2] as WalletType,
75 + fields[3] == null ? false : fields[3] as bool,
76 + fields[4] == null ? 0 : fields[4] as int,
77 + fields[5] == null ? 0 : fields[5] as int,
78 + fields[6] == null ? '' : fields[6] as String,
79 + fields[7] == null ? '' : fields[7] as String,
80 + fields[8] == null ? '' : fields[8] as String,
81 + fields[11] as String?,
82 + fields[12] as String?,
83 + fields[13] as bool?,
84 + fields[20] as DerivationInfo?,
85 + fields[21] as newWi.HardwareWalletType?,
86 + fields[22] as String?,
87 + fields[25] as String?,
88 + fields[26] == null ? false : fields[26] as bool,
89 + )
90 + ..addresses = (fields[10] as Map?)?.cast<String, String>()
91 + ..addressInfos = (fields[14] as Map?)?.map((dynamic k, dynamic v) =>
92 + MapEntry(k as int, (v as List).cast<AddressInfo>()))
93 + ..usedAddresses = (fields[15] as List?)?.cast<String>()
94 + ..derivationType = fields[16] as newWi.DerivationType?
95 + ..derivationPath = fields[17] as String?
96 + ..addressPageType = fields[18] as String?
97 + ..network = fields[19] as String?
98 + ..hiddenAddresses = (fields[23] as List?)?.cast<String>()
99 + ..manualAddresses = (fields[24] as List?)?.cast<String>();
100 + }
101 +
102 + @override
103 + void write(BinaryWriter writer, WalletInfo obj) {
104 + writer
105 + ..writeByte(26)
106 + ..writeByte(0)
107 + ..write(obj.id)
108 + ..writeByte(1)
109 + ..write(obj.name)
110 + ..writeByte(2)
111 + ..write(obj.type)
112 + ..writeByte(3)
113 + ..write(obj.isRecovery)
114 + ..writeByte(4)
115 + ..write(obj.restoreHeight)
116 + ..writeByte(5)
117 + ..write(obj.timestamp)
118 + ..writeByte(6)
119 + ..write(obj.dirPath)
120 + ..writeByte(7)
121 + ..write(obj.path)
122 + ..writeByte(8)
123 + ..write(obj.address)
124 + ..writeByte(10)
125 + ..write(obj.addresses)
126 + ..writeByte(11)
127 + ..write(obj.yatEid)
128 + ..writeByte(12)
129 + ..write(obj.yatLastUsedAddressRaw)
130 + ..writeByte(13)
131 + ..write(obj.showIntroCakePayCard)
132 + ..writeByte(14)
133 + ..write(obj.addressInfos)
134 + ..writeByte(15)
135 + ..write(obj.usedAddresses)
136 + ..writeByte(16)
137 + ..write(obj.derivationType)
138 + ..writeByte(17)
139 + ..write(obj.derivationPath)
140 + ..writeByte(18)
141 + ..write(obj.addressPageType)
142 + ..writeByte(19)
143 + ..write(obj.network)
144 + ..writeByte(20)
145 + ..write(obj.derivationInfo)
146 + ..writeByte(21)
147 + ..write(obj.hardwareWalletType)
148 + ..writeByte(22)
149 + ..write(obj.parentAddress)
150 + ..writeByte(23)
151 + ..write(obj.hiddenAddresses)
152 + ..writeByte(24)
153 + ..write(obj.manualAddresses)
154 + ..writeByte(25)
155 + ..write(obj.hashedWalletIdentifier)
156 + ..writeByte(26)
157 + ..write(obj.isNonSeedWallet);
158 + }
159 +
160 + @override
161 + int get hashCode => typeId.hashCode;
162 +
163 + @override
164 + bool operator ==(Object other) =>
165 + identical(this, other) ||
166 + other is WalletInfoAdapter &&
167 + runtimeType == other.runtimeType &&
168 + typeId == other.typeId;
169 +}
170 +
171 +class DerivationTypeAdapter extends TypeAdapter<newWi.DerivationType> {
172 + @override
173 + final int typeId = 15;
174 +
175 + @override
176 + newWi.DerivationType read(BinaryReader reader) {
177 + switch (reader.readByte()) {
178 + case 0:
179 + return newWi.DerivationType.unknown;
180 + case 1:
181 + return newWi.DerivationType.def;
182 + case 2:
183 + return newWi.DerivationType.nano;
184 + case 3:
185 + return newWi.DerivationType.bip39;
186 + case 4:
187 + return newWi.DerivationType.electrum;
188 + default:
189 + return newWi.DerivationType.unknown;
190 + }
191 + }
192 +
193 + @override
194 + void write(BinaryWriter writer, newWi.DerivationType obj) {
195 + switch (obj) {
196 + case newWi.DerivationType.unknown:
197 + writer.writeByte(0);
198 + break;
199 + case newWi.DerivationType.def:
200 + writer.writeByte(1);
201 + break;
202 + case newWi.DerivationType.nano:
203 + writer.writeByte(2);
204 + break;
205 + case newWi.DerivationType.bip39:
206 + writer.writeByte(3);
207 + break;
208 + case newWi.DerivationType.electrum:
209 + writer.writeByte(4);
210 + break;
211 + }
212 + }
213 +
214 + @override
215 + int get hashCode => typeId.hashCode;
216 +
217 + @override
218 + bool operator ==(Object other) =>
219 + identical(this, other) ||
220 + other is DerivationTypeAdapter &&
221 + runtimeType == other.runtimeType &&
222 + typeId == other.typeId;
223 +}
224 +
225 +class HardwareWalletTypeAdapter extends TypeAdapter<newWi.HardwareWalletType> {
226 + @override
227 + final int typeId = 19;
228 +
229 + @override
230 + newWi.HardwareWalletType read(BinaryReader reader) {
231 + switch (reader.readByte()) {
232 + case 0:
233 + return newWi.HardwareWalletType.ledger;
234 + case 1:
235 + return newWi.HardwareWalletType.bitbox;
236 + case 2:
237 + return newWi.HardwareWalletType.cupcake;
238 + case 3:
239 + return newWi.HardwareWalletType.coldcard;
240 + case 4:
241 + return newWi.HardwareWalletType.seedsigner;
242 + case 5:
243 + return newWi.HardwareWalletType.keystone;
244 + case 6:
245 + return newWi.HardwareWalletType.trezor;
246 + default:
247 + return newWi.HardwareWalletType.ledger;
248 + }
249 + }
250 +
251 + @override
252 + void write(BinaryWriter writer, newWi.HardwareWalletType obj) {
253 + switch (obj) {
254 + case newWi.HardwareWalletType.ledger:
255 + writer.writeByte(0);
256 + break;
257 + case newWi.HardwareWalletType.bitbox:
258 + writer.writeByte(1);
259 + break;
260 + case newWi.HardwareWalletType.cupcake:
261 + writer.writeByte(2);
262 + break;
263 + case newWi.HardwareWalletType.coldcard:
264 + writer.writeByte(3);
265 + break;
266 + case newWi.HardwareWalletType.seedsigner:
267 + writer.writeByte(4);
268 + break;
269 + case newWi.HardwareWalletType.keystone:
270 + writer.writeByte(5);
271 + break;
272 + case newWi.HardwareWalletType.trezor:
273 + writer.writeByte(6);
274 + break;
275 + }
276 + }
277 +
278 + @override
279 + int get hashCode => typeId.hashCode;
280 +
281 + @override
282 + bool operator ==(Object other) =>
283 + identical(this, other) ||
284 + other is HardwareWalletTypeAdapter &&
285 + runtimeType == other.runtimeType &&
286 + typeId == other.typeId;
287 +}
cw_core/lib/wallet_service.dart
+1 -1
@@ -64,5 +64,5 @@ abstract class WalletService<N extends WalletCredentials, RFS extends WalletCred
64
65 /// Check if the Wallet requires a hardware wallet to be connected during
66 /// the opening flow. (Currently only the case for Monero)
67 - bool requireHardwareWalletConnection(String name) => false;
67 + Future<bool> requireHardwareWalletConnection(String name) async => false;
68 }
cw_core/pubspec.lock
+64
@@ -674,6 +674,62 @@ packages:
674 url: "https://pub.dev"
675 source: hosted
676 version: "1.10.1"
677 + sqflite:
678 + dependency: "direct main"
679 + description:
680 + name: sqflite
681 + sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
682 + url: "https://pub.dev"
683 + source: hosted
684 + version: "2.4.1"
685 + sqflite_android:
686 + dependency: transitive
687 + description:
688 + name: sqflite_android
689 + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
690 + url: "https://pub.dev"
691 + source: hosted
692 + version: "2.4.0"
693 + sqflite_common:
694 + dependency: transitive
695 + description:
696 + name: sqflite_common
697 + sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
698 + url: "https://pub.dev"
699 + source: hosted
700 + version: "2.5.4+6"
701 + sqflite_common_ffi:
702 + dependency: "direct main"
703 + description:
704 + name: sqflite_common_ffi
705 + sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
706 + url: "https://pub.dev"
707 + source: hosted
708 + version: "2.3.4+4"
709 + sqflite_darwin:
710 + dependency: transitive
711 + description:
712 + name: sqflite_darwin
713 + sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
714 + url: "https://pub.dev"
715 + source: hosted
716 + version: "2.4.1+1"
717 + sqflite_platform_interface:
718 + dependency: transitive
719 + description:
720 + name: sqflite_platform_interface
721 + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
722 + url: "https://pub.dev"
723 + source: hosted
724 + version: "2.4.0"
725 + sqlite3:
726 + dependency: transitive
727 + description:
728 + name: sqlite3
729 + sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
730 + url: "https://pub.dev"
731 + source: hosted
732 + version: "2.9.0"
733 stack_trace:
734 dependency: transitive
735 description:
@@ -706,6 +762,14 @@ packages:
762 url: "https://pub.dev"
763 source: hosted
764 version: "1.4.1"
765 + synchronized:
766 + dependency: transitive
767 + description:
768 + name: synchronized
769 + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
770 + url: "https://pub.dev"
771 + source: hosted
772 + version: "3.3.0+3"
773 term_glyph:
774 dependency: transitive
775 description:
cw_core/pubspec.yaml
+3 -1
@@ -14,7 +14,7 @@ dependencies:
14 sdk: flutter
15 http: ^1.1.0
16 file: ^7.0.0
17 - path_provider: ^2.0.11
17 + path_provider: ^2.1.5
18 mobx: ^2.0.7+4
19 flutter_mobx: ^2.0.6+1
20 intl: any
@@ -44,6 +44,8 @@ dependencies:
44 git:
45 url: https://github.com/cake-tech/blockchain_utils
46 ref: cake-update-v2
47 + sqflite: ^2.4.1
48 + sqflite_common_ffi: ^2.3.4+4
49
50 dev_dependencies:
51 flutter_test:
cw_decred/lib/wallet.dart
+6 -6
@@ -37,7 +37,7 @@ class DecredWallet = DecredWalletBase with _$DecredWallet;
37
38 abstract class DecredWalletBase
39 extends WalletBase<DecredBalance, DecredTransactionHistory, DecredTransactionInfo> with Store {
40 - DecredWalletBase(WalletInfo walletInfo, String password, Box<UnspentCoinsInfo> unspentCoinsInfo,
40 + DecredWalletBase(WalletInfo walletInfo, DerivationInfo derivationInfo, String password, Box<UnspentCoinsInfo> unspentCoinsInfo,
41 Libwallet libwallet, Function() closeLibwallet)
42 : _password = password,
43 _libwallet = libwallet,
@@ -45,15 +45,15 @@ abstract class DecredWalletBase
45 this.syncStatus = NotConnectedSyncStatus(),
46 this.unspentCoinsInfo = unspentCoinsInfo,
47 this.watchingOnly =
48 - walletInfo.derivationInfo?.derivationPath == DecredWalletService.pubkeyRestorePath ||
49 - walletInfo.derivationInfo?.derivationPath ==
48 + derivationInfo.derivationPath == DecredWalletService.pubkeyRestorePath ||
49 + derivationInfo.derivationPath ==
50 DecredWalletService.pubkeyRestorePathTestnet,
51 this.balance = ObservableMap.of({CryptoCurrency.dcr: DecredBalance.zero()}),
52 - this.isTestnet = walletInfo.derivationInfo?.derivationPath ==
52 + this.isTestnet = derivationInfo.derivationPath ==
53 DecredWalletService.seedRestorePathTestnet ||
54 - walletInfo.derivationInfo?.derivationPath ==
54 + derivationInfo.derivationPath ==
55 DecredWalletService.pubkeyRestorePathTestnet,
56 - super(walletInfo) {
56 + super(walletInfo, derivationInfo) {
57 walletAddresses = DecredWalletAddresses(walletInfo, libwallet);
58 transactionHistory = DecredTransactionHistory();
59
cw_decred/lib/wallet_addresses.dart
+25 -13
@@ -37,15 +37,11 @@ abstract class DecredWalletAddressesBase extends WalletAddresses with Store {
37
38 @override
39 Future<void> init() async {
40 - if (walletInfo.addresses != null) {
41 - addressesMap = walletInfo.addresses!;
42 - }
43 - if (walletInfo.addressInfos != null) {
44 - addressInfos = walletInfo.addressInfos!;
45 - }
46 - if (walletInfo.usedAddresses != null) {
47 - usedAddresses = {...walletInfo.usedAddresses!};
48 - }
40 + addressesMap = await walletInfo.getAddresses();
41 + addressInfos = await walletInfo.getAddressInfos();
42 + usedAddresses = await walletInfo.getUsedAddresses();
43 + manualAddresses = await walletInfo.getManualAddresses();
44 + hiddenAddresses = await walletInfo.getHiddenAddresses();
45 await updateAddressesInBox();
46 }
47
@@ -61,7 +57,15 @@ abstract class DecredWalletAddressesBase extends WalletAddresses with Store {
57 }
58 addressesMap[addr] = "";
59 addressInfos[0] ??= [];
64 - addressInfos[0]?.add(AddressInfo(address: addr, label: "", accountIndex: 0));
60 + addressInfos[0]?.add(
61 + WalletInfoAddressInfo(
62 + walletInfoId: walletInfo.internalId,
63 + mapKey: 0,
64 + address: addr,
65 + label: "",
66 + accountIndex: 0,
67 + ),
68 + );
69 });
70
71 // Add used addresses.
@@ -79,11 +83,11 @@ abstract class DecredWalletAddressesBase extends WalletAddresses with Store {
83 await saveAddressesInBox();
84 }
85
82 - List<AddressInfo> getAddressInfos() {
86 + List<WalletInfoAddressInfo> getAddressInfos() {
87 if (addressInfos.containsKey(0)) {
88 return addressInfos[0]!;
89 }
86 - return <AddressInfo>[];
90 + return <WalletInfoAddressInfo>[];
91 }
92
93 Future<void> updateAddress(String address, String label) async {
@@ -128,7 +132,15 @@ abstract class DecredWalletAddressesBase extends WalletAddresses with Store {
132 if (!addressesMap.containsKey(addr)) {
133 addressesMap[addr] = "";
134 addressInfos[0] ??= [];
131 - addressInfos[0]?.add(AddressInfo(address: addr, label: label, accountIndex: 0));
135 + addressInfos[0]?.add(
136 + WalletInfoAddressInfo(
137 + walletInfoId: walletInfo.internalId,
138 + mapKey: 0,
139 + address: addr,
140 + label: label,
141 + accountIndex: 0,
142 + ),
143 + );
144 }
145 selectedAddr = addr;
146 await saveAddressesInBox();
cw_decred/lib/wallet_service.dart
+39 -28
@@ -18,9 +18,8 @@ class DecredWalletService extends WalletService<
18 DecredRestoreWalletFromSeedCredentials,
19 DecredRestoreWalletFromPubkeyCredentials,
20 DecredRestoreWalletFromHardwareCredentials> {
21 - DecredWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
21 + DecredWalletService(this.unspentCoinsInfoSource);
22
23 - final Box<WalletInfo> walletInfoSource;
23 final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
24 final seedRestorePath = "m/44'/42'";
25 static final seedRestorePathTestnet = "m/44'/1'";
@@ -68,9 +67,10 @@ class DecredWalletService extends WalletService<
67 "unsyncedaddrs": true,
68 };
69 await libwallet!.createWallet(jsonEncode(config));
71 - final di = DerivationInfo(
72 - derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
73 - credentials.walletInfo!.derivationInfo = di;
70 + final di = await credentials.walletInfo!.getDerivationInfo();
71 + di.derivationPath = isTestnet == true ? seedRestorePathTestnet : seedRestorePath;
72 + await di.save();
73 + credentials.walletInfo!.save();
74 credentials.walletInfo!.network = network;
75 // ios will move our wallet directory when updating. Since we must
76 // recalculate the new path every time we open the wallet, ensure this path
@@ -79,7 +79,7 @@ class DecredWalletService extends WalletService<
79 // going forward.
80 credentials.walletInfo!.dirPath = "";
81 credentials.walletInfo!.path = "";
82 - final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
82 + final wallet = DecredWallet(credentials.walletInfo!, di, credentials.password!,
83 this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
84 await wallet.init();
85 return wallet;
@@ -113,13 +113,17 @@ class DecredWalletService extends WalletService<
113
114 @override
115 Future<DecredWallet> openWallet(String name, String password) async {
116 - final walletInfo = walletInfoSource.values
117 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
116 + final walletInfo = await WalletInfo.get(name, getType());
117 + if (walletInfo == null) {
118 + throw Exception('Wallet not found');
119 + }
120 + final di = await walletInfo.getDerivationInfo();
121 if (walletInfo.network == null || walletInfo.network == "") {
119 - walletInfo.network = walletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
120 - walletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
122 + walletInfo.network = di.derivationPath == seedRestorePathTestnet ||
123 + di.derivationPath == pubkeyRestorePathTestnet
124 ? testnet
125 : mainnet;
126 + walletInfo.save();
127 }
128
129 await this.init();
@@ -149,7 +153,7 @@ class DecredWalletService extends WalletService<
153 };
154 await libwallet!.loadWallet(jsonEncode(config));
155 final wallet =
152 - DecredWallet(walletInfo, password, this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
156 + DecredWallet(walletInfo, di, password, this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
157 await wallet.init();
158 return wallet;
159 }
@@ -157,25 +161,32 @@ class DecredWalletService extends WalletService<
161 @override
162 Future<void> remove(String wallet) async {
163 File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
160 - final walletInfo = walletInfoSource.values
161 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
162 - await walletInfoSource.delete(walletInfo.key);
164 + final walletInfo = await WalletInfo.get(wallet, getType());
165 + if (walletInfo == null) {
166 + throw Exception('Wallet not found');
167 + }
168 + await WalletInfo.delete(walletInfo);
169 }
170
171 @override
172 Future<void> rename(String currentName, String password, String newName) async {
167 - final currentWalletInfo = walletInfoSource.values
168 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
169 - final network = currentWalletInfo.derivationInfo?.derivationPath == seedRestorePathTestnet ||
170 - currentWalletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
173 + final currentWalletInfo = await WalletInfo.get(currentName, getType());
174 + if (currentWalletInfo == null) {
175 + throw Exception('Wallet not found');
176 + }
177 + final di = await currentWalletInfo.getDerivationInfo();
178 + final network = di.derivationPath == seedRestorePathTestnet ||
179 + di.derivationPath == pubkeyRestorePathTestnet
180 ? testnet
181 : mainnet;
182 + currentWalletInfo.network = network;
183 + currentWalletInfo.save();
184 if (libwallet == null) {
185 libwallet = await Libwallet.spawn();
186 libwallet!.initLibdcrwallet("", "err");
187 }
188 final currentWallet = DecredWallet(
178 - currentWalletInfo, password, this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
189 + currentWalletInfo, di, password, this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
190
191 await currentWallet.renameWalletFiles(newName);
192
@@ -185,7 +196,7 @@ class DecredWalletService extends WalletService<
196 newWalletInfo.dirPath = "";
197 newWalletInfo.path = "";
198
188 - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
199 + await newWalletInfo.save();
200 }
201
202 @override
@@ -203,13 +214,13 @@ class DecredWalletService extends WalletService<
214 "unsyncedaddrs": true,
215 };
216 await libwallet!.createWallet(jsonEncode(config));
206 - final di = DerivationInfo(
207 - derivationPath: isTestnet == true ? seedRestorePathTestnet : seedRestorePath);
208 - credentials.walletInfo!.derivationInfo = di;
217 + final di = await credentials.walletInfo!.getDerivationInfo();
218 + di.derivationPath = isTestnet == true ? seedRestorePathTestnet : seedRestorePath;
219 + await di.save();
220 credentials.walletInfo!.network = network;
221 credentials.walletInfo!.dirPath = "";
222 credentials.walletInfo!.path = "";
212 - final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
223 + final wallet = DecredWallet(credentials.walletInfo!, di, credentials.password!,
224 this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
225 await wallet.init();
226 return wallet;
@@ -231,13 +242,13 @@ class DecredWalletService extends WalletService<
242 "unsyncedaddrs": true,
243 };
244 await libwallet!.createWatchOnlyWallet(jsonEncode(config));
234 - final di = DerivationInfo(
235 - derivationPath: isTestnet == true ? pubkeyRestorePathTestnet : pubkeyRestorePath);
236 - credentials.walletInfo!.derivationInfo = di;
245 + final di = await credentials.walletInfo!.getDerivationInfo();
246 + di.derivationPath = isTestnet == true ? pubkeyRestorePathTestnet : pubkeyRestorePath;
247 + await di.save();
248 credentials.walletInfo!.network = network;
249 credentials.walletInfo!.dirPath = "";
250 credentials.walletInfo!.path = "";
240 - final wallet = DecredWallet(credentials.walletInfo!, credentials.password!,
251 + final wallet = DecredWallet(credentials.walletInfo!, di, credentials.password!,
252 this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
253 await wallet.init();
254 return wallet;
cw_decred/pubspec.lock
+64
@@ -705,6 +705,62 @@ packages:
705 url: "https://pub.dev"
706 source: hosted
707 version: "1.10.1"
708 + sqflite:
709 + dependency: transitive
710 + description:
711 + name: sqflite
712 + sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
713 + url: "https://pub.dev"
714 + source: hosted
715 + version: "2.4.1"
716 + sqflite_android:
717 + dependency: transitive
718 + description:
719 + name: sqflite_android
720 + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
721 + url: "https://pub.dev"
722 + source: hosted
723 + version: "2.4.0"
724 + sqflite_common:
725 + dependency: transitive
726 + description:
727 + name: sqflite_common
728 + sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
729 + url: "https://pub.dev"
730 + source: hosted
731 + version: "2.5.4+6"
732 + sqflite_common_ffi:
733 + dependency: transitive
734 + description:
735 + name: sqflite_common_ffi
736 + sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
737 + url: "https://pub.dev"
738 + source: hosted
739 + version: "2.3.4+4"
740 + sqflite_darwin:
741 + dependency: transitive
742 + description:
743 + name: sqflite_darwin
744 + sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
745 + url: "https://pub.dev"
746 + source: hosted
747 + version: "2.4.1+1"
748 + sqflite_platform_interface:
749 + dependency: transitive
750 + description:
751 + name: sqflite_platform_interface
752 + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
753 + url: "https://pub.dev"
754 + source: hosted
755 + version: "2.4.0"
756 + sqlite3:
757 + dependency: transitive
758 + description:
759 + name: sqlite3
760 + sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
761 + url: "https://pub.dev"
762 + source: hosted
763 + version: "2.9.0"
764 stack_trace:
765 dependency: transitive
766 description:
@@ -737,6 +793,14 @@ packages:
793 url: "https://pub.dev"
794 source: hosted
795 version: "1.4.1"
796 + synchronized:
797 + dependency: transitive
798 + description:
799 + name: synchronized
800 + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
801 + url: "https://pub.dev"
802 + source: hosted
803 + version: "3.3.0+3"
804 term_glyph:
805 dependency: transitive
806 description:
cw_dogecoin/lib/src/dogecoin_wallet.dart
+5
@@ -26,6 +26,7 @@ abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
26 required String mnemonic,
27 required String password,
28 required WalletInfo walletInfo,
29 + required DerivationInfo derivationInfo,
30 required Box<UnspentCoinsInfo> unspentCoinsInfo,
31 required Uint8List seedBytes,
32 required EncryptionFileUtils encryptionFileUtils,
@@ -39,6 +40,7 @@ abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
40 mnemonic: mnemonic,
41 password: password,
42 walletInfo: walletInfo,
43 + derivationInfo: derivationInfo,
44 unspentCoinsInfo: unspentCoinsInfo,
45 network: DogecoinNetwork.mainnet,
46 initialAddresses: initialAddresses,
@@ -83,6 +85,7 @@ abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
85 {required String mnemonic,
86 required String password,
87 required WalletInfo walletInfo,
88 + required DerivationInfo derivationInfo,
89 required Box<UnspentCoinsInfo> unspentCoinsInfo,
90 required EncryptionFileUtils encryptionFileUtils,
91 String? passphrase,
@@ -95,6 +98,7 @@ abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
98 mnemonic: mnemonic,
99 password: password,
100 walletInfo: walletInfo,
101 + derivationInfo: derivationInfo,
102 unspentCoinsInfo: unspentCoinsInfo,
103 initialAddresses: initialAddresses,
104 initialBalance: initialBalance,
@@ -148,6 +152,7 @@ abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
152 mnemonic: keysData.mnemonic!,
153 password: password,
154 walletInfo: walletInfo,
155 + derivationInfo: await walletInfo.getDerivationInfo(),
156 unspentCoinsInfo: unspentCoinsInfo,
157 initialAddresses: snp?.addresses,
158 initialBalance: snp?.balance,
cw_dogecoin/lib/src/dogecoin_wallet_service.dart
+17 -11
@@ -18,9 +18,8 @@ class DogeCoinWalletService extends WalletService<
18 DogeCoinRestoreWalletFromSeedCredentials,
19 DogeCoinRestoreWalletFromWIFCredentials,
20 DogeCoinNewWalletCredentials> {
21 - DogeCoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource, this.isDirect);
21 + DogeCoinWalletService(this.unspentCoinsInfoSource, this.isDirect);
22
23 - final Box<WalletInfo> walletInfoSource;
23 final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
24 final bool isDirect;
25
@@ -39,6 +38,7 @@ class DogeCoinWalletService extends WalletService<
38 mnemonic: credentials.mnemonic ?? MnemonicBip39.generate(strength: strength),
39 password: credentials.password!,
40 walletInfo: credentials.walletInfo!,
41 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
42 unspentCoinsInfo: unspentCoinsInfoSource,
43 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
44 passphrase: credentials.passphrase,
@@ -51,9 +51,10 @@ class DogeCoinWalletService extends WalletService<
51
52 @override
53 Future<DogeCoinWallet> openWallet(String name, String password) async {
54 - final walletInfo = walletInfoSource.values
55 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
56 -
54 + final walletInfo = await WalletInfo.get(name, getType());
55 + if (walletInfo == null) {
56 + throw Exception('Wallet not found');
57 + }
58 try {
59 final wallet = await DogeCoinWalletBase.open(
60 password: password,
@@ -82,9 +83,11 @@ class DogeCoinWalletService extends WalletService<
83 @override
84 Future<void> remove(String wallet) async {
85 File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
85 - final walletInfo = walletInfoSource.values
86 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
87 - await walletInfoSource.delete(walletInfo.key);
86 + final walletInfo = await WalletInfo.get(wallet, getType());
87 + if (walletInfo == null) {
88 + throw Exception('Wallet not found');
89 + }
90 + await WalletInfo.delete(walletInfo);
91
92 final unspentCoinsToDelete = unspentCoinsInfoSource.values
93 .where((unspentCoin) => unspentCoin.walletId == walletInfo.id)
@@ -99,8 +102,10 @@ class DogeCoinWalletService extends WalletService<
102
103 @override
104 Future<void> rename(String currentName, String password, String newName) async {
102 - final currentWalletInfo = walletInfoSource.values
103 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
105 + final currentWalletInfo = await WalletInfo.get(currentName, getType());
106 + if (currentWalletInfo == null) {
107 + throw Exception('Wallet not found');
108 + }
109 final currentWallet = await DogeCoinWalletBase.open(
110 password: password,
111 name: currentName,
@@ -115,7 +120,7 @@ class DogeCoinWalletService extends WalletService<
120 newWalletInfo.id = WalletBase.idFor(newName, getType());
121 newWalletInfo.name = newName;
122
118 - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
123 + await newWalletInfo.save();
124 }
125
126 @override
@@ -141,6 +146,7 @@ class DogeCoinWalletService extends WalletService<
146 password: credentials.password!,
147 mnemonic: credentials.mnemonic,
148 walletInfo: credentials.walletInfo!,
149 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
150 unspentCoinsInfo: unspentCoinsInfoSource,
151 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
152 passphrase: credentials.passphrase);
cw_ethereum/lib/ethereum_wallet.dart
+2
@@ -23,6 +23,7 @@ class EthereumWallet extends EVMChainWallet {
23 required super.client,
24 required super.password,
25 required super.walletInfo,
26 + required super.derivationInfo,
27 super.mnemonic,
28 super.initialBalance,
29 super.privateKey,
@@ -176,6 +177,7 @@ class EthereumWallet extends EVMChainWallet {
177
178 return EthereumWallet(
179 walletInfo: walletInfo,
180 + derivationInfo: await walletInfo.getDerivationInfo(),
181 password: password,
182 mnemonic: keysData.mnemonic,
183 privateKey: keysData.privateKey,
cw_ethereum/lib/ethereum_wallet_service.dart
+19 -10
@@ -10,7 +10,7 @@ import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
10 import 'package:cw_evm/evm_chain_wallet_service.dart';
11
12 class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
13 - EthereumWalletService(super.walletInfoSource, super.isDirect, {required this.client});
13 + EthereumWalletService(super.isDirect, {required this.client});
14
15 late EthereumClient client;
16
@@ -25,6 +25,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
25
26 final wallet = EthereumWallet(
27 walletInfo: credentials.walletInfo!,
28 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
29 mnemonic: mnemonic,
30 password: credentials.password!,
31 passphrase: credentials.passphrase,
@@ -41,8 +42,10 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
42
43 @override
44 Future<EthereumWallet> openWallet(String name, String password) async {
44 - final walletInfo =
45 - walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
45 + final walletInfo = await WalletInfo.get(name, getType());
46 + if (walletInfo == null) {
47 + throw Exception('Wallet not found');
48 + }
49
50 try {
51 final wallet = await EthereumWallet.open(
@@ -75,8 +78,10 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
78
79 @override
80 Future<void> rename(String currentName, String password, String newName) async {
78 - final currentWalletInfo = walletInfoSource.values
79 - .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
81 + final currentWalletInfo = await WalletInfo.get(currentName, getType());
82 + if (currentWalletInfo == null) {
83 + throw Exception('Wallet not found');
84 + }
85 final currentWallet = await EthereumWallet.open(
86 password: password,
87 name: currentName,
@@ -91,21 +96,23 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
96 newWalletInfo.id = WalletBase.idFor(newName, getType());
97 newWalletInfo.name = newName;
98
94 - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
99 + await newWalletInfo.save();
100 }
101
102 @override
103 Future<EthereumWallet> restoreFromHardwareWallet(
104 EVMChainRestoreWalletFromHardware credentials) async {
100 - credentials.walletInfo!.derivationInfo = DerivationInfo(
101 - derivationType: DerivationType.bip39,
102 - derivationPath: "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0"
103 - );
105 + final di = await credentials.walletInfo!.getDerivationInfo();
106 + di.derivationType = DerivationType.bip39;
107 + di.derivationPath = "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0";
108 + await di.save();
109 credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
110 credentials.walletInfo!.address = credentials.hwAccountData.address;
111 + credentials.walletInfo!.save();
112
113 final wallet = EthereumWallet(
114 walletInfo: credentials.walletInfo!,
115 + derivationInfo: di,
116 password: credentials.password!,
117 client: client,
118 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -125,6 +132,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
132 password: credentials.password!,
133 privateKey: credentials.privateKey,
134 walletInfo: credentials.walletInfo!,
135 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
136 client: client,
137 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
138 );
@@ -147,6 +155,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
155 password: credentials.password!,
156 mnemonic: credentials.mnemonic,
157 walletInfo: credentials.walletInfo!,
158 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
159 passphrase: credentials.passphrase,
160 client: client,
161 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
cw_evm/lib/evm_chain_wallet.dart
+2 -1
@@ -67,6 +67,7 @@ abstract class EVMChainWalletBase
67 with Store, WalletKeysFile {
68 EVMChainWalletBase({
69 required WalletInfo walletInfo,
70 + required DerivationInfo derivationInfo,
71 required EVMChainClient client,
72 required CryptoCurrency nativeCurrency,
73 String? mnemonic,
@@ -88,7 +89,7 @@ abstract class EVMChainWalletBase
89 nativeCurrency: initialBalance ?? EVMChainERC20Balance(BigInt.zero),
90 },
91 ),
91 - super(walletInfo) {
92 + super(walletInfo, derivationInfo) {
93 this.walletInfo = walletInfo;
94 transactionHistory = setUpTransactionHistory(walletInfo, password, encryptionFileUtils);
95
cw_evm/lib/evm_chain_wallet_service.dart
+6 -5
@@ -15,9 +15,8 @@ abstract class EVMChainWalletService<T extends EVMChainWallet> extends WalletSer
15 EVMChainRestoreWalletFromSeedCredentials,
16 EVMChainRestoreWalletFromPrivateKey,
17 EVMChainRestoreWalletFromHardware> {
18 - EVMChainWalletService(this.walletInfoSource, this.isDirect);
18 + EVMChainWalletService(this.isDirect);
19
20 - final Box<WalletInfo> walletInfoSource;
20 final bool isDirect;
21
22 @override
@@ -48,8 +47,10 @@ abstract class EVMChainWalletService<T extends EVMChainWallet> extends WalletSer
47 @override
48 Future<void> remove(String wallet) async {
49 File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
51 - final walletInfo = walletInfoSource.values
52 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
53 - await walletInfoSource.delete(walletInfo.key);
50 + final walletInfo = await WalletInfo.get(wallet, getType());
51 + if (walletInfo == null) {
52 + throw Exception('Wallet not found');
53 + }
54 + await WalletInfo.delete(walletInfo);
55 }
56 }
cw_evm/lib/hardware/evm_chain_ledger_credentials.dart
+2 -2
@@ -19,8 +19,8 @@ class EvmLedgerCredentials extends CredentialsWithKnownAddress {
19 @override
20 EthereumAddress get address => EthereumAddress.fromHex(_address);
21
22 - void setLedgerConnection(LedgerConnection connection,
23 - [String? derivationPath]) {
22 + Future<void> setLedgerConnection(LedgerConnection connection,
23 + [String? derivationPath]) async {
24 ethereumLedgerApp = EthereumLedgerApp(connection,
25 derivationPath: derivationPath ?? "m/44'/60'/0'/0/0");
26 }
cw_monero/lib/ledger.dart
+1 -1
@@ -15,7 +15,7 @@ String? latestLedgerCommand;
15 typedef LedgerCallback = Void Function(Pointer<UnsignedChar>, UnsignedInt);
16 NativeCallable<LedgerCallback>? callable;
17
18 -void enableLedgerExchange(LedgerConnection connection) {
18 +Future<void> enableLedgerExchange(LedgerConnection connection) async {
19 callable?.close();
20
21 void callback(Pointer<UnsignedChar> request, int requestLength) async {
cw_monero/lib/monero_wallet.dart
+4 -3
@@ -55,6 +55,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
55 MoneroTransactionHistory, MoneroTransactionInfo> with Store {
56 MoneroWalletBase(
57 {required WalletInfo walletInfo,
58 + required DerivationInfo derivationInfo,
59 required Box<UnspentCoinsInfo> unspentCoinsInfo,
60 required String password})
61 : balance = ObservableMap<CryptoCurrency, MoneroBalance>.of({
@@ -70,7 +71,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
71 syncStatus = NotConnectedSyncStatus(),
72 unspentCoins = [],
73 this.unspentCoinsInfo = unspentCoinsInfo,
73 - super(walletInfo) {
74 + super(walletInfo, derivationInfo) {
75 transactionHistory = MoneroTransactionHistory();
76 walletAddresses = MoneroWalletAddresses(walletInfo, transactionHistory);
77
@@ -976,8 +977,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
977 return monero_wallet.verifyMessage(message, address, signature);
978 }
979
979 - void setLedgerConnection(LedgerConnection connection) {
980 - enableLedgerExchange(connection);
980 + Future<void> setLedgerConnection(LedgerConnection connection) async {
981 + await enableLedgerExchange(connection);
982 }
983
984 @override
cw_monero/lib/monero_wallet_addresses.dart
+6 -3
@@ -5,7 +5,6 @@ import 'package:cw_core/utils/print_verbose.dart';
5 import 'package:cw_core/wallet_addresses.dart';
6 import 'package:cw_core/wallet_info.dart';
7 import 'package:cw_monero/api/subaddress_list.dart' as subaddress_list;
8 -import 'package:cw_monero/api/transaction_history.dart';
8 import 'package:cw_monero/api/wallet.dart';
9 import 'package:cw_monero/monero_account_list.dart';
10 import 'package:cw_monero/monero_subaddress_list.dart';
@@ -90,8 +89,12 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
89 _subaddressList.subaddresses.forEach((subaddress) {
90 addressesMap[subaddress.address] = subaddress.label;
91 addressInfos[account.id] ??= [];
93 - addressInfos[account.id]?.add(AddressInfo(
94 - address: subaddress.address, label: subaddress.label, accountIndex: account.id));
92 + addressInfos[account.id]?.add(WalletInfoAddressInfo(
93 + walletInfoId: walletInfo.internalId,
94 + mapKey: account.id,
95 + accountIndex: account.id,
96 + address: subaddress.address,
97 + label: subaddress.label));
98 });
99 });
100
cw_monero/lib/monero_wallet_service.dart
+40 -22
@@ -99,9 +99,8 @@ class MoneroWalletService extends WalletService<
99 MoneroRestoreWalletFromSeedCredentials,
100 MoneroRestoreWalletFromKeysCredentials,
101 MoneroRestoreWalletFromHardwareCredentials> {
102 - MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
102 + MoneroWalletService(this.unspentCoinsInfoSource);
103
104 - final Box<WalletInfo> walletInfoSource;
104 final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
105
106 static bool walletFilesExist(String path) =>
@@ -148,6 +147,7 @@ class MoneroWalletService extends WalletService<
147 passphrase: credentials.passphrase ?? "");
148 final wallet = MoneroWallet(
149 walletInfo: credentials.walletInfo!,
150 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
151 unspentCoinsInfo: unspentCoinsInfoSource,
152 password: credentials.password!);
153 await wallet.init();
@@ -182,10 +182,13 @@ class MoneroWalletService extends WalletService<
182
183 await monero_wallet_manager
184 .openWallet(path: path, password: password);
185 - final walletInfo = walletInfoSource.values
186 - .firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
185 + final walletInfo = await WalletInfo.get(name, getType());
186 + if (walletInfo == null) {
187 + throw Exception('Wallet not found');
188 + }
189 final wallet = MoneroWallet(
190 walletInfo: walletInfo,
191 + derivationInfo: await walletInfo.getDerivationInfo(),
192 unspentCoinsInfo: unspentCoinsInfoSource,
193 password: password);
194
@@ -234,17 +237,22 @@ class MoneroWalletService extends WalletService<
237 await file.delete(recursive: true);
238 }
239
237 - final walletInfo = walletInfoSource.values
238 - .firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
239 - await walletInfoSource.delete(walletInfo.key);
240 + final walletInfo = await WalletInfo.get(wallet, getType());
241 + if (walletInfo == null) {
242 + throw Exception('Wallet not found');
243 + }
244 + await WalletInfo.delete(walletInfo);
245 }
246
247 @override
248 Future<void> rename(String currentName, String password, String newName) async {
244 - final currentWalletInfo = walletInfoSource.values.firstWhere(
245 - (info) => info.id == WalletBase.idFor(currentName, getType()));
249 + final currentWalletInfo = await WalletInfo.get(currentName, getType());
250 + if (currentWalletInfo == null) {
251 + throw Exception('Wallet not found');
252 + }
253 final currentWallet = MoneroWallet(
254 walletInfo: currentWalletInfo,
255 + derivationInfo: await currentWalletInfo.getDerivationInfo(),
256 unspentCoinsInfo: unspentCoinsInfoSource,
257 password: password,
258 );
@@ -255,7 +263,7 @@ class MoneroWalletService extends WalletService<
263 newWalletInfo.id = WalletBase.idFor(newName, getType());
264 newWalletInfo.name = newName;
265
258 - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
266 + await newWalletInfo.save();
267 }
268
269 @override
@@ -273,6 +281,7 @@ class MoneroWalletService extends WalletService<
281 spendKey: credentials.spendKey);
282 final wallet = MoneroWallet(
283 walletInfo: credentials.walletInfo!,
284 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
285 unspentCoinsInfo: unspentCoinsInfoSource,
286 password: credentials.password!);
287 await wallet.init();
@@ -303,6 +312,7 @@ class MoneroWalletService extends WalletService<
312
313 final wallet = MoneroWallet(
314 walletInfo: credentials.walletInfo!,
315 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
316 unspentCoinsInfo: unspentCoinsInfoSource,
317 password: credentials.password!);
318 await wallet.init();
@@ -359,6 +369,7 @@ class MoneroWalletService extends WalletService<
369 restoreHeight: credentials.height!);
370 final wallet = MoneroWallet(
371 walletInfo: credentials.walletInfo!,
372 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
373 unspentCoinsInfo: unspentCoinsInfoSource,
374 password: credentials.password!);
375 await wallet.init();
@@ -379,10 +390,11 @@ class MoneroWalletService extends WalletService<
390 String? passphrase,
391 int? overrideHeight,
392 }) async {
382 - walletInfo.derivationInfo = DerivationInfo(
383 - derivationType: DerivationType.bip39,
384 - derivationPath: "m/44'/128'/0'/0/0",
385 - );
393 + final derivationInfo = await walletInfo.getDerivationInfo();
394 + derivationInfo.derivationType = DerivationType.bip39;
395 + derivationInfo.derivationPath = "m/44'/128'/0'/0/0";
396 + await derivationInfo.save();
397 + walletInfo.save();
398
399 final legacyMnemonic =
400 getLegacySeedFromBip39(mnemonic, passphrase: passphrase ?? "");
@@ -409,6 +421,7 @@ class MoneroWalletService extends WalletService<
421
422 final wallet = MoneroWallet(
423 walletInfo: walletInfo,
424 + derivationInfo: derivationInfo,
425 unspentCoinsInfo: unspentCoinsInfoSource,
426 password: password,
427 );
@@ -453,6 +466,7 @@ class MoneroWalletService extends WalletService<
466
467 final wallet = MoneroWallet(
468 walletInfo: walletInfo,
469 + derivationInfo: await walletInfo.getDerivationInfo(),
470 unspentCoinsInfo: unspentCoinsInfoSource,
471 password: password,
472 );
@@ -485,6 +499,7 @@ class MoneroWalletService extends WalletService<
499
500 final wallet = MoneroWallet(
501 walletInfo: walletInfo,
502 + derivationInfo: await walletInfo.getDerivationInfo(),
503 unspentCoinsInfo: unspentCoinsInfoSource,
504 password: password,
505 );
@@ -529,10 +544,13 @@ class MoneroWalletService extends WalletService<
544
545 await monero_wallet_manager
546 .openWallet(path: path, password: password);
532 - final walletInfo = walletInfoSource.values
533 - .firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
547 + final walletInfo = await WalletInfo.get(name, getType());
548 + if (walletInfo == null) {
549 + throw Exception('Wallet not found');
550 + }
551 final wallet = MoneroWallet(
552 walletInfo: walletInfo,
553 + derivationInfo: await walletInfo.getDerivationInfo(),
554 unspentCoinsInfo: unspentCoinsInfoSource,
555 password: password,
556 );
@@ -544,12 +562,12 @@ class MoneroWalletService extends WalletService<
562 }
563
564 @override
547 - bool requireHardwareWalletConnection(String name) {
548 - return walletInfoSource.values
549 - .firstWhereOrNull(
550 - (info) => info.id == WalletBase.idFor(name, getType()))
551 - ?.isHardwareWallet ??
552 - false;
565 + Future<bool> requireHardwareWalletConnection(String name) async {
566 + final walletInfo = await WalletInfo.get(name, getType());
567 + if (walletInfo == null) {
568 + throw Exception('Wallet not found');
569 + }
570 + return walletInfo.isHardwareWallet;
571 }
572 }
573
cw_monero/pubspec.lock
+64
@@ -818,6 +818,62 @@ packages:
818 url: "https://pub.dev"
819 source: hosted
820 version: "1.10.1"
821 + sqflite:
822 + dependency: transitive
823 + description:
824 + name: sqflite
825 + sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
826 + url: "https://pub.dev"
827 + source: hosted
828 + version: "2.4.1"
829 + sqflite_android:
830 + dependency: transitive
831 + description:
832 + name: sqflite_android
833 + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
834 + url: "https://pub.dev"
835 + source: hosted
836 + version: "2.4.0"
837 + sqflite_common:
838 + dependency: transitive
839 + description:
840 + name: sqflite_common
841 + sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
842 + url: "https://pub.dev"
843 + source: hosted
844 + version: "2.5.4+6"
845 + sqflite_common_ffi:
846 + dependency: "direct dev"
847 + description:
848 + name: sqflite_common_ffi
849 + sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
850 + url: "https://pub.dev"
851 + source: hosted
852 + version: "2.3.4+4"
853 + sqflite_darwin:
854 + dependency: transitive
855 + description:
856 + name: sqflite_darwin
857 + sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
858 + url: "https://pub.dev"
859 + source: hosted
860 + version: "2.4.1+1"
861 + sqflite_platform_interface:
862 + dependency: transitive
863 + description:
864 + name: sqflite_platform_interface
865 + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
866 + url: "https://pub.dev"
867 + source: hosted
868 + version: "2.4.0"
869 + sqlite3:
870 + dependency: transitive
871 + description:
872 + name: sqlite3
873 + sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
874 + url: "https://pub.dev"
875 + source: hosted
876 + version: "2.9.0"
877 stack_trace:
878 dependency: transitive
879 description:
@@ -850,6 +906,14 @@ packages:
906 url: "https://pub.dev"
907 source: hosted
908 version: "1.4.1"
909 + synchronized:
910 + dependency: transitive
911 + description:
912 + name: synchronized
913 + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
914 + url: "https://pub.dev"
915 + source: hosted
916 + version: "3.3.0+3"
917 term_glyph:
918 dependency: transitive
919 description:
cw_monero/pubspec.yaml
+1
@@ -39,6 +39,7 @@ dev_dependencies:
39 mobx_codegen: ^2.0.7
40 mockito: ^5.4.5
41 hive_generator: ^2.0.1
42 + sqflite_common_ffi: ^2.3.4+4
43
44 dependency_overrides:
45 watcher: ^1.1.0
cw_monero/test/monero_wallet_service_test.dart
+7 -4
@@ -1,5 +1,6 @@
1 import 'dart:io';
2
3 +import 'package:cw_core/db/sqlite.dart';
4 import 'package:cw_core/unspent_coins_info.dart';
5 import 'package:cw_core/wallet_base.dart';
6 import 'package:cw_core/wallet_info.dart';
@@ -7,26 +8,28 @@ import 'package:cw_core/wallet_type.dart';
8 import 'package:cw_monero/monero_wallet_service.dart';
9 import 'package:flutter_test/flutter_test.dart';
10 import 'package:hive/hive.dart';
11 +import 'package:sqflite/sqflite.dart';
12 import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
13 +import 'package:sqflite_common_ffi/sqflite_ffi.dart';
14
15 import 'mock/path_provider.dart';
16 import 'utils/setup_monero_c.dart';
17
18 Future<void> main() async {
19 group("MoneroWalletService Tests", () {
17 - Hive.init('./test/data/db');
20 late MoneroWalletService walletService;
21 late File moneroCBinary;
22
23 setUpAll(() async {
24 + databaseFactory = databaseFactoryFfi;
25 + await initDb(pathOverride: './test/data/db');
26 + Hive.init('./test/data/db');
27 PathProviderPlatform.instance = MockPathProviderPlatform();
28
24 - final Box<WalletInfo> walletInfoSource =
25 - await Hive.openBox('testWalletInfo');
29 final Box<UnspentCoinsInfo> unspentCoinsInfoSource =
30 await Hive.openBox('testUnspentCoinsInfo');
31
29 - walletService = MoneroWalletService(walletInfoSource, unspentCoinsInfoSource);
32 + walletService = MoneroWalletService(unspentCoinsInfoSource);
33 moneroCBinary = getMoneroCBinary().copySync(moneroCBinaryName);
34 });
35
cw_nano/lib/nano_wallet.dart
+7 -4
@@ -39,6 +39,7 @@ abstract class NanoWalletBase
39 with Store, WalletKeysFile {
40 NanoWalletBase({
41 required WalletInfo walletInfo,
42 + required DerivationInfo derivationInfo,
43 required String mnemonic,
44 required String password,
45 NanoBalance? initialBalance,
@@ -47,7 +48,7 @@ abstract class NanoWalletBase
48 }) : syncStatus = NotConnectedSyncStatus(),
49 _password = password,
50 _mnemonic = mnemonic,
50 - _derivationType = walletInfo.derivationInfo!.derivationType!,
51 + _derivationType = derivationInfo.derivationType!,
52 _isTransactionUpdating = false,
53 _encryptionFileUtils = encryptionFileUtils,
54 _client = NanoClient(),
@@ -56,7 +57,7 @@ abstract class NanoWalletBase
57 CryptoCurrency.nano: initialBalance ??
58 NanoBalance(currentBalance: BigInt.zero, receivableBalance: BigInt.zero)
59 }),
59 - super(walletInfo) {
60 + super(walletInfo, derivationInfo) {
61 this.walletInfo = walletInfo;
62 transactionHistory = NanoTransactionHistory(
63 walletInfo: walletInfo,
@@ -433,11 +434,13 @@ abstract class NanoWalletBase
434 derivationType = DerivationType.bip39;
435 }
436
436 - walletInfo.derivationInfo ??= DerivationInfo(derivationType: derivationType);
437 - walletInfo.derivationInfo!.derivationType ??= derivationType;
437 + final derivationInfo = await walletInfo.getDerivationInfo();
438 + derivationInfo.derivationType ??= derivationType;
439 + derivationInfo.save();
440
441 return NanoWallet(
442 walletInfo: walletInfo,
443 + derivationInfo: derivationInfo,
444 password: password,
445 mnemonic: keysData.mnemonic!,
446 initialBalance: balance,
cw_nano/lib/nano_wallet_creation_credentials.dart
+1
@@ -14,6 +14,7 @@ class NanoNewWalletCredentials extends WalletCredentials {
14 password: password,
15 walletInfo: walletInfo,
16 passphrase: passphrase,
17 + derivationInfo: DerivationInfo(derivationType: derivationType),
18 );
19
20 final String? mnemonic;
cw_nano/lib/nano_wallet_service.dart
+28 -19
@@ -19,9 +19,8 @@ class NanoWalletService extends WalletService<
19 NanoRestoreWalletFromSeedCredentials,
20 NanoRestoreWalletFromKeysCredentials,
21 NanoNewWalletCredentials> {
22 - NanoWalletService(this.walletInfoSource, this.isDirect);
22 + NanoWalletService(this.isDirect);
23
24 - final Box<WalletInfo> walletInfoSource;
24 final bool isDirect;
25
26 static bool walletFilesExist(String path) =>
@@ -33,7 +32,8 @@ class NanoWalletService extends WalletService<
32 @override
33 Future<WalletBase> create(NanoNewWalletCredentials credentials, {bool? isTestnet}) async {
34 final String mnemonic;
36 - switch (credentials.walletInfo?.derivationInfo?.derivationType) {
35 + final derivationInfo = credentials.derivationInfo ?? await credentials.walletInfo!.getDerivationInfo();
36 + switch (derivationInfo.derivationType) {
37 case DerivationType.nano:
38 String seedKey = NanoSeeds.generateSeed();
39 mnemonic = credentials.mnemonic ?? NanoDerivations.standardSeedToMnemonic(seedKey);
@@ -47,6 +47,7 @@ class NanoWalletService extends WalletService<
47
48 final wallet = NanoWallet(
49 walletInfo: credentials.walletInfo!,
50 + derivationInfo: derivationInfo,
51 mnemonic: mnemonic,
52 password: credentials.password!,
53 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -65,20 +66,25 @@ class NanoWalletService extends WalletService<
66 await file.delete(recursive: true);
67 }
68
68 - final walletInfo = walletInfoSource.values
69 - .firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
70 - await walletInfoSource.delete(walletInfo.key);
69 + final walletInfo = await WalletInfo.get(wallet, getType());
70 + if (walletInfo == null) {
71 + throw Exception('Wallet not found');
72 + }
73 + await WalletInfo.delete(walletInfo);
74 }
75
76 @override
77 Future<void> rename(String currentName, String password, String newName) async {
75 - final currentWalletInfo = walletInfoSource.values
76 - .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
78 + final currentWalletInfo = await WalletInfo.get(currentName, getType());
79 + if (currentWalletInfo == null) {
80 + throw Exception('Wallet not found');
81 + }
82
83 String randomWords =
84 (List<String>.from(nm.NanoMnemomics.WORDLIST)..shuffle()).take(24).join(' ');
85 final currentWallet = NanoWallet(
86 walletInfo: currentWalletInfo,
87 + derivationInfo: await currentWalletInfo.getDerivationInfo(),
88 password: password,
89 mnemonic: randomWords,
90 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -91,7 +97,7 @@ class NanoWalletService extends WalletService<
97 newWalletInfo.id = WalletBase.idFor(newName, getType());
98 newWalletInfo.name = newName;
99
94 - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
100 + await newWalletInfo.save();
101 }
102
103 @override
@@ -115,18 +121,17 @@ class NanoWalletService extends WalletService<
121 throw Exception("Wasn't a valid nano style seed!");
122 }
123 }
118 -
124 + final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
125 // should never happen but just in case:
120 - if (credentials.walletInfo!.derivationInfo == null) {
121 - credentials.walletInfo!.derivationInfo = DerivationInfo(derivationType: DerivationType.nano);
122 - } else if (credentials.walletInfo!.derivationInfo!.derivationType == null) {
123 - credentials.walletInfo!.derivationInfo!.derivationType = DerivationType.nano;
126 + if (derivationInfo.derivationType == null) {
127 + derivationInfo.derivationType = DerivationType.nano;
128 }
129
130 final wallet = await NanoWallet(
131 password: credentials.password!,
132 mnemonic: mnemonic ?? credentials.seedKey,
133 walletInfo: credentials.walletInfo!,
134 + derivationInfo: derivationInfo,
135 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
136 );
137 await wallet.init();
@@ -157,15 +162,17 @@ class NanoWalletService extends WalletService<
162 }
163 }
164
160 - DerivationType derivationType =
161 - credentials.walletInfo?.derivationInfo?.derivationType ?? DerivationType.nano;
165 + final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
166 + DerivationType derivationType = derivationInfo.derivationType ?? DerivationType.nano;
167
163 - credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: derivationType);
168 + derivationInfo.derivationType = derivationType;
169 + derivationInfo.save();
170
171 final wallet = await NanoWallet(
172 password: credentials.password!,
173 mnemonic: credentials.mnemonic,
174 walletInfo: credentials.walletInfo!,
175 + derivationInfo: derivationInfo,
176 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
177 );
178
@@ -180,8 +187,10 @@ class NanoWalletService extends WalletService<
187
188 @override
189 Future<NanoWallet> openWallet(String name, String password) async {
183 - final walletInfo =
184 - walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
190 + final walletInfo = await WalletInfo.get(name, getType());
191 + if (walletInfo == null) {
192 + throw Exception('Wallet not found');
193 + }
194
195 try {
196 final wallet = await NanoWalletBase.open(
cw_nano/pubspec.lock
+64
@@ -823,6 +823,62 @@ packages:
823 url: "https://pub.dev"
824 source: hosted
825 version: "1.10.1"
826 + sqflite:
827 + dependency: transitive
828 + description:
829 + name: sqflite
830 + sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
831 + url: "https://pub.dev"
832 + source: hosted
833 + version: "2.4.1"
834 + sqflite_android:
835 + dependency: transitive
836 + description:
837 + name: sqflite_android
838 + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
839 + url: "https://pub.dev"
840 + source: hosted
841 + version: "2.4.0"
842 + sqflite_common:
843 + dependency: transitive
844 + description:
845 + name: sqflite_common
846 + sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
847 + url: "https://pub.dev"
848 + source: hosted
849 + version: "2.5.4+6"
850 + sqflite_common_ffi:
851 + dependency: transitive
852 + description:
853 + name: sqflite_common_ffi
854 + sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
855 + url: "https://pub.dev"
856 + source: hosted
857 + version: "2.3.4+4"
858 + sqflite_darwin:
859 + dependency: transitive
860 + description:
861 + name: sqflite_darwin
862 + sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
863 + url: "https://pub.dev"
864 + source: hosted
865 + version: "2.4.1+1"
866 + sqflite_platform_interface:
867 + dependency: transitive
868 + description:
869 + name: sqflite_platform_interface
870 + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
871 + url: "https://pub.dev"
872 + source: hosted
873 + version: "2.4.0"
874 + sqlite3:
875 + dependency: transitive
876 + description:
877 + name: sqlite3
878 + sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
879 + url: "https://pub.dev"
880 + source: hosted
881 + version: "2.9.0"
882 stack_trace:
883 dependency: transitive
884 description:
@@ -855,6 +911,14 @@ packages:
911 url: "https://pub.dev"
912 source: hosted
913 version: "1.4.1"
914 + synchronized:
915 + dependency: transitive
916 + description:
917 + name: synchronized
918 + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
919 + url: "https://pub.dev"
920 + source: hosted
921 + version: "3.3.0+3"
922 term_glyph:
923 dependency: transitive
924 description:
cw_polygon/lib/polygon_wallet.dart
+4
@@ -21,6 +21,7 @@ import 'package:cw_polygon/polygon_transaction_info.dart';
21 class PolygonWallet extends EVMChainWallet {
22 PolygonWallet({
23 required super.walletInfo,
24 + required super.derivationInfo,
25 required super.password,
26 super.mnemonic,
27 super.initialBalance,
@@ -152,8 +153,11 @@ class PolygonWallet extends EVMChainWallet {
153 );
154 }
155
156 + final derivationInfo = await walletInfo.getDerivationInfo();
157 +
158 return PolygonWallet(
159 walletInfo: walletInfo,
160 + derivationInfo: derivationInfo,
161 password: password,
162 mnemonic: keysData.mnemonic,
163 privateKey: keysData.privateKey,
cw_polygon/lib/polygon_wallet_service.dart
+18 -11
@@ -10,8 +10,7 @@ import 'package:cw_polygon/polygon_mnemonics_exception.dart';
10 import 'package:cw_polygon/polygon_wallet.dart';
11
12 class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
13 - PolygonWalletService(
14 - super.walletInfoSource, super.isDirect, {
13 + PolygonWalletService(super.isDirect, {
14 required this.client,
15 });
16
@@ -28,6 +27,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
27
28 final wallet = PolygonWallet(
29 walletInfo: credentials.walletInfo!,
30 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
31 mnemonic: mnemonic,
32 password: credentials.password!,
33 passphrase: credentials.passphrase,
@@ -43,8 +43,10 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
43
44 @override
45 Future<PolygonWallet> openWallet(String name, String password) async {
46 - final walletInfo =
47 - walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
46 + final walletInfo = await WalletInfo.get(name, getType());
47 + if (walletInfo == null) {
48 + throw Exception('Wallet not found');
49 + }
50
51 try {
52 final wallet = await PolygonWallet.open(
@@ -82,6 +84,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
84 password: credentials.password!,
85 privateKey: credentials.privateKey,
86 walletInfo: credentials.walletInfo!,
87 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
88 client: client,
89 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
90 );
@@ -95,15 +98,16 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
98 @override
99 Future<PolygonWallet> restoreFromHardwareWallet(
100 EVMChainRestoreWalletFromHardware credentials) async {
98 - credentials.walletInfo!.derivationInfo = DerivationInfo(
99 - derivationType: DerivationType.bip39,
100 - derivationPath: "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0"
101 - );
101 + final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
102 + derivationInfo.derivationType = DerivationType.bip39;
103 + derivationInfo.derivationPath = "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0";
104 + derivationInfo.save();
105 credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
106 credentials.walletInfo!.address = credentials.hwAccountData.address;
107
108 final wallet = PolygonWallet(
109 walletInfo: credentials.walletInfo!,
110 + derivationInfo: derivationInfo,
111 password: credentials.password!,
112 client: client,
113 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -127,6 +131,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
131 password: credentials.password!,
132 mnemonic: credentials.mnemonic,
133 walletInfo: credentials.walletInfo!,
134 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
135 passphrase: credentials.passphrase,
136 client: client,
137 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
@@ -141,8 +146,10 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
146
147 @override
148 Future<void> rename(String currentName, String password, String newName) async {
144 - final currentWalletInfo = walletInfoSource.values
145 - .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
149 + final currentWalletInfo = await WalletInfo.get(currentName, getType());
150 + if (currentWalletInfo == null) {
151 + throw Exception('Wallet not found');
152 + }
153 final currentWallet = await PolygonWallet.open(
154 password: password,
155 name: currentName,
@@ -157,6 +164,6 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
164 newWalletInfo.id = WalletBase.idFor(newName, getType());
165 newWalletInfo.name = newName;
166
160 - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
167 + await newWalletInfo.save();
168 }
169 }
cw_shared_external/lib/cw_shared_external.dart
+1 -1
@@ -8,7 +8,7 @@ class CwSharedExternal {
8 const MethodChannel('cw_shared_external');
9
10 static Future<String> get platformVersion async {
11 - final String version = await _channel.invokeMethod('getPlatformVersion');
11 + final String version = (await _channel.invokeMethod('getPlatformVersion')).toString();
12 return version;
13 }
14 }
cw_solana/lib/solana_wallet.dart
+5 -1
@@ -43,6 +43,7 @@ abstract class SolanaWalletBase
43 with Store, WalletKeysFile {
44 SolanaWalletBase({
45 required WalletInfo walletInfo,
46 + required DerivationInfo derivationInfo,
47 String? mnemonic,
48 String? privateKey,
49 required String password,
@@ -57,7 +58,7 @@ abstract class SolanaWalletBase
58 walletAddresses = SolanaWalletAddresses(walletInfo),
59 balance = ObservableMap<CryptoCurrency, SolanaBalance>.of(
60 {CryptoCurrency.sol: initialBalance ?? SolanaBalance(BigInt.zero.toDouble())}),
60 - super(walletInfo) {
61 + super(walletInfo, derivationInfo) {
62 this.walletInfo = walletInfo;
63 transactionHistory = SolanaTransactionHistory(
64 walletInfo: walletInfo,
@@ -449,8 +450,11 @@ abstract class SolanaWalletBase
450 );
451 }
452
453 + final derivationInfo = await walletInfo.getDerivationInfo();
454 +
455 return SolanaWallet(
456 walletInfo: walletInfo,
457 + derivationInfo: derivationInfo,
458 password: password,
459 passphrase: keysData.passphrase,
460 mnemonic: keysData.mnemonic,
cw_solana/lib/solana_wallet_service.dart
+18 -10
@@ -18,9 +18,8 @@ import 'package:hive/hive.dart';
18
19 class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
20 SolanaRestoreWalletFromSeedCredentials, SolanaRestoreWalletFromPrivateKey, SolanaNewWalletCredentials> {
21 - SolanaWalletService(this.walletInfoSource, this.isDirect);
21 + SolanaWalletService(this.isDirect);
22
23 - final Box<WalletInfo> walletInfoSource;
23 final bool isDirect;
24
25 @override
@@ -31,6 +30,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
30
31 final wallet = SolanaWallet(
32 walletInfo: credentials.walletInfo!,
33 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
34 mnemonic: mnemonic,
35 password: credentials.password!,
36 passphrase: credentials.passphrase,
@@ -52,8 +52,10 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
52
53 @override
54 Future<SolanaWallet> openWallet(String name, String password) async {
55 - final walletInfo =
56 - walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
55 + final walletInfo = await WalletInfo.get(name, getType());
56 + if (walletInfo == null) {
57 + throw Exception('Wallet not found');
58 + }
59
60 try {
61 final wallet = await SolanaWalletBase.open(
@@ -88,9 +90,11 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
90 @override
91 Future<void> remove(String wallet) async {
92 File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
91 - final walletInfo = walletInfoSource.values
92 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
93 - await walletInfoSource.delete(walletInfo.key);
93 + final walletInfo = await WalletInfo.get(wallet, getType());
94 + if (walletInfo == null) {
95 + throw Exception('Wallet not found');
96 + }
97 + await WalletInfo.delete(walletInfo);
98 }
99
100 @override
@@ -100,6 +104,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
104 password: credentials.password!,
105 privateKey: credentials.privateKey,
106 walletInfo: credentials.walletInfo!,
107 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
108 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
109 );
110
@@ -121,6 +126,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
126 password: credentials.password!,
127 mnemonic: credentials.mnemonic,
128 walletInfo: credentials.walletInfo!,
129 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
130 passphrase: credentials.passphrase,
131 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
132 );
@@ -134,8 +140,10 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
140
141 @override
142 Future<void> rename(String currentName, String password, String newName) async {
137 - final currentWalletInfo = walletInfoSource.values
138 - .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
143 + final currentWalletInfo = await WalletInfo.get(currentName, getType());
144 + if (currentWalletInfo == null) {
145 + throw Exception('Wallet not found');
146 + }
147 final currentWallet = await SolanaWalletBase.open(
148 password: password,
149 name: currentName,
@@ -150,7 +158,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
158 newWalletInfo.id = WalletBase.idFor(newName, getType());
159 newWalletInfo.name = newName;
160
153 - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
161 + await newWalletInfo.save();
162 }
163
164 @override
cw_tron/lib/tron_wallet.dart
+5 -1
@@ -42,6 +42,7 @@ abstract class TronWalletBase
42 with Store, WalletKeysFile {
43 TronWalletBase({
44 required WalletInfo walletInfo,
45 + required DerivationInfo derivationInfo,
46 String? mnemonic,
47 String? privateKey,
48 required String password,
@@ -57,7 +58,7 @@ abstract class TronWalletBase
58 balance = ObservableMap<CryptoCurrency, TronBalance>.of(
59 {CryptoCurrency.trx: initialBalance ?? TronBalance(BigInt.zero)},
60 ),
60 - super(walletInfo) {
61 + super(walletInfo, derivationInfo) {
62 this.walletInfo = walletInfo;
63 transactionHistory = TronTransactionHistory(
64 walletInfo: walletInfo, password: password, encryptionFileUtils: encryptionFileUtils);
@@ -163,8 +164,11 @@ abstract class TronWalletBase
164 );
165 }
166
167 + final derivationInfo = await walletInfo.getDerivationInfo();
168 +
169 return TronWallet(
170 walletInfo: walletInfo,
171 + derivationInfo: derivationInfo,
172 password: password,
173 mnemonic: keysData.mnemonic,
174 privateKey: keysData.privateKey,
cw_tron/lib/tron_wallet_service.dart
+18 -11
@@ -1,7 +1,6 @@
1 import 'dart:io';
2
3 import 'package:bip39/bip39.dart' as bip39;
4 -import 'package:collection/collection.dart';
4 import 'package:cw_core/balance.dart';
5 import 'package:cw_core/encryption_file_utils.dart';
6 import 'package:cw_core/pathForWallet.dart';
@@ -22,11 +21,10 @@ class TronWalletService extends WalletService<
21 TronRestoreWalletFromSeedCredentials,
22 TronRestoreWalletFromPrivateKey,
23 TronNewWalletCredentials> {
25 - TronWalletService(this.walletInfoSource, {required this.client, required this.isDirect});
24 + TronWalletService({required this.client, required this.isDirect});
25
26 late TronClient client;
27
29 - final Box<WalletInfo> walletInfoSource;
28 final bool isDirect;
29
30 @override
@@ -40,6 +38,7 @@ class TronWalletService extends WalletService<
38
39 final wallet = TronWallet(
40 walletInfo: credentials.walletInfo!,
41 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
42 mnemonic: mnemonic,
43 password: credentials.password!,
44 passphrase: credentials.passphrase,
@@ -55,8 +54,10 @@ class TronWalletService extends WalletService<
54
55 @override
56 Future<TronWallet> openWallet(String name, String password) async {
58 - final walletInfo =
59 - walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
57 + final walletInfo = await WalletInfo.get(name, getType());
58 + if (walletInfo == null) {
59 + throw Exception('Wallet not found');
60 + }
61
62 try {
63 final wallet = await TronWalletBase.open(
@@ -97,6 +98,7 @@ class TronWalletService extends WalletService<
98 password: credentials.password!,
99 privateKey: credentials.privateKey,
100 walletInfo: credentials.walletInfo!,
101 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
102 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
103 );
104
@@ -120,6 +122,7 @@ class TronWalletService extends WalletService<
122 password: credentials.password!,
123 mnemonic: credentials.mnemonic,
124 walletInfo: credentials.walletInfo!,
125 + derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
126 passphrase: credentials.passphrase,
127 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
128 );
@@ -133,8 +136,10 @@ class TronWalletService extends WalletService<
136
137 @override
138 Future<void> rename(String currentName, String password, String newName) async {
136 - final currentWalletInfo = walletInfoSource.values
137 - .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
139 + final currentWalletInfo = await WalletInfo.get(currentName, getType());
140 + if (currentWalletInfo == null) {
141 + throw Exception('Wallet not found');
142 + }
143 final currentWallet = await TronWalletBase.open(
144 password: password,
145 name: currentName,
@@ -149,7 +154,7 @@ class TronWalletService extends WalletService<
154 newWalletInfo.id = WalletBase.idFor(newName, getType());
155 newWalletInfo.name = newName;
156
152 - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
157 + await newWalletInfo.save();
158 }
159
160 @override
@@ -159,9 +164,11 @@ class TronWalletService extends WalletService<
164 @override
165 Future<void> remove(String wallet) async {
166 File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
162 - final walletInfo = walletInfoSource.values
163 - .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
164 - await walletInfoSource.delete(walletInfo.key);
167 + final walletInfo = await WalletInfo.get(wallet, getType());
168 + if (walletInfo == null) {
169 + throw Exception('Wallet not found');
170 + }
171 + await WalletInfo.delete(walletInfo);
172 }
173
174 @override
cw_wownero/lib/api/wallet.dart
+17 -8
@@ -102,16 +102,22 @@ String getAddress({int accountIndex = 0, int addressIndex = 1}) {
102 return addressCache[wptr!.address]![accountIndex]![addressIndex]!;
103 }
104
105 -int getFullBalance({int accountIndex = 0}) =>
106 - wownero.Wallet_balance(wptr!, accountIndex: accountIndex);
107 -
108 -int getUnlockedBalance({int accountIndex = 0}) =>
109 - wownero.Wallet_unlockedBalance(wptr!, accountIndex: accountIndex);
110 -
111 -int getCurrentHeight() => wownero.Wallet_blockChainHeight(wptr!);
105 +int getFullBalance({int accountIndex = 0}) {
106 + if (wptr == null) return 0;
107 + return wownero.Wallet_balance(wptr!, accountIndex: accountIndex);
108 +}
109 +int getUnlockedBalance({int accountIndex = 0}) {
110 + if (wptr == null) return 0;
111 + return wownero.Wallet_unlockedBalance(wptr!, accountIndex: accountIndex);
112 +}
113 +int getCurrentHeight() {
114 + if (wptr == null) return 0;
115 + return wownero.Wallet_blockChainHeight(wptr!);
116 +}
117
118 int cachedNodeHeight = 0;
119 int getNodeHeightSync() {
120 + if (wptr == null) return 0;
121 (() async {
122 final wptrAddress = wptr!.address;
123 cachedNodeHeight = await Isolate.run(() async {
@@ -121,7 +127,10 @@ int getNodeHeightSync() {
127 return cachedNodeHeight;
128 }
129
124 -bool isConnectedSync() => wownero.Wallet_connected(wptr!) != 0;
130 +bool isConnectedSync() {
131 + if (wptr == null) return false;
132 + return wownero.Wallet_connected(wptr!) != 0;
133 +}
134
135 Future<bool> setupNodeSync(
136 {required String address,
cw_wownero/lib/wownero_wallet.dart
+2 -2
@@ -53,7 +53,7 @@ abstract class WowneroWalletBase
53 extends WalletBase<WowneroBalance, WowneroTransactionHistory, WowneroTransactionInfo>
54 with Store {
55 WowneroWalletBase(
56 - {required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required String password})
56 + {required WalletInfo walletInfo, required DerivationInfo derivationInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo, required String password})
57 : balance = ObservableMap<CryptoCurrency, WowneroBalance>.of({
58 CryptoCurrency.wow: WowneroBalance(
59 fullBalance: wownero_wallet.getFullBalance(accountIndex: 0),
@@ -66,7 +66,7 @@ abstract class WowneroWalletBase
66 syncStatus = NotConnectedSyncStatus(),
67 unspentCoins = [],
68 this.unspentCoinsInfo = unspentCoinsInfo,
69 - super(walletInfo) {
69 + super(walletInfo, derivationInfo) {
70 transactionHistory = WowneroTransactionHistory();
71 walletAddresses = WowneroWalletAddresses(walletInfo, transactionHistory);
72
cw_wownero/lib/wownero_wallet_addresses.dart
+6 -2
@@ -88,8 +88,12 @@ abstract class WowneroWalletAddressesBase extends WalletAddresses with Store {
88 _subaddressList.subaddresses.forEach((subaddress) {
89 addressesMap[subaddress.address] = subaddress.label;
90 addressInfos[account.id] ??= [];
91 - addressInfos[account.id]?.add(AddressInfo(
92 - address: subaddress.address, label: subaddress.label, accountIndex: account.id));
91 + addressInfos[account.id]?.add(WalletInfoAddressInfo(
92 + walletInfoId: walletInfo.internalId,
93 + mapKey: account.id,
94 + accountIndex: account.id,
95 + address: subaddress.address,
96 + label: subaddress.label));
97 });
98 });
99
cw_wownero/lib/wownero_wallet_service.dart
+22 -17
@@ -66,9 +66,8 @@ class WowneroWalletService extends WalletService<
66 WowneroRestoreWalletFromSeedCredentials,
67 WowneroRestoreWalletFromKeysCredentials,
68 WowneroNewWalletCredentials> {
69 - WowneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
69 + WowneroWalletService(this.unspentCoinsInfoSource);
70
71 - final Box<WalletInfo> walletInfoSource;
71 final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
72
73 static bool walletFilesExist(String path) =>
@@ -99,7 +98,7 @@ class WowneroWalletService extends WalletService<
98 await wownero_wallet_manager.createWallet(
99 path: path, password: credentials.password!, language: credentials.language, passphrase: credentials.passphrase??'');
100 final wallet = WowneroWallet(
102 - walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!);
101 + walletInfo: credentials.walletInfo!, derivationInfo: await credentials.walletInfo!.getDerivationInfo(), unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!);
102 await wallet.init();
103
104 return wallet;
@@ -133,9 +132,11 @@ class WowneroWalletService extends WalletService<
132 }
133
134 await wownero_wallet_manager.openWalletAsync({'path': path, 'password': password});
136 - final walletInfo = walletInfoSource.values
137 - .firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
138 - wallet = WowneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource, password: password);
135 + final walletInfo = await WalletInfo.get(name, getType());
136 + if (walletInfo == null) {
137 + throw Exception('Wallet not found');
138 + }
139 + wallet = WowneroWallet(walletInfo: walletInfo, derivationInfo: await walletInfo.getDerivationInfo(), unspentCoinsInfo: unspentCoinsInfoSource, password: password);
140 final isValid = wallet.walletAddresses.validate();
141
142 if (!isValid) {
@@ -206,17 +207,20 @@ class WowneroWalletService extends WalletService<
207 await file.delete(recursive: true);
208 }
209
209 - final walletInfo = walletInfoSource.values
210 - .firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
211 - await walletInfoSource.delete(walletInfo.key);
210 + final walletInfo = await WalletInfo.get(wallet, getType());
211 + if (walletInfo == null) {
212 + throw Exception('Wallet not found');
213 + }
214 + await WalletInfo.delete(walletInfo);
215 }
216
217 @override
218 Future<void> rename(String currentName, String password, String newName) async {
216 - final currentWalletInfo = walletInfoSource.values
217 - .firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
218 - final currentWallet =
219 - WowneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource, password: password);
219 + final currentWalletInfo = await WalletInfo.get(currentName, getType());
220 + if (currentWalletInfo == null) {
221 + throw Exception('Wallet not found');
222 + }
223 + final currentWallet = WowneroWallet(walletInfo: currentWalletInfo, derivationInfo: await currentWalletInfo.getDerivationInfo(), unspentCoinsInfo: unspentCoinsInfoSource, password: password);
224
225 await currentWallet.renameWalletFiles(newName);
226
@@ -224,7 +228,7 @@ class WowneroWalletService extends WalletService<
228 newWalletInfo.id = WalletBase.idFor(newName, getType());
229 newWalletInfo.name = newName;
230
227 - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
231 + await newWalletInfo.save();
232 }
233
234 @override
@@ -241,7 +245,7 @@ class WowneroWalletService extends WalletService<
245 viewKey: credentials.viewKey,
246 spendKey: credentials.spendKey);
247 final wallet = WowneroWallet(
244 - walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!);
248 + walletInfo: credentials.walletInfo!, derivationInfo: await credentials.walletInfo!.getDerivationInfo(), unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!);
249 await wallet.init();
250
251 return wallet;
@@ -275,7 +279,7 @@ class WowneroWalletService extends WalletService<
279 seed: credentials.mnemonic,
280 restoreHeight: credentials.height!);
281 final wallet = WowneroWallet(
278 - walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!);
282 + walletInfo: credentials.walletInfo!, derivationInfo: await credentials.walletInfo!.getDerivationInfo(), unspentCoinsInfo: unspentCoinsInfoSource, password: credentials.password!);
283 await wallet.init();
284
285 return wallet;
@@ -321,6 +325,7 @@ class WowneroWalletService extends WalletService<
325
326 final wallet = WowneroWallet(
327 walletInfo: walletInfo,
328 + derivationInfo: await walletInfo.getDerivationInfo(),
329 unspentCoinsInfo: unspentCoinsInfoSource,
330 password: password,
331 );
@@ -350,7 +355,7 @@ class WowneroWalletService extends WalletService<
355 wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
356 wownero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase??'');
357
353 - final wallet = WowneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource, password: password);
358 + final wallet = WowneroWallet(walletInfo: walletInfo, derivationInfo: await walletInfo.getDerivationInfo(), unspentCoinsInfo: unspentCoinsInfoSource, password: password);
359 await wallet.init();
360
361 return wallet;
cw_wownero/pubspec.lock
+64
@@ -722,6 +722,62 @@ packages:
722 url: "https://pub.dev"
723 source: hosted
724 version: "1.10.1"
725 + sqflite:
726 + dependency: transitive
727 + description:
728 + name: sqflite
729 + sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
730 + url: "https://pub.dev"
731 + source: hosted
732 + version: "2.4.1"
733 + sqflite_android:
734 + dependency: transitive
735 + description:
736 + name: sqflite_android
737 + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
738 + url: "https://pub.dev"
739 + source: hosted
740 + version: "2.4.0"
741 + sqflite_common:
742 + dependency: transitive
743 + description:
744 + name: sqflite_common
745 + sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
746 + url: "https://pub.dev"
747 + source: hosted
748 + version: "2.5.4+6"
749 + sqflite_common_ffi:
750 + dependency: transitive
751 + description:
752 + name: sqflite_common_ffi
753 + sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
754 + url: "https://pub.dev"
755 + source: hosted
756 + version: "2.3.4+4"
757 + sqflite_darwin:
758 + dependency: transitive
759 + description:
760 + name: sqflite_darwin
761 + sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
762 + url: "https://pub.dev"
763 + source: hosted
764 + version: "2.4.1+1"
765 + sqflite_platform_interface:
766 + dependency: transitive
767 + description:
768 + name: sqflite_platform_interface
769 + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
770 + url: "https://pub.dev"
771 + source: hosted
772 + version: "2.4.0"
773 + sqlite3:
774 + dependency: transitive
775 + description:
776 + name: sqlite3
777 + sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
778 + url: "https://pub.dev"
779 + source: hosted
780 + version: "2.9.0"
781 stack_trace:
782 dependency: transitive
783 description:
@@ -754,6 +810,14 @@ packages:
810 url: "https://pub.dev"
811 source: hosted
812 version: "1.4.1"
813 + synchronized:
814 + dependency: transitive
815 + description:
816 + name: synchronized
817 + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
818 + url: "https://pub.dev"
819 + source: hosted
820 + version: "3.3.0+3"
821 term_glyph:
822 dependency: transitive
823 description:
cw_zano/lib/zano_wallet.dart
+6 -6
@@ -105,14 +105,14 @@ abstract class ZanoWalletBase
105 /// number of transactions in each request
106 static final int _txChunkSize = (pow(2, 32) - 1).toInt();
107
108 - ZanoWalletBase(WalletInfo walletInfo, String password)
108 + ZanoWalletBase(WalletInfo walletInfo, DerivationInfo derivationInfo, String password)
109 : balance = ObservableMap.of({CryptoCurrency.zano: ZanoBalance.empty()}),
110 _isTransactionUpdating = false,
111 _hasSyncAfterStartup = false,
112 walletAddresses = ZanoWalletAddresses(walletInfo),
113 syncStatus = NotConnectedSyncStatus(),
114 _password = password,
115 - super(walletInfo) {
115 + super(walletInfo, derivationInfo) {
116 transactionHistory = ZanoTransactionHistory();
117 if (!CakeHive.isAdapterRegistered(ZanoAsset.typeId)) {
118 CakeHive.registerAdapter(ZanoAssetAdapter());
@@ -129,7 +129,7 @@ abstract class ZanoWalletBase
129 }
130
131 static Future<ZanoWallet> create({required WalletCredentials credentials}) async {
132 - final wallet = ZanoWallet(credentials.walletInfo!, credentials.password!);
132 + final wallet = ZanoWallet(credentials.walletInfo!, await credentials.walletInfo!.getDerivationInfo(), credentials.password!);
133 await wallet.initWallet();
134 final path = await pathForWallet(name: credentials.name, type: credentials.walletInfo!.type);
135 final createWalletResult = await wallet.createWallet(path, credentials.password!);
@@ -146,7 +146,7 @@ abstract class ZanoWalletBase
146
147 static Future<ZanoWallet> restore(
148 {required ZanoRestoreWalletFromSeedCredentials credentials}) async {
149 - final wallet = ZanoWallet(credentials.walletInfo!, credentials.password!);
149 + final wallet = ZanoWallet(credentials.walletInfo!, await credentials.walletInfo!.getDerivationInfo(), credentials.password!);
150 await wallet.initWallet();
151 final path = await pathForWallet(name: credentials.name, type: credentials.walletInfo!.type);
152 final createWalletResult = await wallet.restoreWalletFromSeed(
@@ -166,13 +166,13 @@ abstract class ZanoWalletBase
166 {required String name, required String password, required WalletInfo walletInfo}) async {
167 final path = await pathForWallet(name: name, type: walletInfo.type);
168 if (ZanoWalletApi.openWalletCache[path] != null) {
169 - final wallet = ZanoWallet(walletInfo, password);
169 + final wallet = ZanoWallet(walletInfo, await walletInfo.getDerivationInfo(), password);
170 await wallet.parseCreateWalletResult(ZanoWalletApi.openWalletCache[path]!).then((_) {
171 unawaited(wallet.init(ZanoWalletApi.openWalletCache[path]!.wi.address));
172 });
173 return wallet;
174 } else {
175 - final wallet = ZanoWallet(walletInfo, password);
175 + final wallet = ZanoWallet(walletInfo, await walletInfo.getDerivationInfo(), password);
176 await wallet.initWallet();
177 final createWalletResult = await wallet.loadWallet(path, password);
178 await wallet.parseCreateWalletResult(createWalletResult).then((_) {
cw_zano/lib/zano_wallet_service.dart
+16 -9
@@ -43,9 +43,7 @@ class ZanoRestoreWalletFromKeysCredentials extends WalletCredentials {
43
44 class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
45 ZanoRestoreWalletFromSeedCredentials, ZanoRestoreWalletFromKeysCredentials, ZanoNewWalletCredentials> {
46 - ZanoWalletService(this.walletInfoSource);
47 -
48 - final Box<WalletInfo> walletInfoSource;
46 + ZanoWalletService();
47
48 static bool walletFilesExist(String path) => !File(path).existsSync() && !File('$path.keys').existsSync();
49
@@ -68,7 +66,10 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
66
67 @override
68 Future<ZanoWallet> openWallet(String name, String password) async {
71 - final walletInfo = walletInfoSource.values.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
69 + final walletInfo = await WalletInfo.get(name, getType());
70 + if (walletInfo == null) {
71 + throw Exception('Wallet not found');
72 + }
73 try {
74 final wallet = await ZanoWalletBase.open(name: name, password: password, walletInfo: walletInfo);
75 saveBackup(name);
@@ -89,14 +90,20 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
90 await file.delete(recursive: true);
91 }
92
92 - final walletInfo = walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
93 - await walletInfoSource.delete(walletInfo.key);
93 + final walletInfo = await WalletInfo.get(wallet, getType());
94 + if (walletInfo == null) {
95 + throw Exception('Wallet not found');
96 + }
97 + await WalletInfo.delete(walletInfo);
98 }
99
100 @override
101 Future<void> rename(String currentName, String password, String newName) async {
98 - final currentWalletInfo = walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
99 - final currentWallet = ZanoWallet(currentWalletInfo, password);
102 + final currentWalletInfo = await WalletInfo.get(currentName, getType());
103 + if (currentWalletInfo == null) {
104 + throw Exception('Wallet not found');
105 + }
106 + final currentWallet = ZanoWallet(currentWalletInfo, await currentWalletInfo.getDerivationInfo(), password);
107
108 await currentWallet.renameWalletFiles(newName);
109
@@ -104,7 +111,7 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
111 newWalletInfo.id = WalletBase.idFor(newName, getType());
112 newWalletInfo.name = newName;
113
107 - await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
114 + await newWalletInfo.save();
115 }
116
117 @override
cw_zano/pubspec.lock
+64
@@ -719,6 +719,62 @@ packages:
719 url: "https://pub.dev"
720 source: hosted
721 version: "1.10.1"
722 + sqflite:
723 + dependency: transitive
724 + description:
725 + name: sqflite
726 + sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb"
727 + url: "https://pub.dev"
728 + source: hosted
729 + version: "2.4.1"
730 + sqflite_android:
731 + dependency: transitive
732 + description:
733 + name: sqflite_android
734 + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
735 + url: "https://pub.dev"
736 + source: hosted
737 + version: "2.4.0"
738 + sqflite_common:
739 + dependency: transitive
740 + description:
741 + name: sqflite_common
742 + sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709"
743 + url: "https://pub.dev"
744 + source: hosted
745 + version: "2.5.4+6"
746 + sqflite_common_ffi:
747 + dependency: transitive
748 + description:
749 + name: sqflite_common_ffi
750 + sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36"
751 + url: "https://pub.dev"
752 + source: hosted
753 + version: "2.3.4+4"
754 + sqflite_darwin:
755 + dependency: transitive
756 + description:
757 + name: sqflite_darwin
758 + sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c"
759 + url: "https://pub.dev"
760 + source: hosted
761 + version: "2.4.1+1"
762 + sqflite_platform_interface:
763 + dependency: transitive
764 + description:
765 + name: sqflite_platform_interface
766 + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
767 + url: "https://pub.dev"
768 + source: hosted
769 + version: "2.4.0"
770 + sqlite3:
771 + dependency: transitive
772 + description:
773 + name: sqlite3
774 + sha256: f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924
775 + url: "https://pub.dev"
776 + source: hosted
777 + version: "2.9.0"
778 stack_trace:
779 dependency: transitive
780 description:
@@ -751,6 +807,14 @@ packages:
807 url: "https://pub.dev"
808 source: hosted
809 version: "1.4.1"
810 + synchronized:
811 + dependency: transitive
812 + description:
813 + name: synchronized
814 + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
815 + url: "https://pub.dev"
816 + source: hosted
817 + version: "3.3.0+3"
818 term_glyph:
819 dependency: transitive
820 description:
lib/base/cw_base.dart
+6 -5
@@ -4,8 +4,8 @@ class CWBase extends Base {
4 @override
5 List<String> getBaseWordList(String language) => EVMChainMnemonics.englishWordlist;
6
7 - WalletService createBaseWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
8 - BaseWalletService(walletInfoSource, isDirect, client: BaseClient());
7 + WalletService createBaseWalletService(bool isDirect) =>
8 + BaseWalletService(isDirect, client: BaseClient());
9
10 @override
11 WalletCredentials createBaseNewWalletCredentials({
@@ -216,14 +216,15 @@ class CWBase extends Base {
216
217
218 @override
219 - void setHardwareWalletService(WalletBase wallet, HardwareWalletService service) {
219 + Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service) async {
220 if (service is EVMChainLedgerService) {
221 ((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials).setLedgerConnection(
222 - service.ledgerConnection, wallet.walletInfo.derivationInfo?.derivationPath);
222 + service.ledgerConnection, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
223 } else if (service is EVMChainBitboxService) {
224 ((wallet as EVMChainWallet).evmChainPrivateKey as EvmBitboxCredentials)
225 - .setBitbox(service.manager, wallet.walletInfo.derivationInfo?.derivationPath);
225 + .setBitbox(service.manager, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
226 }
227 + return Future.value();
228 }
229
230 @override
lib/bitcoin/cw_bitcoin.dart
+4 -7
@@ -260,17 +260,14 @@ class CWBitcoin extends Bitcoin {
260 }
261
262 WalletService createBitcoinWalletService(
263 - Box<WalletInfo> walletInfoSource,
263 Box<UnspentCoinsInfo> unspentCoinSource,
264 Box<PayjoinSession> payjoinSessionSource,
265 bool isDirect) {
267 - return BitcoinWalletService(
268 - walletInfoSource, unspentCoinSource, payjoinSessionSource, isDirect);
266 + return BitcoinWalletService(unspentCoinSource, payjoinSessionSource, isDirect);
267 }
268
271 - WalletService createLitecoinWalletService(
272 - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
273 - return LitecoinWalletService(walletInfoSource, unspentCoinSource, isDirect);
269 + WalletService createLitecoinWalletService(Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
270 + return LitecoinWalletService(unspentCoinSource, isDirect);
271 }
272
273 @override
@@ -537,7 +534,7 @@ class CWBitcoin extends Bitcoin {
534 }
535
536 @override
540 - void setHardwareWalletService(WalletBase wallet, HardwareWalletService service) {
537 + Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service) async {
538 (wallet as ElectrumWallet).hardwareWalletService = service;
539 }
540
lib/bitcoin_cash/cw_bitcoin_cash.dart
+2 -3
@@ -5,9 +5,8 @@ class CWBitcoinCash extends BitcoinCash {
5 String getCashAddrFormat(String address) => AddressUtils.getCashAddrFormat(address);
6
7 @override
8 - WalletService createBitcoinCashWalletService(
9 - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
10 - return BitcoinCashWalletService(walletInfoSource, unspentCoinSource, isDirect);
8 + WalletService createBitcoinCashWalletService(Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
9 + return BitcoinCashWalletService(unspentCoinSource, isDirect);
10 }
11
12 @override
lib/core/backup_service.dart
+13 -23
@@ -24,7 +24,7 @@ import 'package:cake_wallet/wallet_types.g.dart';
24 import 'package:cake_backup/backup.dart' as cake_backup;
25
26 class $BackupService {
27 - $BackupService(this._secureStorage, this.walletInfoSource, this.transactionDescriptionBox,
27 + $BackupService(this._secureStorage, this.transactionDescriptionBox,
28 this.keyService, this.sharedPreferences)
29 : cipher = Cryptography.instance.chacha20Poly1305Aead(),
30 correctWallets = <WalletInfo>[];
@@ -37,7 +37,6 @@ class $BackupService {
37 final Cipher cipher;
38 final SecureStorage _secureStorage;
39 final SharedPreferences sharedPreferences;
40 - final Box<WalletInfo> walletInfoSource;
40 final Box<TransactionDescription> transactionDescriptionBox;
41 final KeyService keyService;
42 List<WalletInfo> correctWallets;
@@ -110,27 +109,14 @@ class $BackupService {
109 }
110
111 Future<void> verifyWallets() async {
113 - final walletInfoSource = await reloadHiveWalletInfoBox();
114 - correctWallets =
115 - walletInfoSource.values.where((info) => availableWalletTypes.contains(info.type)).toList();
112 + await performHiveMigration(); // for backups made before sqlite migration
113 + correctWallets = (await WalletInfo.getAll()).where((info) => availableWalletTypes.contains(info.type)).toList();
114
115 if (correctWallets.isEmpty) {
118 - throw Exception('Correct wallets not detected');
116 + printV('Correct wallets not detected');
117 }
118 }
119
122 - Future<Box<WalletInfo>> reloadHiveWalletInfoBox() async {
123 - final appDir = await getAppDir();
124 - await CakeHive.close();
125 - CakeHive.init(appDir.path);
126 -
127 - if (!CakeHive.isAdapterRegistered(WalletInfo.typeId)) {
128 - CakeHive.registerAdapter(WalletInfoAdapter());
129 - }
130 -
131 - return await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
132 - }
133 -
120 Future<void> importTransactionDescriptionDump() async {
121 final appDir = await getAppDir();
122 final transactionDescriptionFile = File('${appDir.path}/~_transaction_descriptions_dump');
@@ -191,12 +177,16 @@ class $BackupService {
177 String currentWalletName = data[PreferencesKey.currentWalletName] as String;
178 int currentWalletType = data[PreferencesKey.currentWalletType] as int;
179
194 - final isCorrentCurrentWallet = correctWallets
180 + final isCorrectCurrentWallet = correctWallets
181 .any((info) => info.name == currentWalletName && info.type.index == currentWalletType);
182
197 - if (!isCorrentCurrentWallet) {
198 - currentWalletName = correctWallets.first.name;
199 - currentWalletType = serializeToInt(correctWallets.first.type);
183 + try {
184 + if (!isCorrectCurrentWallet) {
185 + currentWalletName = correctWallets.first.name;
186 + currentWalletType = serializeToInt(correctWallets.first.type);
187 + }
188 + } catch (e) {
189 +
190 }
191
192 if (DeviceInfo.instance.isDesktop) {
@@ -283,7 +273,7 @@ class $BackupService {
273 Future<Uint8List> exportKeychainDumpV2(String password,
274 {String keychainSalt = secrets.backupKeychainSalt}) async {
275 final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
286 - final wallets = await Future.wait(walletInfoSource.values.map((walletInfo) async {
276 + final wallets = await Future.wait((await WalletInfo.getAll()).map((walletInfo) async {
277 try {
278 return {
279 'name': walletInfo.name,
lib/core/backup_service_v3.dart
+3 -6
@@ -11,6 +11,7 @@ import 'package:crypto/crypto.dart';
11 import 'package:cw_core/root_dir.dart';
12 import 'package:cw_core/utils/print_verbose.dart';
13 import 'package:cw_core/wallet_info.dart';
14 +import 'package:cw_core/wallet_type.dart';
15 import 'package:flutter/foundation.dart';
16
17 enum BackupVersion {
@@ -143,7 +144,7 @@ class BackupMetadata {
144 }
145
146 class BackupServiceV3 extends $BackupService {
146 - BackupServiceV3(super.secureStorage, super.walletInfoSource, super.transactionDescriptionBox, super.keyService, super.sharedPreferences);
147 + BackupServiceV3(super.secureStorage, super.transactionDescriptionBox, super.keyService, super.sharedPreferences);
148
149 static BackupVersion get currentVersion => BackupVersion.v3;
150
@@ -317,7 +318,6 @@ class BackupServiceV3 extends $BackupService {
318
319 Future<void> verifyHardwareWallets(String password,
320 {String keychainSalt = secrets.backupKeychainSalt}) async {
320 - final walletInfoSource = await reloadHiveWalletInfoBox();
321 final appDir = await getAppDir();
322 final keychainDumpFile = File('${appDir.path}/~_keychain_dump');
323 final decryptedKeychainDumpFileData = await decryptV2(
@@ -334,10 +334,7 @@ class BackupServiceV3 extends $BackupService {
334
335 for (final expectedHardwareWallet in expectedHardwareWallets) {
336 final info = expectedHardwareWallet as Map<String, dynamic>;
337 - final actualWalletInfo = walletInfoSource.values
338 - .where((e) =>
339 - e.name == info['name'] && e.type.toString() == info['type'])
340 - .firstOrNull;
337 + final actualWalletInfo = await WalletInfo.get(info['name'] as String, WalletType.values.firstWhere((e) => e.toString() == info['type'] as String));
338 if (actualWalletInfo != null &&
339 info["hardwareWalletType"] !=
340 actualWalletInfo.hardwareWalletType?.index) {
lib/core/wallet_creation_service.dart
+9 -11
@@ -16,8 +16,7 @@ class WalletCreationService {
16 {required WalletType initialType,
17 required this.keyService,
18 required this.sharedPreferences,
19 - required this.settingsStore,
20 - required this.walletInfoSource})
19 + required this.settingsStore})
20 : type = initialType {
21 changeWalletType(type: type);
22 }
@@ -26,7 +25,6 @@ class WalletCreationService {
25 final SharedPreferences sharedPreferences;
26 final SettingsStore settingsStore;
27 final KeyService keyService;
29 - final Box<WalletInfo> walletInfoSource;
28 WalletService? _service;
29
30 static const _isNewMoneroWalletPasswordUpdated = true;
@@ -36,23 +34,23 @@ class WalletCreationService {
34 _service = getIt.get<WalletService>(param1: type);
35 }
36
39 - bool exists(String name) {
37 + Future<bool> exists(String name) async {
38 final walletName = name.toLowerCase();
41 - return walletInfoSource.values.any((walletInfo) => walletInfo.name.toLowerCase() == walletName);
39 + return (await WalletInfo.getAll()).any((walletInfo) => walletInfo.name.toLowerCase() == walletName);
40 }
41
44 - bool typeExists(WalletType type) {
45 - return walletInfoSource.values.any((walletInfo) => walletInfo.type == type);
42 + Future<bool> typeExists(WalletType type) async {
43 + return (await WalletInfo.getAll()).any((walletInfo) => walletInfo.type == type);
44 }
45
48 - void checkIfExists(String name) {
49 - if (exists(name)) {
46 + Future<void> checkIfExists(String name) async {
47 + if (await exists(name)) {
48 throw Exception('Wallet with name ${name} already exists!');
49 }
50 }
51
52 Future<WalletBase> create(WalletCredentials credentials, {bool? isTestnet}) async {
55 - checkIfExists(credentials.name);
53 + await checkIfExists(credentials.name);
54
55 if (credentials.password == null) {
56 credentials.password = generateWalletPassword();
@@ -84,12 +82,12 @@ class WalletCreationService {
82 case WalletType.solana:
83 case WalletType.tron:
84 case WalletType.dogecoin:
85 + case WalletType.nano:
86 return true;
87 case WalletType.monero:
88 case WalletType.wownero:
89 case WalletType.none:
90 case WalletType.haven:
92 - case WalletType.nano:
91 case WalletType.banano:
92 case WalletType.zano:
93 case WalletType.decred:
lib/core/wallet_loading_service.dart
+4 -5
@@ -74,7 +74,7 @@ class WalletLoadingService {
74 } catch (error, stack) {
75 await ExceptionHandler.resetLastPopupDate();
76 final isLedgerError = await ExceptionHandler.isLedgerError(error);
77 - if (isLedgerError || requireHardwareWalletConnection(type, name)) rethrow;
77 + if (isLedgerError || await requireHardwareWalletConnection(type, name)) rethrow;
78 await ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack));
79
80
@@ -87,9 +87,8 @@ class WalletLoadingService {
87 }
88
89 // try opening another wallet that is not corrupted to give user access to the app
90 - final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
90 WalletBase? wallet;
92 - for (var walletInfo in walletInfoSource.values) {
91 + for (var walletInfo in await WalletInfo.getAll()) {
92 try {
93 final walletService = walletServiceFactory.call(walletInfo.type);
94 final walletPassword = await keyService.getWalletPassword(walletName: walletInfo.name);
@@ -195,8 +194,8 @@ class WalletLoadingService {
194 return "\n\n$type ($name): ${await walletService.getSeeds(name, password, type)}";
195 }
196
198 - bool requireHardwareWalletConnection(WalletType type, String name) {
197 + Future<bool> requireHardwareWalletConnection(WalletType type, String name) async {
198 final walletService = walletServiceFactory.call(type);
200 - return walletService.requireHardwareWalletConnection(name);
199 + return await walletService.requireHardwareWalletConnection(name);
200 }
201 }
lib/decred/cw_decred.dart
+3 -4
@@ -19,9 +19,8 @@ class CWDecred extends Decred {
19 DecredRestoreWalletFromPubkeyCredentials(name: name, pubkey: pubkey, password: password);
20
21 @override
22 - WalletService createDecredWalletService(
23 - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
24 - return DecredWalletService(walletInfoSource, unspentCoinSource);
22 + WalletService createDecredWalletService(Box<UnspentCoinsInfo> unspentCoinSource) {
23 + return DecredWalletService(unspentCoinSource);
24 }
25
26 @override
@@ -53,7 +52,7 @@ class CWDecred extends Decred {
52 .toList(),
53 priority: priority as DecredTransactionPriority);
54
56 - List<AddressInfo> getAddressInfos(Object wallet) {
55 + List<WalletInfoAddressInfo> getAddressInfos(Object wallet) {
56 final decredWallet = wallet as DecredWallet;
57 return decredWallet.walletAddresses.getAddressInfos();
58 }
lib/di.dart
+24 -43
@@ -2,7 +2,6 @@ import 'dart:async' show Timer;
2
3 import 'package:cake_wallet/.secrets.g.dart' as secrets;
4 import 'package:cake_wallet/anonpay/anonpay_api.dart';
5 -import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
5 import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
6 import 'package:cake_wallet/anypay/anypay_api.dart';
7 import 'package:cake_wallet/base/base.dart';
@@ -301,7 +300,6 @@ import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
300 final getIt = GetIt.instance;
301
302 var _isSetupFinished = false;
304 -late Box<WalletInfo> _walletInfoSource;
303 late Box<Node> _nodeSource;
304 late Box<Node> _powNodeSource;
305 late Box<Contact> _contactSource;
@@ -315,7 +313,6 @@ late Box<PayjoinSession> _payjoinSessionSource;
313 late Box<AnonpayInvoiceInfo> _anonpayInvoiceInfoSource;
314
315 Future<void> setup({
318 - required Box<WalletInfo> walletInfoSource,
316 required Box<Node> nodeSource,
317 required Box<Node> powNodeSource,
318 required Box<Contact> contactSource,
@@ -330,7 +327,6 @@ Future<void> setup({
327 required SecureStorage secureStorage,
328 required GlobalKey<NavigatorState> navigatorKey,
329 }) async {
333 - _walletInfoSource = walletInfoSource;
330 _nodeSource = nodeSource;
331 _powNodeSource = powNodeSource;
332 _contactSource = contactSource;
@@ -432,7 +428,7 @@ Future<void> setup({
428 keyService: getIt.get<KeyService>(),
429 sharedPreferences: getIt.get<SharedPreferences>(),
430 settingsStore: getIt.get<SettingsStore>(),
435 - walletInfoSource: _walletInfoSource));
431 + ));
432
433 getIt.registerFactoryParam<AdvancedPrivacySettingsViewModel, WalletType, void>(
434 (type, _) => AdvancedPrivacySettingsViewModel(type, getIt.get<SettingsStore>()));
@@ -446,17 +442,17 @@ Future<void> setup({
442 (newWalletArgs, _) => WalletNewVM(
443 getIt.get<AppStore>(),
444 getIt.get<WalletCreationService>(param1:newWalletArgs.type),
449 - _walletInfoSource,
445 getIt.get<AdvancedPrivacySettingsViewModel>(param1: newWalletArgs.type),
446 getIt.get<SeedSettingsViewModel>(),
452 - newWalletArguments: newWalletArgs,));
447 + newWalletArguments: newWalletArgs,
448 + ));
449
450
455 - getIt.registerFactory<NewWalletTypeViewModel>(() => NewWalletTypeViewModel(_walletInfoSource));
451 + final walletList = await WalletInfo.getAll();
452 + getIt.registerFactory<NewWalletTypeViewModel>(() => NewWalletTypeViewModel(walletList.isNotEmpty));
453
454 getIt.registerFactory<WalletManager>(
455 () => WalletManager(
459 - _walletInfoSource,
456 getIt.get<SharedPreferences>(),
457 ),
458 );
@@ -535,7 +531,6 @@ Future<void> setup({
531 hardwareWalletVM,
532 getIt.get<AppStore>(),
533 getIt.get<WalletCreationService>(param1: type),
538 - _walletInfoSource,
534 getIt.get<SeedSettingsViewModel>(),
535 type: type));
536
@@ -560,7 +555,6 @@ Future<void> setup({
555 getIt.get<ContactListViewModel>(),
556 getIt.get<UnspentCoinsListViewModel>(),
557 getIt.get<FeesViewModel>(),
563 - _walletInfoSource,
558 getIt.get<FiatConversionStore>(),
559 ),
560 );
@@ -639,7 +633,7 @@ Future<void> setup({
633
634 getIt.registerFactory<AuthPage>(instanceName: 'login', () {
635 return AuthPage(getIt.get<AuthViewModel>(), closable: false,
642 - onAuthenticationFinished: (isAuthenticated, AuthPageState authPageState) {
636 + onAuthenticationFinished: (isAuthenticated, AuthPageState authPageState) async {
637 if (!isAuthenticated) {
638 return;
639 }
@@ -680,7 +674,7 @@ Future<void> setup({
674 );
675 } else {
676 // wallet is already loaded:
683 - if (appStore.wallet != null || requireHardwareWalletConnection()) {
677 + if (appStore.wallet != null || await requireHardwareWalletConnection()) {
678 // goes to the dashboard:
679 authStore.allowed();
680 // trigger any deep links:
@@ -847,8 +841,7 @@ Future<void> setup({
841 : null,
842 coinTypeToSpendFrom: coinTypeToSpendFrom ?? UnspentCoinType.nonMweb,
843 getIt.get<UnspentCoinsListViewModel>(param1: coinTypeToSpendFrom),
850 - getIt.get<FeesViewModel>(),
851 - _walletInfoSource,
844 + getIt.get<FeesViewModel>()
845 ),
846 );
847
@@ -867,7 +860,6 @@ Future<void> setup({
860 if (DeviceInfo.instance.isMobile) {
861 getIt.registerFactory(
862 () => WalletListViewModel(
870 - _walletInfoSource,
863 getIt.get<AppStore>(),
864 getIt.get<WalletLoadingService>(),
865 getIt.get<WalletManager>(),
@@ -878,7 +870,6 @@ Future<void> setup({
870 // from multiple places at the same time (Wallets DropDown, Wallets List in settings)
871 getIt.registerLazySingleton(
872 () => WalletListViewModel(
881 - _walletInfoSource,
873 getIt.get<AppStore>(),
874 getIt.get<WalletLoadingService>(),
875 getIt.get<WalletManager>(),
@@ -890,7 +881,7 @@ Future<void> setup({
881 (Function(BuildContext)? onWalletLoaded, _) => WalletListPage(
882 walletListViewModel: getIt.get<WalletListViewModel>(),
883 authService: getIt.get<AuthService>(),
893 - onWalletLoaded: onWalletLoaded,
884 + onWalletLoaded: onWalletLoaded as Future<void> Function(BuildContext)?,
885 ));
886
887 getIt.registerFactoryParam<WalletEditViewModel, WalletListViewModel, void>(
@@ -1034,7 +1025,7 @@ Future<void> setup({
1025
1026 getIt.registerFactoryParam<ContactListViewModel, CryptoCurrency?, void>(
1027 (CryptoCurrency? cur, _) =>
1037 - ContactListViewModel(_contactSource, _walletInfoSource, cur, getIt.get<SettingsStore>()));
1028 + ContactListViewModel(_contactSource, walletList, cur, getIt.get<SettingsStore>()));
1029
1030 getIt.registerFactoryParam<ContactListPage, CryptoCurrency?, void>((CryptoCurrency? cur, _) =>
1031 ContactListPage(getIt.get<ContactListViewModel>(param1: cur), getIt.get<AuthService>()));
@@ -1190,62 +1181,53 @@ Future<void> setup({
1181
1182 getIt.registerFactory(() => PaymentViewModel(
1183 appStore: getIt.get<AppStore>(),
1193 - walletInfoSource: _walletInfoSource,
1184 ));
1185
1186 getIt.registerFactory(() => WalletSwitcherViewModel(
1187 appStore: getIt.get<AppStore>(),
1188 walletLoadingService: getIt.get<WalletLoadingService>(),
1199 - walletInfoSource: _walletInfoSource,
1189 ));
1190
1191 getIt.registerFactoryParam<WalletService, WalletType, void>((WalletType param1, __) {
1192 switch (param1) {
1193 case WalletType.monero:
1205 - return monero!.createMoneroWalletService(_walletInfoSource, _unspentCoinsInfoSource);
1194 + return monero!.createMoneroWalletService(_unspentCoinsInfoSource);
1195 case WalletType.bitcoin:
1196 return bitcoin!.createBitcoinWalletService(
1208 - _walletInfoSource,
1197 _unspentCoinsInfoSource,
1198 _payjoinSessionSource,
1199 SettingsStoreBase.walletPasswordDirectInput,
1200 );
1201 case WalletType.litecoin:
1202 return bitcoin!.createLitecoinWalletService(
1215 - _walletInfoSource,
1203 _unspentCoinsInfoSource,
1204 SettingsStoreBase.walletPasswordDirectInput,
1205 );
1206 case WalletType.ethereum:
1220 - return ethereum!.createEthereumWalletService(
1221 - _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
1207 + return ethereum!.createEthereumWalletService(SettingsStoreBase.walletPasswordDirectInput);
1208 case WalletType.bitcoinCash:
1223 - return bitcoinCash!.createBitcoinCashWalletService(_walletInfoSource,
1224 - _unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput);
1209 + return bitcoinCash!.createBitcoinCashWalletService(_unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput);
1210 case WalletType.dogecoin:
1226 - return dogecoin!.createDogeCoinWalletService(_walletInfoSource,
1227 - _unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput);
1211 + return dogecoin!.createDogeCoinWalletService(_unspentCoinsInfoSource, SettingsStoreBase.walletPasswordDirectInput);
1212 case WalletType.nano:
1213 case WalletType.banano:
1230 - return nano!.createNanoWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
1214 + return nano!.createNanoWalletService(SettingsStoreBase.walletPasswordDirectInput);
1215 case WalletType.polygon:
1232 - return polygon!.createPolygonWalletService(
1233 - _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
1216 + return polygon!.createPolygonWalletService(SettingsStoreBase.walletPasswordDirectInput);
1217 case WalletType.solana:
1235 - return solana!.createSolanaWalletService(
1236 - _walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
1218 + return solana!.createSolanaWalletService(SettingsStoreBase.walletPasswordDirectInput);
1219 case WalletType.tron:
1238 - return tron!.createTronWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
1220 + return tron!.createTronWalletService(SettingsStoreBase.walletPasswordDirectInput);
1221 case WalletType.wownero:
1240 - return wownero!.createWowneroWalletService(_walletInfoSource, _unspentCoinsInfoSource);
1222 + return wownero!.createWowneroWalletService(_unspentCoinsInfoSource);
1223 case WalletType.zano:
1242 - return zano!.createZanoWalletService(_walletInfoSource);
1224 + return zano!.createZanoWalletService();
1225 case WalletType.decred:
1244 - return decred!.createDecredWalletService(_walletInfoSource, _unspentCoinsInfoSource);
1226 + return decred!.createDecredWalletService(_unspentCoinsInfoSource);
1227 case WalletType.base:
1246 - return base!.createBaseWalletService(_walletInfoSource, SettingsStoreBase.walletPasswordDirectInput);
1228 + return base!.createBaseWalletService(SettingsStoreBase.walletPasswordDirectInput);
1229 case WalletType.haven:
1248 - return HavenWalletService(_walletInfoSource);
1230 + return HavenWalletService();
1231 case WalletType.none:
1232 throw Exception('Unexpected token: ${param1.toString()} for generating of WalletService');
1233 }
@@ -1275,7 +1257,6 @@ Future<void> setup({
1257 return WalletRestoreViewModel(
1258 getIt.get<AppStore>(),
1259 getIt.get<WalletCreationService>(param1: type),
1278 - _walletInfoSource,
1260 getIt.get<SeedSettingsViewModel>(),
1261 type: type,
1262 restoredWallet: restoredWallet,
@@ -1351,7 +1332,7 @@ Future<void> setup({
1332
1333 getIt.registerFactory(() => CakeFeaturesViewModel(getIt.get<CakePayService>()));
1334
1354 - getIt.registerFactory(() => BackupServiceV3(getIt.get<SecureStorage>(), _walletInfoSource,
1335 + getIt.registerFactory(() => BackupServiceV3(getIt.get<SecureStorage>(),
1336 _transactionDescriptionBox,
1337 getIt.get<KeyService>(), getIt.get<SharedPreferences>()));
1338
lib/dogecoin/cw_dogecoin.dart
+2 -3
@@ -4,9 +4,8 @@ part of 'dogecoin.dart';
4 class CWDogeCoin extends DogeCoin {
5
6 @override
7 - WalletService createDogeCoinWalletService(
8 - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
9 - return DogeCoinWalletService(walletInfoSource, unspentCoinSource, isDirect);
7 + WalletService createDogeCoinWalletService(Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
8 + return DogeCoinWalletService(unspentCoinSource, isDirect);
9 }
10
11 @override
lib/entities/default_settings_migration.dart
+32 -29
@@ -5,6 +5,7 @@ import 'package:cake_wallet/core/secure_storage.dart';
5 import 'package:cake_wallet/entities/exchange_api_mode.dart';
6 import 'package:cake_wallet/entities/fiat_api_mode.dart';
7 import 'package:cake_wallet/entities/haven_seed_store.dart';
8 +import 'package:cw_core/cake_hive.dart';
9 import 'package:cw_core/pathForWallet.dart';
10 import 'package:cake_wallet/entities/secret_store_key.dart';
11 import 'package:cw_core/root_dir.dart';
@@ -21,6 +22,7 @@ import 'package:cake_wallet/monero/monero.dart';
22 import 'package:cake_wallet/entities/contact.dart';
23 import 'package:cake_wallet/entities/fs_migration.dart';
24 import 'package:cw_core/wallet_info.dart';
25 +import 'package:cw_core/wallet_info_legacy.dart' as wiLegacy;
26 import 'package:cake_wallet/exchange/trade.dart';
27 import 'package:encrypt/encrypt.dart' as encrypt;
28 import 'package:collection/collection.dart';
@@ -55,12 +57,11 @@ Future<void> defaultSettingsMigration(
57 required SecureStorage secureStorage,
58 required Box<Node> nodes,
59 required Box<Node> powNodes,
58 - required Box<WalletInfo> walletInfoSource,
60 required Box<Trade> tradeSource,
61 required Box<Contact> contactSource,
62 required Box<HavenSeedStore> havenSeedStore}) async {
63 if (Platform.isIOS) {
63 - await ios_migrate_v1(walletInfoSource, tradeSource, contactSource);
64 + await ios_migrate_v1(tradeSource, contactSource);
65 }
66
67 // check current nodes for nullability regardless of the version
@@ -69,7 +70,7 @@ Future<void> defaultSettingsMigration(
70 final isNewInstall =
71 sharedPreferences.getInt(PreferencesKey.currentDefaultSettingsMigrationVersion) == null;
72
72 - await _validateWalletInfoBoxData(walletInfoSource);
73 + await _validateWalletInfoBoxData();
74
75 await sharedPreferences.setBool(PreferencesKey.isNewInstall, isNewInstall);
76
@@ -162,7 +163,7 @@ Future<void> defaultSettingsMigration(
163 break;
164
165 case 5:
165 - await addAddressesForMoneroWallets(walletInfoSource);
166 + await addAddressesForMoneroWallets();
167 break;
168
169 case 6:
@@ -323,7 +324,7 @@ Future<void> defaultSettingsMigration(
324 await updateNanoNodeList(nodes: nodes);
325 break;
326 case 32:
326 - await updateBtcNanoWalletInfos(walletInfoSource);
327 + await updateBtcNanoWalletInfos();
328 break;
329 case 33:
330 await addWalletNodeList(nodes: nodes, type: WalletType.tron);
@@ -361,7 +362,7 @@ Future<void> defaultSettingsMigration(
362 // await replaceTronDefaultNode(sharedPreferences: sharedPreferences, nodes: nodes);
363 break;
364 case 38:
364 - await fixBtcDerivationPaths(walletInfoSource);
365 + await fixBtcDerivationPaths();
366 break;
367 case 39:
368 _fixNodesUseSSLFlag(nodes);
@@ -738,7 +739,7 @@ Future<void> _updateMoneroPriority(SharedPreferences sharedPreferences) async {
739 }
740 }
741
741 -Future<void> _validateWalletInfoBoxData(Box<WalletInfo> walletInfoSource) async {
742 +Future<void> _validateWalletInfoBoxData() async {
743 try {
744 final root = await getAppDir();
745
@@ -780,7 +781,7 @@ Future<void> _validateWalletInfoBoxData(Box<WalletInfo> walletInfoSource) async
781 }
782
783 final id = prefix + '_' + name;
783 - final exist = walletInfoSource.values.any((el) => el.id == id);
784 + final exist = (await WalletInfo.getAll()).any((el) => el.id == id);
785
786 if (exist) {
787 continue;
@@ -799,7 +800,7 @@ Future<void> _validateWalletInfoBoxData(Box<WalletInfo> walletInfoSource) async
800 showIntroCakePayCard: false,
801 );
802
802 - walletInfoSource.add(walletInfo);
803 + await walletInfo.save();
804 }
805 }
806 } catch (_) {}
@@ -967,8 +968,8 @@ Future<void> updateNodeTypes({required Box<Node> nodes}) async {
968 });
969 }
970
970 -Future<void> addAddressesForMoneroWallets(Box<WalletInfo> walletInfoSource) async {
971 - final moneroWalletsInfo = walletInfoSource.values.where((info) => info.type == WalletType.monero);
971 +Future<void> addAddressesForMoneroWallets() async {
972 + final moneroWalletsInfo = (await WalletInfo.getAll()).where((info) => info.type == WalletType.monero);
973 moneroWalletsInfo.forEach((info) async {
974 try {
975 final walletPath = await pathForWallet(name: info.name, type: WalletType.monero);
@@ -1016,32 +1017,34 @@ Future<void> changeTransactionPriorityAndFeeRateKeys(SharedPreferences sharedPre
1017 bitcoin!.getMediumTransactionPriority().serialize());
1018 }
1019
1019 -Future<void> fixBtcDerivationPaths(Box<WalletInfo> walletsInfoSource) async {
1020 - for (WalletInfo walletInfo in walletsInfoSource.values) {
1020 +Future<void> fixBtcDerivationPaths() async {
1021 + for (WalletInfo walletInfo in await WalletInfo.getAll()) {
1022 if (walletInfo.type == WalletType.bitcoin ||
1023 walletInfo.type == WalletType.bitcoinCash ||
1024 walletInfo.type == WalletType.litecoin) {
1024 - if (walletInfo.derivationInfo?.derivationPath == "m/0'/0") {
1025 - walletInfo.derivationInfo!.derivationPath = "m/0'";
1025 + final derivationInfo = await walletInfo.getDerivationInfo();
1026 + if (derivationInfo?.derivationPath == "m/0'/0") {
1027 + derivationInfo!.derivationPath = "m/0'";
1028 await walletInfo.save();
1029 }
1030 }
1031 }
1032 }
1031 -
1032 -Future<void> updateBtcNanoWalletInfos(Box<WalletInfo> walletsInfoSource) async {
1033 - for (WalletInfo walletInfo in walletsInfoSource.values) {
1034 - if (walletInfo.type == WalletType.nano || walletInfo.type == WalletType.bitcoin) {
1035 - walletInfo.derivationInfo = DerivationInfo(
1036 - derivationPath: walletInfo.derivationPath,
1037 - derivationType: walletInfo.derivationType,
1038 - address: walletInfo.address,
1039 - transactionsCount: walletInfo.restoreHeight,
1040 - );
1041 - await walletInfo.save();
1042 - }
1043 - }
1044 -}
1033 +Future<void> updateBtcNanoWalletInfos() async {}
1034 +// Future<void> updateBtcNanoWalletInfos() async {
1035 +// for (WalletInfo walletInfo in await WalletInfo.getAll()) {
1036 +// if (walletInfo.type == WalletType.nano || walletInfo.type == WalletType.bitcoin) {
1037 +// final derivationInfo = await walletInfo.getDerivationInfo();
1038 +// derivationInfo = DerivationInfo(
1039 +// derivationPath: derivationInfo?.derivationPath,
1040 +// derivationType: derivationInfo?.derivationType,
1041 +// address: walletInfo.address,
1042 +// transactionsCount: walletInfo.restoreHeight,
1043 +// );
1044 +// await walletInfo.save();
1045 +// }
1046 +// }
1047 +// }
1048
1049 Future<void> checkCurrentNodes(
1050 Box<Node> nodeSource, Box<Node> powNodeSource, SharedPreferences sharedPreferences) async {
lib/entities/fs_migration.dart
+8 -6
@@ -29,8 +29,7 @@ Future<void> migrate_android_v1() async {
29 await android_migrate_wallets(appDocDir: appDocDir);
30 }
31
32 -Future<void> ios_migrate_v1(
33 - Box<WalletInfo> walletInfoSource, Box<Trade> tradeSource, Box<Contact> contactSource) async {
32 +Future<void> ios_migrate_v1(Box<Trade> tradeSource, Box<Contact> contactSource) async {
33 final prefs = await SharedPreferences.getInstance();
34
35 if (prefs.getBool('ios_migration_v1_completed') ?? false) {
@@ -40,7 +39,7 @@ Future<void> ios_migrate_v1(
39 await ios_migrate_user_defaults();
40 await ios_migrate_pin();
41 await ios_migrate_wallet_passwords();
43 - await ios_migrate_wallet_info(walletInfoSource);
42 + await ios_migrate_wallet_info();
43 await ios_migrate_trades_list(tradeSource);
44 await ios_migrate_address_book(contactSource);
45
@@ -278,7 +277,7 @@ Future<void> android_migrate_wallets({required Directory appDocDir}) async {
277 });
278 }
279
281 -Future<void> ios_migrate_wallet_info(Box<WalletInfo> walletsInfoSource) async {
280 +Future<void> ios_migrate_wallet_info() async {
281 final prefs = await SharedPreferences.getInstance();
282
283 if (prefs.getBool('ios_migration_wallet_info_completed') ?? false) {
@@ -289,6 +288,7 @@ Future<void> ios_migrate_wallet_info(Box<WalletInfo> walletsInfoSource) async {
288 final appDocDir = await getApplicationDocumentsDirectory();
289 final walletsDir = Directory('${appDocDir.path}/wallets');
290 final moneroWalletsDir = Directory('${walletsDir.path}/monero');
291 + final walletsInfo = await WalletInfo.getAll();
292 final infoRecords = moneroWalletsDir
293 .listSync()
294 .map((item) {
@@ -307,7 +307,7 @@ Future<void> ios_migrate_wallet_info(Box<WalletInfo> walletsInfoSource) async {
307 final timestamp = dateAsDouble.toInt() * 1000;
308 final date = DateTime.fromMillisecondsSinceEpoch(timestamp);
309 final id = walletTypeToString(WalletType.monero).toLowerCase() + '_' + name;
310 - final exist = walletsInfoSource.values.firstWhereOrNull((el) => el.id == id) != null;
310 + final exist = walletsInfo.firstWhereOrNull((el) => el.id == id) != null;
311
312 if (exist) {
313 return null;
@@ -334,7 +334,9 @@ Future<void> ios_migrate_wallet_info(Box<WalletInfo> walletsInfoSource) async {
334 .where((el) => el != null)
335 .whereType<WalletInfo>()
336 .toList();
337 - await walletsInfoSource.addAll(infoRecords);
337 + for (final info in infoRecords) {
338 + await info.save();
339 + }
340 await prefs.setBool('ios_migration_wallet_info_completed', true);
341 } catch (e) {
342 printV(e.toString());
lib/entities/hardware_wallet/require_hardware_wallet_connection.dart
+2 -2
@@ -4,7 +4,7 @@ import 'package:cake_wallet/entities/preferences_key.dart';
4 import 'package:cw_core/wallet_type.dart';
5 import 'package:shared_preferences/shared_preferences.dart';
6
7 -bool requireHardwareWalletConnection() {
7 +Future<bool> requireHardwareWalletConnection() async {
8 final name = getIt
9 .get<SharedPreferences>()
10 .getString(PreferencesKey.currentWalletName);
@@ -21,5 +21,5 @@ bool requireHardwareWalletConnection() {
21
22 final type = deserializeFromInt(typeRaw);
23 final walletLoadingService = getIt.get<WalletLoadingService>();
24 - return walletLoadingService.requireHardwareWalletConnection(type, name);
24 + return await walletLoadingService.requireHardwareWalletConnection(type, name);
25 }
lib/entities/wallet_manager.dart
+6 -7
@@ -8,17 +8,16 @@ import 'package:hive/hive.dart';
8 import 'package:shared_preferences/shared_preferences.dart';
9
10 class WalletManager {
11 - WalletManager(this._walletInfoSource, this._sharedPreferences);
11 + WalletManager(this._sharedPreferences);
12
13 - final Box<WalletInfo> _walletInfoSource;
13 final SharedPreferences _sharedPreferences;
14
15 final List<WalletGroup> walletGroups = [];
16
18 - void updateWalletGroups() {
17 + Future<void> updateWalletGroups() async {
18 walletGroups.clear();
19
21 - for (final walletInfo in _walletInfoSource.values) {
20 + for (final walletInfo in await WalletInfo.getAll()) {
21 final groupKey = _resolveGroupKey(walletInfo);
22 final group = _getOrCreateGroup(groupKey);
23 group.wallets.add(walletInfo);
@@ -115,7 +114,7 @@ class WalletManager {
114 // If the openedWallet already has an hash, then there is nothing to do
115 if (walletInfo.hashedWalletIdentifier != null &&
116 walletInfo.hashedWalletIdentifier!.isNotEmpty) {
118 - updateWalletGroups(); // Still skeptical of calling this here. Looking for a better spot.
117 + await updateWalletGroups(); // Still skeptical of calling this here. Looking for a better spot.
118 return;
119 }
120
@@ -123,7 +122,7 @@ class WalletManager {
122 final oldGroupKey = _resolveGroupKey(walletInfo); // parentAddress fallback
123
124 // Find all wallets that share this old group key (i.e the old group)
126 - final oldGroupWallets = _walletInfoSource.values.where((w) {
125 + final oldGroupWallets = (await WalletInfo.getAll()).where((w) {
126 final key = w.hashedWalletIdentifier != null && w.hashedWalletIdentifier!.isNotEmpty
127 ? w.hashedWalletIdentifier
128 : (w.parentAddress ?? w.address);
@@ -150,7 +149,7 @@ class WalletManager {
149 }
150
151 // Finally, we rebuild the groups so that these wallets are now in the new group
153 - updateWalletGroups();
152 + await updateWalletGroups();
153 }
154
155 /// Copy an old group name to the new group key, then remove the old key.
lib/ethereum/cw_ethereum.dart
+7 -6
@@ -4,8 +4,8 @@ class CWEthereum extends Ethereum {
4 @override
5 List<String> getEthereumWordList(String language) => EVMChainMnemonics.englishWordlist;
6
7 - WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
8 - EthereumWalletService(walletInfoSource, isDirect, client: EthereumClient());
7 + WalletService createEthereumWalletService(bool isDirect) =>
8 + EthereumWalletService(isDirect, client: EthereumClient());
9
10 @override
11 WalletCredentials createEthereumNewWalletCredentials({
@@ -181,18 +181,19 @@ class CWEthereum extends Ethereum {
181 String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
182
183 @override
184 - void setHardwareWalletService(WalletBase wallet, HardwareWalletService service) {
184 + Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service) async {
185 if (service is EVMChainLedgerService) {
186 ((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials).setLedgerConnection(
187 - service.ledgerConnection, wallet.walletInfo.derivationInfo?.derivationPath);
187 + service.ledgerConnection, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
188 } else if (service is EVMChainBitboxService) {
189 ((wallet as EVMChainWallet).evmChainPrivateKey as EvmBitboxCredentials)
190 - .setBitbox(service.manager, wallet.walletInfo.derivationInfo?.derivationPath);
190 + .setBitbox(service.manager, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
191 } else if (service is EVMChainTrezorService) {
192 ((wallet as EVMChainWallet).evmChainPrivateKey as EvmTrezorCredentials)
193 - .setTrezorConnect(service.connect, wallet.walletInfo.derivationInfo?.derivationPath);
193 + .setTrezorConnect(service.connect, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
194 }
195 }
196 +
197 @override
198 HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection) =>
199 EVMChainLedgerService(connection);
lib/haven/cw_haven.dart
+7 -6
@@ -12,16 +12,15 @@ import 'package:cw_core/wallet_type.dart';
12 import 'package:hive/hive.dart';
13
14 class HavenWalletService extends WalletService {
15 - final Box<WalletInfo> walletInfoSource;
15
17 - HavenWalletService(this.walletInfoSource);
16 + HavenWalletService();
17
18 @override
19 WalletType getType() => WalletType.haven;
20
21 @override
22 Future<void> remove(String wallet) async {
24 - final path = await pathForWalletDir(name: wallet, type: WalletType.haven);
23 + final path = await pathForWalletDir(name: wallet, type: getType());
24
25 final file = Directory(path);
26 final isExist = file.existsSync();
@@ -30,9 +29,11 @@ class HavenWalletService extends WalletService {
29 await file.delete(recursive: true);
30 }
31
33 - final walletInfo = walletInfoSource.values
34 - .firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
35 - await walletInfoSource.delete(walletInfo.key);
32 + final walletInfo = await WalletInfo.get(wallet, getType());
33 + if (walletInfo == null) {
34 + throw Exception('Wallet not found');
35 + }
36 + await WalletInfo.delete(walletInfo);
37 }
38
39 @override
lib/locales/yoruba_intl.dart
+1 -1
@@ -1032,7 +1032,7 @@ class YoCupertinoLocalizations extends GlobalCupertinoLocalizations {
1032 @override
1033 // TODO: implement backButtonLabel
1034 String get backButtonLabel => "backButtonLabel";
1035 -
1035 +
1036 @override
1037 // TODO: implement cancelButtonLabel
1038 String get cancelButtonLabel => "cancelButtonLabel";
lib/main.dart
+8 -23
@@ -47,8 +47,9 @@ import 'package:cw_core/unspent_coins_info.dart';
47 import 'package:cw_core/utils/print_verbose.dart';
48 import 'package:cw_core/utils/proxy_logger/memory_proxy_logger.dart';
49 import 'package:cw_core/utils/proxy_wrapper.dart';
50 -import 'package:cw_core/utils/tor/abstract.dart';
50 +import 'package:cw_core/db/sqlite.dart';
51 import 'package:cw_core/wallet_info.dart';
52 +import 'package:cw_core/utils/tor/abstract.dart';
53 import 'package:cw_core/wallet_type.dart';
54 import 'package:flutter/foundation.dart';
55 import 'package:flutter/material.dart';
@@ -86,12 +87,16 @@ Future<void> runAppWithZone({Key? topLevelKey}) async {
87
88 return true;
89 };
90 +
91 await FlutterDaemon().unmarkBackgroundSync();
92 + await initDb();
93 +
94 try {
95 CakeTor.instance = await CakeTorInstance.getInstance();
96 } catch (e) {
97 printV("Failed to initialize tor: $e");
98 }
99 +
100 await initializeAppAtRoot();
101
102 if (kDebugMode) {
@@ -164,22 +169,6 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
169 CakeHive.registerAdapter(AddressInfoAdapter());
170 }
171
167 - if (!CakeHive.isAdapterRegistered(WalletInfo.typeId)) {
168 - CakeHive.registerAdapter(WalletInfoAdapter());
169 - }
170 -
171 - if (!CakeHive.isAdapterRegistered(DERIVATION_TYPE_TYPE_ID)) {
172 - CakeHive.registerAdapter(DerivationTypeAdapter());
173 - }
174 -
175 - if (!CakeHive.isAdapterRegistered(DERIVATION_INFO_TYPE_ID)) {
176 - CakeHive.registerAdapter(DerivationInfoAdapter());
177 - }
178 -
179 - if (!CakeHive.isAdapterRegistered(HARDWARE_WALLET_TYPE_TYPE_ID)) {
180 - CakeHive.registerAdapter(HardwareWalletTypeAdapter());
181 - }
182 -
172 if (!CakeHive.isAdapterRegistered(WALLET_TYPE_TYPE_ID)) {
173 CakeHive.registerAdapter(WalletTypeAdapter());
174 }
@@ -227,6 +216,7 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
216 if (!CakeHive.isAdapterRegistered(TronToken.typeId)) {
217 CakeHive.registerAdapter(TronTokenAdapter());
218 }
219 + await performHiveMigration();
220
221 final secureStorage = secureStorageShared;
222 final transactionDescriptionsBoxKey =
@@ -242,7 +232,6 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
232 encryptionKey: transactionDescriptionsBoxKey);
233 final trades = await CakeHive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
234 final orders = await CakeHive.openBox<Order>(Order.boxName, encryptionKey: ordersBoxKey);
245 - final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
235 final templates = await CakeHive.openBox<Template>(Template.boxName);
236 final exchangeTemplates = await CakeHive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
237 final anonpayInvoiceInfo = await CakeHive.openBox<AnonpayInvoiceInfo>(AnonpayInvoiceInfo.boxName);
@@ -259,7 +248,6 @@ Future<void> initializeAppConfigs({bool loadWallet = true}) async {
248 sharedPreferences: await SharedPreferences.getInstance(),
249 nodes: nodes,
250 powNodes: powNodes,
262 - walletInfoSource: walletInfoSource,
251 contactSource: contacts,
252 tradesSource: trades,
253 ordersSource: orders,
@@ -281,7 +269,6 @@ Future<void> initialSetup({
269 required SharedPreferences sharedPreferences,
270 required Box<Node> nodes,
271 required Box<Node> powNodes,
284 - required Box<WalletInfo> walletInfoSource,
272 required Box<Contact> contactSource,
273 required Box<Trade> tradesSource,
274 required Box<Order> ordersSource,
@@ -294,21 +281,19 @@ Future<void> initialSetup({
281 required Box<UnspentCoinsInfo> unspentCoinsInfoSource,
282 required Box<PayjoinSession> payjoinSessionSource,
283 required Box<HavenSeedStore> havenSeedStore,
297 - int initialMigrationVersion = 15,
284 + required int initialMigrationVersion,
285 }) async {
286 LanguageService.loadLocaleList();
287 await defaultSettingsMigration(
288 secureStorage: secureStorage,
289 version: initialMigrationVersion,
290 sharedPreferences: sharedPreferences,
304 - walletInfoSource: walletInfoSource,
291 contactSource: contactSource,
292 tradeSource: tradesSource,
293 nodes: nodes,
294 powNodes: powNodes,
295 havenSeedStore: havenSeedStore);
296 await setup(
311 - walletInfoSource: walletInfoSource,
297 nodeSource: nodes,
298 powNodeSource: powNodes,
299 contactSource: contactSource,
lib/monero/cw_monero.dart
+4 -5
@@ -346,9 +346,8 @@ class CWMonero extends Monero {
346 }
347
348 @override
349 - WalletService createMoneroWalletService(
350 - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) =>
351 - MoneroWalletService(walletInfoSource, unspentCoinSource);
349 + WalletService createMoneroWalletService(Box<UnspentCoinsInfo> unspentCoinSource) =>
350 + MoneroWalletService(unspentCoinSource);
351
352 @override
353 String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) {
@@ -416,9 +415,9 @@ class CWMonero extends Monero {
415 }
416
417 @override
419 - void setLedgerConnection(Object wallet, ledger.LedgerConnection connection) {
418 + Future<void> setLedgerConnection(Object wallet, ledger.LedgerConnection connection) async {
419 final moneroWallet = wallet as MoneroWallet;
421 - moneroWallet.setLedgerConnection(connection);
420 + await moneroWallet.setLedgerConnection(connection);
421 }
422
423 @override
lib/nano/cw_nano.dart
+2 -2
@@ -75,8 +75,8 @@ class CWNano extends Nano {
75 }
76
77 @override
78 - WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) {
79 - return NanoWalletService(walletInfoSource, isDirect);
78 + WalletService createNanoWalletService(bool isDirect) {
79 + return NanoWalletService(isDirect);
80 }
81
82 @override
lib/polygon/cw_polygon.dart
+6 -6
@@ -4,8 +4,8 @@ class CWPolygon extends Polygon {
4 @override
5 List<String> getPolygonWordList(String language) => EVMChainMnemonics.englishWordlist;
6
7 - WalletService createPolygonWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
8 - PolygonWalletService(walletInfoSource, isDirect, client: PolygonClient());
7 + WalletService createPolygonWalletService(bool isDirect) =>
8 + PolygonWalletService(isDirect, client: PolygonClient());
9
10 @override
11 WalletCredentials createPolygonNewWalletCredentials({
@@ -206,16 +206,16 @@ class CWPolygon extends Polygon {
206 );
207
208 @override
209 - void setHardwareWalletService(WalletBase wallet, HardwareWalletService service) {
209 + Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service) async {
210 if (service is EVMChainLedgerService) {
211 ((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials).setLedgerConnection(
212 - service.ledgerConnection, wallet.walletInfo.derivationInfo?.derivationPath);
212 + service.ledgerConnection, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
213 } else if (service is EVMChainBitboxService) {
214 ((wallet as EVMChainWallet).evmChainPrivateKey as EvmBitboxCredentials)
215 - .setBitbox(service.manager, wallet.walletInfo.derivationInfo?.derivationPath);
215 + .setBitbox(service.manager, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
216 } else if (service is EVMChainTrezorService) {
217 ((wallet as EVMChainWallet).evmChainPrivateKey as EvmTrezorCredentials)
218 - .setTrezorConnect(service.connect, wallet.walletInfo.derivationInfo?.derivationPath);
218 + .setTrezorConnect(service.connect, (await wallet.walletInfo.getDerivationInfo()).derivationPath);
219 }
220 }
221
lib/reactions/on_authentication_state_change.dart
+2 -2
@@ -43,7 +43,7 @@ void startAuthenticationStateChange(
43 if (state == AuthenticationState.installed &&
44 !SettingsStoreBase.walletPasswordDirectInput) {
45 try {
46 - if (!requireHardwareWalletConnection()) await loadCurrentWallet();
46 + if (!(await requireHardwareWalletConnection())) await loadCurrentWallet();
47 } catch (error, stack) {
48 loginError = error;
49 await ExceptionHandler.resetLastPopupDate();
@@ -56,7 +56,7 @@ void startAuthenticationStateChange(
56 if ([AuthenticationState.allowed, AuthenticationState.allowedCreate]
57 .contains(state)) {
58 if (state == AuthenticationState.allowed &&
59 - requireHardwareWalletConnection()) {
59 + (await requireHardwareWalletConnection())) {
60 await navigatorKey.currentState!.pushNamedAndRemoveUntil(
61 Routes.connectDevices,
62 (route) => false,
lib/reactions/on_current_wallet_change.dart
+1 -3
@@ -99,9 +99,7 @@ void startCurrentWalletChangeReaction(
99 if (wallet.walletInfo.address.isEmpty) {
100 wallet.walletInfo.address = wallet.walletAddresses.address;
101
102 - if (wallet.walletInfo.isInBox) {
103 - await wallet.walletInfo.save();
104 - }
102 + await wallet.walletInfo.save();
103 }
104 } catch (e) {
105 printV(e.toString());
lib/solana/cw_solana.dart
+2 -2
@@ -4,8 +4,8 @@ class CWSolana extends Solana {
4 @override
5 List<String> getSolanaWordList(String language) => SolanaMnemonics.englishWordlist;
6
7 - WalletService createSolanaWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
8 - SolanaWalletService(walletInfoSource, isDirect);
7 + WalletService createSolanaWalletService(bool isDirect) =>
8 + SolanaWalletService(isDirect);
9
10 @override
11 WalletCredentials createSolanaNewWalletCredentials({
lib/src/screens/contact/contact_list_page.dart
+4 -1
@@ -17,6 +17,7 @@ import 'package:cw_core/wallet_type.dart';
17 import 'package:flutter/cupertino.dart';
18 import 'package:flutter/material.dart';
19 import 'package:flutter/services.dart';
20 +import 'package:flutter_mobx/flutter_mobx.dart';
21 import 'package:flutter_slidable/flutter_slidable.dart';
22
23 class ContactListPage extends BasePage {
@@ -150,7 +151,9 @@ class _ContactPageBodyState extends State<ContactPageBody> with SingleTickerProv
151 child: TabBarView(
152 controller: _tabController,
153 children: [
153 - _buildWalletContacts(context),
154 + Observer(
155 + builder: (final BuildContext context) => _buildWalletContacts(context),
156 + ),
157 ContactListBody(
158 contactListViewModel: widget.contactListViewModel,
159 tabController: _tabController,
lib/src/screens/new_wallet/new_wallet_page.dart
+1 -1
@@ -351,7 +351,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
351 _formProcessing = false;
352 return;
353 }
354 - if (_walletNewVM.nameExists(_walletNewVM.name)) {
354 + if (await _walletNewVM.nameExists(_walletNewVM.name)) {
355 await showPopUp<void>(
356 context: context,
357 builder: (_) {
lib/src/screens/restore/wallet_restore_page.dart
+1 -1
@@ -258,7 +258,7 @@ class WalletRestorePage extends BasePage {
258 return;
259 }
260
261 - if (walletRestoreViewModel.nameExists(name)) {
261 + if (await walletRestoreViewModel.nameExists(name)) {
262 showNameExistsAlert(formContext!);
263 _formProcessing = false;
264 return;
lib/src/screens/settings/other_settings_page.dart
+12
@@ -3,6 +3,7 @@ import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
3 import 'package:cake_wallet/generated/i18n.dart';
4 import 'package:cake_wallet/routes.dart';
5 import 'package:cake_wallet/src/screens/base_page.dart';
6 +import 'package:cake_wallet/src/screens/dev/moneroc_cache_debug.dart';
7 import 'package:cake_wallet/src/screens/settings/widgets/setting_priority_picker_cell.dart';
8 import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
9 import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
@@ -11,6 +12,7 @@ import 'package:cake_wallet/utils/feature_flag.dart';
12 import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
13 import 'package:cw_core/wallet_info.dart';
14 import 'package:cw_core/wallet_type.dart';
15 +import 'package:cw_core/db/sqlite.dart';
16 import 'package:flutter/material.dart';
17 import 'package:flutter_mobx/flutter_mobx.dart';
18
@@ -144,6 +146,16 @@ class OtherSettingsPage extends BasePage {
146 handler: (BuildContext context) =>
147 Navigator.of(context).pushNamed(Routes.devExchangeProviderLogs),
148 ),
149 + if (FeatureFlag.hasDevOptions)
150 + SettingsCellWithArrow(
151 + title: '[dev] browse sqlite db',
152 + handler: (BuildContext context) async {
153 + final data = await dumpDb();
154 + Navigator.of(context).push(
155 + MaterialPageRoute(builder: (context) => JsonExplorerPage(data: data, title: 'sqlite db')),
156 + );
157 + }
158 + ),
159 Spacer(),
160 SettingsVersionCell(
161 title: S.of(context).version(_otherSettingsViewModel.currentVersion),
lib/src/screens/wallet/wallet_edit_page.dart
+11 -5
@@ -1,3 +1,5 @@
1 +import 'dart:async';
2 +
3 import 'package:another_flushbar/flushbar.dart';
4 import 'package:cake_wallet/core/wallet_name_validator.dart';
5 import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
@@ -10,6 +12,7 @@ import 'package:cake_wallet/store/settings_store.dart';
12 import 'package:cake_wallet/utils/show_bar.dart';
13 import 'package:cake_wallet/utils/show_pop_up.dart';
14 import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
15 +import 'package:cw_core/utils/print_verbose.dart';
16 import 'package:flutter/material.dart';
17 import 'package:cake_wallet/generated/i18n.dart';
18 import 'package:cake_wallet/src/widgets/primary_button.dart';
@@ -86,7 +89,7 @@ class WalletEditPage extends BasePage {
89 onPressed: () async {
90 if (_formKey.currentState?.validate() ?? false) {
91 if (!pageArguments.isWalletGroup &&
89 - pageArguments.walletNewVM!
92 + await pageArguments.walletNewVM!
93 .nameExists(walletEditViewModel.newName)) {
94 showPopUp<void>(
95 context: context,
@@ -217,9 +220,12 @@ class WalletEditPage extends BasePage {
220 }
221
222 Future<void> hideProgressText() async {
220 - await Future.delayed(Duration(milliseconds: 50), () {
221 - _progressBar?.dismiss();
222 - _progressBar = null;
223 - });
223 + try {
224 + await Future.delayed(Duration(milliseconds: 250));
225 + await _progressBar?.dismiss();
226 + } catch (e) {
227 + printV(e);
228 + }
229 + _progressBar = null;
230 }
231 }
lib/src/screens/wallet_list/wallet_list_page.dart
+23 -13
@@ -1,3 +1,5 @@
1 +import 'dart:async';
2 +
3 import 'package:another_flushbar/flushbar.dart';
4 import 'package:cake_wallet/core/auth_service.dart';
5 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';
29 import 'package:cake_wallet/wallet_type_utils.dart';
30 import 'package:cw_core/currency_for_wallet_type.dart';
31 import 'package:cw_core/wallet_info.dart';
32 +import 'package:cw_core/utils/print_verbose.dart';
33 import 'package:cw_core/wallet_type.dart';
34 import 'package:flutter/material.dart';
35 import 'package:flutter_mobx/flutter_mobx.dart';
@@ -40,17 +43,26 @@ class WalletListPage extends BasePage {
43
44 final WalletListViewModel walletListViewModel;
45 final AuthService authService;
43 - final Function(BuildContext)? onWalletLoaded;
46 + final Future<void> Function(BuildContext)? onWalletLoaded;
47
48 @override
49 String get title => S.current.wallets;
50
51 @override
49 - Widget body(BuildContext context) => WalletListBody(
52 + Widget body(BuildContext context) => Observer(
53 + builder: (_) {
54 + if (walletListViewModel.singleWalletsList.isEmpty && walletListViewModel.multiWalletGroups.isEmpty) {
55 + return Center(
56 + child: CircularProgressIndicator(),
57 + );
58 + }
59 + return WalletListBody(
60 walletListViewModel: walletListViewModel,
61 authService: authService,
62 onWalletLoaded: onWalletLoaded ?? (context) => Navigator.of(context).pop(),
63 );
64 + }
65 + );
66
67 @override
68 Widget trailing(BuildContext context) {
@@ -459,7 +471,7 @@ class WalletListBodyState extends State<WalletListBody> {
471
472 try {
473 final requireHardwareWalletConnection =
462 - widget.walletListViewModel.requireHardwareWalletConnection(wallet);
474 + await widget.walletListViewModel.requireHardwareWalletConnection(wallet);
475 if (requireHardwareWalletConnection) {
476 bool didConnect = false;
477 await Navigator.of(context).pushNamed(
@@ -489,22 +501,20 @@ class WalletListBodyState extends State<WalletListBody> {
501 buttonAction: () => Navigator.of(context).pop()),
502 );
503 }
492 -
504 changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
505 await widget.walletListViewModel.loadWallet(wallet);
495 - await hideProgressText();
506 // only pop the wallets route in mobile as it will go back to dashboard page
507 // in desktop platforms the navigation tree is different
508 if (responsiveLayoutUtil.shouldRenderMobileUI) {
499 - WidgetsBinding.instance.addPostFrameCallback((_) {
500 - if (this.mounted) {
501 - if (requireHardwareWalletConnection) {
502 - Navigator.of(context).pop();
503 - }
504 - widget.onWalletLoaded.call(context);
505 - }
506 - });
509 + // await Future.delayed(Duration(seconds: 1));
510 + // if (!this.mounted) return;
511 + if (!context.mounted) return;
512 + if (requireHardwareWalletConnection) {
513 + Navigator.of(context).pop();
514 + }
515 + await widget.onWalletLoaded.call(context);
516 }
517 + unawaited(hideProgressText());
518 } catch (e) {
519 await ExceptionHandler.resetLastPopupDate();
520 final err = e.toString();
lib/src/widgets/bottom_sheet/wallet_switcher_bottom_sheet.dart
+70 -67
@@ -52,76 +52,79 @@ class _WalletSwitcherContent extends StatelessWidget {
52
53 @override
54 Widget build(BuildContext context) {
55 - return Observer(
56 - builder: (_) {
57 - final wallets = viewModel.getWallets(filterWalletType);
58 -
59 - if (viewModel.isProcessing) {
60 - return Container(
61 - height: 200,
62 - child: Center(
63 - child: CircularProgressIndicator(
64 - color: Theme.of(context).colorScheme.primary,
55 + return FutureBuilder(
56 + future: viewModel.getWallets(filterWalletType),
57 + builder: (context, snapshot) => Observer(
58 + builder: (_) {
59 + final List<WalletInfo> wallets = (snapshot.data ?? []);
60 +
61 + if (viewModel.isProcessing) {
62 + return Container(
63 + height: 200,
64 + child: Center(
65 + child: CircularProgressIndicator(
66 + color: Theme.of(context).colorScheme.primary,
67 + ),
68 ),
66 - ),
67 - );
68 - }
69 -
70 - return Container(
71 - height: 400,
72 - child: Column(
73 - crossAxisAlignment: CrossAxisAlignment.start,
74 - children: [
75 - Expanded(
76 - child: StandardList(
77 - itemCount: wallets.length,
78 - itemBuilder: (context, index) {
79 - final wallet = wallets[index];
80 -
81 - return InkWell(
82 - onTap: () {
83 - viewModel.selectWallet(wallet);
84 - Navigator.of(context).pop();
85 - },
86 - borderRadius: BorderRadius.circular(12),
87 - child: Container(
88 - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
89 - decoration: BoxDecoration(
90 - color: Theme.of(context).colorScheme.surfaceContainer,
91 - borderRadius: BorderRadius.circular(16),
92 - ),
93 - height: 60,
94 - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
95 - child: Row(
96 - children: [
97 - Image.asset(
98 - walletTypeToCryptoCurrency(wallet.type).iconPath!,
99 - width: 32,
100 - height: 32,
101 - ),
102 - const SizedBox(width: 16),
103 - Text(
104 - wallet.name,
105 - style: Theme.of(context).textTheme.bodyMedium!.copyWith(
106 - fontSize: 18,
107 - fontWeight: FontWeight.w700,
108 - color: Theme.of(context).colorScheme.onSurface,
109 - letterSpacing: 0.0,
110 - ),
111 - maxLines: 1,
112 - overflow: TextOverflow.ellipsis,
113 - ),
114 - ],
69 + );
70 + }
71 +
72 + return Container(
73 + height: 400,
74 + child: Column(
75 + crossAxisAlignment: CrossAxisAlignment.start,
76 + children: [
77 + Expanded(
78 + child: StandardList(
79 + itemCount: wallets.length,
80 + itemBuilder: (context, index) {
81 + final wallet = wallets[index];
82 +
83 + return InkWell(
84 + onTap: () {
85 + viewModel.selectWallet(wallet);
86 + Navigator.of(context).pop();
87 + },
88 + borderRadius: BorderRadius.circular(12),
89 + child: Container(
90 + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
91 + decoration: BoxDecoration(
92 + color: Theme.of(context).colorScheme.surfaceContainer,
93 + borderRadius: BorderRadius.circular(16),
94 + ),
95 + height: 60,
96 + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
97 + child: Row(
98 + children: [
99 + Image.asset(
100 + walletTypeToCryptoCurrency(wallet.type).iconPath!,
101 + width: 32,
102 + height: 32,
103 + ),
104 + const SizedBox(width: 16),
105 + Text(
106 + wallet.name,
107 + style: Theme.of(context).textTheme.bodyMedium!.copyWith(
108 + fontSize: 18,
109 + fontWeight: FontWeight.w700,
110 + color: Theme.of(context).colorScheme.onSurface,
111 + letterSpacing: 0.0,
112 + ),
113 + maxLines: 1,
114 + overflow: TextOverflow.ellipsis,
115 + ),
116 + ],
117 + ),
118 ),
116 - ),
117 - );
118 - },
119 + );
120 + },
121 + ),
122 ),
120 - ),
121 - ],
122 - ),
123 - );
124 - },
123 + ],
124 + ),
125 + );
126 + },
127 + ),
128 );
129 }
130 }
lib/store/app_store.dart
+2 -2
@@ -55,8 +55,8 @@ abstract class AppStoreBase with Store {
55 getIt.get<WalletKitService>().create();
56 await getIt.get<WalletKitService>().init();
57 }
58 - getIt.get<SharedPreferences>().setString(PreferencesKey.currentWalletName, wallet.name);
59 - getIt
58 + await getIt.get<SharedPreferences>().setString(PreferencesKey.currentWalletName, wallet.name);
59 + await getIt
60 .get<SharedPreferences>()
61 .setInt(PreferencesKey.currentWalletType, serializeToInt(wallet.type));
62 }
lib/store/yat/yat_store.dart
+1 -3
@@ -243,9 +243,7 @@ abstract class YatStoreBase with Store {
243
244 walletInfo!.yatEid = emoji;
245
246 - if (walletInfo!.isInBox) {
247 - walletInfo!.save();
248 - }
246 + walletInfo!.save();
247 } catch (e) {
248 printV(e.toString());
249 }
lib/tron/cw_tron.dart
+2 -2
@@ -5,8 +5,8 @@ class CWTron extends Tron {
5 List<String> getTronWordList(String language) => EVMChainMnemonics.englishWordlist;
6
7 @override
8 - WalletService createTronWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) =>
9 - TronWalletService(walletInfoSource, client: TronClient(), isDirect: isDirect);
8 + WalletService createTronWalletService(bool isDirect) =>
9 + TronWalletService(client: TronClient(), isDirect: isDirect);
10
11 @override
12 WalletCredentials createTronNewWalletCredentials({
lib/utils/exception_handler.dart
+2
@@ -290,6 +290,8 @@ class ExceptionHandler {
290 "core/key_service.dart:14",
291 "Wallet is null",
292 "Wrong Device Status: 0x5515 (UNKNOWN)",
293 +
294 + "FocusScopeNode was used after being disposed",
295 ];
296
297 static Future<void> _addDeviceInfo(File file) async {
lib/utils/token_utilities.dart
+13 -17
@@ -10,11 +10,10 @@ import 'package:cw_core/wallet_type.dart';
10 import 'package:hive/hive.dart';
11
12 class TokenUtilities {
13 - static Future<List<Erc20Token>> loadAllUniqueEvmTokens(
14 - Box<WalletInfo> walletInfoSource,
15 - ) async {
16 - final evmWallets = walletInfoSource.values.where(
17 - (w) => isEVMCompatibleChain(w.type),
13 + static Future<List<Erc20Token>> loadAllUniqueEvmTokens() async {
14 + final allWi = await WalletInfo.getAll();
15 + final evmWallets = allWi.where(
16 + (w) => isEVMCompatibleChain(w.type),
17 );
18
19 final seen = <String>{};
@@ -35,10 +34,9 @@ class TokenUtilities {
34 return unique;
35 }
36
38 - static Future<List<SPLToken>> loadAllUniqueSolTokens(
39 - Box<WalletInfo> walletInfoSource,
40 - ) async {
41 - final solWallets = walletInfoSource.values.where(
37 + static Future<List<SPLToken>> loadAllUniqueSolTokens() async {
38 + final allWi = await WalletInfo.getAll();
39 + final solWallets = allWi.where(
40 (w) => w.type == WalletType.solana,
41 );
42
@@ -57,10 +55,9 @@ class TokenUtilities {
55 return unique;
56 }
57
60 - static Future<List<TronToken>> loadAllUniqueTronTokens(
61 - Box<WalletInfo> walletInfoSource,
62 - ) async {
63 - final tronWallets = walletInfoSource.values.where(
58 + static Future<List<TronToken>> loadAllUniqueTronTokens() async {
59 + final allWi = await WalletInfo.getAll();
60 + final tronWallets = allWi.where(
61 (w) => w.type == WalletType.tron,
62 );
63
@@ -82,7 +79,6 @@ class TokenUtilities {
79 /// - Tron: match by contractAddress
80 static Future<CryptoCurrency?> findTokenByAddress({
81 required WalletType walletType,
85 - required Box<WalletInfo> walletInfoSource,
82 required String address,
83 }) async {
84 final lower = address.toLowerCase();
@@ -90,19 +86,19 @@ class TokenUtilities {
86 case WalletType.ethereum:
87 case WalletType.polygon:
88 case WalletType.base:
93 - final tokens = await loadAllUniqueEvmTokens(walletInfoSource);
89 + final tokens = await loadAllUniqueEvmTokens();
90 for (final t in tokens) {
91 if (t.contractAddress.toLowerCase() == lower) return t;
92 }
93 return null;
94 case WalletType.solana:
99 - final solTokens = await loadAllUniqueSolTokens(walletInfoSource);
95 + final solTokens = await loadAllUniqueSolTokens();
96 for (final t in solTokens) {
97 if (t.mintAddress.toLowerCase() == lower) return t;
98 }
99 return null;
100 case WalletType.tron:
105 - final tronTokens = await loadAllUniqueTronTokens(walletInfoSource);
101 + final tronTokens = await loadAllUniqueTronTokens();
102 for (final t in tronTokens) {
103 if (t.contractAddress.toLowerCase() == lower) return t;
104 }
lib/view_model/contact_list/contact_list_view_model.dart
+18 -11
@@ -22,16 +22,22 @@ class ContactListViewModel = ContactListViewModelBase with _$ContactListViewMode
22
23 abstract class ContactListViewModelBase with Store {
24 ContactListViewModelBase(
25 - this.contactSource, this.walletInfoSource, this._currency, this.settingsStore)
25 + this.contactSource, List<WalletInfo> wallets, this._currency, this.settingsStore)
26 : contacts = ObservableList<ContactRecord>(),
27 - walletContacts = [],
27 isAutoGenerateEnabled =
28 settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.enabled {
30 - walletInfoSource.values.forEach((info) {
29 + unawaited(_init());
30 + }
31 +
32 + Future<void> _init() async {
33 + final walletInfos = await WalletInfo.getAll();
34 + for (final info in walletInfos) {
35 + final addressInfos = await info.getAddressInfos();
36 + final addresses = await info.getAddresses();
37 if ([WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type) &&
32 - info.addressInfos != null) {
33 - for (var key in info.addressInfos!.keys) {
34 - final value = info.addressInfos![key];
38 + addressInfos.isNotEmpty) {
39 + for (var key in addressInfos.keys) {
40 + final value = addressInfos[key];
41 final address = value?.first;
42 if (address != null) {
43 final name = _createName(info.name, address.label, key: key);
@@ -42,7 +48,7 @@ abstract class ContactListViewModelBase with Store {
48 ));
49 }
50 }
45 - } else if (info.addresses?.isNotEmpty == true && info.addresses!.length > 1) {
51 + } else if (addresses.isNotEmpty == true && addresses.length > 1) {
52 if ([WalletType.monero, WalletType.wownero, WalletType.haven, WalletType.decred]
53 .contains(info.type)) {
54 final address = info.address;
@@ -53,7 +59,7 @@ abstract class ContactListViewModelBase with Store {
59 walletTypeToCryptoCurrency(info.type),
60 ));
61 } else {
56 - info.addresses!.forEach((address, label) {
62 + addresses.forEach((address, label) {
63 if (label.isEmpty) {
64 return;
65 }
@@ -78,13 +84,14 @@ abstract class ContactListViewModelBase with Store {
84 walletTypeToCryptoCurrency(info.type),
85 ));
86 }
81 - });
87 + }
88
89 _subscription = contactSource.bindToListWithTransform(
90 contacts, (Contact contact) => ContactRecord(contactSource, contact),
91 initialFire: true);
92
93 setOrderType(settingsStore.contactListOrder);
94 + walletContacts = walletContacts.toList(); // rebuild
95 }
96
97 String _createName(String walletName, String label, {int? key = null}) {
@@ -97,9 +104,9 @@ abstract class ContactListViewModelBase with Store {
104
105 final bool isAutoGenerateEnabled;
106 final Box<Contact> contactSource;
100 - final Box<WalletInfo> walletInfoSource;
107 final ObservableList<ContactRecord> contacts;
102 - final List<WalletContact> walletContacts;
108 + @observable
109 + List<WalletContact> walletContacts = [];
110 final CryptoCurrency? _currency;
111 StreamSubscription<BoxEvent>? _subscription;
112 final SettingsStore settingsStore;
lib/view_model/dashboard/dashboard_view_model.dart
+4 -5
@@ -1098,8 +1098,8 @@ abstract class DashboardViewModelBase with Store {
1098 void setSyncAll(bool value) => settingsStore.currentSyncAll = value;
1099
1100 Future<List<String>> checkForHavenWallets() async {
1101 - final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
1102 - return walletInfoSource.values
1101 + final walletInfos = await WalletInfo.getAll();
1102 + return walletInfos
1103 .where((element) => element.type == WalletType.haven)
1104 .map((e) => e.name)
1105 .toList();
@@ -1112,10 +1112,9 @@ abstract class DashboardViewModelBase with Store {
1112 .loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt');
1113 final vulnerableSeeds = vulnerableSeedsString.split("\n");
1114
1115 - final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
1116 -
1115 List<String> affectedWallets = [];
1118 - for (var walletInfo in walletInfoSource.values) {
1116 + final walletInfos = await WalletInfo.getAll();
1117 + for (var walletInfo in walletInfos) {
1118 if (walletInfo.type == WalletType.bitcoin) {
1119 final password = await keyService.getWalletPassword(walletName: walletInfo.name);
1120 final path = await pathForWallet(name: walletInfo.name, type: walletInfo.type);
lib/view_model/exchange/exchange_view_model.dart
+4 -6
@@ -44,6 +44,7 @@ import 'package:cake_wallet/utils/token_utilities.dart';
44 import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
45 import 'package:cake_wallet/view_model/send/fees_view_model.dart';
46 import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
47 +import 'package:cw_core/cake_hive.dart';
48 import 'package:cw_core/crypto_amount_format.dart';
49 import 'package:cw_core/crypto_currency.dart';
50 import 'package:cw_core/erc20_token.dart';
@@ -82,7 +83,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
83 this.contactListViewModel,
84 this.unspentCoinsListViewModel,
85 this.feesViewModel,
85 - this.walletInfoSource,
86 this.fiatConversionStore,
87 ) : _cryptoNumberFormat = NumberFormat(),
88 isSendAllEnabled = false,
@@ -369,8 +369,6 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
369
370 final FeesViewModel feesViewModel;
371
372 - final Box<WalletInfo> walletInfoSource;
373 -
372 @observable
373 double bestRate = 0.0;
374
@@ -1096,7 +1094,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
1094
1095 @action
1096 Future<void> _injectUserEthTokensIntoCurrencyLists() async {
1099 - final userTokens = await TokenUtilities.loadAllUniqueEvmTokens(walletInfoSource);
1097 + final userTokens = await TokenUtilities.loadAllUniqueEvmTokens();
1098
1099 final toAddReceive = <CryptoCurrency>[];
1100 final toAddDeposit = <CryptoCurrency>[];
@@ -1134,7 +1132,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
1132
1133 @action
1134 Future<void> _injectUserSplTokensIntoCurrencyLists() async {
1137 - final userTokens = await TokenUtilities.loadAllUniqueSolTokens(walletInfoSource);
1135 + final userTokens = await TokenUtilities.loadAllUniqueSolTokens();
1136
1137 final toAddReceive = <CryptoCurrency>[];
1138 final toAddDeposit = <CryptoCurrency>[];
@@ -1162,7 +1160,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
1160
1161 @action
1162 Future<void> _injectUserTronTokensIntoCurrencyLists() async {
1165 - final userTokens = await TokenUtilities.loadAllUniqueTronTokens(walletInfoSource);
1163 + final userTokens = await TokenUtilities.loadAllUniqueTronTokens();
1164
1165 final toAddReceive = <CryptoCurrency>[];
1166 final toAddDeposit = <CryptoCurrency>[];
lib/view_model/hardware_wallet/bitbox_view_model.dart
+4 -4
@@ -98,15 +98,15 @@ abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store {
98 }
99
100 @override
101 - void initWallet(WalletBase wallet) {
101 + Future<void> initWallet(WalletBase wallet) async {
102 switch (wallet.type) {
103 case WalletType.bitcoin:
104 case WalletType.litecoin:
105 - return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
105 + return bitcoin!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
106 case WalletType.ethereum:
107 - return ethereum!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
107 + return ethereum!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
108 case WalletType.polygon:
109 - return polygon!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
109 + return polygon!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
110 default:
111 throw Exception('Unexpected wallet type: ${wallet.type}');
112 }
lib/view_model/hardware_wallet/hardware_wallet_view_model.dart
+1 -1
@@ -22,7 +22,7 @@ abstract class HardwareWalletViewModel {
22
23 HardwareWalletService getHardwareWalletService(WalletType type);
24
25 - void initWallet(WalletBase wallet);
25 + Future<void> initWallet(WalletBase wallet);
26
27 String? interpretErrorCode(String error) => null;
28 }
lib/view_model/hardware_wallet/ledger_view_model.dart
+5 -5
@@ -178,18 +178,18 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
178 sdk.LedgerConnection get connection => _connection!;
179
180 @override
181 - void initWallet(WalletBase wallet) {
181 + Future<void> initWallet(WalletBase wallet) async {
182 switch (wallet.type) {
183 case WalletType.monero:
184 return monero!.setLedgerConnection(wallet, connection);
185 case WalletType.bitcoin:
186 - return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
186 + return bitcoin!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
187 case WalletType.litecoin:
188 - return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
188 + return bitcoin!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
189 case WalletType.ethereum:
190 - return ethereum!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
190 + return ethereum!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
191 case WalletType.polygon:
192 - return polygon!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
192 + return polygon!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
193 default:
194 throw Exception('Unexpected wallet type: ${wallet.type}');
195 }
lib/view_model/hardware_wallet/trezor_view_model.dart
+4 -4
@@ -66,15 +66,15 @@ abstract class TrezorViewModelBase extends HardwareWalletViewModel with Store {
66 }
67
68 @override
69 - void initWallet(WalletBase wallet) {
69 + Future<void> initWallet(WalletBase wallet) async {
70 switch (wallet.type) {
71 case WalletType.bitcoin:
72 case WalletType.litecoin:
73 - return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
73 + return bitcoin!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
74 case WalletType.ethereum:
75 - return ethereum!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
75 + return ethereum!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
76 case WalletType.polygon:
77 - return polygon!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
77 + return polygon!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
78 default:
79 throw Exception('Unexpected wallet type: ${wallet.type}');
80 }
lib/view_model/new_wallet_type_view_model.dart
+2 -7
@@ -1,5 +1,3 @@
1 -import 'package:cw_core/wallet_info.dart';
2 -import 'package:hive/hive.dart';
1 import 'package:mobx/mobx.dart';
2
3 part 'new_wallet_type_view_model.g.dart';
@@ -7,10 +5,7 @@ part 'new_wallet_type_view_model.g.dart';
5 class NewWalletTypeViewModel = NewWalletTypeViewModelBase with _$NewWalletTypeViewModel;
6
7 abstract class NewWalletTypeViewModelBase with Store {
10 - NewWalletTypeViewModelBase(this._walletInfoSource);
8 + NewWalletTypeViewModelBase(this.hasExisitingWallet);
9
12 - @computed
13 - bool get hasExisitingWallet => _walletInfoSource.isNotEmpty;
14 -
15 - final Box<WalletInfo> _walletInfoSource;
10 + final bool hasExisitingWallet;
11 }
lib/view_model/payment/payment_view_model.dart
+3 -5
@@ -15,11 +15,9 @@ class PaymentViewModel = PaymentViewModelBase with _$PaymentViewModel;
15 abstract class PaymentViewModelBase with Store {
16 PaymentViewModelBase({
17 required this.appStore,
18 - required this.walletInfoSource,
18 });
19
20 final AppStore appStore;
22 - final Box<WalletInfo> walletInfoSource;
21
22 @observable
23 WalletType? detectedWalletType;
@@ -52,7 +50,7 @@ abstract class PaymentViewModelBase with Store {
50 return PaymentFlowResult.currentWalletCompatible();
51 }
52
55 - final compatibleWallets = getWalletsByType(detectedWalletType!);
53 + final compatibleWallets = await getWalletsByType(detectedWalletType!);
54
55 switch (compatibleWallets.length) {
56 case 0:
@@ -70,8 +68,8 @@ abstract class PaymentViewModelBase with Store {
68 }
69 }
70
73 - List<WalletInfo> getWalletsByType(WalletType walletType) {
74 - return walletInfoSource.values.where((wallet) => wallet.type == walletType).toList();
71 + Future<List<WalletInfo>> getWalletsByType(WalletType walletType) async {
72 + return (await WalletInfo.getAll()).where((wallet) => wallet.type == walletType).toList();
73 }
74 }
75
lib/view_model/restore_from_backup_view_model.dart
+2 -2
@@ -45,11 +45,11 @@ abstract class RestoreFromBackupViewModelBase with Store {
45
46 try {
47 await backupService.importBackupFile(file, password);
48 - } catch (e) {
48 + } catch (e, s) {
49 if (e.toString().contains("unknown_backup_version")) {
50 state = FailureState('This is not a valid backup file, please make sure you have selected the correct one');
51 } else {
52 - state = FailureState(e.toString());
52 + state = FailureState(e.toString() + "\n" + s.toString());
53 }
54 }
55
lib/view_model/send/send_view_model.dart
+1 -5
@@ -100,8 +100,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
100 this.transactionDescriptionBox,
101 this.hardwareWalletViewModel,
102 this.unspentCoinsListViewModel,
103 - this.feesViewModel,
104 - this.walletInfoSource, {
103 + this.feesViewModel, {
104 this.coinTypeToSpendFrom = UnspentCoinType.nonMweb,
105 }) : state = InitialExecutionState(),
106 currencies = appStore.wallet!.balance.keys.toList(),
@@ -130,8 +129,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
129
130 ObservableList<Output> outputs;
131
133 - final Box<WalletInfo> walletInfoSource;
134 -
132 @observable
133 UnspentCoinType coinTypeToSpendFrom;
134
@@ -1214,7 +1211,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
1211 Future<void> fetchTokenForContractAddress(String contractAddress) async {
1212 final token = await TokenUtilities.findTokenByAddress(
1213 walletType: wallet.type,
1217 - walletInfoSource: walletInfoSource,
1214 address: contractAddress,
1215 );
1216
lib/view_model/settings/other_settings_view_model.dart
+1 -1
@@ -116,7 +116,7 @@ abstract class OtherSettingsViewModelBase with Store {
116 @action
117 Future<void> onHardwareWalletTypeChanged(HardwareWalletType hwType) async {
118 _wallet.walletInfo.hardwareWalletType = hwType;
119 - return _wallet.walletInfo.save();
119 + await _wallet.walletInfo.save();
120 }
121
122 @computed
lib/view_model/wallet_creation_vm.dart
+17 -9
@@ -26,7 +26,7 @@ part 'wallet_creation_vm.g.dart';
26 class WalletCreationVM = WalletCreationVMBase with _$WalletCreationVM;
27
28 abstract class WalletCreationVMBase with Store {
29 - WalletCreationVMBase(this._appStore, this._walletInfoSource, this.walletCreationService,
29 + WalletCreationVMBase(this._appStore, this.walletCreationService,
30 this.seedSettingsViewModel,
31 {required this.type, required this.isRecovery})
32 : state = InitialExecutionState(),
@@ -54,7 +54,6 @@ abstract class WalletCreationVMBase with Store {
54 WalletType type;
55 final bool isRecovery;
56 final WalletCreationService walletCreationService;
57 - final Box<WalletInfo> _walletInfoSource;
57 final AppStore _appStore;
58 final SeedSettingsViewModel seedSettingsViewModel;
59
@@ -62,9 +61,9 @@ abstract class WalletCreationVMBase with Store {
61 [WalletType.monero, WalletType.wownero].contains(type) &&
62 (Polyseed.isValidSeed(seed) || (seed.split(" ").length == 14));
63
65 - bool nameExists(String name) => walletCreationService.exists(name);
64 + Future<bool> nameExists(String name) => walletCreationService.exists(name);
65
67 - bool typeExists(WalletType type) => walletCreationService.typeExists(type);
66 + Future<bool> typeExists(WalletType type) => walletCreationService.typeExists(type);
67
68 Future<void> create({dynamic options}) async {
69 final type = this.type;
@@ -88,6 +87,13 @@ abstract class WalletCreationVMBase with Store {
87
88 final credentials = getCredentials(options);
89
90 + final di = ((credentials.derivationInfo?.derivationPath??"") == "")
91 + ? getDefaultCreateDerivation()
92 + : credentials.derivationInfo;
93 +
94 + final diId = await di!.save();
95 + credentials.derivationInfo = di;
96 +
97 final walletInfo = WalletInfo.external(
98 id: WalletBase.idFor(name, type),
99 name: name,
@@ -98,19 +104,21 @@ abstract class WalletCreationVMBase with Store {
104 path: path,
105 dirPath: dirPath,
106 address: '',
101 - showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven,
102 - derivationInfo: credentials.derivationInfo ?? getDefaultCreateDerivation(),
107 + showIntroCakePayCard: (!await walletCreationService.typeExists(type)) && type != WalletType.haven,
108 + derivationInfoId: diId,
109 hardwareWalletType: credentials.hardwareWalletType,
110 );
111
112 credentials.walletInfo = walletInfo;
113 + // await walletInfo.save();
114 + printV("derivationInfo: ${(await walletInfo.getDerivationInfo()).toJson()}");
115 final wallet = await process(credentials);
116
117 final isNonSeedWallet = isRecovery ? wallet.seed == null : false;
118 walletInfo.isNonSeedWallet = isNonSeedWallet;
119 walletInfo.hashedWalletIdentifier = createHashedWalletIdentifier(wallet);
120 walletInfo.address = wallet.walletAddresses.address;
113 - await _walletInfoSource.add(walletInfo);
121 + await walletInfo.save();
122 await _appStore.changeCurrentWallet(wallet);
123 _appStore.authenticationStore.allowedCreate();
124 state = ExecutedSuccessfullyState();
@@ -125,7 +133,7 @@ abstract class WalletCreationVMBase with Store {
133 }
134 }
135
128 - DerivationInfo? getDefaultCreateDerivation() {
136 + DerivationInfo getDefaultCreateDerivation() {
137 final useBip39ForBitcoin = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.bip39;
138 final useBip39ForNano = seedSettingsViewModel.nanoSeedType.type == DerivationType.bip39;
139 switch (type) {
@@ -155,7 +163,7 @@ abstract class WalletCreationVMBase with Store {
163 }
164 return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
165 default:
158 - return null;
166 + return DerivationInfo(derivationType: DerivationType.unknown);
167 }
168 }
169
lib/view_model/wallet_groups_display_view_model.dart
+24 -22
@@ -1,3 +1,5 @@
1 +import 'dart:async';
2 +
3 import 'package:cake_wallet/core/wallet_loading_service.dart';
4 import 'package:cake_wallet/entities/wallet_group.dart';
5 import 'package:cake_wallet/entities/wallet_manager.dart';
@@ -6,7 +8,6 @@ import 'package:cake_wallet/store/app_store.dart';
8 import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
9 import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
10 import 'package:cake_wallet/wallet_types.g.dart';
9 -import 'package:cw_core/utils/print_verbose.dart';
11 import 'package:cw_core/wallet_info.dart';
12 import 'package:cw_core/wallet_type.dart';
13 import 'package:mobx/mobx.dart';
@@ -23,11 +24,9 @@ abstract class WalletGroupsDisplayViewModelBase with Store {
24 this._walletManager,
25 this.walletListViewModel, {
26 required this.type,
26 - }) : isFetchingMnemonic = false,
27 - multiWalletGroups = ObservableList<WalletGroup>(),
28 - singleWalletsList = ObservableList<WalletInfo>() {
29 - reaction((_) => _appStore.wallet, (_) => updateWalletInfoSourceList());
30 - updateWalletInfoSourceList();
27 + }) : isFetchingMnemonic = false {
28 + reaction((_) => _appStore.wallet, (_) => unawaited(updateWalletInfoSourceList()));
29 + unawaited(updateWalletInfoSourceList());
30 }
31
32 final WalletType type;
@@ -37,10 +36,10 @@ abstract class WalletGroupsDisplayViewModelBase with Store {
36 final WalletListViewModel walletListViewModel;
37
38 @observable
40 - ObservableList<WalletGroup> multiWalletGroups;
39 + ObservableList<WalletGroup> multiWalletGroups = ObservableList<WalletGroup>();
40
41 @observable
43 - ObservableList<WalletInfo> singleWalletsList;
42 + ObservableList<WalletInfo> singleWalletsList = ObservableList<WalletInfo>();
43
44 @observable
45 WalletGroup? selectedWalletGroup;
@@ -97,31 +96,33 @@ abstract class WalletGroupsDisplayViewModelBase with Store {
96 }
97
98 @action
100 - void updateWalletInfoSourceList() {
99 + Future<void> updateWalletInfoSourceList() async {
100 List<WalletGroup> wallets = [];
101
102 multiWalletGroups.clear();
103 singleWalletsList.clear();
104
106 - _walletManager.updateWalletGroups();
105 + await _walletManager.updateWalletGroups();
106
107 final walletGroups = _walletManager.walletGroups;
108
109 // Iterate through the wallet groups to filter and categorize wallets
110 for (var group in walletGroups) {
111 // Handle group wallet filtering
113 - bool shouldExcludeGroup = group.wallets.any((wallet) {
112 + bool shouldExcludeGroup = false;
113 + for (final wallet in group.wallets) {
114 // Check for non-BIP39 wallet types
115 bool isNonBIP39Wallet = !isBIP39Wallet(wallet.type);
116
117 // Check for nano derivation type
118 + final di = await wallet.getDerivationInfo();
119 bool isNanoDerivationType = wallet.type == WalletType.nano &&
119 - wallet.derivationInfo?.derivationType == DerivationType.nano;
120 + di.derivationType == DerivationType.nano;
121
122 // Check for electrum derivation type
123 bool isElectrumDerivationType =
124 (wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) &&
124 - wallet.derivationInfo?.derivationType == DerivationType.electrum;
125 + di.derivationType == DerivationType.electrum;
126
127 // Check that selected wallet type is not present already in group
128 bool isSameTypeAsSelectedWallet = wallet.type == type;
@@ -129,16 +130,17 @@ abstract class WalletGroupsDisplayViewModelBase with Store {
130 bool isNonSeedWallet = wallet.isNonSeedWallet;
131
132 bool isNotMoneroBip39Wallet = wallet.type == WalletType.monero &&
132 - wallet.derivationInfo?.derivationType != DerivationType.bip39;
133 + di.derivationType != DerivationType.bip39;
134
135 // Exclude if any of these conditions are true
135 - return isNonBIP39Wallet ||
136 - isNanoDerivationType ||
137 - isElectrumDerivationType ||
138 - isSameTypeAsSelectedWallet ||
139 - isNonSeedWallet ||
140 - isNotMoneroBip39Wallet;
141 - });
136 + shouldExcludeGroup = shouldExcludeGroup ||
137 + isNonBIP39Wallet ||
138 + isNanoDerivationType ||
139 + isElectrumDerivationType ||
140 + isSameTypeAsSelectedWallet ||
141 + isNonSeedWallet ||
142 + isNotMoneroBip39Wallet;
143 + }
144
145 if (shouldExcludeGroup) continue;
146
@@ -159,7 +161,7 @@ abstract class WalletGroupsDisplayViewModelBase with Store {
161 return WalletListItem(
162 name: info.name,
163 type: info.type,
162 - key: info.key,
164 + key: info.id,
165 isCurrent: info.name == _appStore.wallet?.name && info.type == _appStore.wallet?.type,
166 isEnabled: availableWalletTypes.contains(info.type),
167 isTestnet: info.network?.toLowerCase().contains('testnet') ?? false,
lib/view_model/wallet_hardware_restore_view_model.dart
+3 -4
@@ -33,10 +33,9 @@ abstract class WalletHardwareRestoreViewModelBase extends WalletCreationVM with
33 this.hardwareWalletVM,
34 AppStore appStore,
35 WalletCreationService walletCreationService,
36 - Box<WalletInfo> walletInfoSource,
36 SeedSettingsViewModel seedSettingsViewModel,
37 {required WalletType type})
39 - : super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
38 + : super(appStore, walletCreationService, seedSettingsViewModel,
39 type: type, isRecovery: true);
40
41 @observable
@@ -57,8 +56,8 @@ abstract class WalletHardwareRestoreViewModelBase extends WalletCreationVM with
56 @action
57 Future<void> getNextAvailableAccounts(int limit) async {
58 try {
60 - List<HardwareAccountData> accounts = await hardwareWalletVM
61 - .getHardwareWalletService(type)
59 + final service = await hardwareWalletVM.getHardwareWalletService(type);
60 + List<HardwareAccountData> accounts = await service
61 .getAvailableAccounts(index: _nextIndex, limit: limit);
62
63 availableAccounts.addAll(accounts);
lib/view_model/wallet_list/wallet_edit_view_model.dart
+1 -1
@@ -46,7 +46,7 @@ abstract class WalletEditViewModelBase with Store {
46 state = WalletEditRenamePending();
47
48 if (isWalletGroup) {
49 - _walletManager.updateWalletGroups();
49 + await _walletManager.updateWalletGroups();
50
51 _walletManager.setGroupName(walletGroupKey!, newName);
52 } else {
lib/view_model/wallet_list/wallet_list_view_model.dart
+69 -54
@@ -2,12 +2,12 @@ import 'package:cake_wallet/core/wallet_loading_service.dart';
2 import 'package:cake_wallet/entities/wallet_group.dart';
3 import 'package:cake_wallet/entities/wallet_list_order_types.dart';
4 import 'package:cake_wallet/entities/wallet_manager.dart';
5 -import 'package:hive/hive.dart';
5 import 'package:mobx/mobx.dart';
6 import 'package:cake_wallet/store/app_store.dart';
7 import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
8 import 'package:cw_core/wallet_info.dart';
9 import 'package:cw_core/wallet_type.dart';
10 +import 'package:cw_core/utils/print_verbose.dart';
11 import 'package:cake_wallet/wallet_types.g.dart';
12
13 part 'wallet_list_view_model.g.dart';
@@ -16,7 +16,6 @@ class WalletListViewModel = WalletListViewModelBase with _$WalletListViewModel;
16
17 abstract class WalletListViewModelBase with Store {
18 WalletListViewModelBase(
19 - this._walletInfoSource,
19 this._appStore,
20 this._walletLoadingService,
21 this._walletManager,
@@ -25,7 +24,6 @@ abstract class WalletListViewModelBase with Store {
24 singleWalletsList = ObservableList<WalletListItem>(),
25 expansionTileStateTrack = ObservableMap<int, bool>() {
26 setOrderType(_appStore.settingsStore.walletListOrder);
28 - reaction((_) => _appStore.wallet, (_) => updateList());
27 updateList();
28 }
29
@@ -63,12 +61,11 @@ abstract class WalletListViewModelBase with Store {
61
62 final AppStore _appStore;
63 final WalletManager _walletManager;
66 - final Box<WalletInfo> _walletInfoSource;
64 final WalletLoadingService _walletLoadingService;
65
66 WalletType get currentWalletType => _appStore.wallet!.type;
67
71 - bool requireHardwareWalletConnection(WalletListItem walletItem) =>
68 + Future<bool> requireHardwareWalletConnection(WalletListItem walletItem) async =>
69 _walletLoadingService.requireHardwareWalletConnection(
70 walletItem.type, walletItem.name);
71
@@ -87,50 +84,62 @@ abstract class WalletListViewModelBase with Store {
84
85 bool get ascending => _appStore.settingsStore.walletListAscending;
86
87 +
88 + bool isUpdating = false;
89 @action
91 - void updateList() {
92 - wallets.clear();
93 - multiWalletGroups.clear();
94 - singleWalletsList.clear();
95 -
96 - for (var info in _walletInfoSource.values) {
97 - wallets.add(convertWalletInfoToWalletListItem(info));
90 + Future<void> updateList() async {
91 + if (isUpdating) {
92 + return;
93 }
94 + isUpdating = true;
95 + try {
96 + wallets.clear();
97 + multiWalletGroups.clear();
98 + singleWalletsList.clear();
99
100 - //========== Split into shared seed groups and single wallets list
101 - _walletManager.updateWalletGroups();
100 + final list = await WalletInfo.getAll();
101
103 - final walletGroupsFromManager = _walletManager.walletGroups;
104 -
105 - for (var group in walletGroupsFromManager) {
106 - if (group.wallets.length == 1) {
107 - singleWalletsList.add(convertWalletInfoToWalletListItem(group.wallets.first));
108 - continue;
102 + for (var info in list) {
103 + wallets.add(convertWalletInfoToWalletListItem(info));
104 }
105
111 - multiWalletGroups.add(group);
106 + //========== Split into shared seed groups and single wallets list
107 + await _walletManager.updateWalletGroups();
108 +
109 + final walletGroupsFromManager = _walletManager.walletGroups;
110 +
111 + for (var group in walletGroupsFromManager) {
112 + if (group.wallets.length == 1) {
113 + singleWalletsList.add(convertWalletInfoToWalletListItem(group.wallets.first));
114 + continue;
115 + }
116 +
117 + multiWalletGroups.add(group);
118 + }
119 + } finally {
120 + isUpdating = false;
121 }
122 }
123
124 Future<void> reorderAccordingToWalletList() async {
125 if (wallets.isEmpty) {
117 - updateList();
126 + await updateList();
127 return;
128 }
129
130 _appStore.settingsStore.walletListOrder = FilterListOrderType.Custom;
131
132 // make a copy of the walletInfoSource:
124 - List<WalletInfo> walletInfoSourceCopy = _walletInfoSource.values.toList();
125 - // delete all wallets from walletInfoSource:
126 - await _walletInfoSource.clear();
133 + List<WalletInfo> wiList = await WalletInfo.getAll();
134
135 // Reorder single wallets using the singleWalletsList
136 + int oldI = 0;
137 for (WalletListItem wallet in singleWalletsList) {
130 - for (int i = 0; i < walletInfoSourceCopy.length; i++) {
131 - if (walletInfoSourceCopy[i].name == wallet.name) {
132 - await _walletInfoSource.add(walletInfoSourceCopy[i]);
133 - walletInfoSourceCopy.removeAt(i);
138 + for (int i = 0; i < wiList.length; i++) {
139 + if (wiList[i].id == wallet.key) {
140 + oldI++;
141 + wiList[i].sortOrder = oldI;
142 + await wiList[i].save();
143 break;
144 }
145 }
@@ -139,10 +148,11 @@ abstract class WalletListViewModelBase with Store {
148 // Reorder wallets within multi-wallet groups
149 for (WalletGroup group in multiWalletGroups) {
150 for (WalletInfo walletInfo in group.wallets) {
142 - for (int i = 0; i < walletInfoSourceCopy.length; i++) {
143 - if (walletInfoSourceCopy[i].name == walletInfo.name) {
144 - await _walletInfoSource.add(walletInfoSourceCopy[i]);
145 - walletInfoSourceCopy.removeAt(i);
151 + for (int i = 0; i < wiList.length; i++) {
152 + if (wiList[i].name == walletInfo.name) {
153 + wiList[i].sortOrder = i+oldI;
154 + await wiList[i].save();
155 + wiList.removeAt(i);
156 break;
157 }
158 }
@@ -150,47 +160,52 @@ abstract class WalletListViewModelBase with Store {
160 }
161
162 // Rebuild the list of wallets and groups
153 - updateList();
163 + await updateList();
164 }
165
166 Future<void> sortGroupByType() async {
167 // sort the wallets by type:
158 - List<WalletInfo> walletInfoSourceCopy = _walletInfoSource.values.toList();
159 - await _walletInfoSource.clear();
168 + List<WalletInfo> wiList = await WalletInfo.getAll();
169 if (ascending) {
161 - walletInfoSourceCopy
162 - .sort((a, b) => a.type.toString().compareTo(b.type.toString()));
170 + wiList.sort((a, b) => a.type.toString().compareTo(b.type.toString()));
171 } else {
164 - walletInfoSourceCopy
165 - .sort((a, b) => b.type.toString().compareTo(a.type.toString()));
172 + wiList.sort((a, b) => b.type.toString().compareTo(a.type.toString()));
173 }
167 - await _walletInfoSource.addAll(walletInfoSourceCopy);
168 - updateList();
174 + for (int i = 0; i < wiList.length; i++) {
175 + wiList[i].sortOrder = i;
176 + await wiList[i].save();
177 + }
178 + await updateList();
179 }
180
181 Future<void> sortAlphabetically() async {
182 // sort the wallets alphabetically:
173 - List<WalletInfo> walletInfoSourceCopy = _walletInfoSource.values.toList();
174 - await _walletInfoSource.clear();
183 + List<WalletInfo> wiList = await WalletInfo.getAll();
184 if (ascending) {
176 - walletInfoSourceCopy.sort((a, b) => a.name.compareTo(b.name));
185 + wiList.sort((a, b) => a.name.compareTo(b.name));
186 } else {
178 - walletInfoSourceCopy.sort((a, b) => b.name.compareTo(a.name));
187 + wiList.sort((a, b) => b.name.compareTo(a.name));
188 }
180 - await _walletInfoSource.addAll(walletInfoSourceCopy);
181 - updateList();
189 + for (int i = 0; i < wiList.length; i++) {
190 + wiList[i].sortOrder = i;
191 + await wiList[i].save();
192 + }
193 + await updateList();
194 }
195
196 Future<void> sortByCreationDate() async {
197 // sort the wallets by creation date:
186 - List<WalletInfo> walletInfoSourceCopy = _walletInfoSource.values.toList();
187 - await _walletInfoSource.clear();
198 + List<WalletInfo> wiList = await WalletInfo.getAll();
199 if (ascending) {
189 - walletInfoSourceCopy.sort((a, b) => a.date.compareTo(b.date));
200 + wiList.sort((a, b) => a.date.compareTo(b.date));
201 } else {
191 - walletInfoSourceCopy.sort((a, b) => b.date.compareTo(a.date));
202 + wiList.sort((a, b) => b.date.compareTo(a.date));
203 + }
204 + for (int i = 0; i < wiList.length; i++) {
205 + wiList[i].sortOrder = i;
206 + await wiList[i].save();
207 }
193 - await _walletInfoSource.addAll(walletInfoSourceCopy);
208 +
209 updateList();
210 }
211
@@ -223,7 +238,7 @@ abstract class WalletListViewModelBase with Store {
238 return WalletListItem(
239 name: info.name,
240 type: info.type,
226 - key: info.key,
241 + key: info.id,
242 isCurrent: info.name == _appStore.wallet?.name &&
243 info.type == _appStore.wallet?.type,
244 isEnabled: availableWalletTypes.contains(info.type),
lib/view_model/wallet_new_vm.dart
+1 -2
@@ -34,12 +34,11 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
34 WalletNewVMBase(
35 AppStore appStore,
36 WalletCreationService walletCreationService,
37 - Box<WalletInfo> walletInfoSource,
37 this.advancedPrivacySettingsViewModel,
38 SeedSettingsViewModel seedSettingsViewModel, {
39 required this.newWalletArguments,
40 }) : selectedMnemonicLanguage = '',
42 - super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
41 + super(appStore, walletCreationService, seedSettingsViewModel,
42 type: newWalletArguments!.type, isRecovery: false);
43
44 final NewWalletArguments? newWalletArguments;
lib/view_model/wallet_restore_view_model.dart
+2 -3
@@ -24,7 +24,6 @@ import 'package:cw_core/wallet_base.dart';
24 import 'package:cw_core/wallet_credentials.dart';
25 import 'package:cw_core/wallet_info.dart';
26 import 'package:cw_core/wallet_type.dart';
27 -import 'package:hive/hive.dart';
27 import 'package:mobx/mobx.dart';
28
29 part 'wallet_restore_view_model.g.dart';
@@ -33,12 +32,12 @@ class WalletRestoreViewModel = WalletRestoreViewModelBase with _$WalletRestoreVi
32
33 abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
34 WalletRestoreViewModelBase(AppStore appStore, WalletCreationService walletCreationService,
36 - Box<WalletInfo> walletInfoSource, SeedSettingsViewModel seedSettingsViewModel,
35 + SeedSettingsViewModel seedSettingsViewModel,
36 {required WalletType type, this.restoredWallet, this.hardwareWalletType})
37 : isButtonEnabled = restoredWallet != null,
38 hasPassphrase = false,
39 mode = restoredWallet?.restoreMode ?? WalletRestoreMode.seed,
41 - super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
40 + super(appStore, walletCreationService, seedSettingsViewModel,
41 type: type, isRecovery: true) {
42 switch (type) {
43 case WalletType.monero:
lib/view_model/wallet_switcher_view_model.dart
+4 -6
@@ -5,7 +5,6 @@ import 'package:cake_wallet/core/wallet_loading_service.dart';
5 import 'package:cw_core/wallet_info.dart';
6 import 'package:cw_core/wallet_type.dart';
7 import 'package:cw_core/utils/print_verbose.dart';
8 -import 'package:hive/hive.dart';
8 import 'package:mobx/mobx.dart';
9
10 part 'wallet_switcher_view_model.g.dart';
@@ -16,12 +15,10 @@ abstract class WalletSwitcherViewModelBase with Store {
15 WalletSwitcherViewModelBase({
16 required this.appStore,
17 required this.walletLoadingService,
19 - required this.walletInfoSource,
18 });
19
20 final AppStore appStore;
21 final WalletLoadingService walletLoadingService;
24 - final Box<WalletInfo> walletInfoSource;
22
23 @observable
24 WalletInfo? selectedWallet;
@@ -30,12 +27,13 @@ abstract class WalletSwitcherViewModelBase with Store {
27 bool isProcessing = false;
28
29 @action
33 - List<WalletInfo> getWallets(WalletType? walletType) {
30 + Future<List<WalletInfo>> getWallets(WalletType? walletType) async {
31 + final wiList = await WalletInfo.getAll();
32 if (walletType == null) {
35 - return walletInfoSource.values.toList();
33 + return wiList;
34 }
35
38 - return walletInfoSource.values.where((wallet) => wallet.type == walletType).toList();
36 + return wiList.where((wallet) => wallet.type == walletType).toList();
37 }
38
39 @action
lib/wownero/cw_wownero.dart
+2 -3
@@ -315,9 +315,8 @@ class CWWownero extends Wownero {
315 }
316
317 @override
318 - WalletService createWowneroWalletService(
319 - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) =>
320 - WowneroWalletService(walletInfoSource, unspentCoinSource);
318 + WalletService createWowneroWalletService(Box<UnspentCoinsInfo> unspentCoinSource) =>
319 + WowneroWalletService(unspentCoinSource);
320
321 @override
322 String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) {
lib/zano/cw_zano.dart
+2 -2
@@ -109,8 +109,8 @@ class CWZano extends Zano {
109 // }
110
111 @override
112 - WalletService createZanoWalletService(Box<WalletInfo> walletInfoSource) {
113 - return ZanoWalletService(walletInfoSource);
112 + WalletService createZanoWalletService() {
113 + return ZanoWalletService();
114 }
115
116 @override
tool/configure.dart
+20 -22
@@ -208,8 +208,8 @@ abstract class Bitcoin {
208 List<Unspent> getUnspents(Object wallet, {UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any});
209 Future<void> updateUnspents(Object wallet);
210 WalletService createBitcoinWalletService(
211 - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, Box<PayjoinSession> payjoinSessionSource, bool isDirect);
212 - WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect);
211 + Box<UnspentCoinsInfo> unspentCoinSource, Box<PayjoinSession> payjoinSessionSource, bool isDirect);
212 + WalletService createLitecoinWalletService(Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect);
213 TransactionPriority getBitcoinTransactionPriorityMedium();
214 TransactionPriority getBitcoinTransactionPriorityCustom();
215 TransactionPriority getLitecoinTransactionPriorityMedium();
@@ -252,7 +252,7 @@ abstract class Bitcoin {
252 void deleteSilentPaymentAddress(Object wallet, String address);
253 Future<void> updateFeeRates(Object wallet);
254 int getMaxCustomFeeRate(Object wallet);
255 - void setHardwareWalletService(WalletBase wallet, HardwareWalletService service);
255 + Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service);
256 HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection, bool isBitcoin);
257 HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager, bool isBitcoin);
258 HardwareWalletService getTrezorHardwareWalletService(trezor.TrezorConnect connect, bool isBitcoin);
@@ -451,9 +451,9 @@ WalletCredentials createMoneroNewWalletCredentials({required String name, requir
451 void setCurrentAccount(Object wallet, int id, String label, String? balance);
452 void onStartup();
453 int getTransactionInfoAccountId(TransactionInfo tx);
454 - WalletService createMoneroWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource);
454 + WalletService createMoneroWalletService(Box<UnspentCoinsInfo> unspentCoinSource);
455 Map<String, String> pendingTransactionInfo(Object transaction);
456 - void setLedgerConnection(Object wallet, ledger.LedgerConnection connection);
456 + Future<void> setLedgerConnection(Object wallet, ledger.LedgerConnection connection);
457 void resetLedgerConnection();
458 void setGlobalLedgerConnection(ledger.LedgerConnection connection);
459 String? getLastLedgerCommand();
@@ -641,7 +641,7 @@ abstract class Wownero {
641 void setCurrentAccount(Object wallet, int id, String label, String? balance);
642 void onStartup();
643 int getTransactionInfoAccountId(TransactionInfo tx);
644 - WalletService createWowneroWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource);
644 + WalletService createWowneroWalletService(Box<UnspentCoinsInfo> unspentCoinSource);
645 Map<String, String> pendingTransactionInfo(Object transaction);
646 String getLegacySeed(Object wallet, String langName);
647 Map<String, List<int>> debugCallLength();
@@ -736,7 +736,7 @@ import 'package:eth_sig_util/util/utils.dart';
736 const ethereumContent = """
737 abstract class Ethereum {
738 List<String> getEthereumWordList(String language);
739 - WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource, bool isDirect);
739 + WalletService createEthereumWalletService(bool isDirect);
740 WalletCredentials createEthereumNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password, String? mnemonic, String? passphrase});
741 WalletCredentials createEthereumRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password, String? passphrase});
742 WalletCredentials createEthereumRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password});
@@ -789,7 +789,7 @@ abstract class Ethereum {
789 Future<PendingTransaction> reinvestDEuroInterest(WalletBase wallet, TransactionPriority priority);
790 Future<PendingTransaction> enableDEuroSaving(WalletBase wallet, TransactionPriority priority);
791
792 - void setHardwareWalletService(WalletBase wallet, HardwareWalletService service);
792 + Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service);
793 HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection);
794 HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager);
795 HardwareWalletService getTrezorHardwareWalletService(trezor.TrezorConnect connect);
@@ -866,7 +866,7 @@ import 'package:eth_sig_util/util/utils.dart';
866 const polygonContent = """
867 abstract class Polygon {
868 List<String> getPolygonWordList(String language);
869 - WalletService createPolygonWalletService(Box<WalletInfo> walletInfoSource, bool isDirect);
869 + WalletService createPolygonWalletService(bool isDirect);
870 WalletCredentials createPolygonNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password, String? mnemonic, String? passphrase});
871 WalletCredentials createPolygonRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password, String? passphrase});
872 WalletCredentials createPolygonRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password});
@@ -910,7 +910,7 @@ abstract class Polygon {
910 Web3Client? getWeb3Client(WalletBase wallet);
911 String getTokenAddress(CryptoCurrency asset);
912
913 - void setHardwareWalletService(WalletBase wallet, HardwareWalletService service);
913 + Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service);
914 HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection);
915 HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager);
916 HardwareWalletService getTrezorHardwareWalletService(trezor.TrezorConnect connect);
@@ -959,7 +959,7 @@ abstract class BitcoinCash {
959 String getCashAddrFormat(String address);
960
961 WalletService createBitcoinCashWalletService(
962 - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect);
962 + Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect);
963
964 WalletCredentials createBitcoinCashNewWalletCredentials(
965 {required String name, WalletInfo? walletInfo, String? password, String? passphrase, String? mnemonic});
@@ -1040,7 +1040,7 @@ abstract class Nano {
1040
1041 void setCurrentAccount(Object wallet, int id, String label, String? balance);
1042
1043 - WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource, bool isDirect);
1043 + WalletService createNanoWalletService(bool isDirect);
1044
1045 WalletCredentials createNanoNewWalletCredentials({
1046 required String name,
@@ -1162,7 +1162,7 @@ import 'package:cw_solana/default_spl_tokens.dart';
1162 const solanaContent = """
1163 abstract class Solana {
1164 List<String> getSolanaWordList(String language);
1165 - WalletService createSolanaWalletService(Box<WalletInfo> walletInfoSource, bool isDirect);
1165 + WalletService createSolanaWalletService(bool isDirect);
1166 WalletCredentials createSolanaNewWalletCredentials(
1167 {required String name, WalletInfo? walletInfo, String? password, String? mnemonic, String? passphrase});
1168 WalletCredentials createSolanaRestoreWalletFromSeedCredentials(
@@ -1251,7 +1251,7 @@ import 'package:cw_tron/default_tron_tokens.dart';
1251 const tronContent = """
1252 abstract class Tron {
1253 List<String> getTronWordList(String language);
1254 - WalletService createTronWalletService(Box<WalletInfo> walletInfoSource, bool isDirect);
1254 + WalletService createTronWalletService(bool isDirect);
1255 WalletCredentials createTronNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password, String? mnemonic, String? passphrase});
1256 WalletCredentials createTronRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password, String? passphrase});
1257 WalletCredentials createTronRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password});
@@ -1341,7 +1341,7 @@ abstract class Zano {
1341 Object createZanoTransactionCredentials({required List<Output> outputs, required TransactionPriority priority, required CryptoCurrency currency});
1342 double formatterIntAmountToDouble({required int amount, required CryptoCurrency currency, required bool forFee});
1343 int formatterParseAmount({required String amount, required CryptoCurrency currency});
1344 - WalletService createZanoWalletService(Box<WalletInfo> walletInfoSource);
1344 + WalletService createZanoWalletService();
1345 CryptoCurrency? assetOfTransaction(WalletBase wallet, TransactionInfo tx);
1346 List<ZanoAsset> getZanoAssets(WalletBase wallet);
1347 String getZanoAssetAddress(CryptoCurrency asset);
@@ -1405,8 +1405,7 @@ abstract class Decred {
1405 {required String name, required String mnemonic, required String password});
1406 WalletCredentials createDecredRestoreWalletFromPubkeyCredentials(
1407 {required String name, required String pubkey, required String password});
1408 - WalletService createDecredWalletService(
1409 - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource);
1408 + WalletService createDecredWalletService(Box<UnspentCoinsInfo> unspentCoinSource);
1409
1410 List<TransactionPriority> getTransactionPriorities();
1411 TransactionPriority getDecredTransactionPriorityMedium();
@@ -1415,7 +1414,7 @@ abstract class Decred {
1414
1415 Object createDecredTransactionCredentials(List<Output> outputs, TransactionPriority priority);
1416
1418 - List<AddressInfo> getAddressInfos(Object wallet);
1417 + List<WalletInfoAddressInfo> getAddressInfos(Object wallet);
1418 Future<void> updateAddress(Object wallet, String address, String label);
1419 Future<void> generateNewAddress(Object wallet, String label);
1420
@@ -1468,8 +1467,7 @@ import 'package:cw_dogecoin/cw_dogecoin.dart';
1467 const dogecoinContent = """
1468 abstract class DogeCoin {
1469
1471 - WalletService createDogeCoinWalletService(
1472 - Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect);
1470 + WalletService createDogeCoinWalletService(Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect);
1471
1472 WalletCredentials createDogeCoinNewWalletCredentials(
1473 {required String name, WalletInfo? walletInfo, String? password, String? passphrase, String? mnemonic});
@@ -1552,7 +1550,7 @@ import 'package:eth_sig_util/util/utils.dart';
1550 const baseContent = """
1551 abstract class Base {
1552 List<String> getBaseWordList(String language);
1555 - WalletService createBaseWalletService(Box<WalletInfo> walletInfoSource, bool isDirect);
1553 + WalletService createBaseWalletService(bool isDirect);
1554 WalletCredentials createBaseNewWalletCredentials(
1555 {required String name,
1556 WalletInfo? walletInfo,
@@ -1609,7 +1607,7 @@ abstract class Base {
1607 Web3Client? getWeb3Client(WalletBase wallet);
1608 String getTokenAddress(CryptoCurrency asset);
1609
1612 - void setHardwareWalletService(WalletBase wallet, HardwareWalletService service);
1610 + Future<void> setHardwareWalletService(WalletBase wallet, HardwareWalletService service);
1611 HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection);
1612 HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager);
1613 List<String> getDefaultTokenContractAddresses();
tool/localization/localization_constants.dart
+5 -5
@@ -48,19 +48,19 @@ class S implements WidgetsLocalizations {
48
49 @override
50 String get copyButtonLabel => "copyButtonLabel";
51 -
51 +
52 @override
53 String get cutButtonLabel => "cutButtonLabel";
54 -
54 +
55 @override
56 String get lookUpButtonLabel => "lookUpButtonLabel";
57 -
57 +
58 @override
59 String get pasteButtonLabel => "pasteButtonLabel";
60 -
60 +
61 @override
62 String get searchWebButtonLabel => "searchWebButtonLabel";
63 -
63 +
64 @override
65 String get selectAllButtonLabel => "selectAllButtonLabel";
66