mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-23 19:05:51 +00:00
feat: show xPub corresponding to selected multisig script type
using a hack to add BIP48 derivation paths to Bitcoin wallets as needed
This commit is contained in:
parent
b8105215e2
commit
c16a6ecb67
5 changed files with 143 additions and 104 deletions
|
@ -7,6 +7,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import '../../../../themes/stack_colors.dart';
|
import '../../../../themes/stack_colors.dart';
|
||||||
import '../../../../utilities/text_styles.dart';
|
import '../../../../utilities/text_styles.dart';
|
||||||
import '../../../../widgets/background.dart';
|
import '../../../../widgets/background.dart';
|
||||||
|
import '../../../providers/global/wallets_provider.dart';
|
||||||
|
import '../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart';
|
||||||
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import '../../../widgets/desktop/primary_button.dart';
|
import '../../../widgets/desktop/primary_button.dart';
|
||||||
import '../../../widgets/desktop/secondary_button.dart';
|
import '../../../widgets/desktop/secondary_button.dart';
|
||||||
|
@ -19,68 +21,6 @@ final multisigCoordinatorStateProvider =
|
||||||
return MultisigCoordinatorState();
|
return MultisigCoordinatorState();
|
||||||
});
|
});
|
||||||
|
|
||||||
class MultisigCoordinatorData {
|
|
||||||
const MultisigCoordinatorData({
|
|
||||||
this.threshold = 2,
|
|
||||||
this.totalCosigners = 3,
|
|
||||||
this.coinType = 0, // Bitcoin mainnet.
|
|
||||||
this.accountIndex = 0,
|
|
||||||
this.scriptType = MultisigScriptType.nativeSegwit,
|
|
||||||
this.cosignerXpubs = const [],
|
|
||||||
});
|
|
||||||
|
|
||||||
final int threshold;
|
|
||||||
final int totalCosigners;
|
|
||||||
final int coinType;
|
|
||||||
final int accountIndex;
|
|
||||||
final MultisigScriptType scriptType;
|
|
||||||
final List<String> cosignerXpubs;
|
|
||||||
|
|
||||||
MultisigCoordinatorData copyWith({
|
|
||||||
int? threshold,
|
|
||||||
int? totalCosigners,
|
|
||||||
int? coinType,
|
|
||||||
int? accountIndex,
|
|
||||||
MultisigScriptType? scriptType,
|
|
||||||
List<String>? cosignerXpubs,
|
|
||||||
}) {
|
|
||||||
return MultisigCoordinatorData(
|
|
||||||
threshold: threshold ?? this.threshold,
|
|
||||||
totalCosigners: totalCosigners ?? this.totalCosigners,
|
|
||||||
coinType: coinType ?? this.coinType,
|
|
||||||
accountIndex: accountIndex ?? this.accountIndex,
|
|
||||||
scriptType: scriptType ?? this.scriptType,
|
|
||||||
cosignerXpubs: cosignerXpubs ?? this.cosignerXpubs,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
'threshold': threshold,
|
|
||||||
'totalCosigners': totalCosigners,
|
|
||||||
'coinType': coinType,
|
|
||||||
'accountIndex': accountIndex,
|
|
||||||
'scriptType': scriptType.index,
|
|
||||||
'cosignerXpubs': cosignerXpubs,
|
|
||||||
};
|
|
||||||
|
|
||||||
factory MultisigCoordinatorData.fromJson(Map<String, dynamic> json) {
|
|
||||||
return MultisigCoordinatorData(
|
|
||||||
threshold: json['threshold'] as int,
|
|
||||||
totalCosigners: json['totalCosigners'] as int,
|
|
||||||
coinType: json['coinType'] as int,
|
|
||||||
accountIndex: json['accountIndex'] as int,
|
|
||||||
scriptType: MultisigScriptType.values[json['scriptType'] as int],
|
|
||||||
cosignerXpubs: (json['cosignerXpubs'] as List).cast<String>(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum MultisigScriptType {
|
|
||||||
legacy, // P2SH.
|
|
||||||
segwit, // P2SH-P2WSH.
|
|
||||||
nativeSegwit, // P2WSH.
|
|
||||||
}
|
|
||||||
|
|
||||||
class MultisigCoordinatorState extends StateNotifier<MultisigCoordinatorData> {
|
class MultisigCoordinatorState extends StateNotifier<MultisigCoordinatorData> {
|
||||||
MultisigCoordinatorState() : super(const MultisigCoordinatorData());
|
MultisigCoordinatorState() : super(const MultisigCoordinatorData());
|
||||||
|
|
||||||
|
@ -109,11 +49,13 @@ class MultisigCoordinatorView extends ConsumerStatefulWidget {
|
||||||
const MultisigCoordinatorView({
|
const MultisigCoordinatorView({
|
||||||
super.key,
|
super.key,
|
||||||
required this.walletId,
|
required this.walletId,
|
||||||
|
required this.scriptType,
|
||||||
required this.totalCosigners,
|
required this.totalCosigners,
|
||||||
required this.threshold,
|
required this.threshold,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String walletId;
|
final String walletId;
|
||||||
|
final MultisigScriptType scriptType;
|
||||||
final int totalCosigners;
|
final int totalCosigners;
|
||||||
final int threshold;
|
final int threshold;
|
||||||
|
|
||||||
|
@ -126,6 +68,7 @@ class MultisigCoordinatorView extends ConsumerStatefulWidget {
|
||||||
|
|
||||||
class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
final List<TextEditingController> xpubControllers = [];
|
final List<TextEditingController> xpubControllers = [];
|
||||||
|
String _myXpub = "";
|
||||||
// bool _isNfcAvailable = false;
|
// bool _isNfcAvailable = false;
|
||||||
// String _nfcStatus = 'Checking NFC availability...';
|
// String _nfcStatus = 'Checking NFC availability...';
|
||||||
|
|
||||||
|
@ -134,10 +77,28 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
// Initialize controllers.
|
// Initialize controllers.
|
||||||
for (int i = 0; i < widget.totalCosigners; i++) {
|
for (int i = 0; i < widget.totalCosigners - 1; i++) {
|
||||||
xpubControllers.add(TextEditingController());
|
xpubControllers.add(TextEditingController());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get and set my xpub.
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
final targetPath = _getTargetPathForScriptType(widget.scriptType);
|
||||||
|
final xpubData = await (ref.read(pWallets).getWallet(widget.walletId)
|
||||||
|
as ExtendedKeysInterface)
|
||||||
|
.getXPubs(bip48: true);
|
||||||
|
print(xpubData);
|
||||||
|
|
||||||
|
final matchingPub = xpubData.xpubs.firstWhere(
|
||||||
|
(pub) => pub.path == targetPath,
|
||||||
|
orElse: () => XPub(path: "", encoded: "xPub not found!"),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (matchingPub.path.isNotEmpty && mounted) {
|
||||||
|
setState(() => _myXpub = matchingPub.encoded);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// _checkNfcAvailability();
|
// _checkNfcAvailability();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +198,7 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
"Enter cosigner xpubs",
|
"Enter cosigner xPubs",
|
||||||
style: STextStyles.navBarTitle(context),
|
style: STextStyles.navBarTitle(context),
|
||||||
),
|
),
|
||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
|
@ -256,7 +217,7 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"This is your extended public key (xpub) for each cosigner. "
|
"This is your extended public key (xPub) for each cosigner. "
|
||||||
"Share it with each participant.",
|
"Share it with each participant.",
|
||||||
style: STextStyles.itemSubtitle(context),
|
style: STextStyles.itemSubtitle(context),
|
||||||
),
|
),
|
||||||
|
@ -268,7 +229,7 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Your xpub",
|
"Your xPub",
|
||||||
style: STextStyles.w500_14(context).copyWith(
|
style: STextStyles.w500_14(context).copyWith(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
|
@ -280,15 +241,14 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: xpubControllers[0],
|
controller: TextEditingController(
|
||||||
enabled:
|
text: _myXpub),
|
||||||
false, // Make field non-interactive
|
enabled: false,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "xpub...",
|
hintText: "xPub...",
|
||||||
hintStyle:
|
hintStyle:
|
||||||
STextStyles.fieldLabel(context),
|
STextStyles.fieldLabel(context),
|
||||||
filled:
|
filled: true,
|
||||||
true, // Add background to show disabled state
|
|
||||||
fillColor: Theme.of(context)
|
fillColor: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.textFieldDefaultBG,
|
.textFieldDefaultBG,
|
||||||
|
@ -322,18 +282,8 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
.buttonTextSecondary,
|
.buttonTextSecondary,
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final data = await Clipboard.getData(
|
await Clipboard.setData(
|
||||||
'text/plain');
|
ClipboardData(text: _myXpub));
|
||||||
if (data?.text != null) {
|
|
||||||
xpubControllers[0].text = data!.text!;
|
|
||||||
ref
|
|
||||||
.read(
|
|
||||||
multisigCoordinatorStateProvider
|
|
||||||
.notifier)
|
|
||||||
.addCosignerXpub(data.text!);
|
|
||||||
setState(
|
|
||||||
() {}); // Trigger rebuild to update button state.
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -350,14 +300,14 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
// Generate input fields for each cosigner.
|
// Generate input fields for each cosigner.
|
||||||
for (int i = 0; i < widget.totalCosigners; i++)
|
for (int i = 1; i < widget.totalCosigners; i++)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 16),
|
padding: const EdgeInsets.only(bottom: 16),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Cosigner ${i + 1} xpub",
|
"Cosigner $i xPub",
|
||||||
style:
|
style:
|
||||||
STextStyles.w500_14(context).copyWith(
|
STextStyles.w500_14(context).copyWith(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
|
@ -370,9 +320,9 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: xpubControllers[i],
|
controller: xpubControllers[i - 1],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "Enter xpub",
|
hintText: "Enter cosigner $i xPub",
|
||||||
hintStyle:
|
hintStyle:
|
||||||
STextStyles.fieldLabel(context),
|
STextStyles.fieldLabel(context),
|
||||||
),
|
),
|
||||||
|
@ -419,7 +369,7 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
final data = await Clipboard.getData(
|
final data = await Clipboard.getData(
|
||||||
'text/plain');
|
'text/plain');
|
||||||
if (data?.text != null) {
|
if (data?.text != null) {
|
||||||
xpubControllers[i].text =
|
xpubControllers[i - 1].text =
|
||||||
data!.text!;
|
data!.text!;
|
||||||
ref
|
ref
|
||||||
.read(
|
.read(
|
||||||
|
@ -459,4 +409,77 @@ class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _getTargetPathForScriptType(MultisigScriptType scriptType) {
|
||||||
|
const pathMap = {
|
||||||
|
MultisigScriptType.segwit: "m/48'/0'/0'/1'",
|
||||||
|
MultisigScriptType.nativeSegwit: "m/48'/0'/0'/2'",
|
||||||
|
};
|
||||||
|
return pathMap[scriptType] ?? '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MultisigCoordinatorData {
|
||||||
|
const MultisigCoordinatorData({
|
||||||
|
this.threshold = 2,
|
||||||
|
this.totalCosigners = 3,
|
||||||
|
this.coinType = 0, // Bitcoin mainnet.
|
||||||
|
this.accountIndex = 0,
|
||||||
|
this.scriptType = MultisigScriptType.nativeSegwit,
|
||||||
|
this.cosignerXpubs = const [],
|
||||||
|
});
|
||||||
|
|
||||||
|
final int threshold;
|
||||||
|
final int totalCosigners;
|
||||||
|
final int coinType;
|
||||||
|
final int accountIndex;
|
||||||
|
final MultisigScriptType scriptType;
|
||||||
|
final List<String> cosignerXpubs;
|
||||||
|
|
||||||
|
MultisigCoordinatorData copyWith({
|
||||||
|
int? threshold,
|
||||||
|
int? totalCosigners,
|
||||||
|
int? coinType,
|
||||||
|
int? accountIndex,
|
||||||
|
MultisigScriptType? scriptType,
|
||||||
|
List<String>? cosignerXpubs,
|
||||||
|
}) {
|
||||||
|
return MultisigCoordinatorData(
|
||||||
|
threshold: threshold ?? this.threshold,
|
||||||
|
totalCosigners: totalCosigners ?? this.totalCosigners,
|
||||||
|
coinType: coinType ?? this.coinType,
|
||||||
|
accountIndex: accountIndex ?? this.accountIndex,
|
||||||
|
scriptType: scriptType ?? this.scriptType,
|
||||||
|
cosignerXpubs: cosignerXpubs ?? this.cosignerXpubs,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'threshold': threshold,
|
||||||
|
'totalCosigners': totalCosigners,
|
||||||
|
'coinType': coinType,
|
||||||
|
'accountIndex': accountIndex,
|
||||||
|
'scriptType': scriptType.index,
|
||||||
|
'cosignerXpubs': cosignerXpubs,
|
||||||
|
};
|
||||||
|
|
||||||
|
factory MultisigCoordinatorData.fromJson(Map<String, dynamic> json) {
|
||||||
|
return MultisigCoordinatorData(
|
||||||
|
threshold: json['threshold'] as int,
|
||||||
|
totalCosigners: json['totalCosigners'] as int,
|
||||||
|
coinType: json['coinType'] as int,
|
||||||
|
accountIndex: json['accountIndex'] as int,
|
||||||
|
scriptType: MultisigScriptType.values[json['scriptType'] as int],
|
||||||
|
cosignerXpubs: (json['cosignerXpubs'] as List).cast<String>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MultisigScriptType {
|
||||||
|
// legacy,
|
||||||
|
// P2SH.
|
||||||
|
// "the only script types covered by this BIP are Native Segwit (p2wsh) and
|
||||||
|
// Nested Segwit (p2sh-p2wsh)." (BIP48).
|
||||||
|
segwit, // P2SH-P2WSH.
|
||||||
|
nativeSegwit, // P2WSH.
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,12 +78,6 @@ class MultisigSetupData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MultisigScriptType {
|
|
||||||
legacy, // P2SH.
|
|
||||||
segwit, // P2SH-P2WSH.
|
|
||||||
nativeSegwit, // P2WSH.
|
|
||||||
}
|
|
||||||
|
|
||||||
class MultisigSetupState extends StateNotifier<MultisigSetupData> {
|
class MultisigSetupState extends StateNotifier<MultisigSetupData> {
|
||||||
MultisigSetupState() : super(const MultisigSetupData());
|
MultisigSetupState() : super(const MultisigSetupData());
|
||||||
|
|
||||||
|
@ -112,11 +106,13 @@ class MultisigSetupView extends ConsumerStatefulWidget {
|
||||||
const MultisigSetupView({
|
const MultisigSetupView({
|
||||||
super.key,
|
super.key,
|
||||||
required this.walletId,
|
required this.walletId,
|
||||||
|
this.scriptType,
|
||||||
this.totalCosigners,
|
this.totalCosigners,
|
||||||
this.threshold,
|
this.threshold,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String walletId;
|
final String walletId;
|
||||||
|
final MultisigScriptType? scriptType;
|
||||||
final int? totalCosigners;
|
final int? totalCosigners;
|
||||||
final int? threshold;
|
final int? threshold;
|
||||||
|
|
||||||
|
@ -340,7 +336,7 @@ class _MultisigSetupViewState extends ConsumerState<MultisigSetupView> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final setupData = ref.watch(multisigSetupStateProvider);
|
final setupData = ref.watch(multisigSetupStateProvider);
|
||||||
final bool isDesktop = Util.isDesktop;
|
// final bool isDesktop = Util.isDesktop;
|
||||||
|
|
||||||
return Background(
|
return Background(
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
|
@ -425,9 +421,10 @@ class _MultisigSetupViewState extends ConsumerState<MultisigSetupView> {
|
||||||
items: MultisigScriptType.values.map((type) {
|
items: MultisigScriptType.values.map((type) {
|
||||||
String label;
|
String label;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MultisigScriptType.legacy:
|
// case MultisigScriptType.legacy:
|
||||||
label = "Legacy (P2SH)";
|
// label = "Legacy (P2SH)";
|
||||||
break;
|
// break;
|
||||||
|
// BIP48 does not cover legacy P2SH script types.
|
||||||
case MultisigScriptType.segwit:
|
case MultisigScriptType.segwit:
|
||||||
label = "Nested SegWit (P2SH-P2WSH)";
|
label = "Nested SegWit (P2SH-P2WSH)";
|
||||||
break;
|
break;
|
||||||
|
@ -531,6 +528,7 @@ class _MultisigSetupViewState extends ConsumerState<MultisigSetupView> {
|
||||||
MaterialPageRoute<void>(
|
MaterialPageRoute<void>(
|
||||||
builder: (context) => MultisigCoordinatorView(
|
builder: (context) => MultisigCoordinatorView(
|
||||||
walletId: widget.walletId,
|
walletId: widget.walletId,
|
||||||
|
scriptType: setupData.scriptType, // TODO,
|
||||||
totalCosigners:
|
totalCosigners:
|
||||||
int.parse(_participantsController.text),
|
int.parse(_participantsController.text),
|
||||||
threshold: int.parse(_thresholdController.text),
|
threshold: int.parse(_thresholdController.text),
|
||||||
|
|
|
@ -2184,13 +2184,14 @@ class RouteGenerator {
|
||||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||||
|
|
||||||
case MultisigCoordinatorView.routeName:
|
case MultisigCoordinatorView.routeName:
|
||||||
if (args is Tuple3<String, int, int>) {
|
if (args is Tuple4<String, MultisigScriptType, int, int>) {
|
||||||
return getRoute(
|
return getRoute(
|
||||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||||
builder: (_) => MultisigCoordinatorView(
|
builder: (_) => MultisigCoordinatorView(
|
||||||
walletId: args.item1,
|
walletId: args.item1,
|
||||||
totalCosigners: args.item2,
|
scriptType: args.item2,
|
||||||
threshold: args.item3,
|
totalCosigners: args.item3,
|
||||||
|
threshold: args.item4,
|
||||||
),
|
),
|
||||||
settings: RouteSettings(
|
settings: RouteSettings(
|
||||||
name: settings.name,
|
name: settings.name,
|
||||||
|
|
|
@ -19,7 +19,9 @@ enum DerivePathType {
|
||||||
eCash44,
|
eCash44,
|
||||||
solana,
|
solana,
|
||||||
bip86,
|
bip86,
|
||||||
cardanoShelley;
|
cardanoShelley,
|
||||||
|
bip48p2shp2wsh,
|
||||||
|
bip48p2wsh;
|
||||||
|
|
||||||
AddressType getAddressType() {
|
AddressType getAddressType() {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
|
@ -45,6 +47,12 @@ enum DerivePathType {
|
||||||
|
|
||||||
case DerivePathType.cardanoShelley:
|
case DerivePathType.cardanoShelley:
|
||||||
return AddressType.cardanoShelley;
|
return AddressType.cardanoShelley;
|
||||||
|
|
||||||
|
case DerivePathType.bip48p2shp2wsh:
|
||||||
|
return AddressType.p2sh;
|
||||||
|
|
||||||
|
case DerivePathType.bip48p2wsh:
|
||||||
|
return AddressType.p2wpkh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,18 @@ class XPriv extends XKey {
|
||||||
|
|
||||||
mixin ExtendedKeysInterface<T extends ElectrumXCurrencyInterface>
|
mixin ExtendedKeysInterface<T extends ElectrumXCurrencyInterface>
|
||||||
on ElectrumXInterface<T> {
|
on ElectrumXInterface<T> {
|
||||||
Future<({List<XPub> xpubs, String fingerprint})> getXPubs() async {
|
Future<({List<XPub> xpubs, String fingerprint})> getXPubs(
|
||||||
|
{bool bip48 = false}) async {
|
||||||
final paths = cryptoCurrency.supportedHardenedDerivationPaths;
|
final paths = cryptoCurrency.supportedHardenedDerivationPaths;
|
||||||
|
|
||||||
|
if (bip48) {
|
||||||
|
// This hack was used because I was not able to correctly cast an existing
|
||||||
|
// Bitcoin wallet as a BIP48Bitcoin wallet in order to use its
|
||||||
|
// supportedDerivationPathTypes override.
|
||||||
|
paths.add("m/48'/0'/0'/1'");
|
||||||
|
paths.add("m/48'/0'/0'/2'");
|
||||||
|
}
|
||||||
|
|
||||||
final master = await getRootHDNode();
|
final master = await getRootHDNode();
|
||||||
final fingerprint = master.fingerprint.toRadixString(16);
|
final fingerprint = master.fingerprint.toRadixString(16);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue