Cw 688 avoid wallet file corruption (#1582)
* CW-688 Store Seed and keys in .keys file * CW-688 Open wallet from keys in .keys file and migrate wallets using the old file * CW-688 Open wallet from keys in .keys file and migrate wallets using the old file * CW-688 Restore .keys file from .keys.backup * CW-688 Restore .keys file from .keys.backup * CW-688 Move saving .keys files into the save function instead of the service * CW-688 Handle corrupt wallets * CW-688 Handle corrupt wallets * CW-688 Remove code duplication * CW-688 Reduce cache dependency * wrap any file reading/writing function with try/catch [skip ci] --------- Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com>
Omar Hatem committed
Aug 9, 2024 at 23:15 UTC
fb33a6f23dce32172d9b80f343972dc4e07289c3
18 files changed
+433
-144
cw_bitcoin/lib/bitcoin_wallet.dart
+42
-24
@@ -6,15 +6,16 @@ import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
6
import 'package:convert/convert.dart';
7
import 'package:cw_bitcoin/bitcoin_address_record.dart';
8
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
9
-import 'package:cw_bitcoin/electrum_derivations.dart';
9
import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
10
import 'package:cw_bitcoin/electrum_balance.dart';
11
+import 'package:cw_bitcoin/electrum_derivations.dart';
12
import 'package:cw_bitcoin/electrum_wallet.dart';
13
import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
14
import 'package:cw_bitcoin/psbt_transaction_builder.dart';
15
import 'package:cw_core/crypto_currency.dart';
16
import 'package:cw_core/unspent_coins_info.dart';
17
import 'package:cw_core/wallet_info.dart';
18
+import 'package:cw_core/wallet_keys_file.dart';
19
import 'package:flutter/foundation.dart';
20
import 'package:hive/hive.dart';
21
import 'package:ledger_bitcoin/ledger_bitcoin.dart';
@@ -143,49 +144,66 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
144
final network = walletInfo.network != null
145
? BasedUtxoNetwork.fromName(walletInfo.network!)
146
: BitcoinNetwork.mainnet;
146
- final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, network);
147
148
- walletInfo.derivationInfo ??= DerivationInfo(
149
- derivationType: snp.derivationType ?? DerivationType.electrum,
150
- derivationPath: snp.derivationPath,
151
- );
148
+ final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
149
+
150
+ ElectrumWalletSnapshot? snp = null;
151
+
152
+ try {
153
+ snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, network);
154
+ } catch (e) {
155
+ if (!hasKeysFile) rethrow;
156
+ }
157
+
158
+ final WalletKeysData keysData;
159
+ // Migrate wallet from the old scheme to then new .keys file scheme
160
+ if (!hasKeysFile) {
161
+ keysData =
162
+ WalletKeysData(mnemonic: snp!.mnemonic, xPub: snp.xpub, passphrase: snp.passphrase);
163
+ } else {
164
+ keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
165
+ }
166
+
167
+ walletInfo.derivationInfo ??= DerivationInfo();
168
169
// set the default if not present:
154
- walletInfo.derivationInfo!.derivationPath = snp.derivationPath ?? electrum_path;
155
- walletInfo.derivationInfo!.derivationType = snp.derivationType ?? DerivationType.electrum;
170
+ walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path;
171
+ walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
172
173
Uint8List? seedBytes = null;
174
+ final mnemonic = keysData.mnemonic;
175
+ final passphrase = keysData.passphrase;
176
159
- if (snp.mnemonic != null) {
177
+ if (mnemonic != null) {
178
switch (walletInfo.derivationInfo!.derivationType) {
179
case DerivationType.electrum:
162
- seedBytes = await mnemonicToSeedBytes(snp.mnemonic!);
180
+ seedBytes = await mnemonicToSeedBytes(mnemonic);
181
break;
182
case DerivationType.bip39:
183
default:
184
seedBytes = await bip39.mnemonicToSeed(
167
- snp.mnemonic!,
168
- passphrase: snp.passphrase ?? '',
185
+ mnemonic,
186
+ passphrase: passphrase ?? '',
187
);
188
break;
189
}
190
}
191
192
return BitcoinWallet(
175
- mnemonic: snp.mnemonic,
176
- xpub: snp.xpub,
193
+ mnemonic: mnemonic,
194
+ xpub: keysData.xPub,
195
password: password,
178
- passphrase: snp.passphrase,
196
+ passphrase: passphrase,
197
walletInfo: walletInfo,
198
unspentCoinsInfo: unspentCoinsInfo,
181
- initialAddresses: snp.addresses,
182
- initialSilentAddresses: snp.silentAddresses,
183
- initialSilentAddressIndex: snp.silentAddressIndex,
184
- initialBalance: snp.balance,
199
+ initialAddresses: snp?.addresses,
200
+ initialSilentAddresses: snp?.silentAddresses,
201
+ initialSilentAddressIndex: snp?.silentAddressIndex ?? 0,
202
+ initialBalance: snp?.balance,
203
seedBytes: seedBytes,
186
- initialRegularAddressIndex: snp.regularAddressIndex,
187
- initialChangeAddressIndex: snp.changeAddressIndex,
188
- addressPageType: snp.addressPageType,
204
+ initialRegularAddressIndex: snp?.regularAddressIndex,
205
+ initialChangeAddressIndex: snp?.changeAddressIndex,
206
+ addressPageType: snp?.addressPageType,
207
networkParam: network,
208
alwaysScan: alwaysScan,
209
);
@@ -249,8 +267,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
267
final accountPath = walletInfo.derivationInfo?.derivationPath;
268
final derivationPath = accountPath != null ? "$accountPath/$isChange/$index" : null;
269
252
- final signature = await _bitcoinLedgerApp!
253
- .signMessage(_ledgerDevice!, message: ascii.encode(message), signDerivationPath: derivationPath);
270
+ final signature = await _bitcoinLedgerApp!.signMessage(_ledgerDevice!,
271
+ message: ascii.encode(message), signDerivationPath: derivationPath);
272
return base64Encode(signature);
273
}
274
cw_bitcoin/lib/bitcoin_wallet_service.dart
+2
@@ -41,8 +41,10 @@ class BitcoinWalletService extends WalletService<
41
unspentCoinsInfo: unspentCoinsInfoSource,
42
network: network,
43
);
44
+
45
await wallet.save();
46
await wallet.init();
47
+
48
return wallet;
49
}
50
cw_bitcoin/lib/electrum_wallet.dart
+11
-3
@@ -37,6 +37,7 @@ import 'package:cw_core/unspent_coins_info.dart';
37
import 'package:cw_core/utils/file.dart';
38
import 'package:cw_core/wallet_base.dart';
39
import 'package:cw_core/wallet_info.dart';
40
+import 'package:cw_core/wallet_keys_file.dart';
41
import 'package:cw_core/wallet_type.dart';
42
import 'package:cw_core/get_height_by_date.dart';
43
import 'package:flutter/foundation.dart';
@@ -54,7 +55,7 @@ const int TWEAKS_COUNT = 25;
55
56
abstract class ElectrumWalletBase
57
extends WalletBase<ElectrumBalance, ElectrumTransactionHistory, ElectrumTransactionInfo>
57
- with Store {
58
+ with Store, WalletKeysFile {
59
ElectrumWalletBase({
60
required String password,
61
required WalletInfo walletInfo,
@@ -169,6 +170,10 @@ abstract class ElectrumWalletBase
170
@override
171
String? get seed => _mnemonic;
172
173
+ @override
174
+ WalletKeysData get walletKeysData =>
175
+ WalletKeysData(mnemonic: _mnemonic, xPub: xpub, passphrase: passphrase);
176
+
177
bitcoin.NetworkType networkType;
178
BasedUtxoNetwork network;
179
@@ -1076,6 +1081,11 @@ abstract class ElectrumWalletBase
1081
1082
@override
1083
Future<void> save() async {
1084
+ if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) {
1085
+ await saveKeysFile(_password);
1086
+ saveKeysFile(_password, true);
1087
+ }
1088
+
1089
final path = await makePath();
1090
await write(path: path, password: _password, data: toJSON());
1091
await transactionHistory.save();
@@ -1131,8 +1141,6 @@ abstract class ElectrumWalletBase
1141
_autoSaveTimer?.cancel();
1142
}
1143
1134
- Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
1135
-
1144
@action
1145
Future<void> updateAllUnspents() async {
1146
List<BitcoinUnspent> updatedUnspentCoins = [];
cw_bitcoin/lib/electrum_wallet_snapshot.dart
+7
-1
@@ -32,15 +32,21 @@ class ElectrumWalletSnapshot {
32
final WalletType type;
33
final String? addressPageType;
34
35
+ @deprecated
36
String? mnemonic;
37
+
38
+ @deprecated
39
String? xpub;
40
+
41
+ @deprecated
42
+ String? passphrase;
43
+
44
List<BitcoinAddressRecord> addresses;
45
List<BitcoinSilentPaymentAddressRecord> silentAddresses;
46
ElectrumBalance balance;
47
Map<String, int> regularAddressIndex;
48
Map<String, int> changeAddressIndex;
49
int silentAddressIndex;
43
- String? passphrase;
50
DerivationType? derivationType;
51
String? derivationPath;
52
cw_bitcoin/lib/litecoin_wallet.dart
+37
-18
@@ -1,20 +1,21 @@
1
+import 'package:bip39/bip39.dart' as bip39;
2
import 'package:bitcoin_base/bitcoin_base.dart';
3
+import 'package:cw_bitcoin/bitcoin_address_record.dart';
4
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
5
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
4
-import 'package:cw_core/crypto_currency.dart';
5
-import 'package:cw_core/unspent_coins_info.dart';
6
+import 'package:cw_bitcoin/electrum_balance.dart';
7
+import 'package:cw_bitcoin/electrum_wallet.dart';
8
+import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
9
+import 'package:cw_bitcoin/litecoin_network.dart';
10
import 'package:cw_bitcoin/litecoin_wallet_addresses.dart';
11
+import 'package:cw_core/crypto_currency.dart';
12
import 'package:cw_core/transaction_priority.dart';
13
+import 'package:cw_core/unspent_coins_info.dart';
14
+import 'package:cw_core/wallet_info.dart';
15
+import 'package:cw_core/wallet_keys_file.dart';
16
import 'package:flutter/foundation.dart';
17
import 'package:hive/hive.dart';
18
import 'package:mobx/mobx.dart';
11
-import 'package:cw_core/wallet_info.dart';
12
-import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
13
-import 'package:cw_bitcoin/electrum_wallet.dart';
14
-import 'package:cw_bitcoin/bitcoin_address_record.dart';
15
-import 'package:cw_bitcoin/electrum_balance.dart';
16
-import 'package:cw_bitcoin/litecoin_network.dart';
17
-import 'package:bip39/bip39.dart' as bip39;
19
20
part 'litecoin_wallet.g.dart';
21
@@ -101,19 +102,37 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
102
required Box<UnspentCoinsInfo> unspentCoinsInfo,
103
required String password,
104
}) async {
104
- final snp =
105
- await ElectrumWalletSnapshot.load(name, walletInfo.type, password, LitecoinNetwork.mainnet);
105
+ final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
106
+
107
+ ElectrumWalletSnapshot? snp = null;
108
+
109
+ try {
110
+ snp = await ElectrumWalletSnapshot.load(
111
+ name, walletInfo.type, password, LitecoinNetwork.mainnet);
112
+ } catch (e) {
113
+ if (!hasKeysFile) rethrow;
114
+ }
115
+
116
+ final WalletKeysData keysData;
117
+ // Migrate wallet from the old scheme to then new .keys file scheme
118
+ if (!hasKeysFile) {
119
+ keysData =
120
+ WalletKeysData(mnemonic: snp!.mnemonic, xPub: snp.xpub, passphrase: snp.passphrase);
121
+ } else {
122
+ keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
123
+ }
124
+
125
return LitecoinWallet(
107
- mnemonic: snp.mnemonic!,
126
+ mnemonic: keysData.mnemonic!,
127
password: password,
128
walletInfo: walletInfo,
129
unspentCoinsInfo: unspentCoinsInfo,
111
- initialAddresses: snp.addresses,
112
- initialBalance: snp.balance,
113
- seedBytes: await mnemonicToSeedBytes(snp.mnemonic!),
114
- initialRegularAddressIndex: snp.regularAddressIndex,
115
- initialChangeAddressIndex: snp.changeAddressIndex,
116
- addressPageType: snp.addressPageType,
130
+ initialAddresses: snp?.addresses,
131
+ initialBalance: snp?.balance,
132
+ seedBytes: await mnemonicToSeedBytes(keysData.mnemonic!),
133
+ initialRegularAddressIndex: snp?.regularAddressIndex,
134
+ initialChangeAddressIndex: snp?.changeAddressIndex,
135
+ addressPageType: snp?.addressPageType,
136
);
137
}
138
cw_bitcoin/lib/litecoin_wallet_service.dart
+1
@@ -33,6 +33,7 @@ class LitecoinWalletService extends WalletService<
33
passphrase: credentials.passphrase,
34
walletInfo: credentials.walletInfo!,
35
unspentCoinsInfo: unspentCoinsInfoSource);
36
+
37
await wallet.save();
38
await wallet.init();
39
cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart
+27
-8
@@ -12,6 +12,7 @@ import 'package:cw_core/crypto_currency.dart';
12
import 'package:cw_core/transaction_priority.dart';
13
import 'package:cw_core/unspent_coins_info.dart';
14
import 'package:cw_core/wallet_info.dart';
15
+import 'package:cw_core/wallet_keys_file.dart';
16
import 'package:flutter/foundation.dart';
17
import 'package:hive/hive.dart';
18
import 'package:mobx/mobx.dart';
@@ -89,14 +90,32 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
90
required Box<UnspentCoinsInfo> unspentCoinsInfo,
91
required String password,
92
}) async {
92
- final snp = await ElectrumWalletSnapshot.load(
93
- name, walletInfo.type, password, BitcoinCashNetwork.mainnet);
93
+ final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
94
+
95
+ ElectrumWalletSnapshot? snp = null;
96
+
97
+ try {
98
+ snp = await ElectrumWalletSnapshot.load(
99
+ name, walletInfo.type, password, BitcoinCashNetwork.mainnet);
100
+ } catch (e) {
101
+ if (!hasKeysFile) rethrow;
102
+ }
103
+
104
+ final WalletKeysData keysData;
105
+ // Migrate wallet from the old scheme to then new .keys file scheme
106
+ if (!hasKeysFile) {
107
+ keysData =
108
+ WalletKeysData(mnemonic: snp!.mnemonic, xPub: snp.xpub, passphrase: snp.passphrase);
109
+ } else {
110
+ keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
111
+ }
112
+
113
return BitcoinCashWallet(
95
- mnemonic: snp.mnemonic!,
114
+ mnemonic: keysData.mnemonic!,
115
password: password,
116
walletInfo: walletInfo,
117
unspentCoinsInfo: unspentCoinsInfo,
99
- initialAddresses: snp.addresses.map((addr) {
118
+ initialAddresses: snp?.addresses.map((addr) {
119
try {
120
BitcoinCashAddress(addr.address);
121
return BitcoinAddressRecord(
@@ -116,10 +135,10 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
135
);
136
}
137
}).toList(),
119
- initialBalance: snp.balance,
120
- seedBytes: await Mnemonic.toSeed(snp.mnemonic!),
121
- initialRegularAddressIndex: snp.regularAddressIndex,
122
- initialChangeAddressIndex: snp.changeAddressIndex,
138
+ initialBalance: snp?.balance,
139
+ seedBytes: await Mnemonic.toSeed(keysData.mnemonic!),
140
+ initialRegularAddressIndex: snp?.regularAddressIndex,
141
+ initialChangeAddressIndex: snp?.changeAddressIndex,
142
addressPageType: P2pkhAddressType.p2pkh,
143
);
144
}
cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_service.dart
+2
@@ -34,8 +34,10 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
34
password: credentials.password!,
35
walletInfo: credentials.walletInfo!,
36
unspentCoinsInfo: unspentCoinsInfoSource);
37
+
38
await wallet.save();
39
await wallet.init();
40
+
41
return wallet;
42
}
43
cw_core/lib/wallet_keys_file.dart
new
+110
@@ -0,0 +1,110 @@
1
+import 'dart:convert';
2
+import 'dart:developer' as dev;
3
+import 'dart:io';
4
+
5
+import 'package:cw_core/balance.dart';
6
+import 'package:cw_core/pathForWallet.dart';
7
+import 'package:cw_core/transaction_history.dart';
8
+import 'package:cw_core/transaction_info.dart';
9
+import 'package:cw_core/utils/file.dart';
10
+import 'package:cw_core/wallet_base.dart';
11
+import 'package:cw_core/wallet_type.dart';
12
+
13
+mixin WalletKeysFile<BalanceType extends Balance, HistoryType extends TransactionHistoryBase,
14
+ TransactionType extends TransactionInfo>
15
+ on WalletBase<BalanceType, HistoryType, TransactionType> {
16
+ Future<String> makePath() => pathForWallet(name: walletInfo.name, type: walletInfo.type);
17
+
18
+ // this needs to be overridden
19
+ WalletKeysData get walletKeysData;
20
+
21
+ Future<String> makeKeysFilePath() async => "${await makePath()}.keys";
22
+
23
+ Future<void> saveKeysFile(String password, [bool isBackup = false]) async {
24
+ try {
25
+ final rootPath = await makeKeysFilePath();
26
+ final path = "$rootPath${isBackup ? ".backup" : ""}";
27
+ dev.log("Saving .keys file '$path'");
28
+ await write(path: path, password: password, data: walletKeysData.toJSON());
29
+ } catch (_) {}
30
+ }
31
+
32
+ static Future<void> createKeysFile(
33
+ String name, WalletType type, String password, WalletKeysData walletKeysData,
34
+ [bool withBackup = true]) async {
35
+ try {
36
+ final rootPath = await pathForWallet(name: name, type: type);
37
+ final path = "$rootPath.keys";
38
+
39
+ dev.log("Saving .keys file '$path'");
40
+ await write(path: path, password: password, data: walletKeysData.toJSON());
41
+
42
+ if (withBackup) {
43
+ dev.log("Saving .keys.backup file '$path.backup'");
44
+ await write(path: "$path.backup", password: password, data: walletKeysData.toJSON());
45
+ }
46
+ } catch (_) {}
47
+ }
48
+
49
+ static Future<bool> hasKeysFile(String name, WalletType type) async {
50
+ try {
51
+ final path = await pathForWallet(name: name, type: type);
52
+ return File("$path.keys").existsSync() || File("$path.keys.backup").existsSync();
53
+ } catch (_) {
54
+ return false;
55
+ }
56
+ }
57
+
58
+ static Future<WalletKeysData> readKeysFile(String name, WalletType type, String password) async {
59
+ final path = await pathForWallet(name: name, type: type);
60
+
61
+ var readPath = "$path.keys";
62
+ try {
63
+ if (!File(readPath).existsSync()) throw Exception("No .keys file found for $name $type");
64
+
65
+ final jsonSource = await read(path: readPath, password: password);
66
+ final data = json.decode(jsonSource) as Map<String, dynamic>;
67
+ return WalletKeysData.fromJSON(data);
68
+ } catch (e) {
69
+ dev.log("Failed to read .keys file. Trying .keys.backup file...");
70
+
71
+ readPath = "$readPath.backup";
72
+ if (!File(readPath).existsSync())
73
+ throw Exception("No .keys nor a .keys.backup file found for $name $type");
74
+
75
+ final jsonSource = await read(path: readPath, password: password);
76
+ final data = json.decode(jsonSource) as Map<String, dynamic>;
77
+ final keysData = WalletKeysData.fromJSON(data);
78
+
79
+ dev.log("Restoring .keys from .keys.backup");
80
+ createKeysFile(name, type, password, keysData, false);
81
+ return keysData;
82
+ }
83
+ }
84
+}
85
+
86
+class WalletKeysData {
87
+ final String? privateKey;
88
+ final String? mnemonic;
89
+ final String? altMnemonic;
90
+ final String? passphrase;
91
+ final String? xPub;
92
+
93
+ WalletKeysData({this.privateKey, this.mnemonic, this.altMnemonic, this.passphrase, this.xPub});
94
+
95
+ String toJSON() => jsonEncode({
96
+ "privateKey": privateKey,
97
+ "mnemonic": mnemonic,
98
+ if (altMnemonic != null) "altMnemonic": altMnemonic,
99
+ if (passphrase != null) "passphrase": passphrase,
100
+ if (xPub != null) "xPub": xPub
101
+ });
102
+
103
+ static WalletKeysData fromJSON(Map<String, dynamic> json) => WalletKeysData(
104
+ privateKey: json["privateKey"] as String?,
105
+ mnemonic: json["mnemonic"] as String?,
106
+ altMnemonic: json["altMnemonic"] as String?,
107
+ passphrase: json["passphrase"] as String?,
108
+ xPub: json["xPub"] as String?,
109
+ );
110
+}
cw_ethereum/lib/ethereum_wallet.dart
+26
-7
@@ -6,6 +6,7 @@ import 'package:cw_core/erc20_token.dart';
6
import 'package:cw_core/pathForWallet.dart';
7
import 'package:cw_core/transaction_direction.dart';
8
import 'package:cw_core/wallet_info.dart';
9
+import 'package:cw_core/wallet_keys_file.dart';
10
import 'package:cw_ethereum/default_ethereum_erc20_tokens.dart';
11
import 'package:cw_ethereum/ethereum_client.dart';
12
import 'package:cw_ethereum/ethereum_transaction_history.dart';
@@ -122,19 +123,37 @@ class EthereumWallet extends EVMChainWallet {
123
124
static Future<EthereumWallet> open(
125
{required String name, required String password, required WalletInfo walletInfo}) async {
126
+ final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
127
final path = await pathForWallet(name: name, type: walletInfo.type);
126
- final jsonSource = await read(path: path, password: password);
127
- final data = json.decode(jsonSource) as Map;
128
- final mnemonic = data['mnemonic'] as String?;
129
- final privateKey = data['private_key'] as String?;
130
- final balance = EVMChainERC20Balance.fromJSON(data['balance'] as String) ??
128
+
129
+ Map<String, dynamic>? data;
130
+ try {
131
+ final jsonSource = await read(path: path, password: password);
132
+
133
+ data = json.decode(jsonSource) as Map<String, dynamic>;
134
+ } catch (e) {
135
+ if (!hasKeysFile) rethrow;
136
+ }
137
+
138
+ final balance = EVMChainERC20Balance.fromJSON(data?['balance'] as String) ??
139
EVMChainERC20Balance(BigInt.zero);
140
141
+ final WalletKeysData keysData;
142
+ // Migrate wallet from the old scheme to then new .keys file scheme
143
+ if (!hasKeysFile) {
144
+ final mnemonic = data!['mnemonic'] as String?;
145
+ final privateKey = data['private_key'] as String?;
146
+
147
+ keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey);
148
+ } else {
149
+ keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
150
+ }
151
+
152
return EthereumWallet(
153
walletInfo: walletInfo,
154
password: password,
136
- mnemonic: mnemonic,
137
- privateKey: privateKey,
155
+ mnemonic: keysData.mnemonic,
156
+ privateKey: keysData.privateKey,
157
initialBalance: balance,
158
client: EthereumClient(),
159
);
cw_evm/lib/evm_chain_wallet.dart
+9
-2
@@ -16,6 +16,7 @@ import 'package:cw_core/transaction_priority.dart';
16
import 'package:cw_core/wallet_addresses.dart';
17
import 'package:cw_core/wallet_base.dart';
18
import 'package:cw_core/wallet_info.dart';
19
+import 'package:cw_core/wallet_keys_file.dart';
20
import 'package:cw_core/wallet_type.dart';
21
import 'package:cw_evm/evm_chain_client.dart';
22
import 'package:cw_evm/evm_chain_exceptions.dart';
@@ -58,7 +59,7 @@ abstract class EVMChainWallet = EVMChainWalletBase with _$EVMChainWallet;
59
60
abstract class EVMChainWalletBase
61
extends WalletBase<EVMChainERC20Balance, EVMChainTransactionHistory, EVMChainTransactionInfo>
61
- with Store {
62
+ with Store, WalletKeysFile {
63
EVMChainWalletBase({
64
required WalletInfo walletInfo,
65
required EVMChainClient client,
@@ -508,6 +509,11 @@ abstract class EVMChainWalletBase
509
510
@override
511
Future<void> save() async {
512
+ if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) {
513
+ await saveKeysFile(_password);
514
+ saveKeysFile(_password, true);
515
+ }
516
+
517
await walletAddresses.updateAddressesInBox();
518
final path = await makePath();
519
await write(path: path, password: _password, data: toJSON());
@@ -522,7 +528,8 @@ abstract class EVMChainWalletBase
528
? HEX.encode((evmChainPrivateKey as EthPrivateKey).privateKey)
529
: null;
530
525
- Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
531
+ @override
532
+ WalletKeysData get walletKeysData => WalletKeysData(mnemonic: _mnemonic, privateKey: privateKey);
533
534
String toJSON() => json.encode({
535
'mnemonic': _mnemonic,
cw_nano/lib/nano_wallet.dart
+49
-32
@@ -1,8 +1,12 @@
1
+import 'dart:async';
2
import 'dart:convert';
3
import 'dart:io';
4
5
+import 'package:bip39/bip39.dart' as bip39;
6
import 'package:cw_core/cake_hive.dart';
7
import 'package:cw_core/crypto_currency.dart';
8
+import 'package:cw_core/n2_node.dart';
9
+import 'package:cw_core/nano_account.dart';
10
import 'package:cw_core/nano_account_info_response.dart';
11
import 'package:cw_core/node.dart';
12
import 'package:cw_core/pathForWallet.dart';
@@ -10,23 +14,20 @@ import 'package:cw_core/pending_transaction.dart';
14
import 'package:cw_core/sync_status.dart';
15
import 'package:cw_core/transaction_direction.dart';
16
import 'package:cw_core/transaction_priority.dart';
17
+import 'package:cw_core/wallet_base.dart';
18
import 'package:cw_core/wallet_info.dart';
19
+import 'package:cw_core/wallet_keys_file.dart';
20
import 'package:cw_nano/file.dart';
15
-import 'package:cw_core/nano_account.dart';
16
-import 'package:cw_core/n2_node.dart';
21
import 'package:cw_nano/nano_balance.dart';
22
import 'package:cw_nano/nano_client.dart';
23
import 'package:cw_nano/nano_transaction_credentials.dart';
24
import 'package:cw_nano/nano_transaction_history.dart';
25
import 'package:cw_nano/nano_transaction_info.dart';
26
+import 'package:cw_nano/nano_wallet_addresses.dart';
27
import 'package:cw_nano/nano_wallet_keys.dart';
28
import 'package:cw_nano/pending_nano_transaction.dart';
29
import 'package:mobx/mobx.dart';
25
-import 'dart:async';
26
-import 'package:cw_nano/nano_wallet_addresses.dart';
27
-import 'package:cw_core/wallet_base.dart';
30
import 'package:nanodart/nanodart.dart';
29
-import 'package:bip39/bip39.dart' as bip39;
31
import 'package:nanoutil/nanoutil.dart';
32
33
part 'nano_wallet.g.dart';
@@ -34,7 +35,8 @@ part 'nano_wallet.g.dart';
35
class NanoWallet = NanoWalletBase with _$NanoWallet;
36
37
abstract class NanoWalletBase
37
- extends WalletBase<NanoBalance, NanoTransactionHistory, NanoTransactionInfo> with Store {
38
+ extends WalletBase<NanoBalance, NanoTransactionHistory, NanoTransactionInfo>
39
+ with Store, WalletKeysFile {
40
NanoWalletBase({
41
required WalletInfo walletInfo,
42
required String mnemonic,
@@ -70,6 +72,7 @@ abstract class NanoWalletBase
72
73
String? _representativeAddress;
74
int repScore = 100;
75
+
76
bool get isRepOk => repScore >= 90;
77
78
late final NanoClient _client;
@@ -128,14 +131,10 @@ abstract class NanoWalletBase
131
}
132
133
@override
131
- int calculateEstimatedFee(TransactionPriority priority, int? amount) {
132
- return 0; // always 0 :)
133
- }
134
+ int calculateEstimatedFee(TransactionPriority priority, int? amount) => 0; // always 0 :)
135
136
@override
136
- Future<void> changePassword(String password) {
137
- throw UnimplementedError("changePassword");
138
- }
137
+ Future<void> changePassword(String password) => throw UnimplementedError("changePassword");
138
139
@override
140
void close() {
@@ -170,9 +169,7 @@ abstract class NanoWalletBase
169
}
170
171
@override
173
- Future<void> connectToPowNode({required Node node}) async {
174
- _client.connectPow(node);
175
- }
172
+ Future<void> connectToPowNode({required Node node}) async => _client.connectPow(node);
173
174
@override
175
Future<PendingTransaction> createTransaction(Object credentials) async {
@@ -296,9 +293,7 @@ abstract class NanoWalletBase
293
}
294
295
@override
299
- NanoWalletKeys get keys {
300
- return NanoWalletKeys(seedKey: _hexSeed!);
301
- }
296
+ NanoWalletKeys get keys => NanoWalletKeys(seedKey: _hexSeed!);
297
298
@override
299
String? get privateKey => _privateKey!;
@@ -312,6 +307,11 @@ abstract class NanoWalletBase
307
308
@override
309
Future<void> save() async {
310
+ if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) {
311
+ await saveKeysFile(_password);
312
+ saveKeysFile(_password, true);
313
+ }
314
+
315
await walletAddresses.updateAddressesInBox();
316
final path = await makePath();
317
await write(path: path, password: _password, data: toJSON());
@@ -323,6 +323,9 @@ abstract class NanoWalletBase
323
324
String get hexSeed => _hexSeed!;
325
326
+ @override
327
+ WalletKeysData get walletKeysData => WalletKeysData(mnemonic: _mnemonic, altMnemonic: hexSeed);
328
+
329
String get representative => _representativeAddress ?? "";
330
331
@action
@@ -358,8 +361,6 @@ abstract class NanoWalletBase
361
}
362
}
363
361
- Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
362
-
364
String toJSON() => json.encode({
365
'seedKey': _hexSeed,
366
'mnemonic': _mnemonic,
@@ -373,31 +374,47 @@ abstract class NanoWalletBase
374
required String password,
375
required WalletInfo walletInfo,
376
}) async {
377
+ final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
378
final path = await pathForWallet(name: name, type: walletInfo.type);
377
- final jsonSource = await read(path: path, password: password);
379
379
- final data = json.decode(jsonSource) as Map;
380
- final mnemonic = data['mnemonic'] as String;
380
+ Map<String, dynamic>? data = null;
381
+ try {
382
+ final jsonSource = await read(path: path, password: password);
383
+
384
+ data = json.decode(jsonSource) as Map<String, dynamic>;
385
+ } catch (e) {
386
+ if (!hasKeysFile) rethrow;
387
+ }
388
389
final balance = NanoBalance.fromRawString(
383
- currentBalance: data['currentBalance'] as String? ?? "0",
384
- receivableBalance: data['receivableBalance'] as String? ?? "0",
390
+ currentBalance: data?['currentBalance'] as String? ?? "0",
391
+ receivableBalance: data?['receivableBalance'] as String? ?? "0",
392
);
393
394
+ final WalletKeysData keysData;
395
+ // Migrate wallet from the old scheme to then new .keys file scheme
396
+ if (!hasKeysFile) {
397
+ final mnemonic = data!['mnemonic'] as String;
398
+ final isHexSeed = !mnemonic.contains(' ');
399
+
400
+ keysData = WalletKeysData(
401
+ mnemonic: isHexSeed ? null : mnemonic, altMnemonic: isHexSeed ? mnemonic : null);
402
+ } else {
403
+ keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
404
+ }
405
+
406
DerivationType derivationType = DerivationType.nano;
388
- if (data['derivationType'] == "DerivationType.bip39") {
407
+ if (data?['derivationType'] == "DerivationType.bip39") {
408
derivationType = DerivationType.bip39;
409
}
410
411
walletInfo.derivationInfo ??= DerivationInfo(derivationType: derivationType);
393
- if (walletInfo.derivationInfo!.derivationType == null) {
394
- walletInfo.derivationInfo!.derivationType = derivationType;
395
- }
412
+ walletInfo.derivationInfo!.derivationType ??= derivationType;
413
414
return NanoWallet(
415
walletInfo: walletInfo,
416
password: password,
400
- mnemonic: mnemonic,
417
+ mnemonic: keysData.mnemonic!,
418
initialBalance: balance,
419
);
420
// init() should always be run after this!
@@ -435,7 +452,7 @@ abstract class NanoWalletBase
452
_representativeAddress = await _client.getRepFromPrefs();
453
throw Exception("Failed to get representative address $e");
454
}
438
-
455
+
456
repScore = await _client.getRepScore(_representativeAddress!);
457
}
458
cw_nano/lib/nano_wallet_service.dart
+1
-1
@@ -39,7 +39,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
39
mnemonic: mnemonic,
40
password: credentials.password!,
41
);
42
- wallet.init();
42
+ await wallet.init();
43
return wallet;
44
}
45
cw_polygon/lib/polygon_wallet.dart
+28
-9
@@ -1,11 +1,12 @@
1
import 'dart:convert';
2
3
-import 'package:cw_core/crypto_currency.dart';
3
import 'package:cw_core/cake_hive.dart';
4
+import 'package:cw_core/crypto_currency.dart';
5
import 'package:cw_core/erc20_token.dart';
6
import 'package:cw_core/pathForWallet.dart';
7
import 'package:cw_core/transaction_direction.dart';
8
import 'package:cw_core/wallet_info.dart';
9
+import 'package:cw_core/wallet_keys_file.dart';
10
import 'package:cw_evm/evm_chain_transaction_history.dart';
11
import 'package:cw_evm/evm_chain_transaction_info.dart';
12
import 'package:cw_evm/evm_chain_transaction_model.dart';
@@ -13,9 +14,9 @@ import 'package:cw_evm/evm_chain_wallet.dart';
14
import 'package:cw_evm/evm_erc20_balance.dart';
15
import 'package:cw_evm/file.dart';
16
import 'package:cw_polygon/default_polygon_erc20_tokens.dart';
16
-import 'package:cw_polygon/polygon_transaction_info.dart';
17
import 'package:cw_polygon/polygon_client.dart';
18
import 'package:cw_polygon/polygon_transaction_history.dart';
19
+import 'package:cw_polygon/polygon_transaction_info.dart';
20
21
class PolygonWallet extends EVMChainWallet {
22
PolygonWallet({
@@ -97,19 +98,37 @@ class PolygonWallet extends EVMChainWallet {
98
99
static Future<PolygonWallet> open(
100
{required String name, required String password, required WalletInfo walletInfo}) async {
101
+ final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
102
final path = await pathForWallet(name: name, type: walletInfo.type);
101
- final jsonSource = await read(path: path, password: password);
102
- final data = json.decode(jsonSource) as Map;
103
- final mnemonic = data['mnemonic'] as String?;
104
- final privateKey = data['private_key'] as String?;
105
- final balance = EVMChainERC20Balance.fromJSON(data['balance'] as String) ??
103
+
104
+ Map<String, dynamic>? data;
105
+ try {
106
+ final jsonSource = await read(path: path, password: password);
107
+
108
+ data = json.decode(jsonSource) as Map<String, dynamic>;
109
+ } catch (e) {
110
+ if (!hasKeysFile) rethrow;
111
+ }
112
+
113
+ final balance = EVMChainERC20Balance.fromJSON(data?['balance'] as String) ??
114
EVMChainERC20Balance(BigInt.zero);
115
116
+ final WalletKeysData keysData;
117
+ // Migrate wallet from the old scheme to then new .keys file scheme
118
+ if (!hasKeysFile) {
119
+ final mnemonic = data!['mnemonic'] as String?;
120
+ final privateKey = data['private_key'] as String?;
121
+
122
+ keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey);
123
+ } else {
124
+ keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
125
+ }
126
+
127
return PolygonWallet(
128
walletInfo: walletInfo,
129
password: password,
111
- mnemonic: mnemonic,
112
- privateKey: privateKey,
130
+ mnemonic: keysData.mnemonic,
131
+ privateKey: keysData.privateKey,
132
initialBalance: balance,
133
client: PolygonClient(),
134
);
cw_polygon/lib/polygon_wallet_service.dart
-2
@@ -35,7 +35,6 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
35
await wallet.init();
36
wallet.addInitialTokens();
37
await wallet.save();
38
-
38
return wallet;
39
}
40
@@ -83,7 +82,6 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
82
await wallet.init();
83
wallet.addInitialTokens();
84
await wallet.save();
86
-
85
return wallet;
86
}
87
cw_solana/lib/solana_wallet.dart
+37
-10
@@ -1,6 +1,7 @@
1
import 'dart:async';
2
import 'dart:convert';
3
import 'dart:io';
4
+
5
import 'package:cw_core/cake_hive.dart';
6
import 'package:cw_core/crypto_currency.dart';
7
import 'package:cw_core/node.dart';
@@ -12,6 +13,7 @@ import 'package:cw_core/transaction_priority.dart';
13
import 'package:cw_core/wallet_addresses.dart';
14
import 'package:cw_core/wallet_base.dart';
15
import 'package:cw_core/wallet_info.dart';
16
+import 'package:cw_core/wallet_keys_file.dart';
17
import 'package:cw_solana/default_spl_tokens.dart';
18
import 'package:cw_solana/file.dart';
19
import 'package:cw_solana/solana_balance.dart';
@@ -36,7 +38,8 @@ part 'solana_wallet.g.dart';
38
class SolanaWallet = SolanaWalletBase with _$SolanaWallet;
39
40
abstract class SolanaWalletBase
39
- extends WalletBase<SolanaBalance, SolanaTransactionHistory, SolanaTransactionInfo> with Store {
41
+ extends WalletBase<SolanaBalance, SolanaTransactionHistory, SolanaTransactionInfo>
42
+ with Store, WalletKeysFile {
43
SolanaWalletBase({
44
required WalletInfo walletInfo,
45
String? mnemonic,
@@ -121,6 +124,9 @@ abstract class SolanaWalletBase
124
return privateKey;
125
}
126
127
+ @override
128
+ WalletKeysData get walletKeysData => WalletKeysData(mnemonic: _mnemonic, privateKey: privateKey);
129
+
130
Future<void> init() async {
131
final boxName = "${walletInfo.name.replaceAll(" ", "_")}_${SPLToken.boxName}";
132
@@ -336,6 +342,11 @@ abstract class SolanaWalletBase
342
343
@override
344
Future<void> save() async {
345
+ if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) {
346
+ await saveKeysFile(_password);
347
+ saveKeysFile(_password, true);
348
+ }
349
+
350
await walletAddresses.updateAddressesInBox();
351
final path = await makePath();
352
await write(path: path, password: _password, data: toJSON());
@@ -361,8 +372,6 @@ abstract class SolanaWalletBase
372
}
373
}
374
364
- Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
365
-
375
String toJSON() => json.encode({
376
'mnemonic': _mnemonic,
377
'private_key': _hexPrivateKey,
@@ -374,18 +383,36 @@ abstract class SolanaWalletBase
383
required String password,
384
required WalletInfo walletInfo,
385
}) async {
386
+ final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
387
final path = await pathForWallet(name: name, type: walletInfo.type);
378
- final jsonSource = await read(path: path, password: password);
379
- final data = json.decode(jsonSource) as Map;
380
- final mnemonic = data['mnemonic'] as String?;
381
- final privateKey = data['private_key'] as String?;
382
- final balance = SolanaBalance.fromJSON(data['balance'] as String) ?? SolanaBalance(0.0);
388
+
389
+ Map<String, dynamic>? data;
390
+ try {
391
+ final jsonSource = await read(path: path, password: password);
392
+
393
+ data = json.decode(jsonSource) as Map<String, dynamic>;
394
+ } catch (e) {
395
+ if (!hasKeysFile) rethrow;
396
+ }
397
+
398
+ final balance = SolanaBalance.fromJSON(data?['balance'] as String) ?? SolanaBalance(0.0);
399
+
400
+ final WalletKeysData keysData;
401
+ // Migrate wallet from the old scheme to then new .keys file scheme
402
+ if (!hasKeysFile) {
403
+ final mnemonic = data!['mnemonic'] as String?;
404
+ final privateKey = data['private_key'] as String?;
405
+
406
+ keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey);
407
+ } else {
408
+ keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
409
+ }
410
411
return SolanaWallet(
412
walletInfo: walletInfo,
413
password: password,
387
- mnemonic: mnemonic,
388
- privateKey: privateKey,
414
+ mnemonic: keysData.mnemonic,
415
+ privateKey: keysData.privateKey,
416
initialBalance: balance,
417
);
418
}
cw_tron/lib/tron_wallet.dart
+41
-25
@@ -16,6 +16,7 @@ import 'package:cw_core/transaction_priority.dart';
16
import 'package:cw_core/wallet_addresses.dart';
17
import 'package:cw_core/wallet_base.dart';
18
import 'package:cw_core/wallet_info.dart';
19
+import 'package:cw_core/wallet_keys_file.dart';
20
import 'package:cw_core/wallet_type.dart';
21
import 'package:cw_tron/default_tron_tokens.dart';
22
import 'package:cw_tron/file.dart';
@@ -37,7 +38,8 @@ part 'tron_wallet.g.dart';
38
class TronWallet = TronWalletBase with _$TronWallet;
39
40
abstract class TronWalletBase
40
- extends WalletBase<TronBalance, TronTransactionHistory, TronTransactionInfo> with Store {
41
+ extends WalletBase<TronBalance, TronTransactionHistory, TronTransactionInfo>
42
+ with Store, WalletKeysFile {
43
TronWalletBase({
44
required WalletInfo walletInfo,
45
String? mnemonic,
@@ -124,18 +126,36 @@ abstract class TronWalletBase
126
required String password,
127
required WalletInfo walletInfo,
128
}) async {
129
+ final hasKeysFile = await WalletKeysFile.hasKeysFile(name, walletInfo.type);
130
final path = await pathForWallet(name: name, type: walletInfo.type);
128
- final jsonSource = await read(path: path, password: password);
129
- final data = json.decode(jsonSource) as Map;
130
- final mnemonic = data['mnemonic'] as String?;
131
- final privateKey = data['private_key'] as String?;
132
- final balance = TronBalance.fromJSON(data['balance'] as String) ?? TronBalance(BigInt.zero);
131
+
132
+ Map<String, dynamic>? data;
133
+ try {
134
+ final jsonSource = await read(path: path, password: password);
135
+
136
+ data = json.decode(jsonSource) as Map<String, dynamic>;
137
+ } catch (e) {
138
+ if (!hasKeysFile) rethrow;
139
+ }
140
+
141
+ final balance = TronBalance.fromJSON(data?['balance'] as String) ?? TronBalance(BigInt.zero);
142
+
143
+ final WalletKeysData keysData;
144
+ // Migrate wallet from the old scheme to then new .keys file scheme
145
+ if (!hasKeysFile) {
146
+ final mnemonic = data!['mnemonic'] as String?;
147
+ final privateKey = data['private_key'] as String?;
148
+
149
+ keysData = WalletKeysData(mnemonic: mnemonic, privateKey: privateKey);
150
+ } else {
151
+ keysData = await WalletKeysFile.readKeysFile(name, walletInfo.type, password);
152
+ }
153
154
return TronWallet(
155
walletInfo: walletInfo,
156
password: password,
137
- mnemonic: mnemonic,
138
- privateKey: privateKey,
157
+ mnemonic: keysData.mnemonic,
158
+ privateKey: keysData.privateKey,
159
initialBalance: balance,
160
);
161
}
@@ -163,9 +183,7 @@ abstract class TronWalletBase
183
}) async {
184
assert(mnemonic != null || privateKey != null);
185
166
- if (privateKey != null) {
167
- return TronPrivateKey(privateKey);
168
- }
186
+ if (privateKey != null) return TronPrivateKey(privateKey);
187
188
final seed = bip39.mnemonicToSeed(mnemonic!);
189
@@ -181,14 +199,10 @@ abstract class TronWalletBase
199
int calculateEstimatedFee(TransactionPriority priority, int? amount) => 0;
200
201
@override
184
- Future<void> changePassword(String password) {
185
- throw UnimplementedError("changePassword");
186
- }
202
+ Future<void> changePassword(String password) => throw UnimplementedError("changePassword");
203
204
@override
189
- void close() {
190
- _transactionsUpdateTimer?.cancel();
191
- }
205
+ void close() => _transactionsUpdateTimer?.cancel();
206
207
@action
208
@override
@@ -406,12 +420,15 @@ abstract class TronWalletBase
420
Object get keys => throw UnimplementedError("keys");
421
422
@override
409
- Future<void> rescan({required int height}) {
410
- throw UnimplementedError("rescan");
411
- }
423
+ Future<void> rescan({required int height}) => throw UnimplementedError("rescan");
424
425
@override
426
Future<void> save() async {
427
+ if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) {
428
+ await saveKeysFile(_password);
429
+ saveKeysFile(_password, true);
430
+ }
431
+
432
await walletAddresses.updateAddressesInBox();
433
final path = await makePath();
434
await write(path: path, password: _password, data: toJSON());
@@ -424,7 +441,8 @@ abstract class TronWalletBase
441
@override
442
String get privateKey => _tronPrivateKey.toHex();
443
427
- Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
444
+ @override
445
+ WalletKeysData get walletKeysData => WalletKeysData(mnemonic: _mnemonic, privateKey: privateKey);
446
447
String toJSON() => json.encode({
448
'mnemonic': _mnemonic,
@@ -512,7 +530,7 @@ abstract class TronWalletBase
530
531
@override
532
Future<void> renameWalletFiles(String newWalletName) async {
515
- String transactionHistoryFileNameForWallet = 'tron_transactions.json';
533
+ const transactionHistoryFileNameForWallet = 'tron_transactions.json';
534
535
final currentWalletPath = await pathForWallet(name: walletInfo.name, type: type);
536
final currentWalletFile = File(currentWalletPath);
@@ -550,9 +568,7 @@ abstract class TronWalletBase
568
Future<String> signMessage(String message, {String? address}) async =>
569
_tronPrivateKey.signPersonalMessage(ascii.encode(message));
570
553
- String getTronBase58AddressFromHex(String hexAddress) {
554
- return TronAddress(hexAddress).toAddress();
555
- }
571
+ String getTronBase58AddressFromHex(String hexAddress) => TronAddress(hexAddress).toAddress();
572
573
void updateScanProviderUsageState(bool isEnabled) {
574
if (isEnabled) {
cw_tron/lib/tron_wallet_service.dart
+3
-2
@@ -1,6 +1,7 @@
1
import 'dart:io';
2
3
import 'package:bip39/bip39.dart' as bip39;
4
+import 'package:collection/collection.dart';
5
import 'package:cw_core/balance.dart';
6
import 'package:cw_core/pathForWallet.dart';
7
import 'package:cw_core/transaction_history.dart';
@@ -14,7 +15,6 @@ import 'package:cw_tron/tron_exception.dart';
15
import 'package:cw_tron/tron_wallet.dart';
16
import 'package:cw_tron/tron_wallet_creation_credentials.dart';
17
import 'package:hive/hive.dart';
17
-import 'package:collection/collection.dart';
18
19
class TronWalletService extends WalletService<
20
TronNewWalletCredentials,
@@ -153,7 +153,8 @@ class TronWalletService extends WalletService<
153
}
154
155
@override
156
- Future<WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>> restoreFromHardwareWallet(TronNewWalletCredentials credentials) {
156
+ Future<WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>>
157
+ restoreFromHardwareWallet(TronNewWalletCredentials credentials) {
158
// TODO: implement restoreFromHardwareWallet
159
throw UnimplementedError();
160
}