diff --git a/assets/nano_pow_node_list.yml b/assets/nano_pow_node_list.yml index b90845034..3bbc7c3fb 100644 --- a/assets/nano_pow_node_list.yml +++ b/assets/nano_pow_node_list.yml @@ -6,4 +6,4 @@ uri: workers.perish.co - uri: worker.nanoriver.cc - useSSL: true \ No newline at end of file + useSSL: true diff --git a/cw_bitcoin/lib/bitcoin_mnemonic.dart b/cw_bitcoin/lib/bitcoin_mnemonic.dart index 9163fcb11..4a01d6ddc 100644 --- a/cw_bitcoin/lib/bitcoin_mnemonic.dart +++ b/cw_bitcoin/lib/bitcoin_mnemonic.dart @@ -90,8 +90,7 @@ List prefixMatches(String source, List prefixes) { return prefixes.map((prefix) => hx.startsWith(prefix.toLowerCase())).toList(); } -Future generateMnemonic( - {int strength = 264, String prefix = segwit}) async { +Future generateElectrumMnemonic({int strength = 264, String prefix = segwit}) async { final wordBitlen = logBase(wordlist.length, 2).ceil(); final wordCount = strength / wordBitlen; final byteCount = ((wordCount * wordBitlen).ceil() / 8).ceil(); @@ -106,22 +105,29 @@ Future generateMnemonic( return result; } +Future checkIfMnemonicIsElectrum2(String mnemonic) async { + return prefixMatches(mnemonic, [segwit]).first; +} + +Future getMnemonicHash(String mnemonic) async { + final hmacSha512 = Hmac(sha512, utf8.encode('Seed version')); + final digest = hmacSha512.convert(utf8.encode(normalizeText(mnemonic))); + final hx = digest.toString(); + return hx; +} + Future mnemonicToSeedBytes(String mnemonic, {String prefix = segwit}) async { - final pbkdf2 = cryptography.Pbkdf2( - macAlgorithm: cryptography.Hmac.sha512(), - iterations: 2048, - bits: 512); + final pbkdf2 = + cryptography.Pbkdf2(macAlgorithm: cryptography.Hmac.sha512(), iterations: 2048, bits: 512); final text = normalizeText(mnemonic); // pbkdf2.deriveKey(secretKey: secretKey, nonce: nonce) final key = await pbkdf2.deriveKey( - secretKey: cryptography.SecretKey(text.codeUnits), - nonce: 'electrum'.codeUnits); + secretKey: cryptography.SecretKey(text.codeUnits), nonce: 'electrum'.codeUnits); final bytes = await key.extractBytes(); return Uint8List.fromList(bytes); } -bool matchesAnyPrefix(String mnemonic) => - prefixMatches(mnemonic, [segwit]).any((el) => el); +bool matchesAnyPrefix(String mnemonic) => prefixMatches(mnemonic, [segwit]).any((el) => el); bool validateMnemonic(String mnemonic, {String prefix = segwit}) { try { @@ -208,10 +214,8 @@ String removeCJKSpaces(String source) { } String normalizeText(String source) { - final res = removeCombiningCharacters(unorm.nfkd(source).toLowerCase()) - .trim() - .split('/\s+/') - .join(' '); + final res = + removeCombiningCharacters(unorm.nfkd(source).toLowerCase()).trim().split('/\s+/').join(' '); return removeCJKSpaces(res); } diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index bf59e8637..1d29307ca 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -12,6 +12,7 @@ import 'package:cw_core/wallet_info.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart'; +import 'package:bip39/bip39.dart' as bip39; part 'bitcoin_wallet.g.dart'; @@ -30,8 +31,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { ElectrumBalance? initialBalance, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, + String? passphrase, }) : super( mnemonic: mnemonic, + passphrase: passphrase, password: password, walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfo, @@ -44,14 +47,19 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialBalance: initialBalance, seedBytes: seedBytes, currency: CryptoCurrency.btc) { + // in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here) + // the sideHd derivation path = m/84'/0'/0'/1 (account 1, index unspecified here) + String derivationPath = walletInfo.derivationInfo!.derivationPath!; + String sideDerivationPath = derivationPath.substring(0, derivationPath.length - 1) + "1"; + final hd = bitcoin.HDWallet.fromSeed(seedBytes, network: networkType); walletAddresses = BitcoinWalletAddresses( walletInfo, electrumClient: electrumClient, initialAddresses: initialAddresses, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, - mainHd: hd, - sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"), + mainHd: hd.derivePath(derivationPath), + sideHd: hd.derivePath(sideDerivationPath), network: networkParam ?? network, ); autorun((_) { @@ -64,6 +72,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { required String password, required WalletInfo walletInfo, required Box unspentCoinsInfo, + String? passphrase, String? addressPageType, BasedUtxoNetwork? network, List? initialAddresses, @@ -71,14 +80,29 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, }) async { + late Uint8List seedBytes; + + switch (walletInfo.derivationInfo?.derivationType) { + case DerivationType.bip39: + seedBytes = await bip39.mnemonicToSeed( + mnemonic, + passphrase: passphrase ?? "", + ); + break; + case DerivationType.electrum: + default: + seedBytes = await mnemonicToSeedBytes(mnemonic); + break; + } return BitcoinWallet( mnemonic: mnemonic, + passphrase: passphrase ?? "", password: password, walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfo, initialAddresses: initialAddresses, initialBalance: initialBalance, - seedBytes: await mnemonicToSeedBytes(mnemonic), + seedBytes: seedBytes, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, addressPageType: addressPageType, @@ -97,14 +121,38 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { : BitcoinNetwork.mainnet; final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, network); + walletInfo.derivationInfo ??= DerivationInfo( + derivationType: snp.derivationType ?? DerivationType.electrum, + derivationPath: snp.derivationPath, + ); + + // set the default if not present: + walletInfo.derivationInfo!.derivationPath = snp.derivationPath ?? "m/0'/1"; + + late Uint8List seedBytes; + + switch (walletInfo.derivationInfo!.derivationType) { + case DerivationType.electrum: + seedBytes = await mnemonicToSeedBytes(snp.mnemonic); + break; + case DerivationType.bip39: + default: + seedBytes = await bip39.mnemonicToSeed( + snp.mnemonic, + passphrase: snp.passphrase ?? '', + ); + break; + } + return BitcoinWallet( mnemonic: snp.mnemonic, password: password, + passphrase: snp.passphrase, walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfo, initialAddresses: snp.addresses, initialBalance: snp.balance, - seedBytes: await mnemonicToSeedBytes(snp.mnemonic), + seedBytes: seedBytes, initialRegularAddressIndex: snp.regularAddressIndex, initialChangeAddressIndex: snp.changeAddressIndex, addressPageType: snp.addressPageType, diff --git a/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart b/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart index 37b272a1b..981c7a466 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart @@ -2,14 +2,35 @@ import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; class BitcoinNewWalletCredentials extends WalletCredentials { - BitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo}) - : super(name: name, walletInfo: walletInfo); + BitcoinNewWalletCredentials( + {required String name, + WalletInfo? walletInfo, + DerivationType? derivationType, + String? derivationPath}) + : super( + name: name, + walletInfo: walletInfo, + ); } class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials { - BitcoinRestoreWalletFromSeedCredentials( - {required String name, required String password, required this.mnemonic, WalletInfo? walletInfo}) - : super(name: name, password: password, walletInfo: walletInfo); + BitcoinRestoreWalletFromSeedCredentials({ + required String name, + required String password, + required this.mnemonic, + WalletInfo? walletInfo, + required DerivationType derivationType, + required String derivationPath, + String? passphrase, + }) : super( + name: name, + password: password, + passphrase: passphrase, + walletInfo: walletInfo, + derivationInfo: DerivationInfo( + derivationType: derivationType, + derivationPath: derivationPath, + )); final String mnemonic; } @@ -20,4 +41,4 @@ class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials { : super(name: name, password: password, walletInfo: walletInfo); final String wif; -} \ No newline at end of file +} diff --git a/cw_bitcoin/lib/bitcoin_wallet_service.dart b/cw_bitcoin/lib/bitcoin_wallet_service.dart index 38e769d15..e0548771b 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_service.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_service.dart @@ -12,6 +12,7 @@ import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:hive/hive.dart'; import 'package:collection/collection.dart'; +import 'package:bip39/bip39.dart' as bip39; class BitcoinWalletService extends WalletService { @@ -29,8 +30,9 @@ class BitcoinWalletService extends WalletService restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { - if (!validateMnemonic(credentials.mnemonic)) { + if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) { throw BitcoinMnemonicIsIncorrectException(); } @@ -114,6 +116,7 @@ class BitcoinWalletService extends WalletService> electrum_derivations = { + DerivationType.electrum: [ + DerivationInfo( + derivationType: DerivationType.electrum, + derivationPath: "m/0'/0", + description: "Electrum", + scriptType: "p2wpkh", + ), + ], + DerivationType.bip39: [ + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/44'/0'/0'", + description: "Standard BIP44", + scriptType: "p2pkh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/49'/0'/0'", + description: "Standard BIP49 compatibility segwit", + scriptType: "p2wpkh-p2sh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/84'/0'/0'", + description: "Standard BIP84 native segwit", + scriptType: "p2wpkh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/0'", + description: "Non-standard legacy", + scriptType: "p2pkh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/0'", + description: "Non-standard compatibility segwit", + scriptType: "p2wpkh-p2sh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/0'", + description: "Non-standard native segwit", + scriptType: "p2wpkh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/44'/0'/0'", + description: "Samourai Deposit", + scriptType: "p2wpkh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/49'/0'/0'", + description: "Samourai Deposit", + scriptType: "p2wpkh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/84'/0'/2147483644'", + description: "Samourai Bad Bank (toxic change)", + scriptType: "p2wpkh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/84'/0'/2147483645'", + description: "Samourai Whirlpool Pre Mix", + scriptType: "p2wpkh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/84'/0'/2147483646'", + description: "Samourai Whirlpool Post Mix", + scriptType: "p2wpkh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/44'/0'/2147483647'", + description: "Samourai Ricochet legacy", + scriptType: "p2pkh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/49'/0'/2147483647'", + description: "Samourai Ricochet compatibility segwit", + scriptType: "p2wpkh-p2sh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/84'/0'/2147483647'", + description: "Samourai Ricochet native segwit", + scriptType: "p2wpkh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/84'/2'/0'", + description: "Default Litecoin", + scriptType: "p2wpkh", + ), + ], +}; diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 4a76ee5dd..8342e4816 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -55,13 +55,15 @@ abstract class ElectrumWalletBase required this.networkType, required this.mnemonic, required Uint8List seedBytes, + this.passphrase, List? initialAddresses, ElectrumClient? electrumClient, ElectrumBalance? initialBalance, CryptoCurrency? currency}) : hd = currency == CryptoCurrency.bch ? bitcoinCashHDWallet(seedBytes) - : bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/0"), + : bitcoin.HDWallet.fromSeed(seedBytes, network: networkType) + .derivePath(walletInfo.derivationInfo?.derivationPath ?? "m/0'/0"), syncStatus = NotConnectedSyncStatus(), _password = password, _feeRates = [], @@ -92,6 +94,7 @@ abstract class ElectrumWalletBase final bitcoin.HDWallet hd; final String mnemonic; + final String? passphrase; @override @observable @@ -617,6 +620,7 @@ abstract class ElectrumWalletBase String toJSON() => json.encode({ 'mnemonic': mnemonic, + 'passphrase': passphrase ?? '', 'account_index': walletAddresses.currentReceiveAddressIndexByType, 'change_address_index': walletAddresses.currentChangeAddressIndexByType, 'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(), @@ -624,6 +628,8 @@ abstract class ElectrumWalletBase ? SegwitAddresType.p2wpkh.toString() : walletInfo.addressPageType.toString(), 'balance': balance[currency]?.toJSON(), + 'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index, + 'derivationPath': walletInfo.derivationInfo?.derivationPath, }); int feeRate(TransactionPriority priority) { diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index 6f76ab312..218792e3c 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -3,6 +3,7 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_core/pathForWallet.dart'; +import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/utils/file.dart'; import 'package:cw_core/wallet_type.dart'; @@ -17,6 +18,9 @@ class ElectrumWalletSnapshot { required this.regularAddressIndex, required this.changeAddressIndex, required this.addressPageType, + this.passphrase, + this.derivationType, + this.derivationPath, }); final String name; @@ -29,6 +33,9 @@ class ElectrumWalletSnapshot { ElectrumBalance balance; Map regularAddressIndex; Map changeAddressIndex; + String? passphrase; + DerivationType? derivationType; + String? derivationPath; static Future load( String name, WalletType type, String password, BasedUtxoNetwork network) async { @@ -37,6 +44,7 @@ class ElectrumWalletSnapshot { final data = json.decode(jsonSource) as Map; final addressesTmp = data['addresses'] as List? ?? []; final mnemonic = data['mnemonic'] as String; + final passphrase = data['passphrase'] as String? ?? ''; final addresses = addressesTmp .whereType() .map((addr) => BitcoinAddressRecord.fromJSON(addr, network)) @@ -46,6 +54,10 @@ class ElectrumWalletSnapshot { var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0}; var changeAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0}; + final derivationType = + DerivationType.values[(data['derivationTypeIndex'] as int?) ?? DerivationType.electrum.index]; + final derivationPath = data['derivationPath'] as String? ?? "m/0'/0"; + try { regularAddressIndexByType = { SegwitAddresType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0') @@ -65,12 +77,15 @@ class ElectrumWalletSnapshot { name: name, type: type, password: password, + passphrase: passphrase, mnemonic: mnemonic, addresses: addresses, balance: balance, regularAddressIndex: regularAddressIndexByType, changeAddressIndex: changeAddressIndexByType, addressPageType: data['address_page_type'] as String?, + derivationType: derivationType, + derivationPath: derivationPath, ); } } diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index d2379d5a5..4d166e47b 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -15,6 +15,7 @@ import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/litecoin_network.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; +import 'package:bip39/bip39.dart' as bip39; part 'litecoin_wallet.g.dart'; @@ -62,11 +63,26 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { required String password, required WalletInfo walletInfo, required Box unspentCoinsInfo, + String? passphrase, String? addressPageType, List? initialAddresses, ElectrumBalance? initialBalance, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex}) async { + late Uint8List seedBytes; + + switch (walletInfo.derivationInfo?.derivationType) { + case DerivationType.bip39: + seedBytes = await bip39.mnemonicToSeed( + mnemonic, + passphrase: passphrase ?? "", + ); + break; + case DerivationType.electrum: + default: + seedBytes = await mnemonicToSeedBytes(mnemonic); + break; + } return LitecoinWallet( mnemonic: mnemonic, password: password, @@ -74,7 +90,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unspentCoinsInfo: unspentCoinsInfo, initialAddresses: initialAddresses, initialBalance: initialBalance, - seedBytes: await mnemonicToSeedBytes(mnemonic), + seedBytes: seedBytes, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, addressPageType: addressPageType, diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index ee3b0e628..9143556ab 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -11,6 +11,7 @@ import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:collection/collection.dart'; +import 'package:bip39/bip39.dart' as bip39; class LitecoinWalletService extends WalletService< BitcoinNewWalletCredentials, @@ -27,8 +28,9 @@ class LitecoinWalletService extends WalletService< @override Future create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async { final wallet = await LitecoinWalletBase.create( - mnemonic: await generateMnemonic(), + mnemonic: await generateElectrumMnemonic(), password: credentials.password!, + passphrase: credentials.passphrase, walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); await wallet.save(); @@ -100,12 +102,13 @@ class LitecoinWalletService extends WalletService< @override Future restoreFromSeed( BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { - if (!validateMnemonic(credentials.mnemonic)) { + if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) { throw LitecoinMnemonicIsIncorrectException(); } final wallet = await LitecoinWalletBase.create( password: credentials.password!, + passphrase: credentials.passphrase, mnemonic: credentials.mnemonic, walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource); diff --git a/cw_bitcoin/lib/utils.dart b/cw_bitcoin/lib/utils.dart index b156ccba3..b3707e764 100644 --- a/cw_bitcoin/lib/utils.dart +++ b/cw_bitcoin/lib/utils.dart @@ -5,29 +5,58 @@ import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData; import 'package:hex/hex.dart'; -bitcoin.PaymentData generatePaymentData({required bitcoin.HDWallet hd, required int index}) => - PaymentData(pubkey: Uint8List.fromList(HEX.decode(hd.derive(index).pubKey!))); +bitcoin.PaymentData generatePaymentData({required bitcoin.HDWallet hd, int? index}) { + final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!; + return PaymentData(pubkey: Uint8List.fromList(HEX.decode(pubKey))); +} ECPrivate generateECPrivate( - {required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) => - ECPrivate.fromWif(hd.derive(index).wif!, netVersion: network.wifNetVer); + {required bitcoin.HDWallet hd, required BasedUtxoNetwork network, int? index}) { + final wif = index != null ? hd.derive(index).wif! : hd.wif!; + return ECPrivate.fromWif(wif, netVersion: network.wifNetVer); +} -String generateP2WPKHAddress( - {required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) => - ECPublic.fromHex(hd.derive(index).pubKey!).toP2wpkhAddress().toAddress(network); +String generateP2WPKHAddress({ + required bitcoin.HDWallet hd, + required BasedUtxoNetwork network, + int? index, +}) { + final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!; + return ECPublic.fromHex(pubKey).toP2wpkhAddress().toAddress(network); +} -String generateP2SHAddress( - {required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) => - ECPublic.fromHex(hd.derive(index).pubKey!).toP2wpkhInP2sh().toAddress(network); +String generateP2SHAddress({ + required bitcoin.HDWallet hd, + required BasedUtxoNetwork network, + int? index, +}) { + final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!; + return ECPublic.fromHex(pubKey).toP2wpkhInP2sh().toAddress(network); +} -String generateP2WSHAddress( - {required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) => - ECPublic.fromHex(hd.derive(index).pubKey!).toP2wshAddress().toAddress(network); +String generateP2WSHAddress({ + required bitcoin.HDWallet hd, + required BasedUtxoNetwork network, + int? index, +}) { + final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!; + return ECPublic.fromHex(pubKey).toP2wshAddress().toAddress(network); +} -String generateP2PKHAddress( - {required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) => - ECPublic.fromHex(hd.derive(index).pubKey!).toP2pkhAddress().toAddress(network); +String generateP2PKHAddress({ + required bitcoin.HDWallet hd, + required BasedUtxoNetwork network, + int? index, +}) { + final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!; + return ECPublic.fromHex(pubKey).toP2pkhAddress().toAddress(network); +} -String generateP2TRAddress( - {required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) => - ECPublic.fromHex(hd.derive(index).pubKey!).toTaprootAddress().toAddress(network); +String generateP2TRAddress({ + required bitcoin.HDWallet hd, + required BasedUtxoNetwork network, + int? index, +}) { + final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!; + return ECPublic.fromHex(pubKey).toTaprootAddress().toAddress(network); +} diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 50cd432c0..86d58b9b1 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -217,10 +217,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.17.1" convert: dependency: transitive description: @@ -434,18 +434,18 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.15" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.2.0" meta: dependency: transitive description: @@ -663,10 +663,10 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" stack_trace: dependency: transitive description: @@ -711,10 +711,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.5.1" timing: dependency: transitive description: @@ -748,21 +748,13 @@ packages: source: hosted version: "2.1.4" watcher: - dependency: "direct overridden" + dependency: transitive description: name: watcher sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 - url: "https://pub.dev" - source: hosted - version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -796,5 +788,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.10.0" diff --git a/cw_core/lib/hive_type_ids.dart b/cw_core/lib/hive_type_ids.dart index 3fa2eb647..e0896bab1 100644 --- a/cw_core/lib/hive_type_ids.dart +++ b/cw_core/lib/hive_type_ids.dart @@ -14,4 +14,5 @@ const ERC20_TOKEN_TYPE_ID = 12; const NANO_ACCOUNT_TYPE_ID = 13; const POW_NODE_TYPE_ID = 14; const DERIVATION_TYPE_TYPE_ID = 15; -const SPL_TOKEN_TYPE_ID = 16; +const SPL_TOKEN_TYPE_ID = 16; +const DERIVATION_INFO_TYPE_ID = 17; diff --git a/cw_core/lib/wallet_credentials.dart b/cw_core/lib/wallet_credentials.dart index 4d5f331c9..9b28680f9 100644 --- a/cw_core/lib/wallet_credentials.dart +++ b/cw_core/lib/wallet_credentials.dart @@ -7,15 +7,19 @@ abstract class WalletCredentials { this.seedPhraseLength, this.walletInfo, this.password, - this.derivationType, - this.derivationPath, - }); + this.passphrase, + this.derivationInfo, + }) { + if (this.walletInfo != null && derivationInfo != null) { + this.walletInfo!.derivationInfo = derivationInfo; + } + } final String name; final int? height; int? seedPhraseLength; String? password; - DerivationType? derivationType; - String? derivationPath; + String? passphrase; WalletInfo? walletInfo; + DerivationInfo? derivationInfo; } diff --git a/cw_core/lib/wallet_info.dart b/cw_core/lib/wallet_info.dart index 2a44175a7..4892f6d1d 100644 --- a/cw_core/lib/wallet_info.dart +++ b/cw_core/lib/wallet_info.dart @@ -17,28 +17,42 @@ enum DerivationType { @HiveField(3) bip39, @HiveField(4) - electrum1, - @HiveField(5) - electrum2, + electrum, } -class DerivationInfo { +@HiveType(typeId: DerivationInfo.typeId) +class DerivationInfo extends HiveObject { DerivationInfo({ - required this.derivationType, + this.derivationType, this.derivationPath, this.balance = "", this.address = "", - this.height = 0, - this.script_type, + this.transactionsCount = 0, + this.scriptType, this.description, }); - String balance; + static const typeId = DERIVATION_INFO_TYPE_ID; + + @HiveField(0, defaultValue: '') String address; - int height; - final DerivationType derivationType; - final String? derivationPath; - final String? script_type; + + @HiveField(1, defaultValue: '') + String balance; + + @HiveField(2) + int transactionsCount; + + @HiveField(3) + DerivationType? derivationType; + + @HiveField(4) + String? derivationPath; + + @HiveField(5) + final String? scriptType; + + @HiveField(6) final String? description; } @@ -57,8 +71,7 @@ class WalletInfo extends HiveObject { this.yatEid, this.yatLastUsedAddressRaw, this.showIntroCakePayCard, - this.derivationType, - this.derivationPath) + this.derivationInfo) : _yatLastUsedAddressController = StreamController.broadcast(); factory WalletInfo.external({ @@ -74,24 +87,23 @@ class WalletInfo extends HiveObject { bool? showIntroCakePayCard, String yatEid = '', String yatLastUsedAddressRaw = '', - DerivationType? derivationType, - String? derivationPath, + DerivationInfo? derivationInfo, }) { return WalletInfo( - id, - name, - type, - isRecovery, - restoreHeight, - date.millisecondsSinceEpoch, - dirPath, - path, - address, - yatEid, - yatLastUsedAddressRaw, - showIntroCakePayCard, - derivationType, - derivationPath); + id, + name, + type, + isRecovery, + restoreHeight, + date.millisecondsSinceEpoch, + dirPath, + path, + address, + yatEid, + yatLastUsedAddressRaw, + showIntroCakePayCard, + derivationInfo, + ); } static const typeId = WALLET_INFO_TYPE_ID; @@ -143,10 +155,10 @@ class WalletInfo extends HiveObject { List? usedAddresses; @HiveField(16) - DerivationType? derivationType; + DerivationType? derivationType; // no longer used @HiveField(17) - String? derivationPath; + String? derivationPath; // no longer used @HiveField(18) String? addressPageType; @@ -154,6 +166,9 @@ class WalletInfo extends HiveObject { @HiveField(19) String? network; + @HiveField(20) + DerivationInfo? derivationInfo; + String get yatLastUsedAddress => yatLastUsedAddressRaw ?? ''; set yatLastUsedAddress(String address) { diff --git a/cw_core/pubspec.lock b/cw_core/pubspec.lock index aef76f300..678e57b54 100644 --- a/cw_core/pubspec.lock +++ b/cw_core/pubspec.lock @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.17.1" convert: dependency: transitive description: @@ -343,18 +343,18 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.15" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.2.0" meta: dependency: transitive description: @@ -564,10 +564,10 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" stack_trace: dependency: transitive description: @@ -612,10 +612,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.5.1" timing: dependency: transitive description: @@ -641,21 +641,13 @@ packages: source: hosted version: "2.1.4" watcher: - dependency: "direct overridden" + dependency: transitive description: name: watcher sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 - url: "https://pub.dev" - source: hosted - version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -689,5 +681,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.10.0" diff --git a/cw_nano/lib/nano_wallet.dart b/cw_nano/lib/nano_wallet.dart index 265f78eb7..5efe3006d 100644 --- a/cw_nano/lib/nano_wallet.dart +++ b/cw_nano/lib/nano_wallet.dart @@ -43,7 +43,7 @@ abstract class NanoWalletBase }) : syncStatus = NotConnectedSyncStatus(), _password = password, _mnemonic = mnemonic, - _derivationType = walletInfo.derivationType!, + _derivationType = walletInfo.derivationInfo!.derivationType!, _isTransactionUpdating = false, _client = NanoClient(), walletAddresses = NanoWalletAddresses(walletInfo), @@ -389,7 +389,10 @@ abstract class NanoWalletBase derivationType = DerivationType.bip39; } - walletInfo.derivationType = derivationType; + walletInfo.derivationInfo ??= DerivationInfo(derivationType: derivationType); + if (walletInfo.derivationInfo!.derivationType == null) { + walletInfo.derivationInfo!.derivationType = derivationType; + } return NanoWallet( walletInfo: walletInfo, diff --git a/cw_nano/lib/nano_wallet_creation_credentials.dart b/cw_nano/lib/nano_wallet_creation_credentials.dart index 3616fcf44..4ee79ce48 100644 --- a/cw_nano/lib/nano_wallet_creation_credentials.dart +++ b/cw_nano/lib/nano_wallet_creation_credentials.dart @@ -2,8 +2,15 @@ import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; class NanoNewWalletCredentials extends WalletCredentials { - NanoNewWalletCredentials({required String name, String? password}) - : super(name: name, password: password); + NanoNewWalletCredentials({ + required String name, + String? password, + DerivationType? derivationType, + }) : super( + name: name, + password: password, + derivationInfo: DerivationInfo(derivationType: derivationType), + ); } class NanoRestoreWalletFromSeedCredentials extends WalletCredentials { @@ -11,11 +18,11 @@ class NanoRestoreWalletFromSeedCredentials extends WalletCredentials { required String name, required this.mnemonic, String? password, - DerivationType? derivationType, + required DerivationType derivationType, }) : super( name: name, password: password, - derivationType: derivationType, + derivationInfo: DerivationInfo(derivationType: derivationType), ); final String mnemonic; @@ -30,12 +37,12 @@ class NanoRestoreWalletFromKeysCredentials extends WalletCredentials { NanoRestoreWalletFromKeysCredentials({ required String name, required String password, + required DerivationType derivationType, required this.seedKey, - DerivationType? derivationType, }) : super( name: name, password: password, - derivationType: derivationType, + derivationInfo: DerivationInfo(derivationType: derivationType), ); final String seedKey; diff --git a/cw_nano/lib/nano_wallet_service.dart b/cw_nano/lib/nano_wallet_service.dart index 7ab502d49..b1497a625 100644 --- a/cw_nano/lib/nano_wallet_service.dart +++ b/cw_nano/lib/nano_wallet_service.dart @@ -28,11 +28,11 @@ class NanoWalletService extends WalletService create(NanoNewWalletCredentials credentials, {bool? isTestnet}) async { // nano standard: - DerivationType derivationType = DerivationType.nano; String seedKey = NanoSeeds.generateSeed(); String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey); - credentials.walletInfo!.derivationType = derivationType; + // ensure default if not present: + credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: DerivationType.nano); final wallet = NanoWallet( walletInfo: credentials.walletInfo!, @@ -88,9 +88,6 @@ class NanoWalletService extends WalletService BitcoinTransactionPriority.medium; - - @override - WalletCredentials createBitcoinRestoreWalletFromSeedCredentials( - {required String name, required String mnemonic, required String password}) => - BitcoinRestoreWalletFromSeedCredentials(name: name, mnemonic: mnemonic, password: password); + WalletCredentials createBitcoinRestoreWalletFromSeedCredentials({ + required String name, + required String mnemonic, + required String password, + required DerivationType derivationType, + required String derivationPath, + String? passphrase, + }) => + BitcoinRestoreWalletFromSeedCredentials( + name: name, + mnemonic: mnemonic, + password: password, + derivationType: derivationType, + derivationPath: derivationPath, + passphrase: passphrase, + ); @override WalletCredentials createBitcoinRestoreWalletFromWIFCredentials( @@ -23,6 +32,9 @@ class CWBitcoin extends Bitcoin { {required String name, WalletInfo? walletInfo}) => BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo); + @override + TransactionPriority getMediumTransactionPriority() => BitcoinTransactionPriority.medium; + @override List getWordList() => wordlist; @@ -78,21 +90,20 @@ class CWBitcoin extends Bitcoin { final bitcoinFeeRate = priority == BitcoinTransactionPriority.custom && feeRate != null ? feeRate : null; return BitcoinTransactionCredentials( - outputs - .map((out) => OutputInfo( - fiatAmount: out.fiatAmount, - cryptoAmount: out.cryptoAmount, - address: out.address, - note: out.note, - sendAll: out.sendAll, - extractedAddress: out.extractedAddress, - isParsedAddress: out.isParsedAddress, - formattedCryptoAmount: out.formattedCryptoAmount, - memo: out.memo)) - .toList(), - priority: priority as BitcoinTransactionPriority, - feeRate: bitcoinFeeRate - ); + outputs + .map((out) => OutputInfo( + fiatAmount: out.fiatAmount, + cryptoAmount: out.cryptoAmount, + address: out.address, + note: out.note, + sendAll: out.sendAll, + extractedAddress: out.extractedAddress, + isParsedAddress: out.isParsedAddress, + formattedCryptoAmount: out.formattedCryptoAmount, + memo: out.memo)) + .toList(), + priority: priority as BitcoinTransactionPriority, + feeRate: bitcoinFeeRate); } @override @@ -248,6 +259,137 @@ class CWBitcoin extends Bitcoin { } } + @override + Future> compareDerivationMethods( + {required String mnemonic, required Node node}) async { + if (await checkIfMnemonicIsElectrum2(mnemonic)) { + return [DerivationType.electrum]; + } + + return [DerivationType.bip39, DerivationType.electrum]; + } + + int _countOccurrences(String str, String charToCount) { + int count = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] == charToCount) { + count++; + } + } + return count; + } + + @override + Future> getDerivationsFromMnemonic({ + required String mnemonic, + required Node node, + String? passphrase, + }) async { + List list = []; + + List types = await compareDerivationMethods(mnemonic: mnemonic, node: node); + if (types.length == 1 && types.first == DerivationType.electrum) { + return [ + DerivationInfo( + derivationType: DerivationType.electrum, + derivationPath: "m/0'/0", + description: "Electrum", + scriptType: "p2wpkh", + ) + ]; + } + + final electrumClient = ElectrumClient(); + await electrumClient.connectToUri(node.uri); + + late BasedUtxoNetwork network; + btc.NetworkType networkType; + switch (node.type) { + case WalletType.litecoin: + network = LitecoinNetwork.mainnet; + networkType = litecoinNetwork; + break; + case WalletType.bitcoin: + default: + network = BitcoinNetwork.mainnet; + networkType = btc.bitcoin; + break; + } + + for (DerivationType dType in electrum_derivations.keys) { + late Uint8List seedBytes; + if (dType == DerivationType.electrum) { + seedBytes = await mnemonicToSeedBytes(mnemonic); + } else if (dType == DerivationType.bip39) { + seedBytes = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase ?? ''); + } + + for (DerivationInfo dInfo in electrum_derivations[dType]!) { + try { + DerivationInfo dInfoCopy = DerivationInfo( + derivationType: dInfo.derivationType, + derivationPath: dInfo.derivationPath, + description: dInfo.description, + scriptType: dInfo.scriptType, + ); + + String derivationPath = dInfoCopy.derivationPath!; + int derivationDepth = _countOccurrences(derivationPath, "/"); + + // the correct derivation depth is dependant on the derivation type: + // the derivation paths defined in electrum_derivations are at the ROOT level, i.e.: + // electrum's format doesn't specify subaddresses, just subaccounts: + + // for BIP44 + if (derivationDepth == 3) { + // we add "/0/0" so that we generate account 0, index 0 and correctly get balance + derivationPath += "/0/0"; + // we don't support sub-ACCOUNTS in bitcoin like we do monero, and so the path dInfoCopy + // expects should be ACCOUNT 0, index unspecified: + dInfoCopy.derivationPath = dInfoCopy.derivationPath! + "/0"; + } + + // var hd = bip32.BIP32.fromSeed(seedBytes).derivePath(derivationPath); + final hd = btc.HDWallet.fromSeed( + seedBytes, + network: networkType, + ).derivePath(derivationPath); + + String? address; + switch (dInfoCopy.scriptType) { + case "p2wpkh": + address = generateP2WPKHAddress(hd: hd, network: network); + break; + case "p2pkh": + address = generateP2PKHAddress(hd: hd, network: network); + break; + case "p2wpkh-p2sh": + address = generateP2SHAddress(hd: hd, network: network); + break; + default: + continue; + } + + final sh = scriptHash(address, network: network); + final history = await electrumClient.getHistory(sh); + + final balance = await electrumClient.getBalance(sh); + dInfoCopy.balance = balance.entries.first.value.toString(); + dInfoCopy.address = address; + dInfoCopy.transactionsCount = history.length; + + list.add(dInfoCopy); + } catch (e) { + print(e); + } + } + } + + // sort the list such that derivations with the most transactions are first: + list.sort((a, b) => b.transactionsCount.compareTo(a.transactionsCount)); + return list; + } + @override bool hasTaprootInput(PendingTransaction pendingTransaction) { return (pendingTransaction as PendingBitcoinTransaction).hasTaprootInputs; diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index a0f570e95..99178c815 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -220,6 +220,10 @@ Future defaultSettingsMigration( await updateNanoNodeList(nodes: nodes); break; + case 32: + await updateBtcNanoWalletInfos(walletInfoSource); + break; + default: break; } @@ -756,6 +760,20 @@ Future changeDefaultMoneroNode( } } +Future updateBtcNanoWalletInfos(Box walletsInfoSource) async { + for (WalletInfo walletInfo in walletsInfoSource.values) { + if (walletInfo.type == WalletType.nano || walletInfo.type == WalletType.bitcoin) { + walletInfo.derivationInfo = DerivationInfo( + derivationPath: walletInfo.derivationPath, + derivationType: walletInfo.derivationType, + address: walletInfo.address, + transactionsCount: walletInfo.restoreHeight, + ); + await walletInfo.save(); + } + } +} + Future changeDefaultBitcoinNode( Box nodeSource, SharedPreferences sharedPreferences) async { const cakeWalletBitcoinNodeUriPattern = '.cakewallet.com'; diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart index bab0ef51d..f729e6392 100644 --- a/lib/entities/parse_address_from_domain.dart +++ b/lib/entities/parse_address_from_domain.dart @@ -51,10 +51,12 @@ class AddressResolver { } final match = RegExp(addressPattern).firstMatch(raw); - return match?.group(0)?.replaceAllMapped(RegExp('[^0-9a-zA-Z]|bitcoincash:|nano_'), + return match?.group(0)?.replaceAllMapped(RegExp('[^0-9a-zA-Z]|bitcoincash:|nano_|ban_'), (Match match) { String group = match.group(0)!; - if (group.startsWith('bitcoincash:') || group.startsWith('nano_')) { + if (group.startsWith('bitcoincash:') || + group.startsWith('nano_') || + group.startsWith('ban_')) { return group; } return ''; diff --git a/lib/main.dart b/lib/main.dart index b80c9eb85..ff5b0e5c0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -102,6 +102,10 @@ Future initializeAppConfigs() async { CakeHive.registerAdapter(DerivationTypeAdapter()); } + if (!CakeHive.isAdapterRegistered(DERIVATION_INFO_TYPE_ID)) { + CakeHive.registerAdapter(DerivationInfoAdapter()); + } + if (!CakeHive.isAdapterRegistered(WALLET_TYPE_TYPE_ID)) { CakeHive.registerAdapter(WalletTypeAdapter()); } @@ -163,7 +167,7 @@ Future initializeAppConfigs() async { transactionDescriptions: transactionDescriptions, secureStorage: secureStorage, anonpayInvoiceInfo: anonpayInvoiceInfo, - initialMigrationVersion: 31, + initialMigrationVersion: 32, ); } diff --git a/lib/nano/cw_nano.dart b/lib/nano/cw_nano.dart index 5896f7c26..13800cff6 100644 --- a/lib/nano/cw_nano.dart +++ b/lib/nano/cw_nano.dart @@ -96,6 +96,7 @@ class CWNano extends Nano { NanoNewWalletCredentials( name: name, password: password, + derivationType: DerivationType.nano, ); @override @@ -103,15 +104,10 @@ class CWNano extends Nano { required String name, required String password, required String mnemonic, - DerivationType? derivationType, + required DerivationType derivationType, }) { - if (derivationType == null) { - // figure out the derivation type as best we can, otherwise set it to "unknown" - if (mnemonic.split(" ").length == 12) { - derivationType = DerivationType.bip39; - } else { - derivationType = DerivationType.unknown; - } + if (mnemonic.split(" ").length == 12 && derivationType != DerivationType.bip39) { + throw Exception("Invalid mnemonic for derivation type!"); } return NanoRestoreWalletFromSeedCredentials( @@ -127,15 +123,10 @@ class CWNano extends Nano { required String name, required String password, required String seedKey, - DerivationType? derivationType, + required DerivationType derivationType, }) { - if (derivationType == null) { - // figure out the derivation type as best we can, otherwise set it to "unknown" - if (seedKey.length == 64) { - derivationType = DerivationType.nano; - } else { - derivationType = DerivationType.unknown; - } + if (seedKey.length == 128 && derivationType != DerivationType.bip39) { + throw Exception("Invalid seed key length for derivation type!"); } return NanoRestoreWalletFromKeysCredentials( @@ -199,7 +190,6 @@ class CWNano extends Nano { } class CWNanoUtil extends NanoUtil { - @override bool isValidBip39Seed(String seed) { return NanoDerivations.isValidBip39Seed(seed); @@ -353,4 +343,54 @@ class CWNanoUtil extends NanoUtil { return [DerivationType.nano, DerivationType.bip39]; } } + + @override + Future> getDerivationsFromMnemonic({ + String? mnemonic, + String? seedKey, + required Node node, + }) async { + List list = []; + + List possibleDerivationTypes = await compareDerivationMethods( + mnemonic: mnemonic, + privateKey: seedKey, + node: node, + ); + if (possibleDerivationTypes.length == 1) { + return [DerivationInfo(derivationType: possibleDerivationTypes.first)]; + } + + AccountInfoResponse? bip39Info = await nanoUtil!.getInfoFromSeedOrMnemonic( + DerivationType.bip39, + mnemonic: mnemonic, + seedKey: seedKey, + node: node, + ); + AccountInfoResponse? standardInfo = await nanoUtil!.getInfoFromSeedOrMnemonic( + DerivationType.nano, + mnemonic: mnemonic, + seedKey: seedKey, + node: node, + ); + + if (standardInfo?.confirmationHeight != null && standardInfo!.confirmationHeight > 0) { + list.add(DerivationInfo( + derivationType: DerivationType.nano, + balance: nanoUtil!.getRawAsUsableString(standardInfo.balance, nanoUtil!.rawPerNano), + address: standardInfo.address!, + transactionsCount: standardInfo.confirmationHeight, + )); + } + + if (bip39Info?.confirmationHeight != null && bip39Info!.confirmationHeight > 0) { + list.add(DerivationInfo( + derivationType: DerivationType.bip39, + balance: nanoUtil!.getRawAsUsableString(bip39Info.balance, nanoUtil!.rawPerNano), + address: bip39Info.address!, + transactionsCount: bip39Info.confirmationHeight, + )); + } + return list; + } } diff --git a/lib/src/screens/restore/wallet_restore_choose_derivation.dart b/lib/src/screens/restore/wallet_restore_choose_derivation.dart index 5cf40e588..9a7d8eb67 100644 --- a/lib/src/screens/restore/wallet_restore_choose_derivation.dart +++ b/lib/src/screens/restore/wallet_restore_choose_derivation.dart @@ -1,8 +1,5 @@ -import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; -import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:flutter/material.dart'; @@ -13,15 +10,14 @@ class WalletRestoreChooseDerivationPage extends BasePage { WalletRestoreChooseDerivationPage(this.walletRestoreChooseDerivationViewModel) {} @override - Widget middle(BuildContext context) => Observer( - builder: (_) => Text( - S.current.choose_derivation, - style: TextStyle( - fontSize: 18.0, - fontWeight: FontWeight.bold, - fontFamily: 'Lato', - color: titleColor(context)), - )); + Widget middle(BuildContext context) => Text( + S.current.choose_derivation, + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + fontFamily: 'Lato', + color: titleColor(context)), + ); final WalletRestoreChooseDerivationViewModel walletRestoreChooseDerivationViewModel; DerivationType derivationType = DerivationType.unknown; @@ -105,7 +101,7 @@ class WalletRestoreChooseDerivationPage extends BasePage { ), ), Text( - "${S.current.transactions}: ${derivation.height}", + "${S.current.transactions}: ${derivation.transactionsCount}", style: Theme.of(context).primaryTextTheme.labelMedium!.copyWith( fontSize: 16, fontWeight: FontWeight.w500, diff --git a/lib/src/screens/restore/wallet_restore_from_seed_form.dart b/lib/src/screens/restore/wallet_restore_from_seed_form.dart index 6f8f9eb2b..288862ce7 100644 --- a/lib/src/screens/restore/wallet_restore_from_seed_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_seed_form.dart @@ -20,6 +20,7 @@ class WalletRestoreFromSeedForm extends StatefulWidget { {Key? key, required this.displayLanguageSelector, required this.displayBlockHeightSelector, + required this.displayPassphrase, required this.type, required this.seedTypeViewModel, this.blockHeightFocusNode, @@ -31,6 +32,7 @@ class WalletRestoreFromSeedForm extends StatefulWidget { final WalletType type; final bool displayLanguageSelector; final bool displayBlockHeightSelector; + final bool displayPassphrase; final SeedTypeViewModel seedTypeViewModel; final FocusNode? blockHeightFocusNode; final Function(bool)? onHeightOrDateEntered; @@ -48,6 +50,7 @@ class WalletRestoreFromSeedFormState extends State { formKey = GlobalKey(), languageController = TextEditingController(), nameTextEditingController = TextEditingController(), + passphraseController = TextEditingController(), seedTypeController = TextEditingController(); final GlobalKey seedWidgetStateKey; @@ -55,6 +58,7 @@ class WalletRestoreFromSeedFormState extends State { final TextEditingController languageController; final TextEditingController nameTextEditingController; final TextEditingController seedTypeController; + final TextEditingController passphraseController; final GlobalKey formKey; late ReactionDisposer moneroSeedTypeReaction; String language; @@ -166,15 +170,15 @@ class WalletRestoreFromSeedFormState extends State { ), if (widget.displayLanguageSelector) GestureDetector( - onTap: () async { - await showPopUp( - context: context, - builder: (_) => SeedLanguagePicker( - selected: language, - onItemSelected: _changeLanguage, - seedType: isPolyseed ? SeedType.polyseed : SeedType.legacy, - )); - }, + onTap: () async { + await showPopUp( + context: context, + builder: (_) => SeedLanguagePicker( + selected: language, + onItemSelected: _changeLanguage, + seedType: isPolyseed ? SeedType.polyseed : SeedType.legacy, + )); + }, child: Container( color: Colors.transparent, padding: EdgeInsets.only(top: 20.0), @@ -194,6 +198,14 @@ class WalletRestoreFromSeedFormState extends State { key: blockchainHeightKey, onHeightOrDateEntered: widget.onHeightOrDateEntered, hasDatePicker: widget.type == WalletType.monero), + if (widget.displayPassphrase) ...[ + const SizedBox(height: 10), + BaseTextFormField( + hintText: S.current.passphrase, + controller: passphraseController, + obscureText: true, + ), + ] ])); } diff --git a/lib/src/screens/restore/wallet_restore_page.dart b/lib/src/screens/restore/wallet_restore_page.dart index fe5ac8487..6fcacfb0a 100644 --- a/lib/src/screens/restore/wallet_restore_page.dart +++ b/lib/src/screens/restore/wallet_restore_page.dart @@ -1,7 +1,5 @@ import 'package:cake_wallet/core/execution_state.dart'; -import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; 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 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; -import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; @@ -17,7 +14,6 @@ import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/restore/restore_mode.dart'; import 'package:cake_wallet/view_model/seed_type_view_model.dart'; import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; -import 'package:cw_core/nano_account_info_response.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; @@ -42,6 +38,7 @@ class WalletRestorePage extends BasePage { displayBlockHeightSelector: walletRestoreViewModel.hasBlockchainHeightLanguageSelector, displayLanguageSelector: walletRestoreViewModel.hasSeedLanguageSelector, + displayPassphrase: walletRestoreViewModel.hasPassphrase, type: walletRestoreViewModel.type, key: walletRestoreFromSeedFormKey, blockHeightFocusNode: _blockHeightFocusNode, @@ -99,8 +96,10 @@ class WalletRestorePage extends BasePage { final GlobalKey walletRestoreFromSeedFormKey; final GlobalKey walletRestoreFromKeysFormKey; final FocusNode _blockHeightFocusNode; - DerivationType derivationType = DerivationType.unknown; - String? derivationPath = null; + + // DerivationType derivationType = DerivationType.unknown; + // String? derivationPath = null; + DerivationInfo? derivationInfo; @override Widget body(BuildContext context) { @@ -298,6 +297,11 @@ class WalletRestorePage extends BasePage { -1; } + if (walletRestoreViewModel.hasPassphrase) { + credentials['passphrase'] = + walletRestoreFromSeedFormKey.currentState!.passphraseController.text; + } + credentials['name'] = walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text; } else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) { @@ -318,58 +322,11 @@ class WalletRestorePage extends BasePage { } } - credentials['derivationType'] = this.derivationType; - credentials['derivationPath'] = this.derivationPath; + credentials['derivationInfo'] = this.derivationInfo; credentials['walletType'] = walletRestoreViewModel.type; return credentials; } - Future> getDerivationInfo(dynamic credentials) async { - var list = []; - var walletType = credentials["walletType"] as WalletType; - var appStore = getIt.get(); - var node = appStore.settingsStore.getCurrentNode(walletType); - - switch (walletType) { - case WalletType.nano: - String? mnemonic = credentials['seed'] as String?; - String? seedKey = credentials['private_key'] as String?; - AccountInfoResponse? bip39Info = await nanoUtil!.getInfoFromSeedOrMnemonic( - DerivationType.bip39, - mnemonic: mnemonic, - seedKey: seedKey, - node: node); - AccountInfoResponse? standardInfo = await nanoUtil!.getInfoFromSeedOrMnemonic( - DerivationType.nano, - mnemonic: mnemonic, - seedKey: seedKey, - node: node, - ); - - if (standardInfo?.balance != null) { - list.add(DerivationInfo( - derivationType: DerivationType.nano, - balance: nanoUtil!.getRawAsUsableString(standardInfo!.balance, nanoUtil!.rawPerNano), - address: standardInfo.address!, - height: standardInfo.confirmationHeight, - )); - } - - if (bip39Info?.balance != null) { - list.add(DerivationInfo( - derivationType: DerivationType.bip39, - balance: nanoUtil!.getRawAsUsableString(bip39Info!.balance, nanoUtil!.rawPerNano), - address: bip39Info.address!, - height: bip39Info.confirmationHeight, - )); - } - break; - default: - break; - } - return list; - } - Future _confirmForm(BuildContext context) async { // Dismissing all visible keyboard to provide context for navigation FocusManager.instance.primaryFocus?.unfocus(); @@ -398,51 +355,46 @@ class WalletRestorePage extends BasePage { walletRestoreViewModel.state = IsExecutingState(); - List derivationTypes = - await walletRestoreViewModel.getDerivationTypes(_credentials()); + DerivationInfo? dInfo; - if (derivationTypes[0] == DerivationType.unknown || derivationTypes.length > 1) { - // push screen to choose the derivation type: - List derivations = await getDerivationInfo(_credentials()); + // get info about the different derivations: + List derivations = + await walletRestoreViewModel.getDerivationInfo(_credentials()); - int derivationsWithHistory = 0; - int derivationWithHistoryIndex = 0; - for (int i = 0; i < derivations.length; i++) { - if (derivations[i].height > 0) { - derivationsWithHistory++; - derivationWithHistoryIndex = i; - } + int derivationsWithHistory = 0; + int derivationWithHistoryIndex = 0; + for (int i = 0; i < derivations.length; i++) { + if (derivations[i].transactionsCount > 0) { + derivationsWithHistory++; + derivationWithHistoryIndex = i; } - - DerivationInfo? derivationInfo; - - if (derivationsWithHistory > 1) { - derivationInfo = await Navigator.of(context).pushNamed(Routes.restoreWalletChooseDerivation, - arguments: derivations) as DerivationInfo?; - } else if (derivationsWithHistory == 1) { - derivationInfo = derivations[derivationWithHistoryIndex]; - } else if (derivationsWithHistory == 0) { - // default derivation: - derivationInfo = DerivationInfo( - derivationType: derivationTypes[0], - derivationPath: "m/0'/1", - height: 0, - ); - } - - if (derivationInfo == null) { - walletRestoreViewModel.state = InitialExecutionState(); - return; - } - this.derivationType = derivationInfo.derivationType; - this.derivationPath = derivationInfo.derivationPath; - } else { - // electrum derivation: - this.derivationType = derivationTypes[0]; - this.derivationPath = "m/0'/1"; } - walletRestoreViewModel.state = InitialExecutionState(); + if (derivationsWithHistory > 1) { + dInfo = await Navigator.of(context).pushNamed( + Routes.restoreWalletChooseDerivation, + arguments: derivations, + ) as DerivationInfo?; + } else if (derivationsWithHistory == 1) { + dInfo = derivations[derivationWithHistoryIndex]; + } + + // get the default derivation for this wallet type: + if (dInfo == null) { + // we only return 1 derivation if we're pretty sure we know which one to use: + if (derivations.length == 1) { + dInfo = derivations.first; + } else { + // if we have multiple possible derivations, and none have histories + // we just default to the most common one: + dInfo = walletRestoreViewModel.getCommonRestoreDerivation(); + } + } + + this.derivationInfo = dInfo; + if (this.derivationInfo == null) { + this.derivationInfo = walletRestoreViewModel.getDefaultDerivation(); + } walletRestoreViewModel.create(options: _credentials()); } diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index b57473cba..81c78b1ab 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -343,7 +343,9 @@ class WalletListBodyState extends State { }); } } catch (e) { - changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString())); + if (this.mounted) { + changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString())); + } } }, conditionToDetermineIfToUse2FA: diff --git a/lib/src/widgets/base_text_form_field.dart b/lib/src/widgets/base_text_form_field.dart index 5649a0784..534e6dae2 100644 --- a/lib/src/widgets/base_text_form_field.dart +++ b/lib/src/widgets/base_text_form_field.dart @@ -22,6 +22,7 @@ class BaseTextFormField extends StatelessWidget { this.enabled = true, this.readOnly = false, this.enableInteractiveSelection = true, + this.obscureText = false, this.validator, this.textStyle, this.placeholderTextStyle, @@ -57,6 +58,7 @@ class BaseTextFormField extends StatelessWidget { final String? initialValue; final double borderWidth; final void Function(String)? onSubmit; + final bool obscureText; @override Widget build(BuildContext context) { @@ -70,6 +72,7 @@ class BaseTextFormField extends StatelessWidget { textInputAction: textInputAction, textAlign: textAlign, autovalidateMode: autovalidateMode, + obscureText: obscureText, maxLines: maxLines, inputFormatters: inputFormatters, enabled: enabled, diff --git a/lib/view_model/restore/restore_from_qr_vm.dart b/lib/view_model/restore/restore_from_qr_vm.dart index 31f0bfdd2..b9b493f04 100644 --- a/lib/view_model/restore/restore_from_qr_vm.dart +++ b/lib/view_model/restore/restore_from_qr_vm.dart @@ -30,8 +30,7 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store spendKey = '', wif = '', address = '', - super(appStore, walletInfoSource, walletCreationService, - type: type, isRecovery: true); + super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true); @observable int height; @@ -51,8 +50,16 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store bool get hasRestorationHeight => type == WalletType.monero; @override - WalletCredentials getCredentialsFromRestoredWallet(dynamic options, RestoredWallet restoreWallet) { + WalletCredentials getCredentialsFromRestoredWallet( + dynamic options, RestoredWallet restoreWallet) { final password = generateWalletPassword(); + String? passphrase; + DerivationInfo? derivationInfo; + if (options != null) { + derivationInfo = options["derivationInfo"] as DerivationInfo?; + passphrase = options["passphrase"] as String?; + } + derivationInfo ??= getDefaultDerivation(); switch (restoreWallet.restoreMode) { case WalletRestoreMode.keys: @@ -86,23 +93,37 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store switch (restoreWallet.type) { case WalletType.monero: return monero!.createMoneroRestoreWalletFromSeedCredentials( - name: name, - height: restoreWallet.height ?? 0, - mnemonic: restoreWallet.mnemonicSeed ?? '', - password: password); + name: name, + height: restoreWallet.height ?? 0, + mnemonic: restoreWallet.mnemonicSeed ?? '', + password: password, + ); case WalletType.bitcoin: case WalletType.litecoin: return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials( - name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); + name: name, + mnemonic: restoreWallet.mnemonicSeed ?? '', + password: password, + passphrase: passphrase, + derivationType: derivationInfo!.derivationType!, + derivationPath: derivationInfo.derivationPath!, + ); case WalletType.bitcoinCash: return bitcoinCash!.createBitcoinCashRestoreWalletFromSeedCredentials( - name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); + name: name, + mnemonic: restoreWallet.mnemonicSeed ?? '', + password: password, + ); case WalletType.ethereum: return ethereum!.createEthereumRestoreWalletFromSeedCredentials( name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); case WalletType.nano: return nano!.createNanoRestoreWalletFromSeedCredentials( - name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); + name: name, + mnemonic: restoreWallet.mnemonicSeed ?? '', + password: password, + derivationType: derivationInfo!.derivationType!, + ); case WalletType.polygon: return polygon!.createPolygonRestoreWalletFromSeedCredentials( name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); @@ -118,7 +139,8 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store } @override - Future processFromRestoredWallet(WalletCredentials credentials, RestoredWallet restoreWallet) async { + Future processFromRestoredWallet( + WalletCredentials credentials, RestoredWallet restoreWallet) async { try { switch (restoreWallet.restoreMode) { case WalletRestoreMode.keys: diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index cabb723e1..6c0c3870b 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -423,7 +423,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor Object _credentials() { final priority = _settingsStore.priority[wallet.type]; - if (priority == null && wallet.type != WalletType.nano && wallet.type != WalletType.solana) { + if (priority == null && wallet.type != WalletType.nano && wallet.type != WalletType.banano && wallet.type != WalletType.solana) { throw Exception('Priority is null for wallet type: ${wallet.type}'); } diff --git a/lib/view_model/wallet_creation_vm.dart b/lib/view_model/wallet_creation_vm.dart index 4a1e054d6..5c9c29a16 100644 --- a/lib/view_model/wallet_creation_vm.dart +++ b/lib/view_model/wallet_creation_vm.dart @@ -71,9 +71,9 @@ abstract class WalletCreationVMBase with Store { dirPath: dirPath, address: '', showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven, - derivationPath: credentials.derivationPath, - derivationType: credentials.derivationType, + derivationInfo: credentials.derivationInfo ?? getDefaultDerivation(), ); + credentials.walletInfo = walletInfo; final wallet = restoreWallet != null ? await processFromRestoredWallet(credentials, restoreWallet) @@ -89,6 +89,48 @@ abstract class WalletCreationVMBase with Store { } } + DerivationInfo? getDefaultDerivation() { + switch (this.type) { + case WalletType.nano: + return DerivationInfo( + derivationType: DerivationType.nano, + ); + case WalletType.bitcoin: + case WalletType.litecoin: + return DerivationInfo( + derivationType: DerivationType.electrum, + derivationPath: "m/0'/0", + ); + default: + return null; + } + } + + DerivationInfo? getCommonRestoreDerivation() { + switch (this.type) { + case WalletType.nano: + return DerivationInfo( + derivationType: DerivationType.nano, + ); + case WalletType.bitcoin: + return DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/84'/0'/0'/0", + description: "Standard BIP84 native segwit", + scriptType: "p2wpkh", + ); + case WalletType.litecoin: + return DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/84'/2'/0'/0", + description: "Standard BIP84 native segwit (litecoin)", + scriptType: "p2wpkh", + ); + default: + return null; + } + } + WalletCredentials getCredentials(dynamic options) => throw UnimplementedError(); Future process(WalletCredentials credentials) => throw UnimplementedError(); diff --git a/lib/view_model/wallet_restore_view_model.dart b/lib/view_model/wallet_restore_view_model.dart index 93ca813d6..21339f1ae 100644 --- a/lib/view_model/wallet_restore_view_model.dart +++ b/lib/view_model/wallet_restore_view_model.dart @@ -2,6 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; +import 'package:cw_core/nano_account_info_response.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/solana/solana.dart'; @@ -66,6 +67,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { final bool hasBlockchainHeightLanguageSelector; final bool hasRestoreFromPrivateKey; + bool get hasPassphrase => [WalletType.bitcoin, WalletType.litecoin].contains(type); + @observable WalletRestoreMode mode; @@ -75,10 +78,10 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { @override WalletCredentials getCredentials(dynamic options) { final password = generateWalletPassword(); + String? passphrase = options['passphrase'] as String?; final height = options['height'] as int? ?? 0; name = options['name'] as String; - DerivationType? derivationType = options["derivationType"] as DerivationType?; - String? derivationPath = options["derivationPath"] as String?; + DerivationInfo? derivationInfo = options["derivationInfo"] as DerivationInfo?; if (mode == WalletRestoreMode.seed) { final seed = options['seed'] as String; @@ -87,14 +90,15 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { return monero!.createMoneroRestoreWalletFromSeedCredentials( name: name, height: height, mnemonic: seed, password: password); case WalletType.bitcoin: + case WalletType.litecoin: return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials( name: name, mnemonic: seed, password: password, + passphrase: passphrase, + derivationType: derivationInfo!.derivationType!, + derivationPath: derivationInfo.derivationPath!, ); - case WalletType.litecoin: - return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials( - name: name, mnemonic: seed, password: password); case WalletType.haven: return haven!.createHavenRestoreWalletFromSeedCredentials( name: name, height: height, mnemonic: seed, password: password); @@ -106,7 +110,11 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { name: name, mnemonic: seed, password: password); case WalletType.nano: return nano!.createNanoRestoreWalletFromSeedCredentials( - name: name, mnemonic: seed, password: password, derivationType: derivationType); + name: name, + mnemonic: seed, + password: password, + derivationType: derivationInfo!.derivationType!, + ); case WalletType.polygon: return polygon!.createPolygonRestoreWalletFromSeedCredentials( name: name, @@ -185,23 +193,34 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { throw Exception('Unexpected type: ${type.toString()}'); } - Future> getDerivationTypes(dynamic options) async { - final seedKey = options['private_key'] as String?; - final mnemonic = options['seed'] as String?; - WalletType walletType = options['walletType'] as WalletType; + Future> getDerivationInfo(dynamic credentials) async { + var list = []; + var walletType = credentials["walletType"] as WalletType; var appStore = getIt.get(); var node = appStore.settingsStore.getCurrentNode(walletType); - switch (type) { + switch (walletType) { + case WalletType.bitcoin: + case WalletType.litecoin: + String? mnemonic = credentials['seed'] as String?; + String? passphrase = credentials['passphrase'] as String?; + return bitcoin!.getDerivationsFromMnemonic( + mnemonic: mnemonic!, + node: node, + passphrase: passphrase, + ); case WalletType.nano: - return nanoUtil! - .compareDerivationMethods(mnemonic: mnemonic, privateKey: seedKey, node: node); + String? mnemonic = credentials['seed'] as String?; + String? seedKey = credentials['private_key'] as String?; + return nanoUtil!.getDerivationsFromMnemonic( + mnemonic: mnemonic, + seedKey: seedKey, + node: node, + ); default: break; } - - // throw Exception('Unexpected type: ${type.toString()}'); - return [DerivationType.def]; + return list; } @override @@ -209,7 +228,6 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { if (mode == WalletRestoreMode.keys) { return walletCreationService.restoreFromKeys(credentials, isTestnet: useTestnet); } - return walletCreationService.restoreFromSeed(credentials, isTestnet: useTestnet); } } diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index cf9959f17..16a87a850 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -411,6 +411,7 @@ "outputs": "المخرجات", "overwrite_amount": "تغير المبلغ", "pairingInvalidEvent": "ﺢﻟﺎﺻ ﺮﻴﻏ ﺙﺪﺣ ﻥﺍﺮﻗﺇ", + "passphrase": "عبارة الممر (اختياري)", "password": "كلمة المرور", "paste": "لصق", "pause_wallet_creation": ".ﺎﻴًﻟﺎﺣ ﺎﺘًﻗﺆﻣ ﺔﻔﻗﻮﺘﻣ Haven Wallet ءﺎﺸﻧﺇ ﻰﻠﻋ ﺓﺭﺪﻘﻟﺍ", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index bd1cd645d..4e92cd707 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -411,6 +411,7 @@ "outputs": "Изходи", "overwrite_amount": "Промени сума", "pairingInvalidEvent": "Невалидно събитие при сдвояване", + "passphrase": "Passphrase (по избор)", "password": "Парола", "paste": "Поставяне", "pause_wallet_creation": "Възможността за създаване на Haven Wallet в момента е на пауза.", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 4d3458bec..95fdc2a93 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -411,6 +411,7 @@ "outputs": "Výstupy", "overwrite_amount": "Přepsat částku", "pairingInvalidEvent": "Neplatná událost párování", + "passphrase": "Passphrase (volitelné)", "password": "Heslo", "paste": "Vložit", "pause_wallet_creation": "Možnost vytvářet Haven Wallet je momentálně pozastavena.", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 0546140eb..d2731d3e7 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -411,6 +411,7 @@ "outputs": "Ausgänge", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Paarung ungültiges Ereignis", + "passphrase": "Passphrase (optional)", "password": "Passwort", "paste": "Einfügen", "pause_wallet_creation": "Die Möglichkeit, Haven Wallet zu erstellen, ist derzeit pausiert.", @@ -425,8 +426,8 @@ "placeholder_transactions": "Ihre Transaktionen werden hier angezeigt", "please_fill_totp": "Bitte geben Sie den 8-stelligen Code ein, der auf Ihrem anderen Gerät vorhanden ist", "please_make_selection": "Bitte treffen Sie unten eine Auswahl zum Erstellen oder Wiederherstellen Ihrer Wallet.", - "Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.", "please_reference_document": "Bitte verweisen Sie auf die folgenden Dokumente, um weitere Informationen zu erhalten.", + "Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.", "please_select": "Bitte auswählen:", "please_select_backup_file": "Bitte wählen Sie die Sicherungsdatei und geben Sie das Sicherungskennwort ein.", "please_try_to_connect_to_another_node": "Bitte versuchen Sie, sich mit einem anderen Knoten zu verbinden", @@ -822,4 +823,4 @@ "you_will_get": "Konvertieren zu", "you_will_send": "Konvertieren von", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 8f8b753d6..8c302d096 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -411,6 +411,7 @@ "outputs": "Outputs", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Pairing Invalid Event", + "passphrase": "Passphrase (Optional)", "password": "Password", "paste": "Paste", "pause_wallet_creation": "Ability to create Haven Wallet is currently paused.", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 7de9cff53..17c4ff681 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -411,6 +411,7 @@ "outputs": "Salidas", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Evento de emparejamiento no válido", + "passphrase": "Passfrase (opcional)", "password": "Contraseña", "paste": "Pegar", "pause_wallet_creation": "La capacidad para crear Haven Wallet está actualmente pausada.", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 2c76122fc..12716ab33 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -411,6 +411,7 @@ "outputs": "Les sorties", "overwrite_amount": "Remplacer le montant", "pairingInvalidEvent": "Événement de couplage non valide", + "passphrase": "Phrase de passe (facultative)", "password": "Mot de passe", "paste": "Coller", "pause_wallet_creation": "La possibilité de créer Haven Wallet est actuellement suspendue.", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index bac970207..29754cf72 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -413,6 +413,7 @@ "outputs": "Abubuwan fashewa", "overwrite_amount": "Rubuta adadin", "pairingInvalidEvent": "Haɗa Lamarin mara inganci", + "passphrase": "Passphrase (Zabi)", "password": "Kalmar wucewa", "paste": "Manna", "pause_wallet_creation": "A halin yanzu an dakatar da ikon ƙirƙirar Haven Wallet.", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 5a9706bd5..278adde0f 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -411,6 +411,7 @@ "outputs": "आउटपुट", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "अमान्य ईवेंट युग्मित करना", + "passphrase": "पासफ्रेज़ (वैकल्पिक)", "password": "पारण शब्द", "paste": "पेस्ट करें", "pause_wallet_creation": "हेवन वॉलेट बनाने की क्षमता फिलहाल रुकी हुई है।", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 94f675a1d..7940b1add 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -411,6 +411,7 @@ "outputs": "Izlazi", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Nevažeći događaj uparivanja", + "passphrase": "Prolaznica (neobavezno)", "password": "Lozinka", "paste": "Zalijepi", "pause_wallet_creation": "Mogućnost stvaranja novčanika Haven trenutno je pauzirana.", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 69e270d10..8177afdc2 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -413,6 +413,7 @@ "outputs": "Output", "overwrite_amount": "Timpa jumlah", "pairingInvalidEvent": "Menyandingkan Acara Tidak Valid", + "passphrase": "Frasa sandi (opsional)", "password": "Kata Sandi", "paste": "Tempel", "pause_wallet_creation": "Kemampuan untuk membuat Haven Wallet saat ini dijeda.", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 09b3e43bb..4cc08f9b1 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -413,6 +413,7 @@ "outputs": "Output", "overwrite_amount": "Sovrascrivi quantità", "pairingInvalidEvent": "Associazione evento non valido", + "passphrase": "Passphrase (opzionale)", "password": "Password", "paste": "Incolla", "pause_wallet_creation": "La possibilità di creare Haven Wallet è attualmente sospesa.", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index b067c2721..a72bbb0e4 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -412,6 +412,7 @@ "outputs": "出力", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "ペアリング無効イベント", + "passphrase": "パスフレーズ(オプション)", "password": "パスワード", "paste": "ペースト", "pause_wallet_creation": "Haven Wallet を作成する機能は現在一時停止されています。", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index d9881ad04..b80494e80 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -411,6 +411,7 @@ "outputs": "출력", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "잘못된 이벤트 페어링", + "passphrase": "암호화 (선택 사항)", "password": "암호", "paste": "풀", "pause_wallet_creation": "Haven Wallet 생성 기능이 현재 일시 중지되었습니다.", @@ -425,8 +426,8 @@ "placeholder_transactions": "거래가 여기에 표시됩니다", "please_fill_totp": "다른 기기에 있는 8자리 코드를 입력하세요.", "please_make_selection": "아래에서 선택하십시오 지갑 만들기 또는 복구.", - "please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.", "Please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.", + "please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.", "please_select": "선택 해주세요:", "please_select_backup_file": "백업 파일을 선택하고 백업 암호를 입력하십시오.", "please_try_to_connect_to_another_node": "다른 노드에 연결을 시도하십시오", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index c36f63414..97a22d807 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -411,6 +411,7 @@ "outputs": "ထုတ်လုပ်မှု", "overwrite_amount": "ပမာဏကို ထပ်ရေးပါ။", "pairingInvalidEvent": "မမှန်ကန်သောဖြစ်ရပ်ကို တွဲချိတ်ခြင်း။", + "passphrase": "passphrase (optional)", "password": "စကားဝှက်", "paste": "ငါးပိ", "pause_wallet_creation": "Haven Wallet ဖန်တီးနိုင်မှုကို လောလောဆယ် ခေတ္တရပ်ထားသည်။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index f258b8f62..a64e264c0 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -411,6 +411,7 @@ "outputs": "Uitgangen", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Koppelen Ongeldige gebeurtenis", + "passphrase": "PassaspHRASE (optioneel)", "password": "Wachtwoord", "paste": "Plakken", "pause_wallet_creation": "De mogelijkheid om Haven Wallet te maken is momenteel onderbroken.", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 4627d0242..109a800ad 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -411,6 +411,7 @@ "outputs": "Wyjścia", "overwrite_amount": "Nadpisz ilość", "pairingInvalidEvent": "Nieprawidłowe zdarzenie parowania", + "passphrase": "PassPhraza (opcjonalnie)", "password": "Hasło", "paste": "Wklej", "pause_wallet_creation": "Możliwość utworzenia Portfela Haven jest obecnie wstrzymana.", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 2a781c76b..2d877794a 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -413,6 +413,7 @@ "outputs": "Saídas", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Emparelhamento de evento inválido", + "passphrase": "Senha (opcional)", "password": "Senha", "paste": "Colar", "pause_wallet_creation": "A capacidade de criar a Haven Wallet está atualmente pausada.", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 22bac3e33..e6141c29b 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -412,6 +412,7 @@ "outputs": "Выходы", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Недействительное событие сопряжения", + "passphrase": "Passfrase (необязательно)", "password": "Пароль", "paste": "Вставить", "pause_wallet_creation": "Возможность создания Haven Wallet в настоящее время приостановлена.", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index c0f58495c..ef1a3ea4e 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -411,6 +411,7 @@ "outputs": "เอาต์พุต", "overwrite_amount": "เขียนทับจำนวน", "pairingInvalidEvent": "การจับคู่เหตุการณ์ที่ไม่ถูกต้อง", + "passphrase": "ข้อความรหัสผ่าน (ไม่บังคับ)", "password": "รหัสผ่าน", "paste": "วาง", "pause_wallet_creation": "ขณะนี้ความสามารถในการสร้าง Haven Wallet ถูกหยุดชั่วคราว", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 8c3b13b3b..e0258e38a 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -411,6 +411,7 @@ "outputs": "Mga output", "overwrite_amount": "Overwrite na halaga", "pairingInvalidEvent": "Pagpares ng Di-wastong Kaganapan", + "passphrase": "Passphrase (opsyonal)", "password": "Password", "paste": "I -paste", "pause_wallet_creation": "Kasalukuyang naka-pause ang kakayahang gumawa ng Haven Wallet.", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 534a36c77..6cacbfd42 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -411,6 +411,7 @@ "outputs": "çıktılar", "overwrite_amount": "Miktarın üzerine yaz", "pairingInvalidEvent": "Geçersiz Etkinliği Eşleştirme", + "passphrase": "Passfrase (isteğe bağlı)", "password": "Parola", "paste": "Yapıştır", "pause_wallet_creation": "Haven Cüzdanı oluşturma yeteneği şu anda duraklatıldı.", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 5d1e2be05..d3f1c5088 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -411,6 +411,7 @@ "outputs": "Виходи", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Недійсна подія сполучення", + "passphrase": "Пасофрази (необов’язково)", "password": "Пароль", "paste": "Вставити", "pause_wallet_creation": "Можливість створення гаманця Haven зараз призупинено.", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index d98a85753..97851b210 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -413,6 +413,7 @@ "outputs": "نتائج", "overwrite_amount": "رقم کو اوور رائٹ کریں۔", "pairingInvalidEvent": "ﭧﻧﻮﯾﺍ ﻂﻠﻏ ﺎﻧﺎﻨﺑ ﺍﮌﻮﺟ", + "passphrase": "پاسفریز (اختیاری)", "password": "پاس ورڈ", "paste": "چسپاں کریں۔", "pause_wallet_creation": "Haven Wallet ۔ﮯﮨ ﻑﻮﻗﻮﻣ ﻝﺎﺤﻟﺍ ﯽﻓ ﺖﯿﻠﮨﺍ ﯽﮐ ﮯﻧﺎﻨﺑ", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index faaeb8837..acb533536 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -412,6 +412,7 @@ "outputs": "Awọn iṣan", "overwrite_amount": "Pààrọ̀ iye owó", "pairingInvalidEvent": "Pipọpọ Iṣẹlẹ Ti ko tọ", + "passphrase": "Ọrọ kukuru (iyan)", "password": "Ọ̀rọ̀ aṣínà", "paste": "Fikún ẹ̀dà yín", "pause_wallet_creation": "Agbara lati ṣẹda Haven Wallet ti wa ni idaduro lọwọlọwọ.", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 2d5251d86..e17c4a89b 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -411,6 +411,7 @@ "outputs": "输出", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "配对无效事件", + "passphrase": "密码(可选)", "password": "密码", "paste": "粘贴", "pause_wallet_creation": "创建 Haven 钱包的功能当前已暂停。", diff --git a/tool/configure.dart b/tool/configure.dart index 3b73bfe80..ceb0c9ccc 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -61,6 +61,8 @@ Future main(List args) async { Future generateBitcoin(bool hasImplementation) async { final outputFile = File(bitcoinOutputPath); const bitcoinCommonHeaders = """ +import 'dart:typed_data'; +import 'package:cw_core/node.dart'; import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/receive_page_option.dart'; import 'package:cw_core/unspent_transaction_output.dart'; @@ -73,8 +75,17 @@ import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/view_model/send/output.dart'; import 'package:hive/hive.dart'; -import 'package:bitcoin_base/bitcoin_base.dart';"""; +import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:bitcoin_flutter/bitcoin_flutter.dart' as btc; +import 'package:bip32/bip32.dart' as bip32; +import 'package:bip39/bip39.dart' as bip39; +import 'package:hive/hive.dart'; +"""; const bitcoinCWHeaders = """ +import 'package:cw_bitcoin/utils.dart'; +import 'package:cw_bitcoin/litecoin_network.dart'; +import 'package:cw_bitcoin/electrum_derivations.dart'; +import 'package:cw_bitcoin/electrum.dart'; import 'package:cw_bitcoin/pending_bitcoin_transaction.dart'; import 'package:cw_bitcoin/bitcoin_receive_page_option.dart'; import 'package:cw_bitcoin/electrum_wallet.dart'; @@ -87,6 +98,7 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart'; import 'package:cw_bitcoin/litecoin_wallet_service.dart'; +import 'package:cw_bitcoin/script_hash.dart'; import 'package:cw_bitcoin/pending_bitcoin_transaction.dart'; import 'package:mobx/mobx.dart'; """; @@ -112,7 +124,14 @@ import 'package:mobx/mobx.dart'; abstract class Bitcoin { TransactionPriority getMediumTransactionPriority(); - WalletCredentials createBitcoinRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password}); + WalletCredentials createBitcoinRestoreWalletFromSeedCredentials({ + required String name, + required String mnemonic, + required String password, + required DerivationType derivationType, + required String derivationPath, + String? passphrase, + }); WalletCredentials createBitcoinRestoreWalletFromWIFCredentials({required String name, required String password, required String wif, WalletInfo? walletInfo}); WalletCredentials createBitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo}); List getWordList(); @@ -147,7 +166,10 @@ abstract class Bitcoin { TransactionPriority getLitecoinTransactionPriorityMedium(); TransactionPriority getBitcoinTransactionPrioritySlow(); TransactionPriority getLitecoinTransactionPrioritySlow(); - + Future> compareDerivationMethods( + {required String mnemonic, required Node node}); + Future> getDerivationsFromMnemonic( + {required String mnemonic, required Node node, String? passphrase}); Future setAddressType(Object wallet, dynamic option); ReceivePageOption getSelectedAddressType(Object wallet); List getBitcoinReceivePageOptions(); @@ -838,14 +860,14 @@ abstract class Nano { required String name, required String password, required String mnemonic, - DerivationType? derivationType, + required DerivationType derivationType, }); WalletCredentials createNanoRestoreWalletFromKeysCredentials({ required String name, required String password, required String seedKey, - DerivationType? derivationType, + required DerivationType derivationType, }); List getNanoWordList(String language); @@ -892,6 +914,11 @@ abstract class NanoUtil { String? privateKey, required Node node, }); + Future> getDerivationsFromMnemonic({ + String? mnemonic, + String? seedKey, + required Node node, + }); } """;