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>
This commit is contained in:
Matthew Fosse 2024-04-29 17:49:56 -07:00 committed by GitHub
parent 9e4a7f4331
commit 509b92e97f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
60 changed files with 908 additions and 341 deletions

View file

@ -6,4 +6,4 @@
uri: workers.perish.co uri: workers.perish.co
- -
uri: worker.nanoriver.cc uri: worker.nanoriver.cc
useSSL: true useSSL: true

View file

@ -90,8 +90,7 @@ List<bool> prefixMatches(String source, List<String> prefixes) {
return prefixes.map((prefix) => hx.startsWith(prefix.toLowerCase())).toList(); return prefixes.map((prefix) => hx.startsWith(prefix.toLowerCase())).toList();
} }
Future<String> generateMnemonic( Future<String> generateElectrumMnemonic({int strength = 264, String prefix = segwit}) async {
{int strength = 264, String prefix = segwit}) async {
final wordBitlen = logBase(wordlist.length, 2).ceil(); final wordBitlen = logBase(wordlist.length, 2).ceil();
final wordCount = strength / wordBitlen; final wordCount = strength / wordBitlen;
final byteCount = ((wordCount * wordBitlen).ceil() / 8).ceil(); final byteCount = ((wordCount * wordBitlen).ceil() / 8).ceil();
@ -106,22 +105,29 @@ Future<String> generateMnemonic(
return result; return result;
} }
Future<bool> checkIfMnemonicIsElectrum2(String mnemonic) async {
return prefixMatches(mnemonic, [segwit]).first;
}
Future<String> 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<Uint8List> mnemonicToSeedBytes(String mnemonic, {String prefix = segwit}) async { Future<Uint8List> mnemonicToSeedBytes(String mnemonic, {String prefix = segwit}) async {
final pbkdf2 = cryptography.Pbkdf2( final pbkdf2 =
macAlgorithm: cryptography.Hmac.sha512(), cryptography.Pbkdf2(macAlgorithm: cryptography.Hmac.sha512(), iterations: 2048, bits: 512);
iterations: 2048,
bits: 512);
final text = normalizeText(mnemonic); final text = normalizeText(mnemonic);
// pbkdf2.deriveKey(secretKey: secretKey, nonce: nonce) // pbkdf2.deriveKey(secretKey: secretKey, nonce: nonce)
final key = await pbkdf2.deriveKey( final key = await pbkdf2.deriveKey(
secretKey: cryptography.SecretKey(text.codeUnits), secretKey: cryptography.SecretKey(text.codeUnits), nonce: 'electrum'.codeUnits);
nonce: 'electrum'.codeUnits);
final bytes = await key.extractBytes(); final bytes = await key.extractBytes();
return Uint8List.fromList(bytes); return Uint8List.fromList(bytes);
} }
bool matchesAnyPrefix(String mnemonic) => bool matchesAnyPrefix(String mnemonic) => prefixMatches(mnemonic, [segwit]).any((el) => el);
prefixMatches(mnemonic, [segwit]).any((el) => el);
bool validateMnemonic(String mnemonic, {String prefix = segwit}) { bool validateMnemonic(String mnemonic, {String prefix = segwit}) {
try { try {
@ -208,10 +214,8 @@ String removeCJKSpaces(String source) {
} }
String normalizeText(String source) { String normalizeText(String source) {
final res = removeCombiningCharacters(unorm.nfkd(source).toLowerCase()) final res =
.trim() removeCombiningCharacters(unorm.nfkd(source).toLowerCase()).trim().split('/\s+/').join(' ');
.split('/\s+/')
.join(' ');
return removeCJKSpaces(res); return removeCJKSpaces(res);
} }

View file

@ -12,6 +12,7 @@ import 'package:cw_core/wallet_info.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart'; import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
import 'package:bip39/bip39.dart' as bip39;
part 'bitcoin_wallet.g.dart'; part 'bitcoin_wallet.g.dart';
@ -30,8 +31,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
ElectrumBalance? initialBalance, ElectrumBalance? initialBalance,
Map<String, int>? initialRegularAddressIndex, Map<String, int>? initialRegularAddressIndex,
Map<String, int>? initialChangeAddressIndex, Map<String, int>? initialChangeAddressIndex,
String? passphrase,
}) : super( }) : super(
mnemonic: mnemonic, mnemonic: mnemonic,
passphrase: passphrase,
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
@ -44,14 +47,19 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
initialBalance: initialBalance, initialBalance: initialBalance,
seedBytes: seedBytes, seedBytes: seedBytes,
currency: CryptoCurrency.btc) { 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( walletAddresses = BitcoinWalletAddresses(
walletInfo, walletInfo,
electrumClient: electrumClient, electrumClient: electrumClient,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
initialRegularAddressIndex: initialRegularAddressIndex, initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex,
mainHd: hd, mainHd: hd.derivePath(derivationPath),
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"), sideHd: hd.derivePath(sideDerivationPath),
network: networkParam ?? network, network: networkParam ?? network,
); );
autorun((_) { autorun((_) {
@ -64,6 +72,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
required String password, required String password,
required WalletInfo walletInfo, required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
String? passphrase,
String? addressPageType, String? addressPageType,
BasedUtxoNetwork? network, BasedUtxoNetwork? network,
List<BitcoinAddressRecord>? initialAddresses, List<BitcoinAddressRecord>? initialAddresses,
@ -71,14 +80,29 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
Map<String, int>? initialRegularAddressIndex, Map<String, int>? initialRegularAddressIndex,
Map<String, int>? initialChangeAddressIndex, Map<String, int>? initialChangeAddressIndex,
}) async { }) 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( return BitcoinWallet(
mnemonic: mnemonic, mnemonic: mnemonic,
passphrase: passphrase ?? "",
password: password, password: password,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
initialBalance: initialBalance, initialBalance: initialBalance,
seedBytes: await mnemonicToSeedBytes(mnemonic), seedBytes: seedBytes,
initialRegularAddressIndex: initialRegularAddressIndex, initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex,
addressPageType: addressPageType, addressPageType: addressPageType,
@ -97,14 +121,38 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
: BitcoinNetwork.mainnet; : BitcoinNetwork.mainnet;
final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, network); 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( return BitcoinWallet(
mnemonic: snp.mnemonic, mnemonic: snp.mnemonic,
password: password, password: password,
passphrase: snp.passphrase,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp.addresses, initialAddresses: snp.addresses,
initialBalance: snp.balance, initialBalance: snp.balance,
seedBytes: await mnemonicToSeedBytes(snp.mnemonic), seedBytes: seedBytes,
initialRegularAddressIndex: snp.regularAddressIndex, initialRegularAddressIndex: snp.regularAddressIndex,
initialChangeAddressIndex: snp.changeAddressIndex, initialChangeAddressIndex: snp.changeAddressIndex,
addressPageType: snp.addressPageType, addressPageType: snp.addressPageType,

View file

@ -2,14 +2,35 @@ import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
class BitcoinNewWalletCredentials extends WalletCredentials { class BitcoinNewWalletCredentials extends WalletCredentials {
BitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo}) BitcoinNewWalletCredentials(
: super(name: name, walletInfo: walletInfo); {required String name,
WalletInfo? walletInfo,
DerivationType? derivationType,
String? derivationPath})
: super(
name: name,
walletInfo: walletInfo,
);
} }
class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials { class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials {
BitcoinRestoreWalletFromSeedCredentials( BitcoinRestoreWalletFromSeedCredentials({
{required String name, required String password, required this.mnemonic, WalletInfo? walletInfo}) required String name,
: super(name: name, password: password, walletInfo: walletInfo); 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; final String mnemonic;
} }
@ -20,4 +41,4 @@ class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
: super(name: name, password: password, walletInfo: walletInfo); : super(name: name, password: password, walletInfo: walletInfo);
final String wif; final String wif;
} }

View file

@ -12,6 +12,7 @@ import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:bip39/bip39.dart' as bip39;
class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials, class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> { BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
@ -29,8 +30,9 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
credentials.walletInfo?.network = network.value; credentials.walletInfo?.network = network.value;
final wallet = await BitcoinWalletBase.create( final wallet = await BitcoinWalletBase.create(
mnemonic: await generateMnemonic(), mnemonic: await generateElectrumMnemonic(),
password: credentials.password!, password: credentials.password!,
passphrase: credentials.passphrase,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
network: network, network: network,
@ -105,7 +107,7 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
@override @override
Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials, Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials,
{bool? isTestnet}) async { {bool? isTestnet}) async {
if (!validateMnemonic(credentials.mnemonic)) { if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) {
throw BitcoinMnemonicIsIncorrectException(); throw BitcoinMnemonicIsIncorrectException();
} }
@ -114,6 +116,7 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
final wallet = await BitcoinWalletBase.create( final wallet = await BitcoinWalletBase.create(
password: credentials.password!, password: credentials.password!,
passphrase: credentials.passphrase,
mnemonic: credentials.mnemonic, mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,

View file

@ -0,0 +1,104 @@
import 'package:cw_core/wallet_info.dart';
Map<DerivationType, List<DerivationInfo>> 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",
),
],
};

View file

@ -55,13 +55,15 @@ abstract class ElectrumWalletBase
required this.networkType, required this.networkType,
required this.mnemonic, required this.mnemonic,
required Uint8List seedBytes, required Uint8List seedBytes,
this.passphrase,
List<BitcoinAddressRecord>? initialAddresses, List<BitcoinAddressRecord>? initialAddresses,
ElectrumClient? electrumClient, ElectrumClient? electrumClient,
ElectrumBalance? initialBalance, ElectrumBalance? initialBalance,
CryptoCurrency? currency}) CryptoCurrency? currency})
: hd = currency == CryptoCurrency.bch : hd = currency == CryptoCurrency.bch
? bitcoinCashHDWallet(seedBytes) ? 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(), syncStatus = NotConnectedSyncStatus(),
_password = password, _password = password,
_feeRates = <int>[], _feeRates = <int>[],
@ -92,6 +94,7 @@ abstract class ElectrumWalletBase
final bitcoin.HDWallet hd; final bitcoin.HDWallet hd;
final String mnemonic; final String mnemonic;
final String? passphrase;
@override @override
@observable @observable
@ -617,6 +620,7 @@ abstract class ElectrumWalletBase
String toJSON() => json.encode({ String toJSON() => json.encode({
'mnemonic': mnemonic, 'mnemonic': mnemonic,
'passphrase': passphrase ?? '',
'account_index': walletAddresses.currentReceiveAddressIndexByType, 'account_index': walletAddresses.currentReceiveAddressIndexByType,
'change_address_index': walletAddresses.currentChangeAddressIndexByType, 'change_address_index': walletAddresses.currentChangeAddressIndexByType,
'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(), 'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(),
@ -624,6 +628,8 @@ abstract class ElectrumWalletBase
? SegwitAddresType.p2wpkh.toString() ? SegwitAddresType.p2wpkh.toString()
: walletInfo.addressPageType.toString(), : walletInfo.addressPageType.toString(),
'balance': balance[currency]?.toJSON(), 'balance': balance[currency]?.toJSON(),
'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index,
'derivationPath': walletInfo.derivationInfo?.derivationPath,
}); });
int feeRate(TransactionPriority priority) { int feeRate(TransactionPriority priority) {

View file

@ -3,6 +3,7 @@ import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_core/pathForWallet.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/utils/file.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
@ -17,6 +18,9 @@ class ElectrumWalletSnapshot {
required this.regularAddressIndex, required this.regularAddressIndex,
required this.changeAddressIndex, required this.changeAddressIndex,
required this.addressPageType, required this.addressPageType,
this.passphrase,
this.derivationType,
this.derivationPath,
}); });
final String name; final String name;
@ -29,6 +33,9 @@ class ElectrumWalletSnapshot {
ElectrumBalance balance; ElectrumBalance balance;
Map<String, int> regularAddressIndex; Map<String, int> regularAddressIndex;
Map<String, int> changeAddressIndex; Map<String, int> changeAddressIndex;
String? passphrase;
DerivationType? derivationType;
String? derivationPath;
static Future<ElectrumWalletSnapshot> load( static Future<ElectrumWalletSnapshot> load(
String name, WalletType type, String password, BasedUtxoNetwork network) async { String name, WalletType type, String password, BasedUtxoNetwork network) async {
@ -37,6 +44,7 @@ class ElectrumWalletSnapshot {
final data = json.decode(jsonSource) as Map; final data = json.decode(jsonSource) as Map;
final addressesTmp = data['addresses'] as List? ?? <Object>[]; final addressesTmp = data['addresses'] as List? ?? <Object>[];
final mnemonic = data['mnemonic'] as String; final mnemonic = data['mnemonic'] as String;
final passphrase = data['passphrase'] as String? ?? '';
final addresses = addressesTmp final addresses = addressesTmp
.whereType<String>() .whereType<String>()
.map((addr) => BitcoinAddressRecord.fromJSON(addr, network)) .map((addr) => BitcoinAddressRecord.fromJSON(addr, network))
@ -46,6 +54,10 @@ class ElectrumWalletSnapshot {
var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0}; var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
var changeAddressIndexByType = {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 { try {
regularAddressIndexByType = { regularAddressIndexByType = {
SegwitAddresType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0') SegwitAddresType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0')
@ -65,12 +77,15 @@ class ElectrumWalletSnapshot {
name: name, name: name,
type: type, type: type,
password: password, password: password,
passphrase: passphrase,
mnemonic: mnemonic, mnemonic: mnemonic,
addresses: addresses, addresses: addresses,
balance: balance, balance: balance,
regularAddressIndex: regularAddressIndexByType, regularAddressIndex: regularAddressIndexByType,
changeAddressIndex: changeAddressIndexByType, changeAddressIndex: changeAddressIndexByType,
addressPageType: data['address_page_type'] as String?, addressPageType: data['address_page_type'] as String?,
derivationType: derivationType,
derivationPath: derivationPath,
); );
} }
} }

View file

@ -15,6 +15,7 @@ import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_bitcoin/litecoin_network.dart'; import 'package:cw_bitcoin/litecoin_network.dart';
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:bip39/bip39.dart' as bip39;
part 'litecoin_wallet.g.dart'; part 'litecoin_wallet.g.dart';
@ -62,11 +63,26 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
required String password, required String password,
required WalletInfo walletInfo, required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
String? passphrase,
String? addressPageType, String? addressPageType,
List<BitcoinAddressRecord>? initialAddresses, List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance, ElectrumBalance? initialBalance,
Map<String, int>? initialRegularAddressIndex, Map<String, int>? initialRegularAddressIndex,
Map<String, int>? initialChangeAddressIndex}) async { Map<String, int>? 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( return LitecoinWallet(
mnemonic: mnemonic, mnemonic: mnemonic,
password: password, password: password,
@ -74,7 +90,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
initialBalance: initialBalance, initialBalance: initialBalance,
seedBytes: await mnemonicToSeedBytes(mnemonic), seedBytes: seedBytes,
initialRegularAddressIndex: initialRegularAddressIndex, initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex,
addressPageType: addressPageType, addressPageType: addressPageType,

View file

@ -11,6 +11,7 @@ import 'package:cw_core/wallet_type.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:bip39/bip39.dart' as bip39;
class LitecoinWalletService extends WalletService< class LitecoinWalletService extends WalletService<
BitcoinNewWalletCredentials, BitcoinNewWalletCredentials,
@ -27,8 +28,9 @@ class LitecoinWalletService extends WalletService<
@override @override
Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async { Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
final wallet = await LitecoinWalletBase.create( final wallet = await LitecoinWalletBase.create(
mnemonic: await generateMnemonic(), mnemonic: await generateElectrumMnemonic(),
password: credentials.password!, password: credentials.password!,
passphrase: credentials.passphrase,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource); unspentCoinsInfo: unspentCoinsInfoSource);
await wallet.save(); await wallet.save();
@ -100,12 +102,13 @@ class LitecoinWalletService extends WalletService<
@override @override
Future<LitecoinWallet> restoreFromSeed( Future<LitecoinWallet> restoreFromSeed(
BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
if (!validateMnemonic(credentials.mnemonic)) { if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) {
throw LitecoinMnemonicIsIncorrectException(); throw LitecoinMnemonicIsIncorrectException();
} }
final wallet = await LitecoinWalletBase.create( final wallet = await LitecoinWalletBase.create(
password: credentials.password!, password: credentials.password!,
passphrase: credentials.passphrase,
mnemonic: credentials.mnemonic, mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource); unspentCoinsInfo: unspentCoinsInfoSource);

View file

@ -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:bitcoin_flutter/src/payments/index.dart' show PaymentData;
import 'package:hex/hex.dart'; import 'package:hex/hex.dart';
bitcoin.PaymentData generatePaymentData({required bitcoin.HDWallet hd, required int index}) => bitcoin.PaymentData generatePaymentData({required bitcoin.HDWallet hd, int? index}) {
PaymentData(pubkey: Uint8List.fromList(HEX.decode(hd.derive(index).pubKey!))); final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
return PaymentData(pubkey: Uint8List.fromList(HEX.decode(pubKey)));
}
ECPrivate generateECPrivate( ECPrivate generateECPrivate(
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) => {required bitcoin.HDWallet hd, required BasedUtxoNetwork network, int? index}) {
ECPrivate.fromWif(hd.derive(index).wif!, netVersion: network.wifNetVer); final wif = index != null ? hd.derive(index).wif! : hd.wif!;
return ECPrivate.fromWif(wif, netVersion: network.wifNetVer);
}
String generateP2WPKHAddress( String generateP2WPKHAddress({
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) => required bitcoin.HDWallet hd,
ECPublic.fromHex(hd.derive(index).pubKey!).toP2wpkhAddress().toAddress(network); required BasedUtxoNetwork network,
int? index,
}) {
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
return ECPublic.fromHex(pubKey).toP2wpkhAddress().toAddress(network);
}
String generateP2SHAddress( String generateP2SHAddress({
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) => required bitcoin.HDWallet hd,
ECPublic.fromHex(hd.derive(index).pubKey!).toP2wpkhInP2sh().toAddress(network); required BasedUtxoNetwork network,
int? index,
}) {
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
return ECPublic.fromHex(pubKey).toP2wpkhInP2sh().toAddress(network);
}
String generateP2WSHAddress( String generateP2WSHAddress({
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) => required bitcoin.HDWallet hd,
ECPublic.fromHex(hd.derive(index).pubKey!).toP2wshAddress().toAddress(network); required BasedUtxoNetwork network,
int? index,
}) {
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
return ECPublic.fromHex(pubKey).toP2wshAddress().toAddress(network);
}
String generateP2PKHAddress( String generateP2PKHAddress({
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) => required bitcoin.HDWallet hd,
ECPublic.fromHex(hd.derive(index).pubKey!).toP2pkhAddress().toAddress(network); required BasedUtxoNetwork network,
int? index,
}) {
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
return ECPublic.fromHex(pubKey).toP2pkhAddress().toAddress(network);
}
String generateP2TRAddress( String generateP2TRAddress({
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) => required bitcoin.HDWallet hd,
ECPublic.fromHex(hd.derive(index).pubKey!).toTaprootAddress().toAddress(network); required BasedUtxoNetwork network,
int? index,
}) {
final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!;
return ECPublic.fromHex(pubKey).toTaprootAddress().toAddress(network);
}

View file

@ -217,10 +217,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.2" version: "1.17.1"
convert: convert:
dependency: transitive dependency: transitive
description: description:
@ -434,18 +434,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.16" version: "0.12.15"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.0" version: "0.2.0"
meta: meta:
dependency: transitive dependency: transitive
description: description:
@ -663,10 +663,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: source_span name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.0" version: "1.9.1"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -711,10 +711,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.0" version: "0.5.1"
timing: timing:
dependency: transitive dependency: transitive
description: description:
@ -748,21 +748,13 @@ packages:
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
watcher: watcher:
dependency: "direct overridden" dependency: transitive
description: description:
name: watcher name: watcher
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" 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: web_socket_channel:
dependency: transitive dependency: transitive
description: description:
@ -796,5 +788,5 @@ packages:
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
sdks: sdks:
dart: ">=3.1.0-185.0.dev <4.0.0" dart: ">=3.0.0 <4.0.0"
flutter: ">=3.10.0" flutter: ">=3.10.0"

View file

@ -14,4 +14,5 @@ const ERC20_TOKEN_TYPE_ID = 12;
const NANO_ACCOUNT_TYPE_ID = 13; const NANO_ACCOUNT_TYPE_ID = 13;
const POW_NODE_TYPE_ID = 14; const POW_NODE_TYPE_ID = 14;
const DERIVATION_TYPE_TYPE_ID = 15; const DERIVATION_TYPE_TYPE_ID = 15;
const SPL_TOKEN_TYPE_ID = 16; const SPL_TOKEN_TYPE_ID = 16;
const DERIVATION_INFO_TYPE_ID = 17;

View file

@ -7,15 +7,19 @@ abstract class WalletCredentials {
this.seedPhraseLength, this.seedPhraseLength,
this.walletInfo, this.walletInfo,
this.password, this.password,
this.derivationType, this.passphrase,
this.derivationPath, this.derivationInfo,
}); }) {
if (this.walletInfo != null && derivationInfo != null) {
this.walletInfo!.derivationInfo = derivationInfo;
}
}
final String name; final String name;
final int? height; final int? height;
int? seedPhraseLength; int? seedPhraseLength;
String? password; String? password;
DerivationType? derivationType; String? passphrase;
String? derivationPath;
WalletInfo? walletInfo; WalletInfo? walletInfo;
DerivationInfo? derivationInfo;
} }

View file

@ -17,28 +17,42 @@ enum DerivationType {
@HiveField(3) @HiveField(3)
bip39, bip39,
@HiveField(4) @HiveField(4)
electrum1, electrum,
@HiveField(5)
electrum2,
} }
class DerivationInfo { @HiveType(typeId: DerivationInfo.typeId)
class DerivationInfo extends HiveObject {
DerivationInfo({ DerivationInfo({
required this.derivationType, this.derivationType,
this.derivationPath, this.derivationPath,
this.balance = "", this.balance = "",
this.address = "", this.address = "",
this.height = 0, this.transactionsCount = 0,
this.script_type, this.scriptType,
this.description, this.description,
}); });
String balance; static const typeId = DERIVATION_INFO_TYPE_ID;
@HiveField(0, defaultValue: '')
String address; String address;
int height;
final DerivationType derivationType; @HiveField(1, defaultValue: '')
final String? derivationPath; String balance;
final String? script_type;
@HiveField(2)
int transactionsCount;
@HiveField(3)
DerivationType? derivationType;
@HiveField(4)
String? derivationPath;
@HiveField(5)
final String? scriptType;
@HiveField(6)
final String? description; final String? description;
} }
@ -57,8 +71,7 @@ class WalletInfo extends HiveObject {
this.yatEid, this.yatEid,
this.yatLastUsedAddressRaw, this.yatLastUsedAddressRaw,
this.showIntroCakePayCard, this.showIntroCakePayCard,
this.derivationType, this.derivationInfo)
this.derivationPath)
: _yatLastUsedAddressController = StreamController<String>.broadcast(); : _yatLastUsedAddressController = StreamController<String>.broadcast();
factory WalletInfo.external({ factory WalletInfo.external({
@ -74,24 +87,23 @@ class WalletInfo extends HiveObject {
bool? showIntroCakePayCard, bool? showIntroCakePayCard,
String yatEid = '', String yatEid = '',
String yatLastUsedAddressRaw = '', String yatLastUsedAddressRaw = '',
DerivationType? derivationType, DerivationInfo? derivationInfo,
String? derivationPath,
}) { }) {
return WalletInfo( return WalletInfo(
id, id,
name, name,
type, type,
isRecovery, isRecovery,
restoreHeight, restoreHeight,
date.millisecondsSinceEpoch, date.millisecondsSinceEpoch,
dirPath, dirPath,
path, path,
address, address,
yatEid, yatEid,
yatLastUsedAddressRaw, yatLastUsedAddressRaw,
showIntroCakePayCard, showIntroCakePayCard,
derivationType, derivationInfo,
derivationPath); );
} }
static const typeId = WALLET_INFO_TYPE_ID; static const typeId = WALLET_INFO_TYPE_ID;
@ -143,10 +155,10 @@ class WalletInfo extends HiveObject {
List<String>? usedAddresses; List<String>? usedAddresses;
@HiveField(16) @HiveField(16)
DerivationType? derivationType; DerivationType? derivationType; // no longer used
@HiveField(17) @HiveField(17)
String? derivationPath; String? derivationPath; // no longer used
@HiveField(18) @HiveField(18)
String? addressPageType; String? addressPageType;
@ -154,6 +166,9 @@ class WalletInfo extends HiveObject {
@HiveField(19) @HiveField(19)
String? network; String? network;
@HiveField(20)
DerivationInfo? derivationInfo;
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? ''; String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
set yatLastUsedAddress(String address) { set yatLastUsedAddress(String address) {

View file

@ -149,10 +149,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.2" version: "1.17.1"
convert: convert:
dependency: transitive dependency: transitive
description: description:
@ -343,18 +343,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.16" version: "0.12.15"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.0" version: "0.2.0"
meta: meta:
dependency: transitive dependency: transitive
description: description:
@ -564,10 +564,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: source_span name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.0" version: "1.9.1"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -612,10 +612,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.0" version: "0.5.1"
timing: timing:
dependency: transitive dependency: transitive
description: description:
@ -641,21 +641,13 @@ packages:
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
watcher: watcher:
dependency: "direct overridden" dependency: transitive
description: description:
name: watcher name: watcher
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" 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: web_socket_channel:
dependency: transitive dependency: transitive
description: description:
@ -689,5 +681,5 @@ packages:
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
sdks: sdks:
dart: ">=3.1.0-185.0.dev <4.0.0" dart: ">=3.0.0 <4.0.0"
flutter: ">=3.10.0" flutter: ">=3.10.0"

View file

@ -43,7 +43,7 @@ abstract class NanoWalletBase
}) : syncStatus = NotConnectedSyncStatus(), }) : syncStatus = NotConnectedSyncStatus(),
_password = password, _password = password,
_mnemonic = mnemonic, _mnemonic = mnemonic,
_derivationType = walletInfo.derivationType!, _derivationType = walletInfo.derivationInfo!.derivationType!,
_isTransactionUpdating = false, _isTransactionUpdating = false,
_client = NanoClient(), _client = NanoClient(),
walletAddresses = NanoWalletAddresses(walletInfo), walletAddresses = NanoWalletAddresses(walletInfo),
@ -389,7 +389,10 @@ abstract class NanoWalletBase
derivationType = DerivationType.bip39; derivationType = DerivationType.bip39;
} }
walletInfo.derivationType = derivationType; walletInfo.derivationInfo ??= DerivationInfo(derivationType: derivationType);
if (walletInfo.derivationInfo!.derivationType == null) {
walletInfo.derivationInfo!.derivationType = derivationType;
}
return NanoWallet( return NanoWallet(
walletInfo: walletInfo, walletInfo: walletInfo,

View file

@ -2,8 +2,15 @@ import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
class NanoNewWalletCredentials extends WalletCredentials { class NanoNewWalletCredentials extends WalletCredentials {
NanoNewWalletCredentials({required String name, String? password}) NanoNewWalletCredentials({
: super(name: name, password: password); required String name,
String? password,
DerivationType? derivationType,
}) : super(
name: name,
password: password,
derivationInfo: DerivationInfo(derivationType: derivationType),
);
} }
class NanoRestoreWalletFromSeedCredentials extends WalletCredentials { class NanoRestoreWalletFromSeedCredentials extends WalletCredentials {
@ -11,11 +18,11 @@ class NanoRestoreWalletFromSeedCredentials extends WalletCredentials {
required String name, required String name,
required this.mnemonic, required this.mnemonic,
String? password, String? password,
DerivationType? derivationType, required DerivationType derivationType,
}) : super( }) : super(
name: name, name: name,
password: password, password: password,
derivationType: derivationType, derivationInfo: DerivationInfo(derivationType: derivationType),
); );
final String mnemonic; final String mnemonic;
@ -30,12 +37,12 @@ class NanoRestoreWalletFromKeysCredentials extends WalletCredentials {
NanoRestoreWalletFromKeysCredentials({ NanoRestoreWalletFromKeysCredentials({
required String name, required String name,
required String password, required String password,
required DerivationType derivationType,
required this.seedKey, required this.seedKey,
DerivationType? derivationType,
}) : super( }) : super(
name: name, name: name,
password: password, password: password,
derivationType: derivationType, derivationInfo: DerivationInfo(derivationType: derivationType),
); );
final String seedKey; final String seedKey;

View file

@ -28,11 +28,11 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
@override @override
Future<WalletBase> create(NanoNewWalletCredentials credentials, {bool? isTestnet}) async { Future<WalletBase> create(NanoNewWalletCredentials credentials, {bool? isTestnet}) async {
// nano standard: // nano standard:
DerivationType derivationType = DerivationType.nano;
String seedKey = NanoSeeds.generateSeed(); String seedKey = NanoSeeds.generateSeed();
String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey); String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
credentials.walletInfo!.derivationType = derivationType; // ensure default if not present:
credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: DerivationType.nano);
final wallet = NanoWallet( final wallet = NanoWallet(
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
@ -88,9 +88,6 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
} }
} }
DerivationType derivationType = credentials.derivationType ?? DerivationType.nano;
credentials.walletInfo!.derivationType = derivationType;
String? mnemonic; String? mnemonic;
// we can't derive the mnemonic from the key in all cases, only if it's a "nano" seed // 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,
} }
} }
DerivationType derivationType = credentials.derivationType ?? DerivationType.nano; DerivationType derivationType =
credentials.walletInfo?.derivationInfo?.derivationType ?? DerivationType.nano;
credentials.walletInfo!.derivationType = derivationType; credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: derivationType);
final wallet = await NanoWallet( final wallet = await NanoWallet(
password: credentials.password!, password: credentials.password!,

View file

@ -1,13 +1,22 @@
part of 'bitcoin.dart'; part of 'bitcoin.dart';
class CWBitcoin extends Bitcoin { class CWBitcoin extends Bitcoin {
@override WalletCredentials createBitcoinRestoreWalletFromSeedCredentials({
TransactionPriority getMediumTransactionPriority() => BitcoinTransactionPriority.medium; required String name,
required String mnemonic,
@override required String password,
WalletCredentials createBitcoinRestoreWalletFromSeedCredentials( required DerivationType derivationType,
{required String name, required String mnemonic, required String password}) => required String derivationPath,
BitcoinRestoreWalletFromSeedCredentials(name: name, mnemonic: mnemonic, password: password); String? passphrase,
}) =>
BitcoinRestoreWalletFromSeedCredentials(
name: name,
mnemonic: mnemonic,
password: password,
derivationType: derivationType,
derivationPath: derivationPath,
passphrase: passphrase,
);
@override @override
WalletCredentials createBitcoinRestoreWalletFromWIFCredentials( WalletCredentials createBitcoinRestoreWalletFromWIFCredentials(
@ -23,6 +32,9 @@ class CWBitcoin extends Bitcoin {
{required String name, WalletInfo? walletInfo}) => {required String name, WalletInfo? walletInfo}) =>
BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo); BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo);
@override
TransactionPriority getMediumTransactionPriority() => BitcoinTransactionPriority.medium;
@override @override
List<String> getWordList() => wordlist; List<String> getWordList() => wordlist;
@ -78,21 +90,20 @@ class CWBitcoin extends Bitcoin {
final bitcoinFeeRate = final bitcoinFeeRate =
priority == BitcoinTransactionPriority.custom && feeRate != null ? feeRate : null; priority == BitcoinTransactionPriority.custom && feeRate != null ? feeRate : null;
return BitcoinTransactionCredentials( return BitcoinTransactionCredentials(
outputs outputs
.map((out) => OutputInfo( .map((out) => OutputInfo(
fiatAmount: out.fiatAmount, fiatAmount: out.fiatAmount,
cryptoAmount: out.cryptoAmount, cryptoAmount: out.cryptoAmount,
address: out.address, address: out.address,
note: out.note, note: out.note,
sendAll: out.sendAll, sendAll: out.sendAll,
extractedAddress: out.extractedAddress, extractedAddress: out.extractedAddress,
isParsedAddress: out.isParsedAddress, isParsedAddress: out.isParsedAddress,
formattedCryptoAmount: out.formattedCryptoAmount, formattedCryptoAmount: out.formattedCryptoAmount,
memo: out.memo)) memo: out.memo))
.toList(), .toList(),
priority: priority as BitcoinTransactionPriority, priority: priority as BitcoinTransactionPriority,
feeRate: bitcoinFeeRate feeRate: bitcoinFeeRate);
);
} }
@override @override
@ -248,6 +259,137 @@ class CWBitcoin extends Bitcoin {
} }
} }
@override
Future<List<DerivationType>> 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<List<DerivationInfo>> getDerivationsFromMnemonic({
required String mnemonic,
required Node node,
String? passphrase,
}) async {
List<DerivationInfo> list = [];
List<DerivationType> 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 @override
bool hasTaprootInput(PendingTransaction pendingTransaction) { bool hasTaprootInput(PendingTransaction pendingTransaction) {
return (pendingTransaction as PendingBitcoinTransaction).hasTaprootInputs; return (pendingTransaction as PendingBitcoinTransaction).hasTaprootInputs;

View file

@ -220,6 +220,10 @@ Future<void> defaultSettingsMigration(
await updateNanoNodeList(nodes: nodes); await updateNanoNodeList(nodes: nodes);
break; break;
case 32:
await updateBtcNanoWalletInfos(walletInfoSource);
break;
default: default:
break; break;
} }
@ -756,6 +760,20 @@ Future<void> changeDefaultMoneroNode(
} }
} }
Future<void> updateBtcNanoWalletInfos(Box<WalletInfo> walletsInfoSource) async {
for (WalletInfo walletInfo in walletsInfoSource.values) {
if (walletInfo.type == WalletType.nano || walletInfo.type == WalletType.bitcoin) {
walletInfo.derivationInfo = DerivationInfo(
derivationPath: walletInfo.derivationPath,
derivationType: walletInfo.derivationType,
address: walletInfo.address,
transactionsCount: walletInfo.restoreHeight,
);
await walletInfo.save();
}
}
}
Future<void> changeDefaultBitcoinNode( Future<void> changeDefaultBitcoinNode(
Box<Node> nodeSource, SharedPreferences sharedPreferences) async { Box<Node> nodeSource, SharedPreferences sharedPreferences) async {
const cakeWalletBitcoinNodeUriPattern = '.cakewallet.com'; const cakeWalletBitcoinNodeUriPattern = '.cakewallet.com';

View file

@ -51,10 +51,12 @@ class AddressResolver {
} }
final match = RegExp(addressPattern).firstMatch(raw); 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) { (Match match) {
String group = match.group(0)!; 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 group;
} }
return ''; return '';

View file

@ -102,6 +102,10 @@ Future<void> initializeAppConfigs() async {
CakeHive.registerAdapter(DerivationTypeAdapter()); CakeHive.registerAdapter(DerivationTypeAdapter());
} }
if (!CakeHive.isAdapterRegistered(DERIVATION_INFO_TYPE_ID)) {
CakeHive.registerAdapter(DerivationInfoAdapter());
}
if (!CakeHive.isAdapterRegistered(WALLET_TYPE_TYPE_ID)) { if (!CakeHive.isAdapterRegistered(WALLET_TYPE_TYPE_ID)) {
CakeHive.registerAdapter(WalletTypeAdapter()); CakeHive.registerAdapter(WalletTypeAdapter());
} }
@ -163,7 +167,7 @@ Future<void> initializeAppConfigs() async {
transactionDescriptions: transactionDescriptions, transactionDescriptions: transactionDescriptions,
secureStorage: secureStorage, secureStorage: secureStorage,
anonpayInvoiceInfo: anonpayInvoiceInfo, anonpayInvoiceInfo: anonpayInvoiceInfo,
initialMigrationVersion: 31, initialMigrationVersion: 32,
); );
} }

View file

@ -96,6 +96,7 @@ class CWNano extends Nano {
NanoNewWalletCredentials( NanoNewWalletCredentials(
name: name, name: name,
password: password, password: password,
derivationType: DerivationType.nano,
); );
@override @override
@ -103,15 +104,10 @@ class CWNano extends Nano {
required String name, required String name,
required String password, required String password,
required String mnemonic, required String mnemonic,
DerivationType? derivationType, required DerivationType derivationType,
}) { }) {
if (derivationType == null) { if (mnemonic.split(" ").length == 12 && derivationType != DerivationType.bip39) {
// figure out the derivation type as best we can, otherwise set it to "unknown" throw Exception("Invalid mnemonic for derivation type!");
if (mnemonic.split(" ").length == 12) {
derivationType = DerivationType.bip39;
} else {
derivationType = DerivationType.unknown;
}
} }
return NanoRestoreWalletFromSeedCredentials( return NanoRestoreWalletFromSeedCredentials(
@ -127,15 +123,10 @@ class CWNano extends Nano {
required String name, required String name,
required String password, required String password,
required String seedKey, required String seedKey,
DerivationType? derivationType, required DerivationType derivationType,
}) { }) {
if (derivationType == null) { if (seedKey.length == 128 && derivationType != DerivationType.bip39) {
// figure out the derivation type as best we can, otherwise set it to "unknown" throw Exception("Invalid seed key length for derivation type!");
if (seedKey.length == 64) {
derivationType = DerivationType.nano;
} else {
derivationType = DerivationType.unknown;
}
} }
return NanoRestoreWalletFromKeysCredentials( return NanoRestoreWalletFromKeysCredentials(
@ -199,7 +190,6 @@ class CWNano extends Nano {
} }
class CWNanoUtil extends NanoUtil { class CWNanoUtil extends NanoUtil {
@override @override
bool isValidBip39Seed(String seed) { bool isValidBip39Seed(String seed) {
return NanoDerivations.isValidBip39Seed(seed); return NanoDerivations.isValidBip39Seed(seed);
@ -353,4 +343,54 @@ class CWNanoUtil extends NanoUtil {
return [DerivationType.nano, DerivationType.bip39]; return [DerivationType.nano, DerivationType.bip39];
} }
} }
@override
Future<List<DerivationInfo>> getDerivationsFromMnemonic({
String? mnemonic,
String? seedKey,
required Node node,
}) async {
List<DerivationInfo> list = [];
List<DerivationType> 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;
}
} }

View file

@ -1,8 +1,5 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/generated/i18n.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/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:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -13,15 +10,14 @@ class WalletRestoreChooseDerivationPage extends BasePage {
WalletRestoreChooseDerivationPage(this.walletRestoreChooseDerivationViewModel) {} WalletRestoreChooseDerivationPage(this.walletRestoreChooseDerivationViewModel) {}
@override @override
Widget middle(BuildContext context) => Observer( Widget middle(BuildContext context) => Text(
builder: (_) => Text( S.current.choose_derivation,
S.current.choose_derivation, style: TextStyle(
style: TextStyle( fontSize: 18.0,
fontSize: 18.0, fontWeight: FontWeight.bold,
fontWeight: FontWeight.bold, fontFamily: 'Lato',
fontFamily: 'Lato', color: titleColor(context)),
color: titleColor(context)), );
));
final WalletRestoreChooseDerivationViewModel walletRestoreChooseDerivationViewModel; final WalletRestoreChooseDerivationViewModel walletRestoreChooseDerivationViewModel;
DerivationType derivationType = DerivationType.unknown; DerivationType derivationType = DerivationType.unknown;
@ -105,7 +101,7 @@ class WalletRestoreChooseDerivationPage extends BasePage {
), ),
), ),
Text( Text(
"${S.current.transactions}: ${derivation.height}", "${S.current.transactions}: ${derivation.transactionsCount}",
style: Theme.of(context).primaryTextTheme.labelMedium!.copyWith( style: Theme.of(context).primaryTextTheme.labelMedium!.copyWith(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,

View file

@ -20,6 +20,7 @@ class WalletRestoreFromSeedForm extends StatefulWidget {
{Key? key, {Key? key,
required this.displayLanguageSelector, required this.displayLanguageSelector,
required this.displayBlockHeightSelector, required this.displayBlockHeightSelector,
required this.displayPassphrase,
required this.type, required this.type,
required this.seedTypeViewModel, required this.seedTypeViewModel,
this.blockHeightFocusNode, this.blockHeightFocusNode,
@ -31,6 +32,7 @@ class WalletRestoreFromSeedForm extends StatefulWidget {
final WalletType type; final WalletType type;
final bool displayLanguageSelector; final bool displayLanguageSelector;
final bool displayBlockHeightSelector; final bool displayBlockHeightSelector;
final bool displayPassphrase;
final SeedTypeViewModel seedTypeViewModel; final SeedTypeViewModel seedTypeViewModel;
final FocusNode? blockHeightFocusNode; final FocusNode? blockHeightFocusNode;
final Function(bool)? onHeightOrDateEntered; final Function(bool)? onHeightOrDateEntered;
@ -48,6 +50,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
formKey = GlobalKey<FormState>(), formKey = GlobalKey<FormState>(),
languageController = TextEditingController(), languageController = TextEditingController(),
nameTextEditingController = TextEditingController(), nameTextEditingController = TextEditingController(),
passphraseController = TextEditingController(),
seedTypeController = TextEditingController(); seedTypeController = TextEditingController();
final GlobalKey<SeedWidgetState> seedWidgetStateKey; final GlobalKey<SeedWidgetState> seedWidgetStateKey;
@ -55,6 +58,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
final TextEditingController languageController; final TextEditingController languageController;
final TextEditingController nameTextEditingController; final TextEditingController nameTextEditingController;
final TextEditingController seedTypeController; final TextEditingController seedTypeController;
final TextEditingController passphraseController;
final GlobalKey<FormState> formKey; final GlobalKey<FormState> formKey;
late ReactionDisposer moneroSeedTypeReaction; late ReactionDisposer moneroSeedTypeReaction;
String language; String language;
@ -166,15 +170,15 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
), ),
if (widget.displayLanguageSelector) if (widget.displayLanguageSelector)
GestureDetector( GestureDetector(
onTap: () async { onTap: () async {
await showPopUp<void>( await showPopUp<void>(
context: context, context: context,
builder: (_) => SeedLanguagePicker( builder: (_) => SeedLanguagePicker(
selected: language, selected: language,
onItemSelected: _changeLanguage, onItemSelected: _changeLanguage,
seedType: isPolyseed ? SeedType.polyseed : SeedType.legacy, seedType: isPolyseed ? SeedType.polyseed : SeedType.legacy,
)); ));
}, },
child: Container( child: Container(
color: Colors.transparent, color: Colors.transparent,
padding: EdgeInsets.only(top: 20.0), padding: EdgeInsets.only(top: 20.0),
@ -194,6 +198,14 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
key: blockchainHeightKey, key: blockchainHeightKey,
onHeightOrDateEntered: widget.onHeightOrDateEntered, onHeightOrDateEntered: widget.onHeightOrDateEntered,
hasDatePicker: widget.type == WalletType.monero), hasDatePicker: widget.type == WalletType.monero),
if (widget.displayPassphrase) ...[
const SizedBox(height: 10),
BaseTextFormField(
hintText: S.current.passphrase,
controller: passphraseController,
obscureText: true,
),
]
])); ]));
} }

View file

@ -1,7 +1,5 @@
import 'package:cake_wallet/core/execution_state.dart'; 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/generated/i18n.dart';
import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.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/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/primary_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/keyboard_theme.dart';
import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart'; import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
import 'package:cake_wallet/utils/responsive_layout_util.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/restore/restore_mode.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.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: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_info.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -42,6 +38,7 @@ class WalletRestorePage extends BasePage {
displayBlockHeightSelector: displayBlockHeightSelector:
walletRestoreViewModel.hasBlockchainHeightLanguageSelector, walletRestoreViewModel.hasBlockchainHeightLanguageSelector,
displayLanguageSelector: walletRestoreViewModel.hasSeedLanguageSelector, displayLanguageSelector: walletRestoreViewModel.hasSeedLanguageSelector,
displayPassphrase: walletRestoreViewModel.hasPassphrase,
type: walletRestoreViewModel.type, type: walletRestoreViewModel.type,
key: walletRestoreFromSeedFormKey, key: walletRestoreFromSeedFormKey,
blockHeightFocusNode: _blockHeightFocusNode, blockHeightFocusNode: _blockHeightFocusNode,
@ -99,8 +96,10 @@ class WalletRestorePage extends BasePage {
final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey; final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey; final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey;
final FocusNode _blockHeightFocusNode; final FocusNode _blockHeightFocusNode;
DerivationType derivationType = DerivationType.unknown;
String? derivationPath = null; // DerivationType derivationType = DerivationType.unknown;
// String? derivationPath = null;
DerivationInfo? derivationInfo;
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
@ -298,6 +297,11 @@ class WalletRestorePage extends BasePage {
-1; -1;
} }
if (walletRestoreViewModel.hasPassphrase) {
credentials['passphrase'] =
walletRestoreFromSeedFormKey.currentState!.passphraseController.text;
}
credentials['name'] = credentials['name'] =
walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text; walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text;
} else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) { } else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) {
@ -318,58 +322,11 @@ class WalletRestorePage extends BasePage {
} }
} }
credentials['derivationType'] = this.derivationType; credentials['derivationInfo'] = this.derivationInfo;
credentials['derivationPath'] = this.derivationPath;
credentials['walletType'] = walletRestoreViewModel.type; credentials['walletType'] = walletRestoreViewModel.type;
return credentials; return credentials;
} }
Future<List<DerivationInfo>> getDerivationInfo(dynamic credentials) async {
var list = <DerivationInfo>[];
var walletType = credentials["walletType"] as WalletType;
var appStore = getIt.get<AppStore>();
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<void> _confirmForm(BuildContext context) async { Future<void> _confirmForm(BuildContext context) async {
// Dismissing all visible keyboard to provide context for navigation // Dismissing all visible keyboard to provide context for navigation
FocusManager.instance.primaryFocus?.unfocus(); FocusManager.instance.primaryFocus?.unfocus();
@ -398,51 +355,46 @@ class WalletRestorePage extends BasePage {
walletRestoreViewModel.state = IsExecutingState(); walletRestoreViewModel.state = IsExecutingState();
List<DerivationType> derivationTypes = DerivationInfo? dInfo;
await walletRestoreViewModel.getDerivationTypes(_credentials());
if (derivationTypes[0] == DerivationType.unknown || derivationTypes.length > 1) { // get info about the different derivations:
// push screen to choose the derivation type: List<DerivationInfo> derivations =
List<DerivationInfo> derivations = await getDerivationInfo(_credentials()); await walletRestoreViewModel.getDerivationInfo(_credentials());
int derivationsWithHistory = 0; int derivationsWithHistory = 0;
int derivationWithHistoryIndex = 0; int derivationWithHistoryIndex = 0;
for (int i = 0; i < derivations.length; i++) { for (int i = 0; i < derivations.length; i++) {
if (derivations[i].height > 0) { if (derivations[i].transactionsCount > 0) {
derivationsWithHistory++; derivationsWithHistory++;
derivationWithHistoryIndex = i; 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()); walletRestoreViewModel.create(options: _credentials());
} }

View file

@ -343,7 +343,9 @@ class WalletListBodyState extends State<WalletListBody> {
}); });
} }
} catch (e) { } 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: conditionToDetermineIfToUse2FA:

View file

@ -22,6 +22,7 @@ class BaseTextFormField extends StatelessWidget {
this.enabled = true, this.enabled = true,
this.readOnly = false, this.readOnly = false,
this.enableInteractiveSelection = true, this.enableInteractiveSelection = true,
this.obscureText = false,
this.validator, this.validator,
this.textStyle, this.textStyle,
this.placeholderTextStyle, this.placeholderTextStyle,
@ -57,6 +58,7 @@ class BaseTextFormField extends StatelessWidget {
final String? initialValue; final String? initialValue;
final double borderWidth; final double borderWidth;
final void Function(String)? onSubmit; final void Function(String)? onSubmit;
final bool obscureText;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -70,6 +72,7 @@ class BaseTextFormField extends StatelessWidget {
textInputAction: textInputAction, textInputAction: textInputAction,
textAlign: textAlign, textAlign: textAlign,
autovalidateMode: autovalidateMode, autovalidateMode: autovalidateMode,
obscureText: obscureText,
maxLines: maxLines, maxLines: maxLines,
inputFormatters: inputFormatters, inputFormatters: inputFormatters,
enabled: enabled, enabled: enabled,

View file

@ -30,8 +30,7 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
spendKey = '', spendKey = '',
wif = '', wif = '',
address = '', address = '',
super(appStore, walletInfoSource, walletCreationService, super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true);
type: type, isRecovery: true);
@observable @observable
int height; int height;
@ -51,8 +50,16 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
bool get hasRestorationHeight => type == WalletType.monero; bool get hasRestorationHeight => type == WalletType.monero;
@override @override
WalletCredentials getCredentialsFromRestoredWallet(dynamic options, RestoredWallet restoreWallet) { WalletCredentials getCredentialsFromRestoredWallet(
dynamic options, RestoredWallet restoreWallet) {
final password = generateWalletPassword(); 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) { switch (restoreWallet.restoreMode) {
case WalletRestoreMode.keys: case WalletRestoreMode.keys:
@ -86,23 +93,37 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
switch (restoreWallet.type) { switch (restoreWallet.type) {
case WalletType.monero: case WalletType.monero:
return monero!.createMoneroRestoreWalletFromSeedCredentials( return monero!.createMoneroRestoreWalletFromSeedCredentials(
name: name, name: name,
height: restoreWallet.height ?? 0, height: restoreWallet.height ?? 0,
mnemonic: restoreWallet.mnemonicSeed ?? '', mnemonic: restoreWallet.mnemonicSeed ?? '',
password: password); password: password,
);
case WalletType.bitcoin: case WalletType.bitcoin:
case WalletType.litecoin: case WalletType.litecoin:
return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials( 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: case WalletType.bitcoinCash:
return bitcoinCash!.createBitcoinCashRestoreWalletFromSeedCredentials( return bitcoinCash!.createBitcoinCashRestoreWalletFromSeedCredentials(
name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); name: name,
mnemonic: restoreWallet.mnemonicSeed ?? '',
password: password,
);
case WalletType.ethereum: case WalletType.ethereum:
return ethereum!.createEthereumRestoreWalletFromSeedCredentials( return ethereum!.createEthereumRestoreWalletFromSeedCredentials(
name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password);
case WalletType.nano: case WalletType.nano:
return nano!.createNanoRestoreWalletFromSeedCredentials( return nano!.createNanoRestoreWalletFromSeedCredentials(
name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); name: name,
mnemonic: restoreWallet.mnemonicSeed ?? '',
password: password,
derivationType: derivationInfo!.derivationType!,
);
case WalletType.polygon: case WalletType.polygon:
return polygon!.createPolygonRestoreWalletFromSeedCredentials( return polygon!.createPolygonRestoreWalletFromSeedCredentials(
name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password);
@ -118,7 +139,8 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
} }
@override @override
Future<WalletBase> processFromRestoredWallet(WalletCredentials credentials, RestoredWallet restoreWallet) async { Future<WalletBase> processFromRestoredWallet(
WalletCredentials credentials, RestoredWallet restoreWallet) async {
try { try {
switch (restoreWallet.restoreMode) { switch (restoreWallet.restoreMode) {
case WalletRestoreMode.keys: case WalletRestoreMode.keys:

View file

@ -423,7 +423,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
Object _credentials() { Object _credentials() {
final priority = _settingsStore.priority[wallet.type]; 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}'); throw Exception('Priority is null for wallet type: ${wallet.type}');
} }

View file

@ -71,9 +71,9 @@ abstract class WalletCreationVMBase with Store {
dirPath: dirPath, dirPath: dirPath,
address: '', address: '',
showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven, showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven,
derivationPath: credentials.derivationPath, derivationInfo: credentials.derivationInfo ?? getDefaultDerivation(),
derivationType: credentials.derivationType,
); );
credentials.walletInfo = walletInfo; credentials.walletInfo = walletInfo;
final wallet = restoreWallet != null final wallet = restoreWallet != null
? await processFromRestoredWallet(credentials, restoreWallet) ? 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(); WalletCredentials getCredentials(dynamic options) => throw UnimplementedError();
Future<WalletBase> process(WalletCredentials credentials) => throw UnimplementedError(); Future<WalletBase> process(WalletCredentials credentials) => throw UnimplementedError();

View file

@ -2,6 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/ethereum/ethereum.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/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/solana/solana.dart';
@ -66,6 +67,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
final bool hasBlockchainHeightLanguageSelector; final bool hasBlockchainHeightLanguageSelector;
final bool hasRestoreFromPrivateKey; final bool hasRestoreFromPrivateKey;
bool get hasPassphrase => [WalletType.bitcoin, WalletType.litecoin].contains(type);
@observable @observable
WalletRestoreMode mode; WalletRestoreMode mode;
@ -75,10 +78,10 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
@override @override
WalletCredentials getCredentials(dynamic options) { WalletCredentials getCredentials(dynamic options) {
final password = generateWalletPassword(); final password = generateWalletPassword();
String? passphrase = options['passphrase'] as String?;
final height = options['height'] as int? ?? 0; final height = options['height'] as int? ?? 0;
name = options['name'] as String; name = options['name'] as String;
DerivationType? derivationType = options["derivationType"] as DerivationType?; DerivationInfo? derivationInfo = options["derivationInfo"] as DerivationInfo?;
String? derivationPath = options["derivationPath"] as String?;
if (mode == WalletRestoreMode.seed) { if (mode == WalletRestoreMode.seed) {
final seed = options['seed'] as String; final seed = options['seed'] as String;
@ -87,14 +90,15 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
return monero!.createMoneroRestoreWalletFromSeedCredentials( return monero!.createMoneroRestoreWalletFromSeedCredentials(
name: name, height: height, mnemonic: seed, password: password); name: name, height: height, mnemonic: seed, password: password);
case WalletType.bitcoin: case WalletType.bitcoin:
case WalletType.litecoin:
return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials( return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
name: name, name: name,
mnemonic: seed, mnemonic: seed,
password: password, 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: case WalletType.haven:
return haven!.createHavenRestoreWalletFromSeedCredentials( return haven!.createHavenRestoreWalletFromSeedCredentials(
name: name, height: height, mnemonic: seed, password: password); 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); name: name, mnemonic: seed, password: password);
case WalletType.nano: case WalletType.nano:
return nano!.createNanoRestoreWalletFromSeedCredentials( return nano!.createNanoRestoreWalletFromSeedCredentials(
name: name, mnemonic: seed, password: password, derivationType: derivationType); name: name,
mnemonic: seed,
password: password,
derivationType: derivationInfo!.derivationType!,
);
case WalletType.polygon: case WalletType.polygon:
return polygon!.createPolygonRestoreWalletFromSeedCredentials( return polygon!.createPolygonRestoreWalletFromSeedCredentials(
name: name, name: name,
@ -185,23 +193,34 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
throw Exception('Unexpected type: ${type.toString()}'); throw Exception('Unexpected type: ${type.toString()}');
} }
Future<List<DerivationType>> getDerivationTypes(dynamic options) async { Future<List<DerivationInfo>> getDerivationInfo(dynamic credentials) async {
final seedKey = options['private_key'] as String?; var list = <DerivationInfo>[];
final mnemonic = options['seed'] as String?; var walletType = credentials["walletType"] as WalletType;
WalletType walletType = options['walletType'] as WalletType;
var appStore = getIt.get<AppStore>(); var appStore = getIt.get<AppStore>();
var node = appStore.settingsStore.getCurrentNode(walletType); 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: case WalletType.nano:
return nanoUtil! String? mnemonic = credentials['seed'] as String?;
.compareDerivationMethods(mnemonic: mnemonic, privateKey: seedKey, node: node); String? seedKey = credentials['private_key'] as String?;
return nanoUtil!.getDerivationsFromMnemonic(
mnemonic: mnemonic,
seedKey: seedKey,
node: node,
);
default: default:
break; break;
} }
return list;
// throw Exception('Unexpected type: ${type.toString()}');
return [DerivationType.def];
} }
@override @override
@ -209,7 +228,6 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
if (mode == WalletRestoreMode.keys) { if (mode == WalletRestoreMode.keys) {
return walletCreationService.restoreFromKeys(credentials, isTestnet: useTestnet); return walletCreationService.restoreFromKeys(credentials, isTestnet: useTestnet);
} }
return walletCreationService.restoreFromSeed(credentials, isTestnet: useTestnet); return walletCreationService.restoreFromSeed(credentials, isTestnet: useTestnet);
} }
} }

View file

@ -411,6 +411,7 @@
"outputs": "المخرجات", "outputs": "المخرجات",
"overwrite_amount": "تغير المبلغ", "overwrite_amount": "تغير المبلغ",
"pairingInvalidEvent": "ﺢﻟﺎﺻ ﺮﻴﻏ ﺙﺪﺣ ﻥﺍﺮﻗﺇ", "pairingInvalidEvent": "ﺢﻟﺎﺻ ﺮﻴﻏ ﺙﺪﺣ ﻥﺍﺮﻗﺇ",
"passphrase": "عبارة الممر (اختياري)",
"password": "كلمة المرور", "password": "كلمة المرور",
"paste": "لصق", "paste": "لصق",
"pause_wallet_creation": ".ﺎﻴًﻟﺎﺣ ﺎﺘًﻗﺆﻣ ﺔﻔﻗﻮﺘﻣ Haven Wallet ءﺎﺸﻧﺇ ﻰﻠﻋ ﺓﺭﺪﻘﻟﺍ", "pause_wallet_creation": ".ﺎﻴًﻟﺎﺣ ﺎﺘًﻗﺆﻣ ﺔﻔﻗﻮﺘﻣ Haven Wallet ءﺎﺸﻧﺇ ﻰﻠﻋ ﺓﺭﺪﻘﻟﺍ",

View file

@ -411,6 +411,7 @@
"outputs": "Изходи", "outputs": "Изходи",
"overwrite_amount": "Промени сума", "overwrite_amount": "Промени сума",
"pairingInvalidEvent": "Невалидно събитие при сдвояване", "pairingInvalidEvent": "Невалидно събитие при сдвояване",
"passphrase": "Passphrase (по избор)",
"password": "Парола", "password": "Парола",
"paste": "Поставяне", "paste": "Поставяне",
"pause_wallet_creation": "Възможността за създаване на Haven Wallet в момента е на пауза.", "pause_wallet_creation": "Възможността за създаване на Haven Wallet в момента е на пауза.",

View file

@ -411,6 +411,7 @@
"outputs": "Výstupy", "outputs": "Výstupy",
"overwrite_amount": "Přepsat částku", "overwrite_amount": "Přepsat částku",
"pairingInvalidEvent": "Neplatná událost párování", "pairingInvalidEvent": "Neplatná událost párování",
"passphrase": "Passphrase (volitelné)",
"password": "Heslo", "password": "Heslo",
"paste": "Vložit", "paste": "Vložit",
"pause_wallet_creation": "Možnost vytvářet Haven Wallet je momentálně pozastavena.", "pause_wallet_creation": "Možnost vytvářet Haven Wallet je momentálně pozastavena.",

View file

@ -411,6 +411,7 @@
"outputs": "Ausgänge", "outputs": "Ausgänge",
"overwrite_amount": "Overwrite amount", "overwrite_amount": "Overwrite amount",
"pairingInvalidEvent": "Paarung ungültiges Ereignis", "pairingInvalidEvent": "Paarung ungültiges Ereignis",
"passphrase": "Passphrase (optional)",
"password": "Passwort", "password": "Passwort",
"paste": "Einfügen", "paste": "Einfügen",
"pause_wallet_creation": "Die Möglichkeit, Haven Wallet zu erstellen, ist derzeit pausiert.", "pause_wallet_creation": "Die Möglichkeit, Haven Wallet zu erstellen, ist derzeit pausiert.",
@ -425,8 +426,8 @@
"placeholder_transactions": "Ihre Transaktionen werden hier angezeigt", "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_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_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": "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": "Bitte auswählen:",
"please_select_backup_file": "Bitte wählen Sie die Sicherungsdatei und geben Sie das Sicherungskennwort ein.", "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", "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_get": "Konvertieren zu",
"you_will_send": "Konvertieren von", "you_will_send": "Konvertieren von",
"yy": "YY" "yy": "YY"
} }

View file

@ -411,6 +411,7 @@
"outputs": "Outputs", "outputs": "Outputs",
"overwrite_amount": "Overwrite amount", "overwrite_amount": "Overwrite amount",
"pairingInvalidEvent": "Pairing Invalid Event", "pairingInvalidEvent": "Pairing Invalid Event",
"passphrase": "Passphrase (Optional)",
"password": "Password", "password": "Password",
"paste": "Paste", "paste": "Paste",
"pause_wallet_creation": "Ability to create Haven Wallet is currently paused.", "pause_wallet_creation": "Ability to create Haven Wallet is currently paused.",

View file

@ -411,6 +411,7 @@
"outputs": "Salidas", "outputs": "Salidas",
"overwrite_amount": "Overwrite amount", "overwrite_amount": "Overwrite amount",
"pairingInvalidEvent": "Evento de emparejamiento no válido", "pairingInvalidEvent": "Evento de emparejamiento no válido",
"passphrase": "Passfrase (opcional)",
"password": "Contraseña", "password": "Contraseña",
"paste": "Pegar", "paste": "Pegar",
"pause_wallet_creation": "La capacidad para crear Haven Wallet está actualmente pausada.", "pause_wallet_creation": "La capacidad para crear Haven Wallet está actualmente pausada.",

View file

@ -411,6 +411,7 @@
"outputs": "Les sorties", "outputs": "Les sorties",
"overwrite_amount": "Remplacer le montant", "overwrite_amount": "Remplacer le montant",
"pairingInvalidEvent": "Événement de couplage non valide", "pairingInvalidEvent": "Événement de couplage non valide",
"passphrase": "Phrase de passe (facultative)",
"password": "Mot de passe", "password": "Mot de passe",
"paste": "Coller", "paste": "Coller",
"pause_wallet_creation": "La possibilité de créer Haven Wallet est actuellement suspendue.", "pause_wallet_creation": "La possibilité de créer Haven Wallet est actuellement suspendue.",

View file

@ -413,6 +413,7 @@
"outputs": "Abubuwan fashewa", "outputs": "Abubuwan fashewa",
"overwrite_amount": "Rubuta adadin", "overwrite_amount": "Rubuta adadin",
"pairingInvalidEvent": "Haɗa Lamarin mara inganci", "pairingInvalidEvent": "Haɗa Lamarin mara inganci",
"passphrase": "Passphrase (Zabi)",
"password": "Kalmar wucewa", "password": "Kalmar wucewa",
"paste": "Manna", "paste": "Manna",
"pause_wallet_creation": "A halin yanzu an dakatar da ikon ƙirƙirar Haven Wallet.", "pause_wallet_creation": "A halin yanzu an dakatar da ikon ƙirƙirar Haven Wallet.",

View file

@ -411,6 +411,7 @@
"outputs": "आउटपुट", "outputs": "आउटपुट",
"overwrite_amount": "Overwrite amount", "overwrite_amount": "Overwrite amount",
"pairingInvalidEvent": "अमान्य ईवेंट युग्मित करना", "pairingInvalidEvent": "अमान्य ईवेंट युग्मित करना",
"passphrase": "पासफ्रेज़ (वैकल्पिक)",
"password": "पारण शब्द", "password": "पारण शब्द",
"paste": "पेस्ट करें", "paste": "पेस्ट करें",
"pause_wallet_creation": "हेवन वॉलेट बनाने की क्षमता फिलहाल रुकी हुई है।", "pause_wallet_creation": "हेवन वॉलेट बनाने की क्षमता फिलहाल रुकी हुई है।",

View file

@ -411,6 +411,7 @@
"outputs": "Izlazi", "outputs": "Izlazi",
"overwrite_amount": "Overwrite amount", "overwrite_amount": "Overwrite amount",
"pairingInvalidEvent": "Nevažeći događaj uparivanja", "pairingInvalidEvent": "Nevažeći događaj uparivanja",
"passphrase": "Prolaznica (neobavezno)",
"password": "Lozinka", "password": "Lozinka",
"paste": "Zalijepi", "paste": "Zalijepi",
"pause_wallet_creation": "Mogućnost stvaranja novčanika Haven trenutno je pauzirana.", "pause_wallet_creation": "Mogućnost stvaranja novčanika Haven trenutno je pauzirana.",

View file

@ -413,6 +413,7 @@
"outputs": "Output", "outputs": "Output",
"overwrite_amount": "Timpa jumlah", "overwrite_amount": "Timpa jumlah",
"pairingInvalidEvent": "Menyandingkan Acara Tidak Valid", "pairingInvalidEvent": "Menyandingkan Acara Tidak Valid",
"passphrase": "Frasa sandi (opsional)",
"password": "Kata Sandi", "password": "Kata Sandi",
"paste": "Tempel", "paste": "Tempel",
"pause_wallet_creation": "Kemampuan untuk membuat Haven Wallet saat ini dijeda.", "pause_wallet_creation": "Kemampuan untuk membuat Haven Wallet saat ini dijeda.",

View file

@ -413,6 +413,7 @@
"outputs": "Output", "outputs": "Output",
"overwrite_amount": "Sovrascrivi quantità", "overwrite_amount": "Sovrascrivi quantità",
"pairingInvalidEvent": "Associazione evento non valido", "pairingInvalidEvent": "Associazione evento non valido",
"passphrase": "Passphrase (opzionale)",
"password": "Password", "password": "Password",
"paste": "Incolla", "paste": "Incolla",
"pause_wallet_creation": "La possibilità di creare Haven Wallet è attualmente sospesa.", "pause_wallet_creation": "La possibilità di creare Haven Wallet è attualmente sospesa.",

View file

@ -412,6 +412,7 @@
"outputs": "出力", "outputs": "出力",
"overwrite_amount": "Overwrite amount", "overwrite_amount": "Overwrite amount",
"pairingInvalidEvent": "ペアリング無効イベント", "pairingInvalidEvent": "ペアリング無効イベント",
"passphrase": "パスフレーズ(オプション)",
"password": "パスワード", "password": "パスワード",
"paste": "ペースト", "paste": "ペースト",
"pause_wallet_creation": "Haven Wallet を作成する機能は現在一時停止されています。", "pause_wallet_creation": "Haven Wallet を作成する機能は現在一時停止されています。",

View file

@ -411,6 +411,7 @@
"outputs": "출력", "outputs": "출력",
"overwrite_amount": "Overwrite amount", "overwrite_amount": "Overwrite amount",
"pairingInvalidEvent": "잘못된 이벤트 페어링", "pairingInvalidEvent": "잘못된 이벤트 페어링",
"passphrase": "암호화 (선택 사항)",
"password": "암호", "password": "암호",
"paste": "풀", "paste": "풀",
"pause_wallet_creation": "Haven Wallet 생성 기능이 현재 일시 중지되었습니다.", "pause_wallet_creation": "Haven Wallet 생성 기능이 현재 일시 중지되었습니다.",
@ -425,8 +426,8 @@
"placeholder_transactions": "거래가 여기에 표시됩니다", "placeholder_transactions": "거래가 여기에 표시됩니다",
"please_fill_totp": "다른 기기에 있는 8자리 코드를 입력하세요.", "please_fill_totp": "다른 기기에 있는 8자리 코드를 입력하세요.",
"please_make_selection": "아래에서 선택하십시오 지갑 만들기 또는 복구.", "please_make_selection": "아래에서 선택하십시오 지갑 만들기 또는 복구.",
"please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
"Please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.", "Please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
"please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
"please_select": "선택 해주세요:", "please_select": "선택 해주세요:",
"please_select_backup_file": "백업 파일을 선택하고 백업 암호를 입력하십시오.", "please_select_backup_file": "백업 파일을 선택하고 백업 암호를 입력하십시오.",
"please_try_to_connect_to_another_node": "다른 노드에 연결을 시도하십시오", "please_try_to_connect_to_another_node": "다른 노드에 연결을 시도하십시오",

View file

@ -411,6 +411,7 @@
"outputs": "ထုတ်လုပ်မှု", "outputs": "ထုတ်လုပ်မှု",
"overwrite_amount": "ပမာဏကို ထပ်ရေးပါ။", "overwrite_amount": "ပမာဏကို ထပ်ရေးပါ။",
"pairingInvalidEvent": "မမှန်ကန်သောဖြစ်ရပ်ကို တွဲချိတ်ခြင်း။", "pairingInvalidEvent": "မမှန်ကန်သောဖြစ်ရပ်ကို တွဲချိတ်ခြင်း။",
"passphrase": "passphrase (optional)",
"password": "စကားဝှက်", "password": "စကားဝှက်",
"paste": "ငါးပိ", "paste": "ငါးပိ",
"pause_wallet_creation": "Haven Wallet ဖန်တီးနိုင်မှုကို လောလောဆယ် ခေတ္တရပ်ထားသည်။", "pause_wallet_creation": "Haven Wallet ဖန်တီးနိုင်မှုကို လောလောဆယ် ခေတ္တရပ်ထားသည်။",

View file

@ -411,6 +411,7 @@
"outputs": "Uitgangen", "outputs": "Uitgangen",
"overwrite_amount": "Overwrite amount", "overwrite_amount": "Overwrite amount",
"pairingInvalidEvent": "Koppelen Ongeldige gebeurtenis", "pairingInvalidEvent": "Koppelen Ongeldige gebeurtenis",
"passphrase": "PassaspHRASE (optioneel)",
"password": "Wachtwoord", "password": "Wachtwoord",
"paste": "Plakken", "paste": "Plakken",
"pause_wallet_creation": "De mogelijkheid om Haven Wallet te maken is momenteel onderbroken.", "pause_wallet_creation": "De mogelijkheid om Haven Wallet te maken is momenteel onderbroken.",

View file

@ -411,6 +411,7 @@
"outputs": "Wyjścia", "outputs": "Wyjścia",
"overwrite_amount": "Nadpisz ilość", "overwrite_amount": "Nadpisz ilość",
"pairingInvalidEvent": "Nieprawidłowe zdarzenie parowania", "pairingInvalidEvent": "Nieprawidłowe zdarzenie parowania",
"passphrase": "PassPhraza (opcjonalnie)",
"password": "Hasło", "password": "Hasło",
"paste": "Wklej", "paste": "Wklej",
"pause_wallet_creation": "Możliwość utworzenia Portfela Haven jest obecnie wstrzymana.", "pause_wallet_creation": "Możliwość utworzenia Portfela Haven jest obecnie wstrzymana.",

View file

@ -413,6 +413,7 @@
"outputs": "Saídas", "outputs": "Saídas",
"overwrite_amount": "Overwrite amount", "overwrite_amount": "Overwrite amount",
"pairingInvalidEvent": "Emparelhamento de evento inválido", "pairingInvalidEvent": "Emparelhamento de evento inválido",
"passphrase": "Senha (opcional)",
"password": "Senha", "password": "Senha",
"paste": "Colar", "paste": "Colar",
"pause_wallet_creation": "A capacidade de criar a Haven Wallet está atualmente pausada.", "pause_wallet_creation": "A capacidade de criar a Haven Wallet está atualmente pausada.",

View file

@ -412,6 +412,7 @@
"outputs": "Выходы", "outputs": "Выходы",
"overwrite_amount": "Overwrite amount", "overwrite_amount": "Overwrite amount",
"pairingInvalidEvent": "Недействительное событие сопряжения", "pairingInvalidEvent": "Недействительное событие сопряжения",
"passphrase": "Passfrase (необязательно)",
"password": "Пароль", "password": "Пароль",
"paste": "Вставить", "paste": "Вставить",
"pause_wallet_creation": "Возможность создания Haven Wallet в настоящее время приостановлена.", "pause_wallet_creation": "Возможность создания Haven Wallet в настоящее время приостановлена.",

View file

@ -411,6 +411,7 @@
"outputs": "เอาต์พุต", "outputs": "เอาต์พุต",
"overwrite_amount": "เขียนทับจำนวน", "overwrite_amount": "เขียนทับจำนวน",
"pairingInvalidEvent": "การจับคู่เหตุการณ์ที่ไม่ถูกต้อง", "pairingInvalidEvent": "การจับคู่เหตุการณ์ที่ไม่ถูกต้อง",
"passphrase": "ข้อความรหัสผ่าน (ไม่บังคับ)",
"password": "รหัสผ่าน", "password": "รหัสผ่าน",
"paste": "วาง", "paste": "วาง",
"pause_wallet_creation": "ขณะนี้ความสามารถในการสร้าง Haven Wallet ถูกหยุดชั่วคราว", "pause_wallet_creation": "ขณะนี้ความสามารถในการสร้าง Haven Wallet ถูกหยุดชั่วคราว",

View file

@ -411,6 +411,7 @@
"outputs": "Mga output", "outputs": "Mga output",
"overwrite_amount": "Overwrite na halaga", "overwrite_amount": "Overwrite na halaga",
"pairingInvalidEvent": "Pagpares ng Di-wastong Kaganapan", "pairingInvalidEvent": "Pagpares ng Di-wastong Kaganapan",
"passphrase": "Passphrase (opsyonal)",
"password": "Password", "password": "Password",
"paste": "I -paste", "paste": "I -paste",
"pause_wallet_creation": "Kasalukuyang naka-pause ang kakayahang gumawa ng Haven Wallet.", "pause_wallet_creation": "Kasalukuyang naka-pause ang kakayahang gumawa ng Haven Wallet.",

View file

@ -411,6 +411,7 @@
"outputs": "çıktılar", "outputs": "çıktılar",
"overwrite_amount": "Miktarın üzerine yaz", "overwrite_amount": "Miktarın üzerine yaz",
"pairingInvalidEvent": "Geçersiz Etkinliği Eşleştirme", "pairingInvalidEvent": "Geçersiz Etkinliği Eşleştirme",
"passphrase": "Passfrase (isteğe bağlı)",
"password": "Parola", "password": "Parola",
"paste": "Yapıştır", "paste": "Yapıştır",
"pause_wallet_creation": "Haven Cüzdanı oluşturma yeteneği şu anda duraklatıldı.", "pause_wallet_creation": "Haven Cüzdanı oluşturma yeteneği şu anda duraklatıldı.",

View file

@ -411,6 +411,7 @@
"outputs": "Виходи", "outputs": "Виходи",
"overwrite_amount": "Overwrite amount", "overwrite_amount": "Overwrite amount",
"pairingInvalidEvent": "Недійсна подія сполучення", "pairingInvalidEvent": "Недійсна подія сполучення",
"passphrase": "Пасофрази (необов’язково)",
"password": "Пароль", "password": "Пароль",
"paste": "Вставити", "paste": "Вставити",
"pause_wallet_creation": "Можливість створення гаманця Haven зараз призупинено.", "pause_wallet_creation": "Можливість створення гаманця Haven зараз призупинено.",

View file

@ -413,6 +413,7 @@
"outputs": "نتائج", "outputs": "نتائج",
"overwrite_amount": "رقم کو اوور رائٹ کریں۔", "overwrite_amount": "رقم کو اوور رائٹ کریں۔",
"pairingInvalidEvent": "ﭧﻧﻮﯾﺍ ﻂﻠﻏ ﺎﻧﺎﻨﺑ ﺍﮌﻮﺟ", "pairingInvalidEvent": "ﭧﻧﻮﯾﺍ ﻂﻠﻏ ﺎﻧﺎﻨﺑ ﺍﮌﻮﺟ",
"passphrase": "پاسفریز (اختیاری)",
"password": "پاس ورڈ", "password": "پاس ورڈ",
"paste": "چسپاں کریں۔", "paste": "چسپاں کریں۔",
"pause_wallet_creation": "Haven Wallet ۔ﮯﮨ ﻑﻮﻗﻮﻣ ﻝﺎﺤﻟﺍ ﯽﻓ ﺖﯿﻠﮨﺍ ﯽﮐ ﮯﻧﺎﻨﺑ", "pause_wallet_creation": "Haven Wallet ۔ﮯﮨ ﻑﻮﻗﻮﻣ ﻝﺎﺤﻟﺍ ﯽﻓ ﺖﯿﻠﮨﺍ ﯽﮐ ﮯﻧﺎﻨﺑ",

View file

@ -412,6 +412,7 @@
"outputs": "Awọn iṣan", "outputs": "Awọn iṣan",
"overwrite_amount": "Pààrọ̀ iye owó", "overwrite_amount": "Pààrọ̀ iye owó",
"pairingInvalidEvent": "Pipọpọ Iṣẹlẹ Ti ko tọ", "pairingInvalidEvent": "Pipọpọ Iṣẹlẹ Ti ko tọ",
"passphrase": "Ọrọ kukuru (iyan)",
"password": "Ọ̀rọ̀ aṣínà", "password": "Ọ̀rọ̀ aṣínà",
"paste": "Fikún ẹ̀dà yín", "paste": "Fikún ẹ̀dà yín",
"pause_wallet_creation": "Agbara lati ṣẹda Haven Wallet ti wa ni idaduro lọwọlọwọ.", "pause_wallet_creation": "Agbara lati ṣẹda Haven Wallet ti wa ni idaduro lọwọlọwọ.",

View file

@ -411,6 +411,7 @@
"outputs": "输出", "outputs": "输出",
"overwrite_amount": "Overwrite amount", "overwrite_amount": "Overwrite amount",
"pairingInvalidEvent": "配对无效事件", "pairingInvalidEvent": "配对无效事件",
"passphrase": "密码(可选)",
"password": "密码", "password": "密码",
"paste": "粘贴", "paste": "粘贴",
"pause_wallet_creation": "创建 Haven 钱包的功能当前已暂停。", "pause_wallet_creation": "创建 Haven 钱包的功能当前已暂停。",

View file

@ -61,6 +61,8 @@ Future<void> main(List<String> args) async {
Future<void> generateBitcoin(bool hasImplementation) async { Future<void> generateBitcoin(bool hasImplementation) async {
final outputFile = File(bitcoinOutputPath); final outputFile = File(bitcoinOutputPath);
const bitcoinCommonHeaders = """ const bitcoinCommonHeaders = """
import 'dart:typed_data';
import 'package:cw_core/node.dart';
import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/receive_page_option.dart'; import 'package:cw_core/receive_page_option.dart';
import 'package:cw_core/unspent_transaction_output.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:cw_core/wallet_type.dart';
import 'package:cake_wallet/view_model/send/output.dart'; import 'package:cake_wallet/view_model/send/output.dart';
import 'package:hive/hive.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 = """ 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/pending_bitcoin_transaction.dart';
import 'package:cw_bitcoin/bitcoin_receive_page_option.dart'; import 'package:cw_bitcoin/bitcoin_receive_page_option.dart';
import 'package:cw_bitcoin/electrum_wallet.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_address_record.dart';
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart'; import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
import 'package:cw_bitcoin/litecoin_wallet_service.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:cw_bitcoin/pending_bitcoin_transaction.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
"""; """;
@ -112,7 +124,14 @@ import 'package:mobx/mobx.dart';
abstract class Bitcoin { abstract class Bitcoin {
TransactionPriority getMediumTransactionPriority(); 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 createBitcoinRestoreWalletFromWIFCredentials({required String name, required String password, required String wif, WalletInfo? walletInfo});
WalletCredentials createBitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo}); WalletCredentials createBitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo});
List<String> getWordList(); List<String> getWordList();
@ -147,7 +166,10 @@ abstract class Bitcoin {
TransactionPriority getLitecoinTransactionPriorityMedium(); TransactionPriority getLitecoinTransactionPriorityMedium();
TransactionPriority getBitcoinTransactionPrioritySlow(); TransactionPriority getBitcoinTransactionPrioritySlow();
TransactionPriority getLitecoinTransactionPrioritySlow(); TransactionPriority getLitecoinTransactionPrioritySlow();
Future<List<DerivationType>> compareDerivationMethods(
{required String mnemonic, required Node node});
Future<List<DerivationInfo>> getDerivationsFromMnemonic(
{required String mnemonic, required Node node, String? passphrase});
Future<void> setAddressType(Object wallet, dynamic option); Future<void> setAddressType(Object wallet, dynamic option);
ReceivePageOption getSelectedAddressType(Object wallet); ReceivePageOption getSelectedAddressType(Object wallet);
List<ReceivePageOption> getBitcoinReceivePageOptions(); List<ReceivePageOption> getBitcoinReceivePageOptions();
@ -838,14 +860,14 @@ abstract class Nano {
required String name, required String name,
required String password, required String password,
required String mnemonic, required String mnemonic,
DerivationType? derivationType, required DerivationType derivationType,
}); });
WalletCredentials createNanoRestoreWalletFromKeysCredentials({ WalletCredentials createNanoRestoreWalletFromKeysCredentials({
required String name, required String name,
required String password, required String password,
required String seedKey, required String seedKey,
DerivationType? derivationType, required DerivationType derivationType,
}); });
List<String> getNanoWordList(String language); List<String> getNanoWordList(String language);
@ -892,6 +914,11 @@ abstract class NanoUtil {
String? privateKey, String? privateKey,
required Node node, required Node node,
}); });
Future<List<DerivationInfo>> getDerivationsFromMnemonic({
String? mnemonic,
String? seedKey,
required Node node,
});
} }
"""; """;