mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 03:49:22 +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/util.dart';
|
||||
import '../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||
import '../../../widgets/address_private_key.dart';
|
||||
import '../../../widgets/background.dart';
|
||||
import '../../../widgets/conditional_parent.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/desktop/desktop_dialog.dart';
|
||||
import '../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import '../../../widgets/detail_item.dart';
|
||||
import '../../../widgets/qr.dart';
|
||||
import '../../../widgets/rounded_white_container.dart';
|
||||
import '../../../widgets/transaction_card.dart';
|
||||
|
@ -298,9 +300,9 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
|
|||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
_Item(
|
||||
DetailItem(
|
||||
title: "Address",
|
||||
data: address.value,
|
||||
detail: address.value,
|
||||
button: isDesktop
|
||||
? IconCopyButton(
|
||||
data: address.value,
|
||||
|
@ -312,9 +314,9 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
|
|||
const _Div(
|
||||
height: 12,
|
||||
),
|
||||
_Item(
|
||||
DetailItem(
|
||||
title: "Label",
|
||||
data: label!.value,
|
||||
detail: label!.value,
|
||||
button: SimpleEditButton(
|
||||
editValue: label!.value,
|
||||
editLabel: 'label',
|
||||
|
@ -338,9 +340,9 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
|
|||
height: 12,
|
||||
),
|
||||
if (address.derivationPath != null)
|
||||
_Item(
|
||||
DetailItem(
|
||||
title: "Derivation path",
|
||||
data: address.derivationPath!.value,
|
||||
detail: address.derivationPath!.value,
|
||||
button: Container(),
|
||||
),
|
||||
if (address.type == AddressType.spark)
|
||||
|
@ -348,27 +350,34 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
|
|||
height: 12,
|
||||
),
|
||||
if (address.type == AddressType.spark)
|
||||
_Item(
|
||||
DetailItem(
|
||||
title: "Diversifier",
|
||||
data: address.derivationIndex.toString(),
|
||||
detail: address.derivationIndex.toString(),
|
||||
button: Container(),
|
||||
),
|
||||
const _Div(
|
||||
height: 12,
|
||||
),
|
||||
_Item(
|
||||
DetailItem(
|
||||
title: "Type",
|
||||
data: address.type.readableName,
|
||||
detail: address.type.readableName,
|
||||
button: Container(),
|
||||
),
|
||||
const _Div(
|
||||
height: 12,
|
||||
),
|
||||
_Item(
|
||||
DetailItem(
|
||||
title: "Sub type",
|
||||
data: address.subType.prettyName,
|
||||
detail: address.subType.prettyName,
|
||||
button: Container(),
|
||||
),
|
||||
const _Div(
|
||||
height: 12,
|
||||
),
|
||||
AddressPrivateKey(
|
||||
walletId: widget.walletId,
|
||||
address: address,
|
||||
),
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
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:coinlib_flutter/coinlib_flutter.dart' as coinlib;
|
||||
import 'package:isar/isar.dart';
|
||||
|
@ -6,6 +8,7 @@ import '../../../models/balance.dart';
|
|||
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||
import '../../../utilities/amount/amount.dart';
|
||||
import '../../../utilities/enums/derive_path_type_enum.dart';
|
||||
import '../../../utilities/extensions/extensions.dart';
|
||||
import '../../crypto_currency/intermediate/bip39_hd_currency.dart';
|
||||
import '../wallet_mixin_interfaces/multi_address_interface.dart';
|
||||
import 'bip39_wallet.dart';
|
||||
|
@ -28,6 +31,22 @@ abstract class Bip39HDWallet<T extends Bip39HDCurrency> extends Bip39Wallet<T>
|
|||
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({
|
||||
required DerivePathType derivePathType,
|
||||
}) 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 '../themes/stack_colors.dart';
|
||||
import '../utilities/text_styles.dart';
|
||||
import '../utilities/util.dart';
|
||||
|
@ -27,15 +28,61 @@ class DetailItem extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final TextStyle detailStyle;
|
||||
TextStyle detailStyle = STextStyles.w500_14(context);
|
||||
String _detail = detail;
|
||||
if (overrideDetailTextColor != null) {
|
||||
detailStyle = STextStyles.w500_14(context).copyWith(
|
||||
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(
|
||||
condition: !Util.isDesktop,
|
||||
builder: (child) => RoundedWhiteContainer(
|
||||
|
@ -51,24 +98,8 @@ class DetailItem extends StatelessWidget {
|
|||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
disableSelectableText
|
||||
? Text(
|
||||
title,
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
)
|
||||
: SelectableText(
|
||||
title,
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
disableSelectableText
|
||||
? Text(
|
||||
detail,
|
||||
style: detailStyle,
|
||||
)
|
||||
: SelectableText(
|
||||
detail,
|
||||
style: detailStyle,
|
||||
),
|
||||
title,
|
||||
detail,
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
|
@ -77,48 +108,14 @@ class DetailItem extends StatelessWidget {
|
|||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
disableSelectableText
|
||||
? Text(
|
||||
title,
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
)
|
||||
: SelectableText(
|
||||
title,
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
title,
|
||||
button ?? Container(),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
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,
|
||||
style: detailStyle,
|
||||
)
|
||||
: SelectableText(
|
||||
detail,
|
||||
style: detailStyle,
|
||||
),
|
||||
detail,
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue