mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
sp improvements (#2418)
* feat: show sp keys * fix: merge fixes * feat: show sp receive address on tx details page * feat(keys): show silent payment keys tab & keys tab * Update lib/src/screens/wallet_keys/wallet_keys_page.dart --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
@@ -73,6 +73,19 @@ class CWBitcoin extends Bitcoin {
|
||||
return bitcoinWallet.keys.toJson();
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, String> getSilentPaymentKeys(Object wallet) {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
final keysOwner = bitcoinWallet.walletAddresses.silentAddress!;
|
||||
|
||||
return <String, String>{
|
||||
'privateSpendKey': keysOwner.b_spend.toHex(),
|
||||
'publicSpendKey': keysOwner.B_spend.toHex(),
|
||||
'privateViewKey': keysOwner.b_scan.toHex(),
|
||||
'publicViewKey': keysOwner.B_scan.toHex(),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<TransactionPriority> getTransactionPriorities() => BitcoinTransactionPriority.all;
|
||||
|
||||
@@ -116,9 +129,7 @@ class CWBitcoin extends Bitcoin {
|
||||
String? payjoinUri,
|
||||
}) {
|
||||
final bitcoinFeeRate =
|
||||
priority == BitcoinTransactionPriority.custom && feeRate != null
|
||||
? feeRate
|
||||
: null;
|
||||
priority == BitcoinTransactionPriority.custom && feeRate != null ? feeRate : null;
|
||||
return BitcoinTransactionCredentials(
|
||||
outputs
|
||||
.map((out) => OutputInfo(
|
||||
@@ -237,12 +248,12 @@ class CWBitcoin extends Bitcoin {
|
||||
Box<UnspentCoinsInfo> unspentCoinSource,
|
||||
Box<PayjoinSession> payjoinSessionSource,
|
||||
bool isDirect) {
|
||||
return BitcoinWalletService(walletInfoSource, unspentCoinSource,
|
||||
payjoinSessionSource, isDirect);
|
||||
return BitcoinWalletService(
|
||||
walletInfoSource, unspentCoinSource, payjoinSessionSource, isDirect);
|
||||
}
|
||||
|
||||
WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource,
|
||||
Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
|
||||
WalletService createLitecoinWalletService(
|
||||
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool isDirect) {
|
||||
return LitecoinWalletService(walletInfoSource, unspentCoinSource, isDirect);
|
||||
}
|
||||
|
||||
@@ -775,4 +786,20 @@ class CWBitcoin extends Bitcoin {
|
||||
(_wallet.walletAddresses as BitcoinWalletAddresses).currentPayjoinReceiver = null;
|
||||
(_wallet.walletAddresses as BitcoinWalletAddresses).payjoinEndpoint = null;
|
||||
}
|
||||
|
||||
@override
|
||||
String? getTransactionAddress(Object wallet, TransactionInfo tx) {
|
||||
final bitcoinWallet = wallet as BitcoinWallet;
|
||||
final bitcoinTx = tx as ElectrumTransactionInfo;
|
||||
|
||||
if (bitcoinTx.unspents == null || bitcoinTx.unspents!.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// final bitcoinSPAddrs =
|
||||
// bitcoinTx.unspents!.first.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
|
||||
final bitcoinSPAddrs = wallet.walletAddresses as BitcoinWalletAddresses;
|
||||
final bitcoinSPAddr = bitcoinSPAddrs.silentAddresses.first;
|
||||
return bitcoinSPAddr.address;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/seedphrase_grid_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/text_info_box.dart';
|
||||
@@ -67,6 +68,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late TabController _tabController;
|
||||
late bool showKeyTab;
|
||||
late bool showSilentPaymentsTab;
|
||||
late bool showLegacySeedTab;
|
||||
late bool isLegacySeedOnly;
|
||||
|
||||
@@ -79,10 +81,15 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
super.initState();
|
||||
|
||||
showKeyTab = widget.walletKeysViewModel.items.isNotEmpty;
|
||||
showSilentPaymentsTab =
|
||||
widget.walletKeysViewModel.isBitcoin && widget.walletKeysViewModel.items.length > 4;
|
||||
showLegacySeedTab = widget.walletKeysViewModel.legacySeedSplit.isNotEmpty;
|
||||
isLegacySeedOnly = widget.walletKeysViewModel.isLegacySeedOnly;
|
||||
|
||||
final totalTabs = (_hasSeeds ? 1 : 0) + (showKeyTab ? 1 : 0) + (showLegacySeedTab ? 1 : 0);
|
||||
final totalTabs = (_hasSeeds ? 1 : 0) +
|
||||
(showKeyTab ? 1 : 0) +
|
||||
(showLegacySeedTab ? 1 : 0) +
|
||||
(showSilentPaymentsTab ? 1 : 0);
|
||||
|
||||
_tabController = TabController(length: totalTabs, vsync: this);
|
||||
}
|
||||
@@ -108,13 +115,11 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
isScrollable: true,
|
||||
labelStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 18,
|
||||
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
unselectedLabelStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 18,
|
||||
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant.withOpacity(0.5),
|
||||
),
|
||||
@@ -126,9 +131,20 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
dividerColor: Colors.transparent,
|
||||
padding: EdgeInsets.zero,
|
||||
tabs: [
|
||||
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')),
|
||||
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 (showSilentPaymentsTab)
|
||||
Tab(
|
||||
text: S.of(context).silent_payments,
|
||||
key: ValueKey('wallet_keys_silent_payments_keys'),
|
||||
),
|
||||
if (showLegacySeedTab)
|
||||
Tab(text: S.of(context).legacy, key: ValueKey('wallet_keys_page_seed_legacy')),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -145,7 +161,12 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
if (showKeyTab)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 22, right: 22),
|
||||
child: _buildKeysTab(context),
|
||||
child: _buildKeysTab(context, showSilentPaymentsTab ? widget.walletKeysViewModel.items.sublist(0, 4) : widget.walletKeysViewModel.items),
|
||||
),
|
||||
if (showSilentPaymentsTab)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 22, right: 22),
|
||||
child: _buildKeysTab(context, widget.walletKeysViewModel.items.sublist(4)),
|
||||
),
|
||||
if (showLegacySeedTab)
|
||||
Padding(
|
||||
@@ -163,7 +184,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
Widget _buildSeedTab(BuildContext context, bool isLegacySeed) {
|
||||
return Column(
|
||||
children: [
|
||||
if (isLegacySeedOnly || isLegacySeed) ...[
|
||||
if (isLegacySeedOnly || isLegacySeed || widget.walletKeysViewModel.isBitcoin) ...[
|
||||
_buildHeightBox(),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
@@ -189,15 +210,15 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildKeysTab(BuildContext context) {
|
||||
Widget _buildKeysTab(BuildContext context, List<StandartListItem> items) {
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: widget.walletKeysViewModel.items.length,
|
||||
itemCount: items.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = widget.walletKeysViewModel.items[index];
|
||||
final item = items[index];
|
||||
return TextInfoBox(
|
||||
key: item.key,
|
||||
title: item.title,
|
||||
|
||||
@@ -90,7 +90,7 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
break;
|
||||
case WalletType.none:
|
||||
case WalletType.banano:
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
final descriptionKey = '${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}';
|
||||
@@ -235,9 +235,8 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
void _addMoneroListItems(TransactionInfo tx, DateFormat dateFormat) {
|
||||
final descriptionKey = '${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}';
|
||||
final description = transactionDescriptionBox.values.firstWhere(
|
||||
(val) => val.id == descriptionKey || val.id == transactionInfo.txHash,
|
||||
orElse: () => TransactionDescription(id: descriptionKey));
|
||||
|
||||
(val) => val.id == descriptionKey || val.id == transactionInfo.txHash,
|
||||
orElse: () => TransactionDescription(id: descriptionKey));
|
||||
|
||||
final key = tx.additionalInfo['key'] as String? ?? description.transactionKey;
|
||||
final accountIndex = tx.additionalInfo['accountIndex'] as int;
|
||||
@@ -344,6 +343,25 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||
),
|
||||
];
|
||||
|
||||
if (wallet.type == WalletType.bitcoin && tx.direction == TransactionDirection.incoming) {
|
||||
try {
|
||||
final address = bitcoin!.getTransactionAddress(wallet, tx);
|
||||
|
||||
if (address != null) {
|
||||
isRecipientAddressShown = true;
|
||||
_items.add(
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_recipient_address,
|
||||
value: address,
|
||||
key: ValueKey('standard_list_item_transaction_details_recipient_address_key'),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
printV(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
items.addAll(_items);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ abstract class WalletKeysViewModelBase with Store {
|
||||
}
|
||||
}
|
||||
|
||||
bool get isBitcoin => _wallet.type == WalletType.bitcoin;
|
||||
|
||||
final ObservableList<StandartListItem> items;
|
||||
|
||||
final String title;
|
||||
@@ -66,8 +68,7 @@ abstract class WalletKeysViewModelBase with Store {
|
||||
bool get isLegacySeedOnly =>
|
||||
[WalletType.monero, WalletType.wownero].contains(_wallet.type) &&
|
||||
_wallet.seed != null &&
|
||||
!(Polyseed.isValidSeed(_wallet.seed!) ||
|
||||
_wallet.seed!.split(' ').length == 12);
|
||||
!(Polyseed.isValidSeed(_wallet.seed!) || _wallet.seed!.split(' ').length == 12);
|
||||
|
||||
String get legacySeed {
|
||||
if ((_wallet.type == WalletType.monero || _wallet.type == WalletType.wownero) &&
|
||||
@@ -163,18 +164,22 @@ 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']!),
|
||||
]);
|
||||
if (_wallet.type == WalletType.bitcoin) {
|
||||
keys = bitcoin!.getSilentPaymentKeys(_appStore.wallet!);
|
||||
}
|
||||
|
||||
final electrumKeys = bitcoin!.getWalletKeys(_appStore.wallet!);
|
||||
|
||||
items.addAll([
|
||||
if ((electrumKeys['wif'] ?? '').isNotEmpty)
|
||||
StandartListItem(title: "WIF", value: electrumKeys['wif']!),
|
||||
if ((electrumKeys['privateKey'] ?? '').isNotEmpty)
|
||||
StandartListItem(title: S.current.private_key, value: electrumKeys['privateKey']!),
|
||||
if (electrumKeys['publicKey'] != null)
|
||||
StandartListItem(title: S.current.public_key, value: electrumKeys['publicKey']!),
|
||||
if (electrumKeys['xpub'] != null)
|
||||
StandartListItem(title: "xPub", value: electrumKeys['xpub']!),
|
||||
]);
|
||||
break;
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
|
||||
+10
-12
@@ -253,6 +253,8 @@ abstract class Bitcoin {
|
||||
String getPayjoinEndpoint(Object wallet);
|
||||
void resumePayjoinSessions(Object wallet);
|
||||
void stopPayjoinSessions(Object wallet);
|
||||
Map<String, String> getSilentPaymentKeys(Object wallet);
|
||||
String? getTransactionAddress(Object wallet, TransactionInfo tx);
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -939,15 +941,12 @@ abstract class BitcoinCash {
|
||||
""";
|
||||
|
||||
const bitcoinCashEmptyDefinition = 'BitcoinCash? bitcoinCash;\n';
|
||||
const bitcoinCashCWDefinition =
|
||||
'BitcoinCash? bitcoinCash = CWBitcoinCash();\n';
|
||||
const bitcoinCashCWDefinition = 'BitcoinCash? bitcoinCash = CWBitcoinCash();\n';
|
||||
|
||||
final output = '$bitcoinCashCommonHeaders\n' +
|
||||
(hasImplementation ? '$bitcoinCashCWHeaders\n' : '\n') +
|
||||
(hasImplementation ? '$bitcoinCashCwPart\n\n' : '\n') +
|
||||
(hasImplementation
|
||||
? bitcoinCashCWDefinition
|
||||
: bitcoinCashEmptyDefinition) +
|
||||
(hasImplementation ? bitcoinCashCWDefinition : bitcoinCashEmptyDefinition) +
|
||||
'\n' +
|
||||
bitcoinCashContent;
|
||||
|
||||
@@ -1082,8 +1081,7 @@ abstract class NanoUtil {
|
||||
""";
|
||||
|
||||
const nanoEmptyDefinition = 'Nano? nano;\nNanoUtil? nanoUtil;\n';
|
||||
const nanoCWDefinition =
|
||||
'Nano? nano = CWNano();\nNanoUtil? nanoUtil = CWNanoUtil();\n';
|
||||
const nanoCWDefinition = 'Nano? nano = CWNano();\nNanoUtil? nanoUtil = CWNanoUtil();\n';
|
||||
|
||||
final output = '$nanoCommonHeaders\n' +
|
||||
(hasImplementation ? '$nanoCWHeaders\n' : '\n') +
|
||||
@@ -1324,11 +1322,11 @@ abstract class Zano {
|
||||
const zanoCWDefinition = 'Zano? zano = CWZano();\n';
|
||||
|
||||
final output = '$zanoCommonHeaders\n' +
|
||||
(hasImplementation ? '$zanoCWHeaders\n' : '\n') +
|
||||
(hasImplementation ? '$zanoCwPart\n\n' : '\n') +
|
||||
(hasImplementation ? zanoCWDefinition : zanoEmptyDefinition) +
|
||||
'\n' +
|
||||
zanoContent;
|
||||
(hasImplementation ? '$zanoCWHeaders\n' : '\n') +
|
||||
(hasImplementation ? '$zanoCwPart\n\n' : '\n') +
|
||||
(hasImplementation ? zanoCWDefinition : zanoEmptyDefinition) +
|
||||
'\n' +
|
||||
zanoContent;
|
||||
|
||||
if (outputFile.existsSync()) {
|
||||
await outputFile.delete();
|
||||
|
||||
Reference in New Issue
Block a user