mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 12:29:51 +00:00
feat: bitcoin view-only
This commit is contained in:
parent
1a5601f755
commit
a5b6ae6475
12 changed files with 145 additions and 62 deletions
|
@ -54,6 +54,17 @@ class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
|
|||
final String wif;
|
||||
}
|
||||
|
||||
class BitcoinWalletFromKeysCredentials extends WalletCredentials {
|
||||
BitcoinWalletFromKeysCredentials({
|
||||
required String name,
|
||||
required String password,
|
||||
required this.xpub,
|
||||
WalletInfo? walletInfo,
|
||||
}) : super(name: name, password: password, walletInfo: walletInfo);
|
||||
|
||||
final String xpub;
|
||||
}
|
||||
|
||||
class BitcoinRestoreWalletFromHardware extends WalletCredentials {
|
||||
BitcoinRestoreWalletFromHardware({
|
||||
required String name,
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
class BitcoinWalletKeys {
|
||||
const BitcoinWalletKeys({required this.wif, required this.privateKey, required this.publicKey});
|
||||
const BitcoinWalletKeys({required this.wif, required this.privateKey, required this.publicKey, required this.xpub});
|
||||
|
||||
final String wif;
|
||||
final String privateKey;
|
||||
final String publicKey;
|
||||
final String xpub;
|
||||
|
||||
Map<String, String> toJson() => {
|
||||
'wif': wif,
|
||||
'privateKey': privateKey,
|
||||
'publicKey': publicKey,
|
||||
'xpub': xpub
|
||||
};
|
||||
}
|
|
@ -20,7 +20,7 @@ import 'package:bip39/bip39.dart' as bip39;
|
|||
class BitcoinWalletService extends WalletService<
|
||||
BitcoinNewWalletCredentials,
|
||||
BitcoinRestoreWalletFromSeedCredentials,
|
||||
BitcoinRestoreWalletFromWIFCredentials,
|
||||
BitcoinWalletFromKeysCredentials,
|
||||
BitcoinRestoreWalletFromHardware> {
|
||||
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource,
|
||||
this.payjoinSessionSource, this.alwaysScan, this.isDirect);
|
||||
|
@ -169,9 +169,24 @@ class BitcoinWalletService extends WalletService<
|
|||
}
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials,
|
||||
{bool? isTestnet}) async =>
|
||||
throw UnimplementedError();
|
||||
Future<BitcoinWallet> restoreFromKeys(BitcoinWalletFromKeysCredentials credentials,
|
||||
{bool? isTestnet}) async {
|
||||
final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
|
||||
credentials.walletInfo?.network = network.value;
|
||||
|
||||
final wallet = await BitcoinWallet(
|
||||
password: credentials.password!,
|
||||
xpub: credentials.xpub,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
networkParam: network,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
|
||||
await wallet.save();
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials,
|
||||
|
|
|
@ -280,11 +280,26 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
@override
|
||||
BitcoinWalletKeys get keys => BitcoinWalletKeys(
|
||||
wif: WifEncoder.encode(hd.privateKey.raw, netVer: network.wifNetVer),
|
||||
privateKey: hd.privateKey.toHex(),
|
||||
publicKey: hd.publicKey.toHex(),
|
||||
);
|
||||
BitcoinWalletKeys get keys {
|
||||
String? wif;
|
||||
String? privateKey;
|
||||
String? publicKey;
|
||||
try {
|
||||
wif = WifEncoder.encode(hd.privateKey.raw, netVer: network.wifNetVer);
|
||||
} catch (_) {}
|
||||
try {
|
||||
privateKey = hd.privateKey.toHex();
|
||||
} catch (_) {}
|
||||
try {
|
||||
publicKey = hd.publicKey.toHex();
|
||||
} catch (_) {}
|
||||
return BitcoinWalletKeys(
|
||||
wif: wif ?? '',
|
||||
privateKey: privateKey ?? '',
|
||||
publicKey: publicKey ?? '',
|
||||
xpub: xpub,
|
||||
);
|
||||
}
|
||||
|
||||
String _password;
|
||||
List<BitcoinUnspent> unspentCoins;
|
||||
|
|
|
@ -165,6 +165,6 @@ class PendingBitcoinTransaction with PendingTransaction {
|
|||
|
||||
@override
|
||||
Future<String?> commitUR() {
|
||||
throw UnimplementedError();
|
||||
return Future.value("test");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,14 @@ class CWBitcoin extends Bitcoin {
|
|||
passphrase: passphrase,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createBitcoinWalletFromKeys({
|
||||
required String name,
|
||||
required String password,
|
||||
required String xpub,
|
||||
}) =>
|
||||
BitcoinWalletFromKeysCredentials(name: name, password: password, xpub: xpub);
|
||||
|
||||
@override
|
||||
WalletCredentials createBitcoinRestoreWalletFromWIFCredentials(
|
||||
{required String name,
|
||||
|
@ -62,11 +70,7 @@ class CWBitcoin extends Bitcoin {
|
|||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
final keys = bitcoinWallet.keys;
|
||||
|
||||
return <String, String>{
|
||||
'wif': keys.wif,
|
||||
'privateKey': keys.privateKey,
|
||||
'publicKey': keys.publicKey
|
||||
};
|
||||
return bitcoinWallet.keys.toJson();
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -198,14 +198,14 @@ class WalletRestoreFromKeysFormState extends State<WalletRestoreFromKeysForm> {
|
|||
Widget _restoreFromKeysFormFields() {
|
||||
// Decred can only restore a view only wallet with an account pubkey. Other
|
||||
// fields are not used.
|
||||
if (widget.walletRestoreViewModel.type == WalletType.decred) {
|
||||
if (widget.walletRestoreViewModel.onlyViewKeyRestore) {
|
||||
return Column(
|
||||
children: [
|
||||
BaseTextFormField(
|
||||
controller: viewKeyController,
|
||||
hintText: S.of(context).view_key_public,
|
||||
maxLines: null,
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -253,13 +253,14 @@ class WalletRestoreFromKeysFormState extends State<WalletRestoreFromKeysForm> {
|
|||
maxLines: null,
|
||||
),
|
||||
),
|
||||
BlockchainHeightWidget(
|
||||
key: blockchainHeightKey,
|
||||
hasDatePicker: widget.walletRestoreViewModel.type != WalletType.haven,
|
||||
onHeightChange: (_) => null,
|
||||
onHeightOrDateEntered: widget.onHeightOrDateEntered,
|
||||
walletType: widget.walletRestoreViewModel.type,
|
||||
),
|
||||
if (widget.walletRestoreViewModel.hasBlockchainHeightSelector)
|
||||
BlockchainHeightWidget(
|
||||
key: blockchainHeightKey,
|
||||
hasDatePicker: widget.walletRestoreViewModel.type != WalletType.haven,
|
||||
onHeightChange: (_) => null,
|
||||
onHeightOrDateEntered: widget.onHeightOrDateEntered,
|
||||
walletType: widget.walletRestoreViewModel.type,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -199,7 +199,7 @@ class WalletRestorePage extends BasePage {
|
|||
credentials['seed'] =
|
||||
walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text;
|
||||
|
||||
if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) {
|
||||
if (walletRestoreViewModel.hasBlockchainHeightSelector) {
|
||||
credentials['height'] =
|
||||
walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey.currentState?.height ??
|
||||
-1;
|
||||
|
@ -219,7 +219,7 @@ class WalletRestorePage extends BasePage {
|
|||
credentials['name'] =
|
||||
walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
|
||||
credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
|
||||
if (walletRestoreViewModel.type != WalletType.decred) {
|
||||
if (!walletRestoreViewModel.onlyViewKeyRestore) {
|
||||
credentials['address'] =
|
||||
walletRestoreFromKeysFormKey.currentState!.addressController.text;
|
||||
credentials['spendKey'] =
|
||||
|
@ -519,7 +519,7 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
|
|||
}
|
||||
},
|
||||
onViewKeyEntered: (bool entered) {
|
||||
if (walletRestoreViewModel.type == WalletType.decred) {
|
||||
if (widget.walletRestoreViewModel.onlyViewKeyRestore) {
|
||||
walletRestoreViewModel.isButtonEnabled = entered;
|
||||
}
|
||||
},
|
||||
|
@ -536,7 +536,7 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
|
|||
key: widget.walletRestoreFromSeedFormKey,
|
||||
restoredWallet: walletRestoreViewModel.restoredWallet,
|
||||
seedSettingsViewModel: widget.seedSettingsViewModel,
|
||||
displayBlockHeightSelector: widget.walletRestoreViewModel.hasBlockchainHeightLanguageSelector,
|
||||
displayBlockHeightSelector: widget.walletRestoreViewModel.hasBlockchainHeightSelector,
|
||||
displayLanguageSelector: widget.walletRestoreViewModel.hasSeedLanguageSelector,
|
||||
type: widget.walletRestoreViewModel.type,
|
||||
blockHeightFocusNode: widget.blockHeightFocusNode,
|
||||
|
@ -563,7 +563,7 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
|
|||
}
|
||||
|
||||
void _validateOnChange({bool isPolyseed = false}) {
|
||||
if (!isPolyseed && walletRestoreViewModel.hasBlockchainHeightLanguageSelector) {
|
||||
if (!isPolyseed && walletRestoreViewModel.hasBlockchainHeightSelector) {
|
||||
final hasHeight = walletRestoreFromSeedFormKey
|
||||
.currentState?.blockchainHeightKey.currentState?.restoreHeightController.text.isNotEmpty;
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
|||
showLegacySeedTab = widget.walletKeysViewModel.legacySeedSplit.isNotEmpty;
|
||||
isLegacySeedOnly = widget.walletKeysViewModel.isLegacySeedOnly;
|
||||
|
||||
final totalTabs = 1 + (showKeyTab ? 1 : 0) + (showLegacySeedTab ? 1 : 0);
|
||||
final totalTabs = (_hasSeeds ? 1 : 0) + (showKeyTab ? 1 : 0) + (showLegacySeedTab ? 1 : 0);
|
||||
|
||||
_tabController = TabController(length: totalTabs, vsync: this);
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
|||
dividerColor: Colors.transparent,
|
||||
padding: EdgeInsets.zero,
|
||||
tabs: [
|
||||
Tab(text: S.of(context).widgets_seed, key: ValueKey('wallet_keys_page_seed')),
|
||||
if (_hasSeeds) Tab(text: S.of(context).widgets_seed, key: ValueKey('wallet_keys_page_seed')),
|
||||
if (showKeyTab) Tab(text: S.of(context).keys, key: ValueKey('wallet_keys_page_keys'),),
|
||||
if (showLegacySeedTab) Tab(text: S.of(context).legacy, key: ValueKey('wallet_keys_page_seed_legacy')),
|
||||
],
|
||||
|
@ -137,10 +137,11 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
|||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 22, right: 22),
|
||||
child: _buildSeedTab(context, false),
|
||||
),
|
||||
if (_hasSeeds)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 22, right: 22),
|
||||
child: _buildSeedTab(context, false),
|
||||
),
|
||||
if (showKeyTab)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 22, right: 22),
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
|
@ -162,18 +163,21 @@ abstract class WalletKeysViewModelBase with Store {
|
|||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
final keys = bitcoin!.getWalletKeys(_appStore.wallet!);
|
||||
|
||||
items.addAll([
|
||||
if ((keys['wif']??'').isNotEmpty)
|
||||
StandartListItem(title: "WIF", value: keys['wif']!),
|
||||
if ((keys['privateKey']??'').isNotEmpty)
|
||||
StandartListItem(title: S.current.private_key, value: keys['privateKey']!),
|
||||
if (keys['publicKey'] != null)
|
||||
StandartListItem(title: S.current.public_key, value: keys['publicKey']!),
|
||||
if (keys['xpub'] != null)
|
||||
StandartListItem(title: "xPub", value: keys['xpub']!),
|
||||
]);
|
||||
break;
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
// final keys = bitcoin!.getWalletKeys(_appStore.wallet!);
|
||||
//
|
||||
// items.addAll([
|
||||
// if (keys['wif'] != null)
|
||||
// StandartListItem(title: "WIF", value: keys['wif']!),
|
||||
// if (keys['privateKey'] != null)
|
||||
// StandartListItem(title: S.current.private_key, value: keys['privateKey']!),
|
||||
// if (keys['publicKey'] != null)
|
||||
// StandartListItem(title: S.current.public_key, value: keys['publicKey']!),
|
||||
// ]);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,17 +32,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
|||
WalletRestoreViewModelBase(AppStore appStore, WalletCreationService walletCreationService,
|
||||
Box<WalletInfo> walletInfoSource, SeedSettingsViewModel seedSettingsViewModel,
|
||||
{required WalletType type, this.restoredWallet})
|
||||
: hasSeedLanguageSelector =
|
||||
type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero,
|
||||
hasBlockchainHeightLanguageSelector =
|
||||
type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero,
|
||||
hasRestoreFromPrivateKey = type == WalletType.ethereum ||
|
||||
type == WalletType.polygon ||
|
||||
type == WalletType.nano ||
|
||||
type == WalletType.banano ||
|
||||
type == WalletType.solana ||
|
||||
type == WalletType.tron,
|
||||
isButtonEnabled = false,
|
||||
: isButtonEnabled = false,
|
||||
hasPassphrase = false,
|
||||
mode = restoredWallet?.restoreMode ?? WalletRestoreMode.seed,
|
||||
super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
|
||||
|
@ -60,9 +50,9 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
|||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.decred:
|
||||
case WalletType.bitcoin:
|
||||
availableModes = [WalletRestoreMode.seed, WalletRestoreMode.keys];
|
||||
break;
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.zano:
|
||||
|
@ -82,9 +72,34 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
|||
static const decredSeedMnemonicLength = 15;
|
||||
|
||||
late List<WalletRestoreMode> availableModes;
|
||||
final bool hasSeedLanguageSelector;
|
||||
final bool hasBlockchainHeightLanguageSelector;
|
||||
final bool hasRestoreFromPrivateKey;
|
||||
late final bool hasSeedLanguageSelector = [
|
||||
WalletType.monero,
|
||||
WalletType.haven,
|
||||
WalletType.wownero
|
||||
].contains(type);
|
||||
|
||||
late final bool hasBlockchainHeightSelector = [
|
||||
WalletType.monero,
|
||||
WalletType.haven,
|
||||
WalletType.wownero
|
||||
].contains(type);
|
||||
|
||||
late final bool hasRestoreFromPrivateKey = [
|
||||
WalletType.ethereum,
|
||||
WalletType.polygon,
|
||||
WalletType.nano,
|
||||
WalletType.banano,
|
||||
WalletType.solana,
|
||||
WalletType.tron
|
||||
].contains(type);
|
||||
|
||||
late final bool onlyViewKeyRestore = [
|
||||
WalletType.bitcoin,
|
||||
WalletType.litecoin,
|
||||
WalletType.bitcoinCash,
|
||||
WalletType.decred
|
||||
].contains(type);
|
||||
|
||||
final RestoredWallet? restoredWallet;
|
||||
|
||||
@observable
|
||||
|
@ -198,6 +213,13 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
|||
final address = options['address'] as String?;
|
||||
|
||||
switch (type) {
|
||||
case WalletType.bitcoin:
|
||||
return bitcoin!.createBitcoinWalletFromKeys(
|
||||
name: name,
|
||||
password: password,
|
||||
xpub: viewKey!,
|
||||
);
|
||||
|
||||
case WalletType.monero:
|
||||
return monero!.createMoneroRestoreWalletFromKeysCredentials(
|
||||
name: name,
|
||||
|
@ -276,8 +298,9 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
|||
case WalletType.litecoin:
|
||||
String? mnemonic = credentials['seed'] as String?;
|
||||
String? passphrase = credentials['passphrase'] as String?;
|
||||
if (mnemonic == null) break;
|
||||
return bitcoin!.getDerivationsFromMnemonic(
|
||||
mnemonic: mnemonic!,
|
||||
mnemonic: mnemonic,
|
||||
node: node,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
|
|
|
@ -163,6 +163,7 @@ abstract class Bitcoin {
|
|||
String? passphrase,
|
||||
});
|
||||
WalletCredentials createBitcoinRestoreWalletFromWIFCredentials({required String name, required String password, required String wif, WalletInfo? walletInfo});
|
||||
WalletCredentials createBitcoinWalletFromKeys({required String name, required String password, required String xpub});
|
||||
WalletCredentials createBitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password, String? passphrase, String? mnemonic});
|
||||
WalletCredentials createBitcoinHardwareWalletCredentials({required String name, required HardwareAccountData accountData, WalletInfo? walletInfo});
|
||||
List<String> getWordList();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue