mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 11:59:30 +00:00
address key wif
This commit is contained in:
parent
fe5458928f
commit
f0b62aed92
4 changed files with 177 additions and 130 deletions
|
@ -22,6 +22,7 @@ import '../../../utilities/address_utils.dart';
|
||||||
import '../../../utilities/text_styles.dart';
|
import '../../../utilities/text_styles.dart';
|
||||||
import '../../../utilities/util.dart';
|
import '../../../utilities/util.dart';
|
||||||
import '../../../wallets/isar/providers/wallet_info_provider.dart';
|
import '../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||||
|
import '../../../widgets/address_private_key.dart';
|
||||||
import '../../../widgets/background.dart';
|
import '../../../widgets/background.dart';
|
||||||
import '../../../widgets/conditional_parent.dart';
|
import '../../../widgets/conditional_parent.dart';
|
||||||
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
|
@ -30,6 +31,7 @@ import '../../../widgets/custom_buttons/simple_copy_button.dart';
|
||||||
import '../../../widgets/custom_buttons/simple_edit_button.dart';
|
import '../../../widgets/custom_buttons/simple_edit_button.dart';
|
||||||
import '../../../widgets/desktop/desktop_dialog.dart';
|
import '../../../widgets/desktop/desktop_dialog.dart';
|
||||||
import '../../../widgets/desktop/desktop_dialog_close_button.dart';
|
import '../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
|
import '../../../widgets/detail_item.dart';
|
||||||
import '../../../widgets/qr.dart';
|
import '../../../widgets/qr.dart';
|
||||||
import '../../../widgets/rounded_white_container.dart';
|
import '../../../widgets/rounded_white_container.dart';
|
||||||
import '../../../widgets/transaction_card.dart';
|
import '../../../widgets/transaction_card.dart';
|
||||||
|
@ -298,9 +300,9 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 16,
|
height: 16,
|
||||||
),
|
),
|
||||||
_Item(
|
DetailItem(
|
||||||
title: "Address",
|
title: "Address",
|
||||||
data: address.value,
|
detail: address.value,
|
||||||
button: isDesktop
|
button: isDesktop
|
||||||
? IconCopyButton(
|
? IconCopyButton(
|
||||||
data: address.value,
|
data: address.value,
|
||||||
|
@ -312,9 +314,9 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
|
||||||
const _Div(
|
const _Div(
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
_Item(
|
DetailItem(
|
||||||
title: "Label",
|
title: "Label",
|
||||||
data: label!.value,
|
detail: label!.value,
|
||||||
button: SimpleEditButton(
|
button: SimpleEditButton(
|
||||||
editValue: label!.value,
|
editValue: label!.value,
|
||||||
editLabel: 'label',
|
editLabel: 'label',
|
||||||
|
@ -338,9 +340,9 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
if (address.derivationPath != null)
|
if (address.derivationPath != null)
|
||||||
_Item(
|
DetailItem(
|
||||||
title: "Derivation path",
|
title: "Derivation path",
|
||||||
data: address.derivationPath!.value,
|
detail: address.derivationPath!.value,
|
||||||
button: Container(),
|
button: Container(),
|
||||||
),
|
),
|
||||||
if (address.type == AddressType.spark)
|
if (address.type == AddressType.spark)
|
||||||
|
@ -348,27 +350,34 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
if (address.type == AddressType.spark)
|
if (address.type == AddressType.spark)
|
||||||
_Item(
|
DetailItem(
|
||||||
title: "Diversifier",
|
title: "Diversifier",
|
||||||
data: address.derivationIndex.toString(),
|
detail: address.derivationIndex.toString(),
|
||||||
button: Container(),
|
button: Container(),
|
||||||
),
|
),
|
||||||
const _Div(
|
const _Div(
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
_Item(
|
DetailItem(
|
||||||
title: "Type",
|
title: "Type",
|
||||||
data: address.type.readableName,
|
detail: address.type.readableName,
|
||||||
button: Container(),
|
button: Container(),
|
||||||
),
|
),
|
||||||
const _Div(
|
const _Div(
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
_Item(
|
DetailItem(
|
||||||
title: "Sub type",
|
title: "Sub type",
|
||||||
data: address.subType.prettyName,
|
detail: address.subType.prettyName,
|
||||||
button: Container(),
|
button: Container(),
|
||||||
),
|
),
|
||||||
|
const _Div(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
AddressPrivateKey(
|
||||||
|
walletId: widget.walletId,
|
||||||
|
address: address,
|
||||||
|
),
|
||||||
if (!isDesktop)
|
if (!isDesktop)
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
|
@ -631,64 +640,3 @@ class _Tags extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Item extends StatelessWidget {
|
|
||||||
const _Item({
|
|
||||||
super.key,
|
|
||||||
required this.title,
|
|
||||||
required this.data,
|
|
||||||
required this.button,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String title;
|
|
||||||
final String data;
|
|
||||||
final Widget button;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return ConditionalParent(
|
|
||||||
condition: !Util.isDesktop,
|
|
||||||
builder: (child) => RoundedWhiteContainer(
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
child: ConditionalParent(
|
|
||||||
condition: Util.isDesktop,
|
|
||||||
builder: (child) => Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: STextStyles.itemSubtitle(context),
|
|
||||||
),
|
|
||||||
button,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 5,
|
|
||||||
),
|
|
||||||
data.isNotEmpty
|
|
||||||
? SelectableText(
|
|
||||||
data,
|
|
||||||
style: STextStyles.w500_14(context),
|
|
||||||
)
|
|
||||||
: Text(
|
|
||||||
"$title will appear here",
|
|
||||||
style: STextStyles.w500_14(context).copyWith(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.textSubtitle3,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib;
|
import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib;
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
@ -6,6 +8,7 @@ import '../../../models/balance.dart';
|
||||||
import '../../../models/isar/models/blockchain_data/address.dart';
|
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||||
import '../../../utilities/amount/amount.dart';
|
import '../../../utilities/amount/amount.dart';
|
||||||
import '../../../utilities/enums/derive_path_type_enum.dart';
|
import '../../../utilities/enums/derive_path_type_enum.dart';
|
||||||
|
import '../../../utilities/extensions/extensions.dart';
|
||||||
import '../../crypto_currency/intermediate/bip39_hd_currency.dart';
|
import '../../crypto_currency/intermediate/bip39_hd_currency.dart';
|
||||||
import '../wallet_mixin_interfaces/multi_address_interface.dart';
|
import '../wallet_mixin_interfaces/multi_address_interface.dart';
|
||||||
import 'bip39_wallet.dart';
|
import 'bip39_wallet.dart';
|
||||||
|
@ -28,6 +31,22 @@ abstract class Bip39HDWallet<T extends Bip39HDCurrency> extends Bip39Wallet<T>
|
||||||
return coinlib.HDPrivateKey.fromSeed(seed);
|
return coinlib.HDPrivateKey.fromSeed(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String> getPrivateKeyWIF(Address address) async {
|
||||||
|
final keys =
|
||||||
|
(await getRootHDNode()).derivePath(address.derivationPath!.value);
|
||||||
|
|
||||||
|
final List<int> data = [
|
||||||
|
cryptoCurrency.networkParams.wifPrefix,
|
||||||
|
...keys.privateKey.data,
|
||||||
|
if (keys.privateKey.compressed) 1,
|
||||||
|
];
|
||||||
|
final checksum =
|
||||||
|
coinlib.sha256DoubleHash(Uint8List.fromList(data)).sublist(0, 4);
|
||||||
|
data.addAll(checksum);
|
||||||
|
|
||||||
|
return Uint8List.fromList(data).toBase58Encoded;
|
||||||
|
}
|
||||||
|
|
||||||
Future<Address> generateNextReceivingAddress({
|
Future<Address> generateNextReceivingAddress({
|
||||||
required DerivePathType derivePathType,
|
required DerivePathType derivePathType,
|
||||||
}) async {
|
}) async {
|
||||||
|
|
83
lib/widgets/address_private_key.dart
Normal file
83
lib/widgets/address_private_key.dart
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
import '../models/isar/models/isar_models.dart';
|
||||||
|
import '../providers/global/wallets_provider.dart';
|
||||||
|
import '../utilities/show_loading.dart';
|
||||||
|
import '../utilities/text_styles.dart';
|
||||||
|
import '../utilities/util.dart';
|
||||||
|
import '../wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
||||||
|
import 'custom_buttons/blue_text_button.dart';
|
||||||
|
import 'detail_item.dart';
|
||||||
|
|
||||||
|
class AddressPrivateKey extends ConsumerStatefulWidget {
|
||||||
|
/// The [walletId] MUST be the id of a [Bip39HDWallet]!
|
||||||
|
const AddressPrivateKey({
|
||||||
|
super.key,
|
||||||
|
required this.walletId,
|
||||||
|
required this.address,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String walletId;
|
||||||
|
final Address address;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<AddressPrivateKey> createState() => _AddressPrivateKeyState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AddressPrivateKeyState extends ConsumerState<AddressPrivateKey> {
|
||||||
|
String? _private;
|
||||||
|
|
||||||
|
bool _lock = false;
|
||||||
|
|
||||||
|
Future<void> _loadPrivKey() async {
|
||||||
|
// sanity check that should never actually fail in practice.
|
||||||
|
// Big problems if it actually does though so we check and crash if it fails.
|
||||||
|
assert(widget.walletId == widget.address.walletId);
|
||||||
|
|
||||||
|
if (_lock) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_lock = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final wallet =
|
||||||
|
ref.read(pWallets).getWallet(widget.walletId) as Bip39HDWallet;
|
||||||
|
|
||||||
|
_private = await showLoading(
|
||||||
|
whileFuture: wallet.getPrivateKeyWIF(widget.address),
|
||||||
|
context: context,
|
||||||
|
message: "Loading...",
|
||||||
|
delay: const Duration(milliseconds: 800),
|
||||||
|
rootNavigator: Util.isDesktop,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
setState(() {});
|
||||||
|
} else {
|
||||||
|
_private == null;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_lock = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DetailItemBase(
|
||||||
|
button: CustomTextButton(
|
||||||
|
text: "Show",
|
||||||
|
onTap: _loadPrivKey,
|
||||||
|
enabled: _private == null,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
"Private key",
|
||||||
|
style: STextStyles.itemSubtitle(context),
|
||||||
|
),
|
||||||
|
detail: SelectableText(
|
||||||
|
_private ?? "*" * 52, // 52 is approx length
|
||||||
|
style: STextStyles.w500_14(context),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../themes/stack_colors.dart';
|
import '../themes/stack_colors.dart';
|
||||||
import '../utilities/text_styles.dart';
|
import '../utilities/text_styles.dart';
|
||||||
import '../utilities/util.dart';
|
import '../utilities/util.dart';
|
||||||
|
@ -27,15 +28,61 @@ class DetailItem extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final TextStyle detailStyle;
|
TextStyle detailStyle = STextStyles.w500_14(context);
|
||||||
|
String _detail = detail;
|
||||||
if (overrideDetailTextColor != null) {
|
if (overrideDetailTextColor != null) {
|
||||||
detailStyle = STextStyles.w500_14(context).copyWith(
|
detailStyle = STextStyles.w500_14(context).copyWith(
|
||||||
color: overrideDetailTextColor,
|
color: overrideDetailTextColor,
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
detailStyle = STextStyles.w500_14(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (detail.isEmpty && showEmptyDetail) {
|
||||||
|
_detail = "$title will appear here";
|
||||||
|
detailStyle = detailStyle.copyWith(
|
||||||
|
color: Theme.of(context).extension<StackColors>()!.textSubtitle3,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DetailItemBase(
|
||||||
|
horizontal: horizontal,
|
||||||
|
title: disableSelectableText
|
||||||
|
? Text(
|
||||||
|
title,
|
||||||
|
style: STextStyles.itemSubtitle(context),
|
||||||
|
)
|
||||||
|
: SelectableText(
|
||||||
|
title,
|
||||||
|
style: STextStyles.itemSubtitle(context),
|
||||||
|
),
|
||||||
|
detail: disableSelectableText
|
||||||
|
? Text(
|
||||||
|
_detail,
|
||||||
|
style: detailStyle,
|
||||||
|
)
|
||||||
|
: SelectableText(
|
||||||
|
_detail,
|
||||||
|
style: detailStyle,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DetailItemBase extends StatelessWidget {
|
||||||
|
const DetailItemBase({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.detail,
|
||||||
|
this.button,
|
||||||
|
this.horizontal = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Widget title;
|
||||||
|
final Widget detail;
|
||||||
|
final Widget? button;
|
||||||
|
final bool horizontal;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
return ConditionalParent(
|
return ConditionalParent(
|
||||||
condition: !Util.isDesktop,
|
condition: !Util.isDesktop,
|
||||||
builder: (child) => RoundedWhiteContainer(
|
builder: (child) => RoundedWhiteContainer(
|
||||||
|
@ -51,24 +98,8 @@ class DetailItem extends StatelessWidget {
|
||||||
? Row(
|
? Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
disableSelectableText
|
|
||||||
? Text(
|
|
||||||
title,
|
title,
|
||||||
style: STextStyles.itemSubtitle(context),
|
|
||||||
)
|
|
||||||
: SelectableText(
|
|
||||||
title,
|
|
||||||
style: STextStyles.itemSubtitle(context),
|
|
||||||
),
|
|
||||||
disableSelectableText
|
|
||||||
? Text(
|
|
||||||
detail,
|
detail,
|
||||||
style: detailStyle,
|
|
||||||
)
|
|
||||||
: SelectableText(
|
|
||||||
detail,
|
|
||||||
style: detailStyle,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: Column(
|
: Column(
|
||||||
|
@ -77,48 +108,14 @@ class DetailItem extends StatelessWidget {
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
disableSelectableText
|
|
||||||
? Text(
|
|
||||||
title,
|
title,
|
||||||
style: STextStyles.itemSubtitle(context),
|
|
||||||
)
|
|
||||||
: SelectableText(
|
|
||||||
title,
|
|
||||||
style: STextStyles.itemSubtitle(context),
|
|
||||||
),
|
|
||||||
button ?? Container(),
|
button ?? Container(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 5,
|
height: 5,
|
||||||
),
|
),
|
||||||
detail.isEmpty && showEmptyDetail
|
|
||||||
? disableSelectableText
|
|
||||||
? Text(
|
|
||||||
"$title will appear here",
|
|
||||||
style: STextStyles.w500_14(context).copyWith(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.textSubtitle3,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: SelectableText(
|
|
||||||
"$title will appear here",
|
|
||||||
style: STextStyles.w500_14(context).copyWith(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.textSubtitle3,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: disableSelectableText
|
|
||||||
? Text(
|
|
||||||
detail,
|
detail,
|
||||||
style: detailStyle,
|
|
||||||
)
|
|
||||||
: SelectableText(
|
|
||||||
detail,
|
|
||||||
style: detailStyle,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue