Bitcoin derivations (#1089)

* - Update and Fix Conflicts with main * Add Balances for ERC20 tokens * Fix conflicts with main * Add erc20 abi json * Add send erc20 tokens initial function * add missing getHeightByDate in Haven [skip ci] * Allow contacts and wallets from the same tag * Add Shiba Inu icon * Add send ERC-20 tokens initial flow * Add missing import in generated file * Add initial approach for transaction sending for ERC-20 tokens * Refactor signing/sending transactions * Add initial flow for transactions subscription * Refactor signing/sending transactions * Add home settings icon * Fix conflicts with main * Initial flow for home settings * Add logic flow for adding erc20 tokens * Fix initial UI * Finalize UI for Tokens * Integrate UI with Ethereum flow * Add "Enable/Disable" feature for ERC20 tokens * Add initial Erc20 tokens * Add Sorting and Pin Native Token features * Fix price sorting * Sort tokens list as well when Sort criteria changes * - Improve sorting balances flow - Add initial add token from search bar flow * Fix Accounts Popup UI * Fix Pin native token * Fix Enabling/Disabling tokens Fix sorting by fiat once app is opened Improve token availability mechanism * Fix deleting token Fix renaming tokens * Fix issue with search * Add more tokens * - Fix scroll issue - Add ERC20 tokens placeholder image in picker * - Separate and organize default erc20 tokens - Fix scrolling - Add token placeholder images in picker - Sort disabled tokens alphabetically * Change BNB token initial availability [skip ci] * Fix Conflicts with main * Fix Conflicts with main * Add Verse ERC20 token to the initial tokens list * Add rename wallet to Ethereum * Integrate EtherScan API for fetching address transactions Generate Ethereum specific secrets in Ethereum package * Adjust transactions fiat price for ERC20 tokens * Free Up GitHub Actions Ubuntu Runner Disk Space * Free Up GitHub Actions Ubuntu Runner Disk space (trial 2) * Fix Transaction Fee display * Save transaction history * Enhance loading time for erc20 tokens transactions * Minor Fixes and Enhancements * Fix sending erc20 fix block explorer issue * Fix int overflow * Fix transaction amount conversions * Minor: `slow` -> `Slow` [skip-ci] * initial changes * more base config stuff * config changes * successfully builds! * save * successfully add nano wallet * save * seed generation * receive screen + node screen working * tx history working and fiat fixes * balance working * derivation updates * nano-unfinished * sends working * remove fees from send screen, send and receive transactions working * fixes + auto receive incoming txs * fix for scanning QR codes * save * update translations * fixes * more fixes * more strings * small fix * fix github actions workflow * potential fix * potential fix * ci/cd fix * change rep working * seed generation fixes * fixes * save * change rep screen functional * save * banano changes * fixes, start adding ui for PoW * pow node changes * update translations * fix * account changing barely working * save * disable account generation * small fix * save * UI work * save * fixes after merge main * fixes * remove monero stuff, work on derivation ui * lots of fixes + finish up seed derivation * last minute fixes * node related fixes * more fixes * small fix * more fixes * fixes * pretty big refactor for pow, still some bugs * finally works! * get transactions after send * fix * merge conflict fixes * save * fix pow node showing up twice * done * initial changes * small fix * more merge fixes * fixes * more fixes * fix * save * fix manage pow nodes setting appearing on other wallets * fix contact bug * fixes * fiat fixes * save * save * save * save * updates * cleanup * restore fix * fixes * remove deprecated alert * fix * small fix * remove outdated warning * electrum restore fixes * fixes * fixes * fix * derivation fixes * nano fixes pt.1 * nano fixes pt.2 * bip39 fixes * pownode refactor * nodes pages fixes * observer fix * ssl fix * remove old references * remove unused imports * code cleanup * small fix * small potential fix * save * derivation fixes * deterministic fix * fix pt.2 * derivation class fixes * review fixes from nano that also apply here * formatting * stuff that should've stayed deleted * post merge fixes * remove problematic imports and duplicate changes * Delete lib/nano/nano.dart * move wallet restore page proxy code to the view model * fix dashboard page indicators being the same color * debatably better refactoring of derivationInfo, migration needed * additional refactor improvements * blanket comment some stuff out to narrow down this issue * refactor fixes * fix nano exchange * fix , bug, i.e. replace , with . when making a nano transaction * fix nano sending, update restore page wording, and other minor fixes * write migration for existing bitcoin and nano wallets * merge fixes * minor fixes * use default derivation type when restoring from qr code * fixes for restoring * fixes * fixes * merge fix * Fix issues with Creating Electrum and Restoring Bip39 * updates & fixes * Add missing case for no transactions BIP39 wallet restore * Make the default BIP39 the 84 derivation path * Add Samourai Deposit * litecoin mnemonic error fix * Bip39 passphrase support (#1412) * save * passphrase working * fix for when loading wallets + translation update * minor fix * Fix Nano * minor fix [skip ci] --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com> * change error state seed conditions into throwables [skip ci] * litecoin fixes * Bip39 minor enhancements (#1416) * minor enhancements * rename bitcoin_derivations -> electrum_derivations * Remove duplicate derivations handle default case * minor fix * Enable passphrase for Litecoin * obscure text of passphrase --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com> Co-authored-by: Justin Ehrenhofer <justin.ehrenhofer@gmail.com> Co-authored-by: fossephate <fosse@book.local>

Matthew Fosse committed Apr 29, 2024 at 17:49 UTC 509b92e97f51731a73736ca5c27e3e2d7df3def4
60 files changed +905 -338
assets/nano_pow_node_list.yml
+1 -1
@@ -6,4 +6,4 @@
6 uri: workers.perish.co
7 -
8 uri: worker.nanoriver.cc
9 - useSSL: true
\ No newline at end of file
9 + useSSL: true
cw_bitcoin/lib/bitcoin_mnemonic.dart
+18 -14
@@ -90,8 +90,7 @@ List<bool> prefixMatches(String source, List<String> prefixes) {
90 return prefixes.map((prefix) => hx.startsWith(prefix.toLowerCase())).toList();
91 }
92
93 -Future<String> generateMnemonic(
94 - {int strength = 264, String prefix = segwit}) async {
93 +Future<String> generateElectrumMnemonic({int strength = 264, String prefix = segwit}) async {
94 final wordBitlen = logBase(wordlist.length, 2).ceil();
95 final wordCount = strength / wordBitlen;
96 final byteCount = ((wordCount * wordBitlen).ceil() / 8).ceil();
@@ -106,22 +105,29 @@ Future<String> generateMnemonic(
105 return result;
106 }
107
108 +Future<bool> checkIfMnemonicIsElectrum2(String mnemonic) async {
109 + return prefixMatches(mnemonic, [segwit]).first;
110 +}
111 +
112 +Future<String> getMnemonicHash(String mnemonic) async {
113 + final hmacSha512 = Hmac(sha512, utf8.encode('Seed version'));
114 + final digest = hmacSha512.convert(utf8.encode(normalizeText(mnemonic)));
115 + final hx = digest.toString();
116 + return hx;
117 +}
118 +
119 Future<Uint8List> mnemonicToSeedBytes(String mnemonic, {String prefix = segwit}) async {
110 - final pbkdf2 = cryptography.Pbkdf2(
111 - macAlgorithm: cryptography.Hmac.sha512(),
112 - iterations: 2048,
113 - bits: 512);
120 + final pbkdf2 =
121 + cryptography.Pbkdf2(macAlgorithm: cryptography.Hmac.sha512(), iterations: 2048, bits: 512);
122 final text = normalizeText(mnemonic);
123 // pbkdf2.deriveKey(secretKey: secretKey, nonce: nonce)
124 final key = await pbkdf2.deriveKey(
117 - secretKey: cryptography.SecretKey(text.codeUnits),
118 - nonce: 'electrum'.codeUnits);
125 + secretKey: cryptography.SecretKey(text.codeUnits), nonce: 'electrum'.codeUnits);
126 final bytes = await key.extractBytes();
127 return Uint8List.fromList(bytes);
128 }
129
123 -bool matchesAnyPrefix(String mnemonic) =>
124 - prefixMatches(mnemonic, [segwit]).any((el) => el);
130 +bool matchesAnyPrefix(String mnemonic) => prefixMatches(mnemonic, [segwit]).any((el) => el);
131
132 bool validateMnemonic(String mnemonic, {String prefix = segwit}) {
133 try {
@@ -208,10 +214,8 @@ String removeCJKSpaces(String source) {
214 }
215
216 String normalizeText(String source) {
211 - final res = removeCombiningCharacters(unorm.nfkd(source).toLowerCase())
212 - .trim()
213 - .split('/\s+/')
214 - .join(' ');
217 + final res =
218 + removeCombiningCharacters(unorm.nfkd(source).toLowerCase()).trim().split('/\s+/').join(' ');
219
220 return removeCJKSpaces(res);
221 }
cw_bitcoin/lib/bitcoin_wallet.dart
+52 -4
@@ -12,6 +12,7 @@ import 'package:cw_core/wallet_info.dart';
12 import 'package:cw_bitcoin/bitcoin_address_record.dart';
13 import 'package:cw_bitcoin/electrum_balance.dart';
14 import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
15 +import 'package:bip39/bip39.dart' as bip39;
16
17 part 'bitcoin_wallet.g.dart';
18
@@ -30,8 +31,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
31 ElectrumBalance? initialBalance,
32 Map<String, int>? initialRegularAddressIndex,
33 Map<String, int>? initialChangeAddressIndex,
34 + String? passphrase,
35 }) : super(
36 mnemonic: mnemonic,
37 + passphrase: passphrase,
38 password: password,
39 walletInfo: walletInfo,
40 unspentCoinsInfo: unspentCoinsInfo,
@@ -44,14 +47,19 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
47 initialBalance: initialBalance,
48 seedBytes: seedBytes,
49 currency: CryptoCurrency.btc) {
50 + // in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here)
51 + // the sideHd derivation path = m/84'/0'/0'/1 (account 1, index unspecified here)
52 + String derivationPath = walletInfo.derivationInfo!.derivationPath!;
53 + String sideDerivationPath = derivationPath.substring(0, derivationPath.length - 1) + "1";
54 + final hd = bitcoin.HDWallet.fromSeed(seedBytes, network: networkType);
55 walletAddresses = BitcoinWalletAddresses(
56 walletInfo,
57 electrumClient: electrumClient,
58 initialAddresses: initialAddresses,
59 initialRegularAddressIndex: initialRegularAddressIndex,
60 initialChangeAddressIndex: initialChangeAddressIndex,
53 - mainHd: hd,
54 - sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"),
61 + mainHd: hd.derivePath(derivationPath),
62 + sideHd: hd.derivePath(sideDerivationPath),
63 network: networkParam ?? network,
64 );
65 autorun((_) {
@@ -64,6 +72,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
72 required String password,
73 required WalletInfo walletInfo,
74 required Box<UnspentCoinsInfo> unspentCoinsInfo,
75 + String? passphrase,
76 String? addressPageType,
77 BasedUtxoNetwork? network,
78 List<BitcoinAddressRecord>? initialAddresses,
@@ -71,14 +80,29 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
80 Map<String, int>? initialRegularAddressIndex,
81 Map<String, int>? initialChangeAddressIndex,
82 }) async {
83 + late Uint8List seedBytes;
84 +
85 + switch (walletInfo.derivationInfo?.derivationType) {
86 + case DerivationType.bip39:
87 + seedBytes = await bip39.mnemonicToSeed(
88 + mnemonic,
89 + passphrase: passphrase ?? "",
90 + );
91 + break;
92 + case DerivationType.electrum:
93 + default:
94 + seedBytes = await mnemonicToSeedBytes(mnemonic);
95 + break;
96 + }
97 return BitcoinWallet(
98 mnemonic: mnemonic,
99 + passphrase: passphrase ?? "",
100 password: password,
101 walletInfo: walletInfo,
102 unspentCoinsInfo: unspentCoinsInfo,
103 initialAddresses: initialAddresses,
104 initialBalance: initialBalance,
81 - seedBytes: await mnemonicToSeedBytes(mnemonic),
105 + seedBytes: seedBytes,
106 initialRegularAddressIndex: initialRegularAddressIndex,
107 initialChangeAddressIndex: initialChangeAddressIndex,
108 addressPageType: addressPageType,
@@ -97,14 +121,38 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
121 : BitcoinNetwork.mainnet;
122 final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, network);
123
124 + walletInfo.derivationInfo ??= DerivationInfo(
125 + derivationType: snp.derivationType ?? DerivationType.electrum,
126 + derivationPath: snp.derivationPath,
127 + );
128 +
129 + // set the default if not present:
130 + walletInfo.derivationInfo!.derivationPath = snp.derivationPath ?? "m/0'/1";
131 +
132 + late Uint8List seedBytes;
133 +
134 + switch (walletInfo.derivationInfo!.derivationType) {
135 + case DerivationType.electrum:
136 + seedBytes = await mnemonicToSeedBytes(snp.mnemonic);
137 + break;
138 + case DerivationType.bip39:
139 + default:
140 + seedBytes = await bip39.mnemonicToSeed(
141 + snp.mnemonic,
142 + passphrase: snp.passphrase ?? '',
143 + );
144 + break;
145 + }
146 +
147 return BitcoinWallet(
148 mnemonic: snp.mnemonic,
149 password: password,
150 + passphrase: snp.passphrase,
151 walletInfo: walletInfo,
152 unspentCoinsInfo: unspentCoinsInfo,
153 initialAddresses: snp.addresses,
154 initialBalance: snp.balance,
107 - seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
155 + seedBytes: seedBytes,
156 initialRegularAddressIndex: snp.regularAddressIndex,
157 initialChangeAddressIndex: snp.changeAddressIndex,
158 addressPageType: snp.addressPageType,
cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart
+27 -6
@@ -2,14 +2,35 @@ import 'package:cw_core/wallet_credentials.dart';
2 import 'package:cw_core/wallet_info.dart';
3
4 class BitcoinNewWalletCredentials extends WalletCredentials {
5 - BitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo})
6 - : super(name: name, walletInfo: walletInfo);
5 + BitcoinNewWalletCredentials(
6 + {required String name,
7 + WalletInfo? walletInfo,
8 + DerivationType? derivationType,
9 + String? derivationPath})
10 + : super(
11 + name: name,
12 + walletInfo: walletInfo,
13 + );
14 }
15
16 class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials {
10 - BitcoinRestoreWalletFromSeedCredentials(
11 - {required String name, required String password, required this.mnemonic, WalletInfo? walletInfo})
12 - : super(name: name, password: password, walletInfo: walletInfo);
17 + BitcoinRestoreWalletFromSeedCredentials({
18 + required String name,
19 + required String password,
20 + required this.mnemonic,
21 + WalletInfo? walletInfo,
22 + required DerivationType derivationType,
23 + required String derivationPath,
24 + String? passphrase,
25 + }) : super(
26 + name: name,
27 + password: password,
28 + passphrase: passphrase,
29 + walletInfo: walletInfo,
30 + derivationInfo: DerivationInfo(
31 + derivationType: derivationType,
32 + derivationPath: derivationPath,
33 + ));
34
35 final String mnemonic;
36 }
@@ -20,4 +41,4 @@ class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
41 : super(name: name, password: password, walletInfo: walletInfo);
42
43 final String wif;
23 -}
\ No newline at end of file
44 +}
cw_bitcoin/lib/bitcoin_wallet_service.dart
+5 -2
@@ -12,6 +12,7 @@ import 'package:cw_core/wallet_info.dart';
12 import 'package:cw_core/wallet_type.dart';
13 import 'package:hive/hive.dart';
14 import 'package:collection/collection.dart';
15 +import 'package:bip39/bip39.dart' as bip39;
16
17 class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
18 BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
@@ -29,8 +30,9 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
30 credentials.walletInfo?.network = network.value;
31
32 final wallet = await BitcoinWalletBase.create(
32 - mnemonic: await generateMnemonic(),
33 + mnemonic: await generateElectrumMnemonic(),
34 password: credentials.password!,
35 + passphrase: credentials.passphrase,
36 walletInfo: credentials.walletInfo!,
37 unspentCoinsInfo: unspentCoinsInfoSource,
38 network: network,
@@ -105,7 +107,7 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
107 @override
108 Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials,
109 {bool? isTestnet}) async {
108 - if (!validateMnemonic(credentials.mnemonic)) {
110 + if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) {
111 throw BitcoinMnemonicIsIncorrectException();
112 }
113
@@ -114,6 +116,7 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
116
117 final wallet = await BitcoinWalletBase.create(
118 password: credentials.password!,
119 + passphrase: credentials.passphrase,
120 mnemonic: credentials.mnemonic,
121 walletInfo: credentials.walletInfo!,
122 unspentCoinsInfo: unspentCoinsInfoSource,
cw_bitcoin/lib/electrum_derivations.dart new
+104
@@ -0,0 +1,104 @@
1 +import 'package:cw_core/wallet_info.dart';
2 +
3 +Map<DerivationType, List<DerivationInfo>> electrum_derivations = {
4 + DerivationType.electrum: [
5 + DerivationInfo(
6 + derivationType: DerivationType.electrum,
7 + derivationPath: "m/0'/0",
8 + description: "Electrum",
9 + scriptType: "p2wpkh",
10 + ),
11 + ],
12 + DerivationType.bip39: [
13 + DerivationInfo(
14 + derivationType: DerivationType.bip39,
15 + derivationPath: "m/44'/0'/0'",
16 + description: "Standard BIP44",
17 + scriptType: "p2pkh",
18 + ),
19 + DerivationInfo(
20 + derivationType: DerivationType.bip39,
21 + derivationPath: "m/49'/0'/0'",
22 + description: "Standard BIP49 compatibility segwit",
23 + scriptType: "p2wpkh-p2sh",
24 + ),
25 + DerivationInfo(
26 + derivationType: DerivationType.bip39,
27 + derivationPath: "m/84'/0'/0'",
28 + description: "Standard BIP84 native segwit",
29 + scriptType: "p2wpkh",
30 + ),
31 + DerivationInfo(
32 + derivationType: DerivationType.bip39,
33 + derivationPath: "m/0'",
34 + description: "Non-standard legacy",
35 + scriptType: "p2pkh",
36 + ),
37 + DerivationInfo(
38 + derivationType: DerivationType.bip39,
39 + derivationPath: "m/0'",
40 + description: "Non-standard compatibility segwit",
41 + scriptType: "p2wpkh-p2sh",
42 + ),
43 + DerivationInfo(
44 + derivationType: DerivationType.bip39,
45 + derivationPath: "m/0'",
46 + description: "Non-standard native segwit",
47 + scriptType: "p2wpkh",
48 + ),
49 + DerivationInfo(
50 + derivationType: DerivationType.bip39,
51 + derivationPath: "m/44'/0'/0'",
52 + description: "Samourai Deposit",
53 + scriptType: "p2wpkh",
54 + ),
55 + DerivationInfo(
56 + derivationType: DerivationType.bip39,
57 + derivationPath: "m/49'/0'/0'",
58 + description: "Samourai Deposit",
59 + scriptType: "p2wpkh",
60 + ),
61 + DerivationInfo(
62 + derivationType: DerivationType.bip39,
63 + derivationPath: "m/84'/0'/2147483644'",
64 + description: "Samourai Bad Bank (toxic change)",
65 + scriptType: "p2wpkh",
66 + ),
67 + DerivationInfo(
68 + derivationType: DerivationType.bip39,
69 + derivationPath: "m/84'/0'/2147483645'",
70 + description: "Samourai Whirlpool Pre Mix",
71 + scriptType: "p2wpkh",
72 + ),
73 + DerivationInfo(
74 + derivationType: DerivationType.bip39,
75 + derivationPath: "m/84'/0'/2147483646'",
76 + description: "Samourai Whirlpool Post Mix",
77 + scriptType: "p2wpkh",
78 + ),
79 + DerivationInfo(
80 + derivationType: DerivationType.bip39,
81 + derivationPath: "m/44'/0'/2147483647'",
82 + description: "Samourai Ricochet legacy",
83 + scriptType: "p2pkh",
84 + ),
85 + DerivationInfo(
86 + derivationType: DerivationType.bip39,
87 + derivationPath: "m/49'/0'/2147483647'",
88 + description: "Samourai Ricochet compatibility segwit",
89 + scriptType: "p2wpkh-p2sh",
90 + ),
91 + DerivationInfo(
92 + derivationType: DerivationType.bip39,
93 + derivationPath: "m/84'/0'/2147483647'",
94 + description: "Samourai Ricochet native segwit",
95 + scriptType: "p2wpkh",
96 + ),
97 + DerivationInfo(
98 + derivationType: DerivationType.bip39,
99 + derivationPath: "m/84'/2'/0'",
100 + description: "Default Litecoin",
101 + scriptType: "p2wpkh",
102 + ),
103 + ],
104 +};
cw_bitcoin/lib/electrum_wallet.dart
+7 -1
@@ -55,13 +55,15 @@ abstract class ElectrumWalletBase
55 required this.networkType,
56 required this.mnemonic,
57 required Uint8List seedBytes,
58 + this.passphrase,
59 List<BitcoinAddressRecord>? initialAddresses,
60 ElectrumClient? electrumClient,
61 ElectrumBalance? initialBalance,
62 CryptoCurrency? currency})
63 : hd = currency == CryptoCurrency.bch
64 ? bitcoinCashHDWallet(seedBytes)
64 - : bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/0"),
65 + : bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
66 + .derivePath(walletInfo.derivationInfo?.derivationPath ?? "m/0'/0"),
67 syncStatus = NotConnectedSyncStatus(),
68 _password = password,
69 _feeRates = <int>[],
@@ -92,6 +94,7 @@ abstract class ElectrumWalletBase
94
95 final bitcoin.HDWallet hd;
96 final String mnemonic;
97 + final String? passphrase;
98
99 @override
100 @observable
@@ -617,6 +620,7 @@ abstract class ElectrumWalletBase
620
621 String toJSON() => json.encode({
622 'mnemonic': mnemonic,
623 + 'passphrase': passphrase ?? '',
624 'account_index': walletAddresses.currentReceiveAddressIndexByType,
625 'change_address_index': walletAddresses.currentChangeAddressIndexByType,
626 'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(),
@@ -624,6 +628,8 @@ abstract class ElectrumWalletBase
628 ? SegwitAddresType.p2wpkh.toString()
629 : walletInfo.addressPageType.toString(),
630 'balance': balance[currency]?.toJSON(),
631 + 'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index,
632 + 'derivationPath': walletInfo.derivationInfo?.derivationPath,
633 });
634
635 int feeRate(TransactionPriority priority) {
cw_bitcoin/lib/electrum_wallet_snapshot.dart
+15
@@ -3,6 +3,7 @@ import 'package:bitcoin_base/bitcoin_base.dart';
3 import 'package:cw_bitcoin/bitcoin_address_record.dart';
4 import 'package:cw_bitcoin/electrum_balance.dart';
5 import 'package:cw_core/pathForWallet.dart';
6 +import 'package:cw_core/wallet_info.dart';
7 import 'package:cw_core/utils/file.dart';
8 import 'package:cw_core/wallet_type.dart';
9
@@ -17,6 +18,9 @@ class ElectrumWalletSnapshot {
18 required this.regularAddressIndex,
19 required this.changeAddressIndex,
20 required this.addressPageType,
21 + this.passphrase,
22 + this.derivationType,
23 + this.derivationPath,
24 });
25
26 final String name;
@@ -29,6 +33,9 @@ class ElectrumWalletSnapshot {
33 ElectrumBalance balance;
34 Map<String, int> regularAddressIndex;
35 Map<String, int> changeAddressIndex;
36 + String? passphrase;
37 + DerivationType? derivationType;
38 + String? derivationPath;
39
40 static Future<ElectrumWalletSnapshot> load(
41 String name, WalletType type, String password, BasedUtxoNetwork network) async {
@@ -37,6 +44,7 @@ class ElectrumWalletSnapshot {
44 final data = json.decode(jsonSource) as Map;
45 final addressesTmp = data['addresses'] as List? ?? <Object>[];
46 final mnemonic = data['mnemonic'] as String;
47 + final passphrase = data['passphrase'] as String? ?? '';
48 final addresses = addressesTmp
49 .whereType<String>()
50 .map((addr) => BitcoinAddressRecord.fromJSON(addr, network))
@@ -46,6 +54,10 @@ class ElectrumWalletSnapshot {
54 var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
55 var changeAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
56
57 + final derivationType =
58 + DerivationType.values[(data['derivationTypeIndex'] as int?) ?? DerivationType.electrum.index];
59 + final derivationPath = data['derivationPath'] as String? ?? "m/0'/0";
60 +
61 try {
62 regularAddressIndexByType = {
63 SegwitAddresType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0')
@@ -65,12 +77,15 @@ class ElectrumWalletSnapshot {
77 name: name,
78 type: type,
79 password: password,
80 + passphrase: passphrase,
81 mnemonic: mnemonic,
82 addresses: addresses,
83 balance: balance,
84 regularAddressIndex: regularAddressIndexByType,
85 changeAddressIndex: changeAddressIndexByType,
86 addressPageType: data['address_page_type'] as String?,
87 + derivationType: derivationType,
88 + derivationPath: derivationPath,
89 );
90 }
91 }
cw_bitcoin/lib/litecoin_wallet.dart
+17 -1
@@ -15,6 +15,7 @@ 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:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
18 +import 'package:bip39/bip39.dart' as bip39;
19
20 part 'litecoin_wallet.g.dart';
21
@@ -62,11 +63,26 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
63 required String password,
64 required WalletInfo walletInfo,
65 required Box<UnspentCoinsInfo> unspentCoinsInfo,
66 + String? passphrase,
67 String? addressPageType,
68 List<BitcoinAddressRecord>? initialAddresses,
69 ElectrumBalance? initialBalance,
70 Map<String, int>? initialRegularAddressIndex,
71 Map<String, int>? initialChangeAddressIndex}) async {
72 + late Uint8List seedBytes;
73 +
74 + switch (walletInfo.derivationInfo?.derivationType) {
75 + case DerivationType.bip39:
76 + seedBytes = await bip39.mnemonicToSeed(
77 + mnemonic,
78 + passphrase: passphrase ?? "",
79 + );
80 + break;
81 + case DerivationType.electrum:
82 + default:
83 + seedBytes = await mnemonicToSeedBytes(mnemonic);
84 + break;
85 + }
86 return LitecoinWallet(
87 mnemonic: mnemonic,
88 password: password,
@@ -74,7 +90,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
90 unspentCoinsInfo: unspentCoinsInfo,
91 initialAddresses: initialAddresses,
92 initialBalance: initialBalance,
77 - seedBytes: await mnemonicToSeedBytes(mnemonic),
93 + seedBytes: seedBytes,
94 initialRegularAddressIndex: initialRegularAddressIndex,
95 initialChangeAddressIndex: initialChangeAddressIndex,
96 addressPageType: addressPageType,
cw_bitcoin/lib/litecoin_wallet_service.dart
+5 -2
@@ -11,6 +11,7 @@ import 'package:cw_core/wallet_type.dart';
11 import 'package:cw_core/wallet_info.dart';
12 import 'package:cw_core/wallet_base.dart';
13 import 'package:collection/collection.dart';
14 +import 'package:bip39/bip39.dart' as bip39;
15
16 class LitecoinWalletService extends WalletService<
17 BitcoinNewWalletCredentials,
@@ -27,8 +28,9 @@ class LitecoinWalletService extends WalletService<
28 @override
29 Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
30 final wallet = await LitecoinWalletBase.create(
30 - mnemonic: await generateMnemonic(),
31 + mnemonic: await generateElectrumMnemonic(),
32 password: credentials.password!,
33 + passphrase: credentials.passphrase,
34 walletInfo: credentials.walletInfo!,
35 unspentCoinsInfo: unspentCoinsInfoSource);
36 await wallet.save();
@@ -100,12 +102,13 @@ class LitecoinWalletService extends WalletService<
102 @override
103 Future<LitecoinWallet> restoreFromSeed(
104 BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
103 - if (!validateMnemonic(credentials.mnemonic)) {
105 + if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) {
106 throw LitecoinMnemonicIsIncorrectException();
107 }
108
109 final wallet = await LitecoinWalletBase.create(
110 password: credentials.password!,
111 + passphrase: credentials.passphrase,
112 mnemonic: credentials.mnemonic,
113 walletInfo: credentials.walletInfo!,
114 unspentCoinsInfo: unspentCoinsInfoSource);
cw_bitcoin/lib/utils.dart
+48 -19
@@ -5,29 +5,58 @@ import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
5 import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
6 import 'package:hex/hex.dart';
7
8 -bitcoin.PaymentData generatePaymentData({required bitcoin.HDWallet hd, required int index}) =>
9 - PaymentData(pubkey: Uint8List.fromList(HEX.decode(hd.derive(index).pubKey!)));
8 +bitcoin.PaymentData generatePaymentData({required bitcoin.HDWallet hd, int? index}) {
9 + final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
10 + return PaymentData(pubkey: Uint8List.fromList(HEX.decode(pubKey)));
11 +}
12
13 ECPrivate generateECPrivate(
12 - {required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
13 - ECPrivate.fromWif(hd.derive(index).wif!, netVersion: network.wifNetVer);
14 + {required bitcoin.HDWallet hd, required BasedUtxoNetwork network, int? index}) {
15 + final wif = index != null ? hd.derive(index).wif! : hd.wif!;
16 + return ECPrivate.fromWif(wif, netVersion: network.wifNetVer);
17 +}
18
15 -String generateP2WPKHAddress(
16 - {required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
17 - ECPublic.fromHex(hd.derive(index).pubKey!).toP2wpkhAddress().toAddress(network);
19 +String generateP2WPKHAddress({
20 + required bitcoin.HDWallet hd,
21 + required BasedUtxoNetwork network,
22 + int? index,
23 +}) {
24 + final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
25 + return ECPublic.fromHex(pubKey).toP2wpkhAddress().toAddress(network);
26 +}
27
19 -String generateP2SHAddress(
20 - {required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
21 - ECPublic.fromHex(hd.derive(index).pubKey!).toP2wpkhInP2sh().toAddress(network);
28 +String generateP2SHAddress({
29 + required bitcoin.HDWallet hd,
30 + required BasedUtxoNetwork network,
31 + int? index,
32 +}) {
33 + final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
34 + return ECPublic.fromHex(pubKey).toP2wpkhInP2sh().toAddress(network);
35 +}
36
23 -String generateP2WSHAddress(
24 - {required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
25 - ECPublic.fromHex(hd.derive(index).pubKey!).toP2wshAddress().toAddress(network);
37 +String generateP2WSHAddress({
38 + required bitcoin.HDWallet hd,
39 + required BasedUtxoNetwork network,
40 + int? index,
41 +}) {
42 + final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
43 + return ECPublic.fromHex(pubKey).toP2wshAddress().toAddress(network);
44 +}
45
27 -String generateP2PKHAddress(
28 - {required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
29 - ECPublic.fromHex(hd.derive(index).pubKey!).toP2pkhAddress().toAddress(network);
46 +String generateP2PKHAddress({
47 + required bitcoin.HDWallet hd,
48 + required BasedUtxoNetwork network,
49 + int? index,
50 +}) {
51 + final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
52 + return ECPublic.fromHex(pubKey).toP2pkhAddress().toAddress(network);
53 +}
54
31 -String generateP2TRAddress(
32 - {required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
33 - ECPublic.fromHex(hd.derive(index).pubKey!).toTaprootAddress().toAddress(network);
55 +String generateP2TRAddress({
56 + required bitcoin.HDWallet hd,
57 + required BasedUtxoNetwork network,
58 + int? index,
59 +}) {
60 + final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
61 + return ECPublic.fromHex(pubKey).toTaprootAddress().toAddress(network);
62 +}
cw_bitcoin/pubspec.lock
+12 -20
@@ -217,10 +217,10 @@ packages:
217 dependency: transitive
218 description:
219 name: collection
220 - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
220 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
221 url: "https://pub.dev"
222 source: hosted
223 - version: "1.17.2"
223 + version: "1.17.1"
224 convert:
225 dependency: transitive
226 description:
@@ -434,18 +434,18 @@ packages:
434 dependency: transitive
435 description:
436 name: matcher
437 - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
437 + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
438 url: "https://pub.dev"
439 source: hosted
440 - version: "0.12.16"
440 + version: "0.12.15"
441 material_color_utilities:
442 dependency: transitive
443 description:
444 name: material_color_utilities
445 - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
445 + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
446 url: "https://pub.dev"
447 source: hosted
448 - version: "0.5.0"
448 + version: "0.2.0"
449 meta:
450 dependency: transitive
451 description:
@@ -663,10 +663,10 @@ packages:
663 dependency: transitive
664 description:
665 name: source_span
666 - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
666 + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
667 url: "https://pub.dev"
668 source: hosted
669 - version: "1.10.0"
669 + version: "1.9.1"
670 stack_trace:
671 dependency: transitive
672 description:
@@ -711,10 +711,10 @@ packages:
711 dependency: transitive
712 description:
713 name: test_api
714 - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
714 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
715 url: "https://pub.dev"
716 source: hosted
717 - version: "0.6.0"
717 + version: "0.5.1"
718 timing:
719 dependency: transitive
720 description:
@@ -748,21 +748,13 @@ packages:
748 source: hosted
749 version: "2.1.4"
750 watcher:
751 - dependency: "direct overridden"
751 + dependency: transitive
752 description:
753 name: watcher
754 sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
755 url: "https://pub.dev"
756 source: hosted
757 version: "1.1.0"
758 - web:
759 - dependency: transitive
760 - description:
761 - name: web
762 - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
763 - url: "https://pub.dev"
764 - source: hosted
765 - version: "0.1.4-beta"
758 web_socket_channel:
759 dependency: transitive
760 description:
@@ -796,5 +788,5 @@ packages:
788 source: hosted
789 version: "3.1.2"
790 sdks:
799 - dart: ">=3.1.0-185.0.dev <4.0.0"
791 + dart: ">=3.0.0 <4.0.0"
792 flutter: ">=3.10.0"
cw_core/lib/hive_type_ids.dart
+2 -1
@@ -14,4 +14,5 @@ const ERC20_TOKEN_TYPE_ID = 12;
14 const NANO_ACCOUNT_TYPE_ID = 13;
15 const POW_NODE_TYPE_ID = 14;
16 const DERIVATION_TYPE_TYPE_ID = 15;
17 -const SPL_TOKEN_TYPE_ID = 16;
17 +const SPL_TOKEN_TYPE_ID = 16;
18 +const DERIVATION_INFO_TYPE_ID = 17;
cw_core/lib/wallet_credentials.dart
+9 -5
@@ -7,15 +7,19 @@ abstract class WalletCredentials {
7 this.seedPhraseLength,
8 this.walletInfo,
9 this.password,
10 - this.derivationType,
11 - this.derivationPath,
12 - });
10 + this.passphrase,
11 + this.derivationInfo,
12 + }) {
13 + if (this.walletInfo != null && derivationInfo != null) {
14 + this.walletInfo!.derivationInfo = derivationInfo;
15 + }
16 + }
17
18 final String name;
19 final int? height;
20 int? seedPhraseLength;
21 String? password;
18 - DerivationType? derivationType;
19 - String? derivationPath;
22 + String? passphrase;
23 WalletInfo? walletInfo;
24 + DerivationInfo? derivationInfo;
25 }
cw_core/lib/wallet_info.dart
+47 -32
@@ -17,28 +17,42 @@ enum DerivationType {
17 @HiveField(3)
18 bip39,
19 @HiveField(4)
20 - electrum1,
21 - @HiveField(5)
22 - electrum2,
20 + electrum,
21 }
22
25 -class DerivationInfo {
23 +@HiveType(typeId: DerivationInfo.typeId)
24 +class DerivationInfo extends HiveObject {
25 DerivationInfo({
27 - required this.derivationType,
26 + this.derivationType,
27 this.derivationPath,
28 this.balance = "",
29 this.address = "",
31 - this.height = 0,
32 - this.script_type,
30 + this.transactionsCount = 0,
31 + this.scriptType,
32 this.description,
33 });
34
36 - String balance;
35 + static const typeId = DERIVATION_INFO_TYPE_ID;
36 +
37 + @HiveField(0, defaultValue: '')
38 String address;
38 - int height;
39 - final DerivationType derivationType;
40 - final String? derivationPath;
41 - final String? script_type;
39 +
40 + @HiveField(1, defaultValue: '')
41 + String balance;
42 +
43 + @HiveField(2)
44 + int transactionsCount;
45 +
46 + @HiveField(3)
47 + DerivationType? derivationType;
48 +
49 + @HiveField(4)
50 + String? derivationPath;
51 +
52 + @HiveField(5)
53 + final String? scriptType;
54 +
55 + @HiveField(6)
56 final String? description;
57 }
58
@@ -57,8 +71,7 @@ class WalletInfo extends HiveObject {
71 this.yatEid,
72 this.yatLastUsedAddressRaw,
73 this.showIntroCakePayCard,
60 - this.derivationType,
61 - this.derivationPath)
74 + this.derivationInfo)
75 : _yatLastUsedAddressController = StreamController<String>.broadcast();
76
77 factory WalletInfo.external({
@@ -74,24 +87,23 @@ class WalletInfo extends HiveObject {
87 bool? showIntroCakePayCard,
88 String yatEid = '',
89 String yatLastUsedAddressRaw = '',
77 - DerivationType? derivationType,
78 - String? derivationPath,
90 + DerivationInfo? derivationInfo,
91 }) {
92 return WalletInfo(
81 - id,
82 - name,
83 - type,
84 - isRecovery,
85 - restoreHeight,
86 - date.millisecondsSinceEpoch,
87 - dirPath,
88 - path,
89 - address,
90 - yatEid,
91 - yatLastUsedAddressRaw,
92 - showIntroCakePayCard,
93 - derivationType,
94 - derivationPath);
93 + id,
94 + name,
95 + type,
96 + isRecovery,
97 + restoreHeight,
98 + date.millisecondsSinceEpoch,
99 + dirPath,
100 + path,
101 + address,
102 + yatEid,
103 + yatLastUsedAddressRaw,
104 + showIntroCakePayCard,
105 + derivationInfo,
106 + );
107 }
108
109 static const typeId = WALLET_INFO_TYPE_ID;
@@ -143,10 +155,10 @@ class WalletInfo extends HiveObject {
155 List<String>? usedAddresses;
156
157 @HiveField(16)
146 - DerivationType? derivationType;
158 + DerivationType? derivationType; // no longer used
159
160 @HiveField(17)
149 - String? derivationPath;
161 + String? derivationPath; // no longer used
162
163 @HiveField(18)
164 String? addressPageType;
@@ -154,6 +166,9 @@ class WalletInfo extends HiveObject {
166 @HiveField(19)
167 String? network;
168
169 + @HiveField(20)
170 + DerivationInfo? derivationInfo;
171 +
172 String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
173
174 set yatLastUsedAddress(String address) {
cw_core/pubspec.lock
+12 -20
@@ -149,10 +149,10 @@ packages:
149 dependency: transitive
150 description:
151 name: collection
152 - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
152 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
153 url: "https://pub.dev"
154 source: hosted
155 - version: "1.17.2"
155 + version: "1.17.1"
156 convert:
157 dependency: transitive
158 description:
@@ -343,18 +343,18 @@ packages:
343 dependency: transitive
344 description:
345 name: matcher
346 - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
346 + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
347 url: "https://pub.dev"
348 source: hosted
349 - version: "0.12.16"
349 + version: "0.12.15"
350 material_color_utilities:
351 dependency: transitive
352 description:
353 name: material_color_utilities
354 - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
354 + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
355 url: "https://pub.dev"
356 source: hosted
357 - version: "0.5.0"
357 + version: "0.2.0"
358 meta:
359 dependency: transitive
360 description:
@@ -564,10 +564,10 @@ packages:
564 dependency: transitive
565 description:
566 name: source_span
567 - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
567 + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
568 url: "https://pub.dev"
569 source: hosted
570 - version: "1.10.0"
570 + version: "1.9.1"
571 stack_trace:
572 dependency: transitive
573 description:
@@ -612,10 +612,10 @@ packages:
612 dependency: transitive
613 description:
614 name: test_api
615 - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
615 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
616 url: "https://pub.dev"
617 source: hosted
618 - version: "0.6.0"
618 + version: "0.5.1"
619 timing:
620 dependency: transitive
621 description:
@@ -641,21 +641,13 @@ packages:
641 source: hosted
642 version: "2.1.4"
643 watcher:
644 - dependency: "direct overridden"
644 + dependency: transitive
645 description:
646 name: watcher
647 sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
648 url: "https://pub.dev"
649 source: hosted
650 version: "1.1.0"
651 - web:
652 - dependency: transitive
653 - description:
654 - name: web
655 - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
656 - url: "https://pub.dev"
657 - source: hosted
658 - version: "0.1.4-beta"
651 web_socket_channel:
652 dependency: transitive
653 description:
@@ -689,5 +681,5 @@ packages:
681 source: hosted
682 version: "3.1.2"
683 sdks:
692 - dart: ">=3.1.0-185.0.dev <4.0.0"
684 + dart: ">=3.0.0 <4.0.0"
685 flutter: ">=3.10.0"
cw_nano/lib/nano_wallet.dart
+5 -2
@@ -43,7 +43,7 @@ abstract class NanoWalletBase
43 }) : syncStatus = NotConnectedSyncStatus(),
44 _password = password,
45 _mnemonic = mnemonic,
46 - _derivationType = walletInfo.derivationType!,
46 + _derivationType = walletInfo.derivationInfo!.derivationType!,
47 _isTransactionUpdating = false,
48 _client = NanoClient(),
49 walletAddresses = NanoWalletAddresses(walletInfo),
@@ -389,7 +389,10 @@ abstract class NanoWalletBase
389 derivationType = DerivationType.bip39;
390 }
391
392 - walletInfo.derivationType = derivationType;
392 + walletInfo.derivationInfo ??= DerivationInfo(derivationType: derivationType);
393 + if (walletInfo.derivationInfo!.derivationType == null) {
394 + walletInfo.derivationInfo!.derivationType = derivationType;
395 + }
396
397 return NanoWallet(
398 walletInfo: walletInfo,
cw_nano/lib/nano_wallet_creation_credentials.dart
+13 -6
@@ -2,8 +2,15 @@ import 'package:cw_core/wallet_credentials.dart';
2 import 'package:cw_core/wallet_info.dart';
3
4 class NanoNewWalletCredentials extends WalletCredentials {
5 - NanoNewWalletCredentials({required String name, String? password})
6 - : super(name: name, password: password);
5 + NanoNewWalletCredentials({
6 + required String name,
7 + String? password,
8 + DerivationType? derivationType,
9 + }) : super(
10 + name: name,
11 + password: password,
12 + derivationInfo: DerivationInfo(derivationType: derivationType),
13 + );
14 }
15
16 class NanoRestoreWalletFromSeedCredentials extends WalletCredentials {
@@ -11,11 +18,11 @@ class NanoRestoreWalletFromSeedCredentials extends WalletCredentials {
18 required String name,
19 required this.mnemonic,
20 String? password,
14 - DerivationType? derivationType,
21 + required DerivationType derivationType,
22 }) : super(
23 name: name,
24 password: password,
18 - derivationType: derivationType,
25 + derivationInfo: DerivationInfo(derivationType: derivationType),
26 );
27
28 final String mnemonic;
@@ -30,12 +37,12 @@ class NanoRestoreWalletFromKeysCredentials extends WalletCredentials {
37 NanoRestoreWalletFromKeysCredentials({
38 required String name,
39 required String password,
40 + required DerivationType derivationType,
41 required this.seedKey,
34 - DerivationType? derivationType,
42 }) : super(
43 name: name,
44 password: password,
38 - derivationType: derivationType,
45 + derivationInfo: DerivationInfo(derivationType: derivationType),
46 );
47
48 final String seedKey;
cw_nano/lib/nano_wallet_service.dart
+5 -7
@@ -28,11 +28,11 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
28 @override
29 Future<WalletBase> create(NanoNewWalletCredentials credentials, {bool? isTestnet}) async {
30 // nano standard:
31 - DerivationType derivationType = DerivationType.nano;
31 String seedKey = NanoSeeds.generateSeed();
32 String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
33
35 - credentials.walletInfo!.derivationType = derivationType;
34 + // ensure default if not present:
35 + credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: DerivationType.nano);
36
37 final wallet = NanoWallet(
38 walletInfo: credentials.walletInfo!,
@@ -88,9 +88,6 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
88 }
89 }
90
91 - DerivationType derivationType = credentials.derivationType ?? DerivationType.nano;
92 - credentials.walletInfo!.derivationType = derivationType;
93 -
91 String? mnemonic;
92
93 // we can't derive the mnemonic from the key in all cases, only if it's a "nano" seed
@@ -128,9 +125,10 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
125 }
126 }
127
131 - DerivationType derivationType = credentials.derivationType ?? DerivationType.nano;
128 + DerivationType derivationType =
129 + credentials.walletInfo?.derivationInfo?.derivationType ?? DerivationType.nano;
130
133 - credentials.walletInfo!.derivationType = derivationType;
131 + credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: derivationType);
132
133 final wallet = await NanoWallet(
134 password: credentials.password!,
lib/bitcoin/cw_bitcoin.dart
+164 -22
@@ -1,13 +1,22 @@
1 part of 'bitcoin.dart';
2
3 class CWBitcoin extends Bitcoin {
4 - @override
5 - TransactionPriority getMediumTransactionPriority() => BitcoinTransactionPriority.medium;
6 -
7 - @override
8 - WalletCredentials createBitcoinRestoreWalletFromSeedCredentials(
9 - {required String name, required String mnemonic, required String password}) =>
10 - BitcoinRestoreWalletFromSeedCredentials(name: name, mnemonic: mnemonic, password: password);
4 + WalletCredentials createBitcoinRestoreWalletFromSeedCredentials({
5 + required String name,
6 + required String mnemonic,
7 + required String password,
8 + required DerivationType derivationType,
9 + required String derivationPath,
10 + String? passphrase,
11 + }) =>
12 + BitcoinRestoreWalletFromSeedCredentials(
13 + name: name,
14 + mnemonic: mnemonic,
15 + password: password,
16 + derivationType: derivationType,
17 + derivationPath: derivationPath,
18 + passphrase: passphrase,
19 + );
20
21 @override
22 WalletCredentials createBitcoinRestoreWalletFromWIFCredentials(
@@ -23,6 +32,9 @@ class CWBitcoin extends Bitcoin {
32 {required String name, WalletInfo? walletInfo}) =>
33 BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo);
34
35 + @override
36 + TransactionPriority getMediumTransactionPriority() => BitcoinTransactionPriority.medium;
37 +
38 @override
39 List<String> getWordList() => wordlist;
40
@@ -78,21 +90,20 @@ class CWBitcoin extends Bitcoin {
90 final bitcoinFeeRate =
91 priority == BitcoinTransactionPriority.custom && feeRate != null ? feeRate : null;
92 return BitcoinTransactionCredentials(
81 - outputs
82 - .map((out) => OutputInfo(
83 - fiatAmount: out.fiatAmount,
84 - cryptoAmount: out.cryptoAmount,
85 - address: out.address,
86 - note: out.note,
87 - sendAll: out.sendAll,
88 - extractedAddress: out.extractedAddress,
89 - isParsedAddress: out.isParsedAddress,
90 - formattedCryptoAmount: out.formattedCryptoAmount,
91 - memo: out.memo))
92 - .toList(),
93 - priority: priority as BitcoinTransactionPriority,
94 - feeRate: bitcoinFeeRate
95 - );
93 + outputs
94 + .map((out) => OutputInfo(
95 + fiatAmount: out.fiatAmount,
96 + cryptoAmount: out.cryptoAmount,
97 + address: out.address,
98 + note: out.note,
99 + sendAll: out.sendAll,
100 + extractedAddress: out.extractedAddress,
101 + isParsedAddress: out.isParsedAddress,
102 + formattedCryptoAmount: out.formattedCryptoAmount,
103 + memo: out.memo))
104 + .toList(),
105 + priority: priority as BitcoinTransactionPriority,
106 + feeRate: bitcoinFeeRate);
107 }
108
109 @override
@@ -248,6 +259,137 @@ class CWBitcoin extends Bitcoin {
259 }
260 }
261
262 + @override
263 + Future<List<DerivationType>> compareDerivationMethods(
264 + {required String mnemonic, required Node node}) async {
265 + if (await checkIfMnemonicIsElectrum2(mnemonic)) {
266 + return [DerivationType.electrum];
267 + }
268 +
269 + return [DerivationType.bip39, DerivationType.electrum];
270 + }
271 +
272 + int _countOccurrences(String str, String charToCount) {
273 + int count = 0;
274 + for (int i = 0; i < str.length; i++) {
275 + if (str[i] == charToCount) {
276 + count++;
277 + }
278 + }
279 + return count;
280 + }
281 +
282 + @override
283 + Future<List<DerivationInfo>> getDerivationsFromMnemonic({
284 + required String mnemonic,
285 + required Node node,
286 + String? passphrase,
287 + }) async {
288 + List<DerivationInfo> list = [];
289 +
290 + List<DerivationType> types = await compareDerivationMethods(mnemonic: mnemonic, node: node);
291 + if (types.length == 1 && types.first == DerivationType.electrum) {
292 + return [
293 + DerivationInfo(
294 + derivationType: DerivationType.electrum,
295 + derivationPath: "m/0'/0",
296 + description: "Electrum",
297 + scriptType: "p2wpkh",
298 + )
299 + ];
300 + }
301 +
302 + final electrumClient = ElectrumClient();
303 + await electrumClient.connectToUri(node.uri);
304 +
305 + late BasedUtxoNetwork network;
306 + btc.NetworkType networkType;
307 + switch (node.type) {
308 + case WalletType.litecoin:
309 + network = LitecoinNetwork.mainnet;
310 + networkType = litecoinNetwork;
311 + break;
312 + case WalletType.bitcoin:
313 + default:
314 + network = BitcoinNetwork.mainnet;
315 + networkType = btc.bitcoin;
316 + break;
317 + }
318 +
319 + for (DerivationType dType in electrum_derivations.keys) {
320 + late Uint8List seedBytes;
321 + if (dType == DerivationType.electrum) {
322 + seedBytes = await mnemonicToSeedBytes(mnemonic);
323 + } else if (dType == DerivationType.bip39) {
324 + seedBytes = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase ?? '');
325 + }
326 +
327 + for (DerivationInfo dInfo in electrum_derivations[dType]!) {
328 + try {
329 + DerivationInfo dInfoCopy = DerivationInfo(
330 + derivationType: dInfo.derivationType,
331 + derivationPath: dInfo.derivationPath,
332 + description: dInfo.description,
333 + scriptType: dInfo.scriptType,
334 + );
335 +
336 + String derivationPath = dInfoCopy.derivationPath!;
337 + int derivationDepth = _countOccurrences(derivationPath, "/");
338 +
339 + // the correct derivation depth is dependant on the derivation type:
340 + // the derivation paths defined in electrum_derivations are at the ROOT level, i.e.:
341 + // electrum's format doesn't specify subaddresses, just subaccounts:
342 +
343 + // for BIP44
344 + if (derivationDepth == 3) {
345 + // we add "/0/0" so that we generate account 0, index 0 and correctly get balance
346 + derivationPath += "/0/0";
347 + // we don't support sub-ACCOUNTS in bitcoin like we do monero, and so the path dInfoCopy
348 + // expects should be ACCOUNT 0, index unspecified:
349 + dInfoCopy.derivationPath = dInfoCopy.derivationPath! + "/0";
350 + }
351 +
352 + // var hd = bip32.BIP32.fromSeed(seedBytes).derivePath(derivationPath);
353 + final hd = btc.HDWallet.fromSeed(
354 + seedBytes,
355 + network: networkType,
356 + ).derivePath(derivationPath);
357 +
358 + String? address;
359 + switch (dInfoCopy.scriptType) {
360 + case "p2wpkh":
361 + address = generateP2WPKHAddress(hd: hd, network: network);
362 + break;
363 + case "p2pkh":
364 + address = generateP2PKHAddress(hd: hd, network: network);
365 + break;
366 + case "p2wpkh-p2sh":
367 + address = generateP2SHAddress(hd: hd, network: network);
368 + break;
369 + default:
370 + continue;
371 + }
372 +
373 + final sh = scriptHash(address, network: network);
374 + final history = await electrumClient.getHistory(sh);
375 +
376 + final balance = await electrumClient.getBalance(sh);
377 + dInfoCopy.balance = balance.entries.first.value.toString();
378 + dInfoCopy.address = address;
379 + dInfoCopy.transactionsCount = history.length;
380 +
381 + list.add(dInfoCopy);
382 + } catch (e) {
383 + print(e);
384 + }
385 + }
386 + }
387 +
388 + // sort the list such that derivations with the most transactions are first:
389 + list.sort((a, b) => b.transactionsCount.compareTo(a.transactionsCount));
390 + return list;
391 + }
392 +
393 @override
394 bool hasTaprootInput(PendingTransaction pendingTransaction) {
395 return (pendingTransaction as PendingBitcoinTransaction).hasTaprootInputs;
lib/entities/default_settings_migration.dart
+18
@@ -220,6 +220,10 @@ Future<void> defaultSettingsMigration(
220 await updateNanoNodeList(nodes: nodes);
221 break;
222
223 + case 32:
224 + await updateBtcNanoWalletInfos(walletInfoSource);
225 + break;
226 +
227 default:
228 break;
229 }
@@ -756,6 +760,20 @@ Future<void> changeDefaultMoneroNode(
760 }
761 }
762
763 +Future<void> updateBtcNanoWalletInfos(Box<WalletInfo> walletsInfoSource) async {
764 + for (WalletInfo walletInfo in walletsInfoSource.values) {
765 + if (walletInfo.type == WalletType.nano || walletInfo.type == WalletType.bitcoin) {
766 + walletInfo.derivationInfo = DerivationInfo(
767 + derivationPath: walletInfo.derivationPath,
768 + derivationType: walletInfo.derivationType,
769 + address: walletInfo.address,
770 + transactionsCount: walletInfo.restoreHeight,
771 + );
772 + await walletInfo.save();
773 + }
774 + }
775 +}
776 +
777 Future<void> changeDefaultBitcoinNode(
778 Box<Node> nodeSource, SharedPreferences sharedPreferences) async {
779 const cakeWalletBitcoinNodeUriPattern = '.cakewallet.com';
lib/entities/parse_address_from_domain.dart
+4 -2
@@ -51,10 +51,12 @@ class AddressResolver {
51 }
52
53 final match = RegExp(addressPattern).firstMatch(raw);
54 - return match?.group(0)?.replaceAllMapped(RegExp('[^0-9a-zA-Z]|bitcoincash:|nano_'),
54 + return match?.group(0)?.replaceAllMapped(RegExp('[^0-9a-zA-Z]|bitcoincash:|nano_|ban_'),
55 (Match match) {
56 String group = match.group(0)!;
57 - if (group.startsWith('bitcoincash:') || group.startsWith('nano_')) {
57 + if (group.startsWith('bitcoincash:') ||
58 + group.startsWith('nano_') ||
59 + group.startsWith('ban_')) {
60 return group;
61 }
62 return '';
lib/main.dart
+5 -1
@@ -102,6 +102,10 @@ Future<void> initializeAppConfigs() async {
102 CakeHive.registerAdapter(DerivationTypeAdapter());
103 }
104
105 + if (!CakeHive.isAdapterRegistered(DERIVATION_INFO_TYPE_ID)) {
106 + CakeHive.registerAdapter(DerivationInfoAdapter());
107 + }
108 +
109 if (!CakeHive.isAdapterRegistered(WALLET_TYPE_TYPE_ID)) {
110 CakeHive.registerAdapter(WalletTypeAdapter());
111 }
@@ -163,7 +167,7 @@ Future<void> initializeAppConfigs() async {
167 transactionDescriptions: transactionDescriptions,
168 secureStorage: secureStorage,
169 anonpayInvoiceInfo: anonpayInvoiceInfo,
166 - initialMigrationVersion: 31,
170 + initialMigrationVersion: 32,
171 );
172 }
173
lib/nano/cw_nano.dart
+57 -17
@@ -96,6 +96,7 @@ class CWNano extends Nano {
96 NanoNewWalletCredentials(
97 name: name,
98 password: password,
99 + derivationType: DerivationType.nano,
100 );
101
102 @override
@@ -103,15 +104,10 @@ class CWNano extends Nano {
104 required String name,
105 required String password,
106 required String mnemonic,
106 - DerivationType? derivationType,
107 + required DerivationType derivationType,
108 }) {
108 - if (derivationType == null) {
109 - // figure out the derivation type as best we can, otherwise set it to "unknown"
110 - if (mnemonic.split(" ").length == 12) {
111 - derivationType = DerivationType.bip39;
112 - } else {
113 - derivationType = DerivationType.unknown;
114 - }
109 + if (mnemonic.split(" ").length == 12 && derivationType != DerivationType.bip39) {
110 + throw Exception("Invalid mnemonic for derivation type!");
111 }
112
113 return NanoRestoreWalletFromSeedCredentials(
@@ -127,15 +123,10 @@ class CWNano extends Nano {
123 required String name,
124 required String password,
125 required String seedKey,
130 - DerivationType? derivationType,
126 + required DerivationType derivationType,
127 }) {
132 - if (derivationType == null) {
133 - // figure out the derivation type as best we can, otherwise set it to "unknown"
134 - if (seedKey.length == 64) {
135 - derivationType = DerivationType.nano;
136 - } else {
137 - derivationType = DerivationType.unknown;
138 - }
128 + if (seedKey.length == 128 && derivationType != DerivationType.bip39) {
129 + throw Exception("Invalid seed key length for derivation type!");
130 }
131
132 return NanoRestoreWalletFromKeysCredentials(
@@ -199,7 +190,6 @@ class CWNano extends Nano {
190 }
191
192 class CWNanoUtil extends NanoUtil {
202 -
193 @override
194 bool isValidBip39Seed(String seed) {
195 return NanoDerivations.isValidBip39Seed(seed);
@@ -353,4 +343,54 @@ class CWNanoUtil extends NanoUtil {
343 return [DerivationType.nano, DerivationType.bip39];
344 }
345 }
346 +
347 + @override
348 + Future<List<DerivationInfo>> getDerivationsFromMnemonic({
349 + String? mnemonic,
350 + String? seedKey,
351 + required Node node,
352 + }) async {
353 + List<DerivationInfo> list = [];
354 +
355 + List<DerivationType> possibleDerivationTypes = await compareDerivationMethods(
356 + mnemonic: mnemonic,
357 + privateKey: seedKey,
358 + node: node,
359 + );
360 + if (possibleDerivationTypes.length == 1) {
361 + return [DerivationInfo(derivationType: possibleDerivationTypes.first)];
362 + }
363 +
364 + AccountInfoResponse? bip39Info = await nanoUtil!.getInfoFromSeedOrMnemonic(
365 + DerivationType.bip39,
366 + mnemonic: mnemonic,
367 + seedKey: seedKey,
368 + node: node,
369 + );
370 + AccountInfoResponse? standardInfo = await nanoUtil!.getInfoFromSeedOrMnemonic(
371 + DerivationType.nano,
372 + mnemonic: mnemonic,
373 + seedKey: seedKey,
374 + node: node,
375 + );
376 +
377 + if (standardInfo?.confirmationHeight != null && standardInfo!.confirmationHeight > 0) {
378 + list.add(DerivationInfo(
379 + derivationType: DerivationType.nano,
380 + balance: nanoUtil!.getRawAsUsableString(standardInfo.balance, nanoUtil!.rawPerNano),
381 + address: standardInfo.address!,
382 + transactionsCount: standardInfo.confirmationHeight,
383 + ));
384 + }
385 +
386 + if (bip39Info?.confirmationHeight != null && bip39Info!.confirmationHeight > 0) {
387 + list.add(DerivationInfo(
388 + derivationType: DerivationType.bip39,
389 + balance: nanoUtil!.getRawAsUsableString(bip39Info.balance, nanoUtil!.rawPerNano),
390 + address: bip39Info.address!,
391 + transactionsCount: bip39Info.confirmationHeight,
392 + ));
393 + }
394 + return list;
395 + }
396 }
lib/src/screens/restore/wallet_restore_choose_derivation.dart
+9 -13
@@ -1,8 +1,5 @@
1 -import 'package:cake_wallet/di.dart';
1 import 'package:cake_wallet/generated/i18n.dart';
3 -import 'package:cake_wallet/store/settings_store.dart';
2 import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
5 -import 'package:cake_wallet/themes/theme_base.dart';
3 import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart';
4 import 'package:cw_core/wallet_info.dart';
5 import 'package:flutter/material.dart';
@@ -13,15 +10,14 @@ class WalletRestoreChooseDerivationPage extends BasePage {
10 WalletRestoreChooseDerivationPage(this.walletRestoreChooseDerivationViewModel) {}
11
12 @override
16 - Widget middle(BuildContext context) => Observer(
17 - builder: (_) => Text(
18 - S.current.choose_derivation,
19 - style: TextStyle(
20 - fontSize: 18.0,
21 - fontWeight: FontWeight.bold,
22 - fontFamily: 'Lato',
23 - color: titleColor(context)),
24 - ));
13 + Widget middle(BuildContext context) => Text(
14 + S.current.choose_derivation,
15 + style: TextStyle(
16 + fontSize: 18.0,
17 + fontWeight: FontWeight.bold,
18 + fontFamily: 'Lato',
19 + color: titleColor(context)),
20 + );
21
22 final WalletRestoreChooseDerivationViewModel walletRestoreChooseDerivationViewModel;
23 DerivationType derivationType = DerivationType.unknown;
@@ -105,7 +101,7 @@ class WalletRestoreChooseDerivationPage extends BasePage {
101 ),
102 ),
103 Text(
108 - "${S.current.transactions}: ${derivation.height}",
104 + "${S.current.transactions}: ${derivation.transactionsCount}",
105 style: Theme.of(context).primaryTextTheme.labelMedium!.copyWith(
106 fontSize: 16,
107 fontWeight: FontWeight.w500,
lib/src/screens/restore/wallet_restore_from_seed_form.dart
+21 -9
@@ -20,6 +20,7 @@ class WalletRestoreFromSeedForm extends StatefulWidget {
20 {Key? key,
21 required this.displayLanguageSelector,
22 required this.displayBlockHeightSelector,
23 + required this.displayPassphrase,
24 required this.type,
25 required this.seedTypeViewModel,
26 this.blockHeightFocusNode,
@@ -31,6 +32,7 @@ class WalletRestoreFromSeedForm extends StatefulWidget {
32 final WalletType type;
33 final bool displayLanguageSelector;
34 final bool displayBlockHeightSelector;
35 + final bool displayPassphrase;
36 final SeedTypeViewModel seedTypeViewModel;
37 final FocusNode? blockHeightFocusNode;
38 final Function(bool)? onHeightOrDateEntered;
@@ -48,6 +50,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
50 formKey = GlobalKey<FormState>(),
51 languageController = TextEditingController(),
52 nameTextEditingController = TextEditingController(),
53 + passphraseController = TextEditingController(),
54 seedTypeController = TextEditingController();
55
56 final GlobalKey<SeedWidgetState> seedWidgetStateKey;
@@ -55,6 +58,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
58 final TextEditingController languageController;
59 final TextEditingController nameTextEditingController;
60 final TextEditingController seedTypeController;
61 + final TextEditingController passphraseController;
62 final GlobalKey<FormState> formKey;
63 late ReactionDisposer moneroSeedTypeReaction;
64 String language;
@@ -166,15 +170,15 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
170 ),
171 if (widget.displayLanguageSelector)
172 GestureDetector(
169 - onTap: () async {
170 - await showPopUp<void>(
171 - context: context,
172 - builder: (_) => SeedLanguagePicker(
173 - selected: language,
174 - onItemSelected: _changeLanguage,
175 - seedType: isPolyseed ? SeedType.polyseed : SeedType.legacy,
176 - ));
177 - },
173 + onTap: () async {
174 + await showPopUp<void>(
175 + context: context,
176 + builder: (_) => SeedLanguagePicker(
177 + selected: language,
178 + onItemSelected: _changeLanguage,
179 + seedType: isPolyseed ? SeedType.polyseed : SeedType.legacy,
180 + ));
181 + },
182 child: Container(
183 color: Colors.transparent,
184 padding: EdgeInsets.only(top: 20.0),
@@ -194,6 +198,14 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
198 key: blockchainHeightKey,
199 onHeightOrDateEntered: widget.onHeightOrDateEntered,
200 hasDatePicker: widget.type == WalletType.monero),
201 + if (widget.displayPassphrase) ...[
202 + const SizedBox(height: 10),
203 + BaseTextFormField(
204 + hintText: S.current.passphrase,
205 + controller: passphraseController,
206 + obscureText: true,
207 + ),
208 + ]
209 ]));
210 }
211
lib/src/screens/restore/wallet_restore_page.dart
+43 -91
@@ -1,7 +1,5 @@
1 import 'package:cake_wallet/core/execution_state.dart';
2 -import 'package:cake_wallet/di.dart';
2 import 'package:cake_wallet/generated/i18n.dart';
4 -import 'package:cake_wallet/nano/nano.dart';
3 import 'package:cake_wallet/routes.dart';
4 import 'package:cake_wallet/src/screens/base_page.dart';
5 import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.dart';
@@ -9,7 +7,6 @@ import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.da
7 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
8 import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
9 import 'package:cake_wallet/src/widgets/primary_button.dart';
12 -import 'package:cake_wallet/store/app_store.dart';
10 import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
11 import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
12 import 'package:cake_wallet/utils/responsive_layout_util.dart';
@@ -17,7 +14,6 @@ import 'package:cake_wallet/utils/show_pop_up.dart';
14 import 'package:cake_wallet/view_model/restore/restore_mode.dart';
15 import 'package:cake_wallet/view_model/seed_type_view_model.dart';
16 import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
20 -import 'package:cw_core/nano_account_info_response.dart';
17 import 'package:cw_core/wallet_info.dart';
18 import 'package:cw_core/wallet_type.dart';
19 import 'package:flutter/material.dart';
@@ -42,6 +38,7 @@ class WalletRestorePage extends BasePage {
38 displayBlockHeightSelector:
39 walletRestoreViewModel.hasBlockchainHeightLanguageSelector,
40 displayLanguageSelector: walletRestoreViewModel.hasSeedLanguageSelector,
41 + displayPassphrase: walletRestoreViewModel.hasPassphrase,
42 type: walletRestoreViewModel.type,
43 key: walletRestoreFromSeedFormKey,
44 blockHeightFocusNode: _blockHeightFocusNode,
@@ -99,8 +96,10 @@ class WalletRestorePage extends BasePage {
96 final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
97 final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey;
98 final FocusNode _blockHeightFocusNode;
102 - DerivationType derivationType = DerivationType.unknown;
103 - String? derivationPath = null;
99 +
100 + // DerivationType derivationType = DerivationType.unknown;
101 + // String? derivationPath = null;
102 + DerivationInfo? derivationInfo;
103
104 @override
105 Widget body(BuildContext context) {
@@ -298,6 +297,11 @@ class WalletRestorePage extends BasePage {
297 -1;
298 }
299
300 + if (walletRestoreViewModel.hasPassphrase) {
301 + credentials['passphrase'] =
302 + walletRestoreFromSeedFormKey.currentState!.passphraseController.text;
303 + }
304 +
305 credentials['name'] =
306 walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text;
307 } else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) {
@@ -318,58 +322,11 @@ class WalletRestorePage extends BasePage {
322 }
323 }
324
321 - credentials['derivationType'] = this.derivationType;
322 - credentials['derivationPath'] = this.derivationPath;
325 + credentials['derivationInfo'] = this.derivationInfo;
326 credentials['walletType'] = walletRestoreViewModel.type;
327 return credentials;
328 }
329
327 - Future<List<DerivationInfo>> getDerivationInfo(dynamic credentials) async {
328 - var list = <DerivationInfo>[];
329 - var walletType = credentials["walletType"] as WalletType;
330 - var appStore = getIt.get<AppStore>();
331 - var node = appStore.settingsStore.getCurrentNode(walletType);
332 -
333 - switch (walletType) {
334 - case WalletType.nano:
335 - String? mnemonic = credentials['seed'] as String?;
336 - String? seedKey = credentials['private_key'] as String?;
337 - AccountInfoResponse? bip39Info = await nanoUtil!.getInfoFromSeedOrMnemonic(
338 - DerivationType.bip39,
339 - mnemonic: mnemonic,
340 - seedKey: seedKey,
341 - node: node);
342 - AccountInfoResponse? standardInfo = await nanoUtil!.getInfoFromSeedOrMnemonic(
343 - DerivationType.nano,
344 - mnemonic: mnemonic,
345 - seedKey: seedKey,
346 - node: node,
347 - );
348 -
349 - if (standardInfo?.balance != null) {
350 - list.add(DerivationInfo(
351 - derivationType: DerivationType.nano,
352 - balance: nanoUtil!.getRawAsUsableString(standardInfo!.balance, nanoUtil!.rawPerNano),
353 - address: standardInfo.address!,
354 - height: standardInfo.confirmationHeight,
355 - ));
356 - }
357 -
358 - if (bip39Info?.balance != null) {
359 - list.add(DerivationInfo(
360 - derivationType: DerivationType.bip39,
361 - balance: nanoUtil!.getRawAsUsableString(bip39Info!.balance, nanoUtil!.rawPerNano),
362 - address: bip39Info.address!,
363 - height: bip39Info.confirmationHeight,
364 - ));
365 - }
366 - break;
367 - default:
368 - break;
369 - }
370 - return list;
371 - }
372 -
330 Future<void> _confirmForm(BuildContext context) async {
331 // Dismissing all visible keyboard to provide context for navigation
332 FocusManager.instance.primaryFocus?.unfocus();
@@ -398,51 +355,46 @@ class WalletRestorePage extends BasePage {
355
356 walletRestoreViewModel.state = IsExecutingState();
357
401 - List<DerivationType> derivationTypes =
402 - await walletRestoreViewModel.getDerivationTypes(_credentials());
358 + DerivationInfo? dInfo;
359
404 - if (derivationTypes[0] == DerivationType.unknown || derivationTypes.length > 1) {
405 - // push screen to choose the derivation type:
406 - List<DerivationInfo> derivations = await getDerivationInfo(_credentials());
360 + // get info about the different derivations:
361 + List<DerivationInfo> derivations =
362 + await walletRestoreViewModel.getDerivationInfo(_credentials());
363
408 - int derivationsWithHistory = 0;
409 - int derivationWithHistoryIndex = 0;
410 - for (int i = 0; i < derivations.length; i++) {
411 - if (derivations[i].height > 0) {
412 - derivationsWithHistory++;
413 - derivationWithHistoryIndex = i;
414 - }
364 + int derivationsWithHistory = 0;
365 + int derivationWithHistoryIndex = 0;
366 + for (int i = 0; i < derivations.length; i++) {
367 + if (derivations[i].transactionsCount > 0) {
368 + derivationsWithHistory++;
369 + derivationWithHistoryIndex = i;
370 }
371 + }
372
417 - DerivationInfo? derivationInfo;
418 -
419 - if (derivationsWithHistory > 1) {
420 - derivationInfo = await Navigator.of(context).pushNamed(Routes.restoreWalletChooseDerivation,
421 - arguments: derivations) as DerivationInfo?;
422 - } else if (derivationsWithHistory == 1) {
423 - derivationInfo = derivations[derivationWithHistoryIndex];
424 - } else if (derivationsWithHistory == 0) {
425 - // default derivation:
426 - derivationInfo = DerivationInfo(
427 - derivationType: derivationTypes[0],
428 - derivationPath: "m/0'/1",
429 - height: 0,
430 - );
431 - }
373 + if (derivationsWithHistory > 1) {
374 + dInfo = await Navigator.of(context).pushNamed(
375 + Routes.restoreWalletChooseDerivation,
376 + arguments: derivations,
377 + ) as DerivationInfo?;
378 + } else if (derivationsWithHistory == 1) {
379 + dInfo = derivations[derivationWithHistoryIndex];
380 + }
381
433 - if (derivationInfo == null) {
434 - walletRestoreViewModel.state = InitialExecutionState();
435 - return;
382 + // get the default derivation for this wallet type:
383 + if (dInfo == null) {
384 + // we only return 1 derivation if we're pretty sure we know which one to use:
385 + if (derivations.length == 1) {
386 + dInfo = derivations.first;
387 + } else {
388 + // if we have multiple possible derivations, and none have histories
389 + // we just default to the most common one:
390 + dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
391 }
437 - this.derivationType = derivationInfo.derivationType;
438 - this.derivationPath = derivationInfo.derivationPath;
439 - } else {
440 - // electrum derivation:
441 - this.derivationType = derivationTypes[0];
442 - this.derivationPath = "m/0'/1";
392 }
393
445 - walletRestoreViewModel.state = InitialExecutionState();
394 + this.derivationInfo = dInfo;
395 + if (this.derivationInfo == null) {
396 + this.derivationInfo = walletRestoreViewModel.getDefaultDerivation();
397 + }
398
399 walletRestoreViewModel.create(options: _credentials());
400 }
lib/src/screens/wallet_list/wallet_list_page.dart
+3 -1
@@ -343,7 +343,9 @@ class WalletListBodyState extends State<WalletListBody> {
343 });
344 }
345 } catch (e) {
346 - changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
346 + if (this.mounted) {
347 + changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
348 + }
349 }
350 },
351 conditionToDetermineIfToUse2FA:
lib/src/widgets/base_text_form_field.dart
+3
@@ -22,6 +22,7 @@ class BaseTextFormField extends StatelessWidget {
22 this.enabled = true,
23 this.readOnly = false,
24 this.enableInteractiveSelection = true,
25 + this.obscureText = false,
26 this.validator,
27 this.textStyle,
28 this.placeholderTextStyle,
@@ -57,6 +58,7 @@ class BaseTextFormField extends StatelessWidget {
58 final String? initialValue;
59 final double borderWidth;
60 final void Function(String)? onSubmit;
61 + final bool obscureText;
62
63 @override
64 Widget build(BuildContext context) {
@@ -70,6 +72,7 @@ class BaseTextFormField extends StatelessWidget {
72 textInputAction: textInputAction,
73 textAlign: textAlign,
74 autovalidateMode: autovalidateMode,
75 + obscureText: obscureText,
76 maxLines: maxLines,
77 inputFormatters: inputFormatters,
78 enabled: enabled,
lib/view_model/restore/restore_from_qr_vm.dart
+33 -11
@@ -30,8 +30,7 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
30 spendKey = '',
31 wif = '',
32 address = '',
33 - super(appStore, walletInfoSource, walletCreationService,
34 - type: type, isRecovery: true);
33 + super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true);
34
35 @observable
36 int height;
@@ -51,8 +50,16 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
50 bool get hasRestorationHeight => type == WalletType.monero;
51
52 @override
54 - WalletCredentials getCredentialsFromRestoredWallet(dynamic options, RestoredWallet restoreWallet) {
53 + WalletCredentials getCredentialsFromRestoredWallet(
54 + dynamic options, RestoredWallet restoreWallet) {
55 final password = generateWalletPassword();
56 + String? passphrase;
57 + DerivationInfo? derivationInfo;
58 + if (options != null) {
59 + derivationInfo = options["derivationInfo"] as DerivationInfo?;
60 + passphrase = options["passphrase"] as String?;
61 + }
62 + derivationInfo ??= getDefaultDerivation();
63
64 switch (restoreWallet.restoreMode) {
65 case WalletRestoreMode.keys:
@@ -86,23 +93,37 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
93 switch (restoreWallet.type) {
94 case WalletType.monero:
95 return monero!.createMoneroRestoreWalletFromSeedCredentials(
89 - name: name,
90 - height: restoreWallet.height ?? 0,
91 - mnemonic: restoreWallet.mnemonicSeed ?? '',
92 - password: password);
96 + name: name,
97 + height: restoreWallet.height ?? 0,
98 + mnemonic: restoreWallet.mnemonicSeed ?? '',
99 + password: password,
100 + );
101 case WalletType.bitcoin:
102 case WalletType.litecoin:
103 return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
96 - name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password);
104 + name: name,
105 + mnemonic: restoreWallet.mnemonicSeed ?? '',
106 + password: password,
107 + passphrase: passphrase,
108 + derivationType: derivationInfo!.derivationType!,
109 + derivationPath: derivationInfo.derivationPath!,
110 + );
111 case WalletType.bitcoinCash:
112 return bitcoinCash!.createBitcoinCashRestoreWalletFromSeedCredentials(
99 - name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password);
113 + name: name,
114 + mnemonic: restoreWallet.mnemonicSeed ?? '',
115 + password: password,
116 + );
117 case WalletType.ethereum:
118 return ethereum!.createEthereumRestoreWalletFromSeedCredentials(
119 name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password);
120 case WalletType.nano:
121 return nano!.createNanoRestoreWalletFromSeedCredentials(
105 - name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password);
122 + name: name,
123 + mnemonic: restoreWallet.mnemonicSeed ?? '',
124 + password: password,
125 + derivationType: derivationInfo!.derivationType!,
126 + );
127 case WalletType.polygon:
128 return polygon!.createPolygonRestoreWalletFromSeedCredentials(
129 name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password);
@@ -118,7 +139,8 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
139 }
140
141 @override
121 - Future<WalletBase> processFromRestoredWallet(WalletCredentials credentials, RestoredWallet restoreWallet) async {
142 + Future<WalletBase> processFromRestoredWallet(
143 + WalletCredentials credentials, RestoredWallet restoreWallet) async {
144 try {
145 switch (restoreWallet.restoreMode) {
146 case WalletRestoreMode.keys:
lib/view_model/send/send_view_model.dart
+1 -1
@@ -423,7 +423,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
423 Object _credentials() {
424 final priority = _settingsStore.priority[wallet.type];
425
426 - if (priority == null && wallet.type != WalletType.nano && wallet.type != WalletType.solana) {
426 + if (priority == null && wallet.type != WalletType.nano && wallet.type != WalletType.banano && wallet.type != WalletType.solana) {
427 throw Exception('Priority is null for wallet type: ${wallet.type}');
428 }
429
lib/view_model/wallet_creation_vm.dart
+44 -2
@@ -71,9 +71,9 @@ abstract class WalletCreationVMBase with Store {
71 dirPath: dirPath,
72 address: '',
73 showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven,
74 - derivationPath: credentials.derivationPath,
75 - derivationType: credentials.derivationType,
74 + derivationInfo: credentials.derivationInfo ?? getDefaultDerivation(),
75 );
76 +
77 credentials.walletInfo = walletInfo;
78 final wallet = restoreWallet != null
79 ? await processFromRestoredWallet(credentials, restoreWallet)
@@ -89,6 +89,48 @@ abstract class WalletCreationVMBase with Store {
89 }
90 }
91
92 + DerivationInfo? getDefaultDerivation() {
93 + switch (this.type) {
94 + case WalletType.nano:
95 + return DerivationInfo(
96 + derivationType: DerivationType.nano,
97 + );
98 + case WalletType.bitcoin:
99 + case WalletType.litecoin:
100 + return DerivationInfo(
101 + derivationType: DerivationType.electrum,
102 + derivationPath: "m/0'/0",
103 + );
104 + default:
105 + return null;
106 + }
107 + }
108 +
109 + DerivationInfo? getCommonRestoreDerivation() {
110 + switch (this.type) {
111 + case WalletType.nano:
112 + return DerivationInfo(
113 + derivationType: DerivationType.nano,
114 + );
115 + case WalletType.bitcoin:
116 + return DerivationInfo(
117 + derivationType: DerivationType.bip39,
118 + derivationPath: "m/84'/0'/0'/0",
119 + description: "Standard BIP84 native segwit",
120 + scriptType: "p2wpkh",
121 + );
122 + case WalletType.litecoin:
123 + return DerivationInfo(
124 + derivationType: DerivationType.bip39,
125 + derivationPath: "m/84'/2'/0'/0",
126 + description: "Standard BIP84 native segwit (litecoin)",
127 + scriptType: "p2wpkh",
128 + );
129 + default:
130 + return null;
131 + }
132 + }
133 +
134 WalletCredentials getCredentials(dynamic options) => throw UnimplementedError();
135
136 Future<WalletBase> process(WalletCredentials credentials) => throw UnimplementedError();
lib/view_model/wallet_restore_view_model.dart
+35 -17
@@ -2,6 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 import 'package:cake_wallet/di.dart';
3 import 'package:cake_wallet/nano/nano.dart';
4 import 'package:cake_wallet/ethereum/ethereum.dart';
5 +import 'package:cw_core/nano_account_info_response.dart';
6 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
7 import 'package:cake_wallet/polygon/polygon.dart';
8 import 'package:cake_wallet/solana/solana.dart';
@@ -66,6 +67,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
67 final bool hasBlockchainHeightLanguageSelector;
68 final bool hasRestoreFromPrivateKey;
69
70 + bool get hasPassphrase => [WalletType.bitcoin, WalletType.litecoin].contains(type);
71 +
72 @observable
73 WalletRestoreMode mode;
74
@@ -75,10 +78,10 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
78 @override
79 WalletCredentials getCredentials(dynamic options) {
80 final password = generateWalletPassword();
81 + String? passphrase = options['passphrase'] as String?;
82 final height = options['height'] as int? ?? 0;
83 name = options['name'] as String;
80 - DerivationType? derivationType = options["derivationType"] as DerivationType?;
81 - String? derivationPath = options["derivationPath"] as String?;
84 + DerivationInfo? derivationInfo = options["derivationInfo"] as DerivationInfo?;
85
86 if (mode == WalletRestoreMode.seed) {
87 final seed = options['seed'] as String;
@@ -87,14 +90,15 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
90 return monero!.createMoneroRestoreWalletFromSeedCredentials(
91 name: name, height: height, mnemonic: seed, password: password);
92 case WalletType.bitcoin:
93 + case WalletType.litecoin:
94 return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
95 name: name,
96 mnemonic: seed,
97 password: password,
98 + passphrase: passphrase,
99 + derivationType: derivationInfo!.derivationType!,
100 + derivationPath: derivationInfo.derivationPath!,
101 );
95 - case WalletType.litecoin:
96 - return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
97 - name: name, mnemonic: seed, password: password);
102 case WalletType.haven:
103 return haven!.createHavenRestoreWalletFromSeedCredentials(
104 name: name, height: height, mnemonic: seed, password: password);
@@ -106,7 +110,11 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
110 name: name, mnemonic: seed, password: password);
111 case WalletType.nano:
112 return nano!.createNanoRestoreWalletFromSeedCredentials(
109 - name: name, mnemonic: seed, password: password, derivationType: derivationType);
113 + name: name,
114 + mnemonic: seed,
115 + password: password,
116 + derivationType: derivationInfo!.derivationType!,
117 + );
118 case WalletType.polygon:
119 return polygon!.createPolygonRestoreWalletFromSeedCredentials(
120 name: name,
@@ -185,23 +193,34 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
193 throw Exception('Unexpected type: ${type.toString()}');
194 }
195
188 - Future<List<DerivationType>> getDerivationTypes(dynamic options) async {
189 - final seedKey = options['private_key'] as String?;
190 - final mnemonic = options['seed'] as String?;
191 - WalletType walletType = options['walletType'] as WalletType;
196 + Future<List<DerivationInfo>> getDerivationInfo(dynamic credentials) async {
197 + var list = <DerivationInfo>[];
198 + var walletType = credentials["walletType"] as WalletType;
199 var appStore = getIt.get<AppStore>();
200 var node = appStore.settingsStore.getCurrentNode(walletType);
201
195 - switch (type) {
202 + switch (walletType) {
203 + case WalletType.bitcoin:
204 + case WalletType.litecoin:
205 + String? mnemonic = credentials['seed'] as String?;
206 + String? passphrase = credentials['passphrase'] as String?;
207 + return bitcoin!.getDerivationsFromMnemonic(
208 + mnemonic: mnemonic!,
209 + node: node,
210 + passphrase: passphrase,
211 + );
212 case WalletType.nano:
197 - return nanoUtil!
198 - .compareDerivationMethods(mnemonic: mnemonic, privateKey: seedKey, node: node);
213 + String? mnemonic = credentials['seed'] as String?;
214 + String? seedKey = credentials['private_key'] as String?;
215 + return nanoUtil!.getDerivationsFromMnemonic(
216 + mnemonic: mnemonic,
217 + seedKey: seedKey,
218 + node: node,
219 + );
220 default:
221 break;
222 }
202 -
203 - // throw Exception('Unexpected type: ${type.toString()}');
204 - return [DerivationType.def];
223 + return list;
224 }
225
226 @override
@@ -209,7 +228,6 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
228 if (mode == WalletRestoreMode.keys) {
229 return walletCreationService.restoreFromKeys(credentials, isTestnet: useTestnet);
230 }
212 -
231 return walletCreationService.restoreFromSeed(credentials, isTestnet: useTestnet);
232 }
233 }
res/values/strings_ar.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "المخرجات",
412 "overwrite_amount": "تغير المبلغ",
413 "pairingInvalidEvent": "ﺢﻟﺎﺻ ﺮﻴﻏ ﺙﺪﺣ ﻥﺍﺮﻗﺇ",
414 + "passphrase": "عبارة الممر (اختياري)",
415 "password": "كلمة المرور",
416 "paste": "لصق",
417 "pause_wallet_creation": ".ﺎﻴًﻟﺎﺣ ﺎﺘًﻗﺆﻣ ﺔﻔﻗﻮﺘﻣ Haven Wallet ءﺎﺸﻧﺇ ﻰﻠﻋ ﺓﺭﺪﻘﻟﺍ",
res/values/strings_bg.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "Изходи",
412 "overwrite_amount": "Промени сума",
413 "pairingInvalidEvent": "Невалидно събитие при сдвояване",
414 + "passphrase": "Passphrase (по избор)",
415 "password": "Парола",
416 "paste": "Поставяне",
417 "pause_wallet_creation": "Възможността за създаване на Haven Wallet в момента е на пауза.",
res/values/strings_cs.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "Výstupy",
412 "overwrite_amount": "Přepsat částku",
413 "pairingInvalidEvent": "Neplatná událost párování",
414 + "passphrase": "Passphrase (volitelné)",
415 "password": "Heslo",
416 "paste": "Vložit",
417 "pause_wallet_creation": "Možnost vytvářet Haven Wallet je momentálně pozastavena.",
res/values/strings_de.arb
+3 -2
@@ -411,6 +411,7 @@
411 "outputs": "Ausgänge",
412 "overwrite_amount": "Overwrite amount",
413 "pairingInvalidEvent": "Paarung ungültiges Ereignis",
414 + "passphrase": "Passphrase (optional)",
415 "password": "Passwort",
416 "paste": "Einfügen",
417 "pause_wallet_creation": "Die Möglichkeit, Haven Wallet zu erstellen, ist derzeit pausiert.",
@@ -425,8 +426,8 @@
426 "placeholder_transactions": "Ihre Transaktionen werden hier angezeigt",
427 "please_fill_totp": "Bitte geben Sie den 8-stelligen Code ein, der auf Ihrem anderen Gerät vorhanden ist",
428 "please_make_selection": "Bitte treffen Sie unten eine Auswahl zum Erstellen oder Wiederherstellen Ihrer Wallet.",
428 - "Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.",
429 "please_reference_document": "Bitte verweisen Sie auf die folgenden Dokumente, um weitere Informationen zu erhalten.",
430 + "Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.",
431 "please_select": "Bitte auswählen:",
432 "please_select_backup_file": "Bitte wählen Sie die Sicherungsdatei und geben Sie das Sicherungskennwort ein.",
433 "please_try_to_connect_to_another_node": "Bitte versuchen Sie, sich mit einem anderen Knoten zu verbinden",
@@ -822,4 +823,4 @@
823 "you_will_get": "Konvertieren zu",
824 "you_will_send": "Konvertieren von",
825 "yy": "YY"
825 -}
826 +}
\ No newline at end of file
res/values/strings_en.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "Outputs",
412 "overwrite_amount": "Overwrite amount",
413 "pairingInvalidEvent": "Pairing Invalid Event",
414 + "passphrase": "Passphrase (Optional)",
415 "password": "Password",
416 "paste": "Paste",
417 "pause_wallet_creation": "Ability to create Haven Wallet is currently paused.",
res/values/strings_es.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "Salidas",
412 "overwrite_amount": "Overwrite amount",
413 "pairingInvalidEvent": "Evento de emparejamiento no válido",
414 + "passphrase": "Passfrase (opcional)",
415 "password": "Contraseña",
416 "paste": "Pegar",
417 "pause_wallet_creation": "La capacidad para crear Haven Wallet está actualmente pausada.",
res/values/strings_fr.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "Les sorties",
412 "overwrite_amount": "Remplacer le montant",
413 "pairingInvalidEvent": "Événement de couplage non valide",
414 + "passphrase": "Phrase de passe (facultative)",
415 "password": "Mot de passe",
416 "paste": "Coller",
417 "pause_wallet_creation": "La possibilité de créer Haven Wallet est actuellement suspendue.",
res/values/strings_ha.arb
+1
@@ -413,6 +413,7 @@
413 "outputs": "Abubuwan fashewa",
414 "overwrite_amount": "Rubuta adadin",
415 "pairingInvalidEvent": "Haɗa Lamarin mara inganci",
416 + "passphrase": "Passphrase (Zabi)",
417 "password": "Kalmar wucewa",
418 "paste": "Manna",
419 "pause_wallet_creation": "A halin yanzu an dakatar da ikon ƙirƙirar Haven Wallet.",
res/values/strings_hi.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "आउटपुट",
412 "overwrite_amount": "Overwrite amount",
413 "pairingInvalidEvent": "अमान्य ईवेंट युग्मित करना",
414 + "passphrase": "पासफ्रेज़ (वैकल्पिक)",
415 "password": "पारण शब्द",
416 "paste": "पेस्ट करें",
417 "pause_wallet_creation": "हेवन वॉलेट बनाने की क्षमता फिलहाल रुकी हुई है।",
res/values/strings_hr.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "Izlazi",
412 "overwrite_amount": "Overwrite amount",
413 "pairingInvalidEvent": "Nevažeći događaj uparivanja",
414 + "passphrase": "Prolaznica (neobavezno)",
415 "password": "Lozinka",
416 "paste": "Zalijepi",
417 "pause_wallet_creation": "Mogućnost stvaranja novčanika Haven trenutno je pauzirana.",
res/values/strings_id.arb
+1
@@ -413,6 +413,7 @@
413 "outputs": "Output",
414 "overwrite_amount": "Timpa jumlah",
415 "pairingInvalidEvent": "Menyandingkan Acara Tidak Valid",
416 + "passphrase": "Frasa sandi (opsional)",
417 "password": "Kata Sandi",
418 "paste": "Tempel",
419 "pause_wallet_creation": "Kemampuan untuk membuat Haven Wallet saat ini dijeda.",
res/values/strings_it.arb
+1
@@ -413,6 +413,7 @@
413 "outputs": "Output",
414 "overwrite_amount": "Sovrascrivi quantità",
415 "pairingInvalidEvent": "Associazione evento non valido",
416 + "passphrase": "Passphrase (opzionale)",
417 "password": "Password",
418 "paste": "Incolla",
419 "pause_wallet_creation": "La possibilità di creare Haven Wallet è attualmente sospesa.",
res/values/strings_ja.arb
+1
@@ -412,6 +412,7 @@
412 "outputs": "出力",
413 "overwrite_amount": "Overwrite amount",
414 "pairingInvalidEvent": "ペアリング無効イベント",
415 + "passphrase": "パスフレーズ(オプション)",
416 "password": "パスワード",
417 "paste": "ペースト",
418 "pause_wallet_creation": "Haven Wallet を作成する機能は現在一時停止されています。",
res/values/strings_ko.arb
+2 -1
@@ -411,6 +411,7 @@
411 "outputs": "출력",
412 "overwrite_amount": "Overwrite amount",
413 "pairingInvalidEvent": "잘못된 이벤트 페어링",
414 + "passphrase": "암호화 (선택 사항)",
415 "password": "암호",
416 "paste": "풀",
417 "pause_wallet_creation": "Haven Wallet 생성 기능이 현재 일시 중지되었습니다.",
@@ -425,8 +426,8 @@
426 "placeholder_transactions": "거래가 여기에 표시됩니다",
427 "please_fill_totp": "다른 기기에 있는 8자리 코드를 입력하세요.",
428 "please_make_selection": "아래에서 선택하십시오 지갑 만들기 또는 복구.",
428 - "please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
429 "Please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
430 + "please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
431 "please_select": "선택 해주세요:",
432 "please_select_backup_file": "백업 파일을 선택하고 백업 암호를 입력하십시오.",
433 "please_try_to_connect_to_another_node": "다른 노드에 연결을 시도하십시오",
res/values/strings_my.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "ထုတ်လုပ်မှု",
412 "overwrite_amount": "ပမာဏကို ထပ်ရေးပါ။",
413 "pairingInvalidEvent": "မမှန်ကန်သောဖြစ်ရပ်ကို တွဲချိတ်ခြင်း။",
414 + "passphrase": "passphrase (optional)",
415 "password": "စကားဝှက်",
416 "paste": "ငါးပိ",
417 "pause_wallet_creation": "Haven Wallet ဖန်တီးနိုင်မှုကို လောလောဆယ် ခေတ္တရပ်ထားသည်။",
res/values/strings_nl.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "Uitgangen",
412 "overwrite_amount": "Overwrite amount",
413 "pairingInvalidEvent": "Koppelen Ongeldige gebeurtenis",
414 + "passphrase": "PassaspHRASE (optioneel)",
415 "password": "Wachtwoord",
416 "paste": "Plakken",
417 "pause_wallet_creation": "De mogelijkheid om Haven Wallet te maken is momenteel onderbroken.",
res/values/strings_pl.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "Wyjścia",
412 "overwrite_amount": "Nadpisz ilość",
413 "pairingInvalidEvent": "Nieprawidłowe zdarzenie parowania",
414 + "passphrase": "PassPhraza (opcjonalnie)",
415 "password": "Hasło",
416 "paste": "Wklej",
417 "pause_wallet_creation": "Możliwość utworzenia Portfela Haven jest obecnie wstrzymana.",
res/values/strings_pt.arb
+1
@@ -413,6 +413,7 @@
413 "outputs": "Saídas",
414 "overwrite_amount": "Overwrite amount",
415 "pairingInvalidEvent": "Emparelhamento de evento inválido",
416 + "passphrase": "Senha (opcional)",
417 "password": "Senha",
418 "paste": "Colar",
419 "pause_wallet_creation": "A capacidade de criar a Haven Wallet está atualmente pausada.",
res/values/strings_ru.arb
+1
@@ -412,6 +412,7 @@
412 "outputs": "Выходы",
413 "overwrite_amount": "Overwrite amount",
414 "pairingInvalidEvent": "Недействительное событие сопряжения",
415 + "passphrase": "Passfrase (необязательно)",
416 "password": "Пароль",
417 "paste": "Вставить",
418 "pause_wallet_creation": "Возможность создания Haven Wallet в настоящее время приостановлена.",
res/values/strings_th.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "เอาต์พุต",
412 "overwrite_amount": "เขียนทับจำนวน",
413 "pairingInvalidEvent": "การจับคู่เหตุการณ์ที่ไม่ถูกต้อง",
414 + "passphrase": "ข้อความรหัสผ่าน (ไม่บังคับ)",
415 "password": "รหัสผ่าน",
416 "paste": "วาง",
417 "pause_wallet_creation": "ขณะนี้ความสามารถในการสร้าง Haven Wallet ถูกหยุดชั่วคราว",
res/values/strings_tl.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "Mga output",
412 "overwrite_amount": "Overwrite na halaga",
413 "pairingInvalidEvent": "Pagpares ng Di-wastong Kaganapan",
414 + "passphrase": "Passphrase (opsyonal)",
415 "password": "Password",
416 "paste": "I -paste",
417 "pause_wallet_creation": "Kasalukuyang naka-pause ang kakayahang gumawa ng Haven Wallet.",
res/values/strings_tr.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "çıktılar",
412 "overwrite_amount": "Miktarın üzerine yaz",
413 "pairingInvalidEvent": "Geçersiz Etkinliği Eşleştirme",
414 + "passphrase": "Passfrase (isteğe bağlı)",
415 "password": "Parola",
416 "paste": "Yapıştır",
417 "pause_wallet_creation": "Haven Cüzdanı oluşturma yeteneği şu anda duraklatıldı.",
res/values/strings_uk.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "Виходи",
412 "overwrite_amount": "Overwrite amount",
413 "pairingInvalidEvent": "Недійсна подія сполучення",
414 + "passphrase": "Пасофрази (необов’язково)",
415 "password": "Пароль",
416 "paste": "Вставити",
417 "pause_wallet_creation": "Можливість створення гаманця Haven зараз призупинено.",
res/values/strings_ur.arb
+1
@@ -413,6 +413,7 @@
413 "outputs": "نتائج",
414 "overwrite_amount": "رقم کو اوور رائٹ کریں۔",
415 "pairingInvalidEvent": "ﭧﻧﻮﯾﺍ ﻂﻠﻏ ﺎﻧﺎﻨﺑ ﺍﮌﻮﺟ",
416 + "passphrase": "پاسفریز (اختیاری)",
417 "password": "پاس ورڈ",
418 "paste": "چسپاں کریں۔",
419 "pause_wallet_creation": "Haven Wallet ۔ﮯﮨ ﻑﻮﻗﻮﻣ ﻝﺎﺤﻟﺍ ﯽﻓ ﺖﯿﻠﮨﺍ ﯽﮐ ﮯﻧﺎﻨﺑ",
res/values/strings_yo.arb
+1
@@ -412,6 +412,7 @@
412 "outputs": "Awọn iṣan",
413 "overwrite_amount": "Pààrọ̀ iye owó",
414 "pairingInvalidEvent": "Pipọpọ Iṣẹlẹ Ti ko tọ",
415 + "passphrase": "Ọrọ kukuru (iyan)",
416 "password": "Ọ̀rọ̀ aṣínà",
417 "paste": "Fikún ẹ̀dà yín",
418 "pause_wallet_creation": "Agbara lati ṣẹda Haven Wallet ti wa ni idaduro lọwọlọwọ.",
res/values/strings_zh.arb
+1
@@ -411,6 +411,7 @@
411 "outputs": "输出",
412 "overwrite_amount": "Overwrite amount",
413 "pairingInvalidEvent": "配对无效事件",
414 + "passphrase": "密码(可选)",
415 "password": "密码",
416 "paste": "粘贴",
417 "pause_wallet_creation": "创建 Haven 钱包的功能当前已暂停。",
tool/configure.dart
+32 -5
@@ -61,6 +61,8 @@ Future<void> main(List<String> args) async {
61 Future<void> generateBitcoin(bool hasImplementation) async {
62 final outputFile = File(bitcoinOutputPath);
63 const bitcoinCommonHeaders = """
64 +import 'dart:typed_data';
65 +import 'package:cw_core/node.dart';
66 import 'package:cw_core/pending_transaction.dart';
67 import 'package:cw_core/receive_page_option.dart';
68 import 'package:cw_core/unspent_transaction_output.dart';
@@ -73,8 +75,17 @@ import 'package:cw_core/wallet_service.dart';
75 import 'package:cw_core/wallet_type.dart';
76 import 'package:cake_wallet/view_model/send/output.dart';
77 import 'package:hive/hive.dart';
76 -import 'package:bitcoin_base/bitcoin_base.dart';""";
78 +import 'package:bitcoin_base/bitcoin_base.dart';
79 +import 'package:bitcoin_flutter/bitcoin_flutter.dart' as btc;
80 +import 'package:bip32/bip32.dart' as bip32;
81 +import 'package:bip39/bip39.dart' as bip39;
82 +import 'package:hive/hive.dart';
83 +""";
84 const bitcoinCWHeaders = """
85 +import 'package:cw_bitcoin/utils.dart';
86 +import 'package:cw_bitcoin/litecoin_network.dart';
87 +import 'package:cw_bitcoin/electrum_derivations.dart';
88 +import 'package:cw_bitcoin/electrum.dart';
89 import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
90 import 'package:cw_bitcoin/bitcoin_receive_page_option.dart';
91 import 'package:cw_bitcoin/electrum_wallet.dart';
@@ -87,6 +98,7 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart';
98 import 'package:cw_bitcoin/bitcoin_address_record.dart';
99 import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
100 import 'package:cw_bitcoin/litecoin_wallet_service.dart';
101 +import 'package:cw_bitcoin/script_hash.dart';
102 import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
103 import 'package:mobx/mobx.dart';
104 """;
@@ -112,7 +124,14 @@ import 'package:mobx/mobx.dart';
124 abstract class Bitcoin {
125 TransactionPriority getMediumTransactionPriority();
126
115 - WalletCredentials createBitcoinRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password});
127 + WalletCredentials createBitcoinRestoreWalletFromSeedCredentials({
128 + required String name,
129 + required String mnemonic,
130 + required String password,
131 + required DerivationType derivationType,
132 + required String derivationPath,
133 + String? passphrase,
134 + });
135 WalletCredentials createBitcoinRestoreWalletFromWIFCredentials({required String name, required String password, required String wif, WalletInfo? walletInfo});
136 WalletCredentials createBitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo});
137 List<String> getWordList();
@@ -147,7 +166,10 @@ abstract class Bitcoin {
166 TransactionPriority getLitecoinTransactionPriorityMedium();
167 TransactionPriority getBitcoinTransactionPrioritySlow();
168 TransactionPriority getLitecoinTransactionPrioritySlow();
150 -
169 + Future<List<DerivationType>> compareDerivationMethods(
170 + {required String mnemonic, required Node node});
171 + Future<List<DerivationInfo>> getDerivationsFromMnemonic(
172 + {required String mnemonic, required Node node, String? passphrase});
173 Future<void> setAddressType(Object wallet, dynamic option);
174 ReceivePageOption getSelectedAddressType(Object wallet);
175 List<ReceivePageOption> getBitcoinReceivePageOptions();
@@ -838,14 +860,14 @@ abstract class Nano {
860 required String name,
861 required String password,
862 required String mnemonic,
841 - DerivationType? derivationType,
863 + required DerivationType derivationType,
864 });
865
866 WalletCredentials createNanoRestoreWalletFromKeysCredentials({
867 required String name,
868 required String password,
869 required String seedKey,
848 - DerivationType? derivationType,
870 + required DerivationType derivationType,
871 });
872
873 List<String> getNanoWordList(String language);
@@ -892,6 +914,11 @@ abstract class NanoUtil {
914 String? privateKey,
915 required Node node,
916 });
917 + Future<List<DerivationInfo>> getDerivationsFromMnemonic({
918 + String? mnemonic,
919 + String? seedKey,
920 + required Node node,
921 + });
922 }
923 """;
924