What changed, and why it matters
This commit makes several user-interface and wallet-management tweaks for Firo 'view-only' wallets in the Stack Wallet app. It adds a new option to create a Spark-specific view-only wallet, moves the 'Spark view key' display behind an authentication step, and cleans up how Spark addresses are generated and saved. There is no clear security vulnerability in the diff itself; it mostly reorganizes existing functionality and adds a new view-only path.
Review the full implementation of `SparkInterface.sparkViewKey` and `generateNextSparkAddress` to confirm the view key and generated addresses are handled consistently and not exposed in logs or UI without authentication. Verify that the new `convertToViewOnlySpark` path does not bypass existing view-only validation. No urgent patch is indicated by this diff alone.
Security signals we found
New view-only wallet type (Spark) introduced
Spark view key display moved behind authentication
xPub display also moved behind authentication
Manual database writes for Spark address generation replaced with a single helper
No obvious secret leakage, injection, or cryptographic weakness visible in the diff
Evidence from the diff
The patch extends the view-only wallet creation flow for Firo by adding a convertToViewOnlySpark flag and a Spark/XPub toggle. It updates VerifyRecoveryPhraseView to derive and store SparkViewOnlyWalletData using the sparkViewKey. It moves the ‘Show Spark view key’ action from the main wallet navigation bar into the wallet settings menu and wraps both xPub and Spark view key display behind an authentication dialog (PinpadDialog / RequestDesktopAuthDialog). It also refactors multiple call sites to use generateNextSparkAddress(saveToDB: true) instead of manually writing to the Isar database. The changes are largely defensive UI/UX hardening rather than a fix for an exploitable bug.
Changed components
Firo wallet implementation (`firo_wallet.dart`)Spark interface (`spark_interface.dart`)View-only wallet creation and restore flowsWallet settings / Spark view key viewReceive and address-book Spark address handlingDesktop wallet options buttonInspect captured patch +934 / −941
diff --git a/lib/pages/add_wallet_views/new_wallet_options/new_wallet_options_view.dart b/lib/pages/add_wallet_views/new_wallet_options/new_wallet_options_view.dart
index bfe41c3..1e7e9b9 100644
--- a/lib/pages/add_wallet_views/new_wallet_options/new_wallet_options_view.dart
+++ b/lib/pages/add_wallet_views/new_wallet_options/new_wallet_options_view.dart
@@ -22,24 +22,23 @@ import '../../../widgets/desktop/desktop_scaffold.dart';
import '../../../widgets/desktop/primary_button.dart';
import '../../../widgets/rounded_white_container.dart';
import '../../../widgets/stack_text_field.dart';
+import '../../../widgets/toggle.dart';
import '../create_or_restore_wallet_view/sub_widgets/coin_image.dart';
import '../new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart';
import '../restore_wallet_view/restore_options_view/sub_widgets/mobile_mnemonic_length_selector.dart';
import '../restore_wallet_view/sub_widgets/mnemonic_word_count_select_sheet.dart';
-final pNewWalletOptions = StateProvider<
- ({
- String mnemonicPassphrase,
- int mnemonicWordsCount,
- bool convertToViewOnly,
- })?>(
- (ref) => null,
-);
+final pNewWalletOptions =
+ StateProvider<
+ ({
+ String mnemonicPassphrase,
+ int mnemonicWordsCount,
+ bool convertToViewOnly,
+ bool convertToViewOnlySpark,
+ })?
+ >((ref) => null);
-enum NewWalletOptions {
- Default,
- Advanced;
-}
+enum NewWalletOptions { Default, Advanced }
class NewWalletOptionsView extends ConsumerStatefulWidget {
const NewWalletOptionsView({
@@ -66,6 +65,7 @@ class _NewWalletOptionsViewState extends ConsumerState<NewWalletOptionsView> {
NewWalletOptions _selectedOptions = NewWalletOptions.Default;
bool _convertToViewOnly = false;
+ bool _firoFlag = true;
@override
void initState() {
@@ -94,17 +94,15 @@ class _NewWalletOptionsViewState extends ConsumerState<NewWalletOptionsView> {
leading: AppBarBackButton(),
trailing: ExitToMyStackButton(),
),
- body: SizedBox(
- width: 480,
- child: child,
- ),
+ body: SizedBox(width: 480, child: child),
),
child: ConditionalParent(
condition: !Util.isDesktop,
builder: (child) => Background(
child: Scaffold(
- backgroundColor:
- Theme.of(context).extension<StackColors>()!.background,
+ backgroundColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
appBar: AppBar(
leading: const AppBarBackButton(),
title: Text(
@@ -135,20 +133,10 @@ class _NewWalletOptionsViewState extends ConsumerState<NewWalletOptionsView> {
),
child: Column(
children: [
- if (Util.isDesktop)
- const Spacer(
- flex: 10,
- ),
+ if (Util.isDesktop) const Spacer(flex: 10),
+ if (!Util.isDesktop) const SizedBox(height: 16),
if (!Util.isDesktop)
- const SizedBox(
- height: 16,
- ),
- if (!Util.isDesktop)
- CoinImage(
- coin: widget.coin,
- height: 100,
- width: 100,
- ),
+ CoinImage(coin: widget.coin, height: 100, width: 100),
if (Util.isDesktop)
Text(
"Wallet options",
@@ -157,9 +145,7 @@ class _NewWalletOptionsViewState extends ConsumerState<NewWalletOptionsView> {
? STextStyles.desktopH2(context)
: STextStyles.pageTitleH1(context),
),
- SizedBox(
- height: Util.isDesktop ? 32 : 16,
- ),
+ SizedBox(height: Util.isDesktop ? 32 : 16),
DropdownButtonHideUnderline(
child: DropdownButton2<NewWalletOptions>(
value: _selectedOptions,
@@ -187,34 +173,29 @@ class _NewWalletOptionsViewState extends ConsumerState<NewWalletOptionsView> {
Assets.svg.chevronDown,
width: 12,
height: 6,
- color: Theme.of(context)
- .extension<StackColors>()!
- .textFieldActiveSearchIconRight,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldActiveSearchIconRight,
),
),
dropdownStyleData: DropdownStyleData(
offset: const Offset(0, -10),
elevation: 0,
decoration: BoxDecoration(
- color: Theme.of(context)
- .extension<StackColors>()!
- .textFieldDefaultBG,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
),
menuItemStyleData: const MenuItemStyleData(
- padding: EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 8,
- ),
+ padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
),
),
- const SizedBox(
- height: 24,
- ),
+ const SizedBox(height: 24),
if (_selectedOptions == NewWalletOptions.Advanced)
Column(
children: [
@@ -238,8 +219,9 @@ class _NewWalletOptionsViewState extends ConsumerState<NewWalletOptionsView> {
onChanged: (value) {
if (value is int) {
ref
- .read(mnemonicWordCountStateProvider.state)
- .state = value;
+ .read(mnemonicWordCountStateProvider.state)
+ .state =
+ value;
}
},
isExpanded: true,
@@ -257,9 +239,9 @@ class _NewWalletOptionsViewState extends ConsumerState<NewWalletOptionsView> {
offset: const Offset(0, -10),
elevation: 0,
decoration: BoxDecoration(
- color: Theme.of(context)
- .extension<StackColors>()!
- .textFieldDefaultBG,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
@@ -293,30 +275,28 @@ class _NewWalletOptionsViewState extends ConsumerState<NewWalletOptionsView> {
},
),
if (widget.coin.hasMnemonicPassphraseSupport)
- const SizedBox(
- height: 24,
- ),
+ const SizedBox(height: 24),
if (widget.coin.hasMnemonicPassphraseSupport)
RoundedWhiteContainer(
child: Center(
child: Text(
"You may add a BIP39 passphrase. This is optional. "
- "You will need BOTH your seed and your passphrase to recover the wallet.",
+ "You will need BOTH your seed and your passphrase to "
+ "recover the wallet.",
style: Util.isDesktop
- ? STextStyles.desktopTextExtraSmall(context)
- .copyWith(
- color: Theme.of(context)
- .extension<StackColors>()!
- .textSubtitle1,
+ ? STextStyles.desktopTextExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textSubtitle1,
)
: STextStyles.itemSubtitle(context),
),
),
),
if (widget.coin.hasMnemonicPassphraseSupport)
- const SizedBox(
- height: 8,
- ),
+ const SizedBox(height: 8),
if (widget.coin.hasMnemonicPassphraseSupport)
ClipRRect(
borderRadius: BorderRadius.circular(
@@ -327,89 +307,121 @@ class _NewWalletOptionsViewState extends ConsumerState<NewWalletOptionsView> {
focusNode: passwordFocusNode,
controller: passwordController,
style: Util.isDesktop
- ? STextStyles.desktopTextMedium(context).copyWith(
- height: 2,
- )
+ ? STextStyles.desktopTextMedium(
+ context,
+ ).copyWith(height: 2)
: STextStyles.field(context),
obscureText: hidePassword,
enableSuggestions: false,
autocorrect: false,
- decoration: standardInputDecoration(
- "BIP39 passphrase",
- passwordFocusNode,
- context,
- ).copyWith(
- suffixIcon: UnconstrainedBox(
- child: ConditionalParent(
- condition: Util.isDesktop,
- builder: (child) => SizedBox(
- height: 70,
- child: child,
- ),
- child: Row(
- children: [
- SizedBox(
- width: Util.isDesktop ? 24 : 16,
- ),
- GestureDetector(
- key: const Key(
- "mnemonicPassphraseFieldShowPasswordButtonKey",
- ),
- onTap: () async {
- setState(() {
- hidePassword = !hidePassword;
- });
- },
- child: SvgPicture.asset(
- hidePassword
- ? Assets.svg.eye
- : Assets.svg.eyeSlash,
- color: Theme.of(context)
- .extension<StackColors>()!
- .textDark3,
- width: Util.isDesktop ? 24 : 16,
- height: Util.isDesktop ? 24 : 16,
- ),
+ decoration:
+ standardInputDecoration(
+ "BIP39 passphrase",
+ passwordFocusNode,
+ context,
+ ).copyWith(
+ suffixIcon: UnconstrainedBox(
+ child: ConditionalParent(
+ condition: Util.isDesktop,
+ builder: (child) =>
+ SizedBox(height: 70, child: child),
+ child: Row(
+ children: [
+ SizedBox(width: Util.isDesktop ? 24 : 16),
+ GestureDetector(
+ key: const Key(
+ "mnemonicPassphraseFieldShowPasswordButtonKey",
+ ),
+ onTap: () async {
+ setState(() {
+ hidePassword = !hidePassword;
+ });
+ },
+ child: SvgPicture.asset(
+ hidePassword
+ ? Assets.svg.eye
+ : Assets.svg.eyeSlash,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textDark3,
+ width: Util.isDesktop ? 24 : 16,
+ height: Util.isDesktop ? 24 : 16,
+ ),
+ ),
+ const SizedBox(width: 12),
+ ],
),
- const SizedBox(
- width: 12,
- ),
- ],
+ ),
),
),
- ),
- ),
),
),
if (widget.coin is ViewOnlyOptionCurrencyInterface)
- const SizedBox(
- height: 24,
- ),
+ const SizedBox(height: 24),
if (widget.coin is ViewOnlyOptionCurrencyInterface)
CheckboxTextButton(
- label: "Convert to view only wallet. "
+ label:
+ "Convert to view only wallet. "
"You will only be shown the seed phrase once. "
"Save it somewhere. "
- "If you lose it you will lose access to any funds in this wallet.",
+ "If you lose it you will lose access to any funds in"
+ " this wallet.",
onChanged: (value) {
_convertToViewOnly = value;
+ if (mounted && widget.coin is Firo) {
+ setState(() {});
+ }
},
),
+ if (_convertToViewOnly &&
+ widget.coin is ViewOnlyOptionCurrencyInterface &&
+ widget.coin is Firo)
+ const SizedBox(height: 24),
+ if (_convertToViewOnly &&
+ widget.coin is ViewOnlyOptionCurrencyInterface &&
+ widget.coin is Firo)
+ SizedBox(
+ height: 48,
+ child: Toggle(
+ key: UniqueKey(),
+ onColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.popupBG,
+ offColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
+ onText: "Spark",
+ offText: "XPub",
+ isOn: !_firoFlag,
+ onValueChanged: (value) {
+ FocusManager.instance.primaryFocus?.unfocus();
+ setState(() {
+ _firoFlag = !value;
+ });
+ },
+ decoration: BoxDecoration(
+ color: Colors.transparent,
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ ),
+ ),
+ ),
],
),
if (!Util.isDesktop) const Spacer(),
- SizedBox(
- height: Util.isDesktop ? 32 : 16,
- ),
+ SizedBox(height: Util.isDesktop ? 32 : 16),
PrimaryButton(
label: "Continue",
onPressed: () {
if (_selectedOptions == NewWalletOptions.Advanced) {
ref.read(pNewWalletOptions.notifier).state = (
- mnemonicWordsCount:
- ref.read(mnemonicWordCountStateProvider.state).state,
+ mnemonicWordsCount: ref
+ .read(mnemonicWordCountStateProvider.state)
+ .state,
mnemonicPassphrase: passwordController.text,
convertToViewOnly: _convertToViewOnly,
+ convertToViewOnlySpark: _convertToViewOnly && _firoFlag,
);
} else {
ref.read(pNewWalletOptions.notifier).state = null;
@@ -417,21 +429,12 @@ class _NewWalletOptionsViewState extends ConsumerState<NewWalletOptionsView> {
Navigator.of(context).pushNamed(
NewWalletRecoveryPhraseWarningView.routeName,
- arguments: Tuple2(
- widget.walletName,
- widget.coin,
- ),
+ arguments: Tuple2(widget.walletName, widget.coin),
);
},
),
- if (!Util.isDesktop)
- const SizedBox(
- height: 16,
- ),
- if (Util.isDesktop)
- const Spacer(
- flex: 15,
- ),
+ if (!Util.isDesktop) const SizedBox(height: 16),
+ if (Util.isDesktop) const Spacer(flex: 15),
],
),
),
diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_view_only_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_view_only_wallet_view.dart
index b4a6953..4dd084f 100644
--- a/lib/pages/add_wallet_views/restore_wallet_view/restore_view_only_wallet_view.dart
+++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_view_only_wallet_view.dart
@@ -108,6 +108,7 @@ class _RestoreViewOnlyWalletViewState
ViewOnlyWalletType viewOnlyWalletType = _walletType;
if (widget.coin is Bip39HDCurrency) {
+ // already set above
} else if (widget.coin is CryptonoteCurrency) {
viewOnlyWalletType = ViewOnlyWalletType.cryptonote;
} else {
@@ -125,6 +126,7 @@ class _RestoreViewOnlyWalletViewState
name: widget.walletName,
restoreHeight: widget.restoreBlockHeight,
otherDataJsonString: jsonEncode(otherDataJson),
+ overrideAddressType: viewOnlyWalletType == .spark ? .spark : null,
);
bool isRestoring = true;
diff --git a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart
index e57507c..c00dab0 100644
--- a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart
+++ b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart
@@ -39,6 +39,7 @@ import '../../../wallets/wallet/impl/xelis_wallet.dart';
import '../../../wallets/wallet/intermediate/cryptonote_wallet.dart';
import '../../../wallets/wallet/wallet.dart';
import '../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart';
+import '../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
import '../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart';
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
import '../../../widgets/desktop/desktop_app_bar.dart';
@@ -101,14 +102,16 @@ class _VerifyRecoveryPhraseViewState
return result == "verified";
}
- Future<void> _convertToViewOnly() async {
+ Future<void> _convertToViewOnly(bool firoSpark) async {
int height = 0;
final Map<String, dynamic> otherDataJson = {
WalletInfoKeys.isViewOnlyKey: true,
};
final ViewOnlyWalletType viewOnlyWalletType;
- if (widget.wallet is ExtendedKeysInterface) {
+ if (firoSpark) {
+ viewOnlyWalletType = .spark;
+ } else if (widget.wallet is ExtendedKeysInterface) {
viewOnlyWalletType = ViewOnlyWalletType.xPub;
} else if (widget.wallet is CryptonoteWallet) {
if (widget.wallet.cryptoCurrency is Monero) {
@@ -143,10 +146,18 @@ class _VerifyRecoveryPhraseViewState
name: widget.wallet.info.name,
restoreHeight: height,
otherDataJsonString: jsonEncode(otherDataJson),
+ overrideAddressType: viewOnlyWalletType == .spark ? .spark : null,
);
final ViewOnlyWalletData viewOnlyData;
- if (widget.wallet is ExtendedKeysInterface) {
+ if (viewOnlyWalletType == .spark) {
+ final sparkViewKey = (widget.wallet as SparkInterface).sparkViewKey;
+
+ viewOnlyData = SparkViewOnlyWalletData(
+ walletId: voInfo.walletId,
+ viewKey: sparkViewKey!,
+ );
+ } else if (widget.wallet is ExtendedKeysInterface) {
final extendedKeyInfo = await (widget.wallet as ExtendedKeysInterface)
.getXPubs();
final testPath = (_coin as Bip39HDCurrency).constructDerivePath(
@@ -299,7 +310,9 @@ class _VerifyRecoveryPhraseViewState
try {
Exception? ex;
await showLoading(
- whileFuture: _convertToViewOnly(),
+ whileFuture: _convertToViewOnly(
+ ref.read(pNewWalletOptions)?.convertToViewOnlySpark == true,
+ ),
context: context,
message: "Converting to view only wallet",
rootNavigator: Util.isDesktop,
diff --git a/lib/pages/address_book_views/address_book_view.dart b/lib/pages/address_book_views/address_book_view.dart
index 64af275..4a873cb 100644
--- a/lib/pages/address_book_views/address_book_view.dart
+++ b/lib/pages/address_book_views/address_book_view.dart
@@ -64,8 +64,9 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
final coins = [...AppConfig.coins];
coins.removeWhere((e) => e is Firo && e.network.isTestNet);
- final bool showTestNet =
- ref.read(prefsChangeNotifierProvider).showTestNetCoins;
+ final bool showTestNet = ref
+ .read(prefsChangeNotifierProvider)
+ .showTestNetCoins;
if (showTestNet) {
ref.read(addressBookFilterProvider).addAll(coins, false);
@@ -88,10 +89,7 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
final String addressString;
if (wallet is SparkInterface) {
Address? address = await wallet.getCurrentReceivingSparkAddress();
- if (address == null) {
- address = await wallet.generateNextSparkAddress();
- await ref.read(mainDBProvider).updateOrPutAddresses([address]);
- }
+ address ??= await wallet.generateNextSparkAddress(saveToDB: true);
addressString = address.value;
} else {
final address = await wallet.getCurrentReceivingAddress();
@@ -137,8 +135,9 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
builder: (child) {
return Background(
child: Scaffold(
- backgroundColor:
- Theme.of(context).extension<StackColors>()!.background,
+ backgroundColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () {
@@ -162,16 +161,14 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
key: const Key("addressBookFilterViewButton"),
size: 36,
shadows: const [],
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.background,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
icon: SvgPicture.asset(
Assets.svg.filter,
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.accentColorDark,
width: 20,
height: 20,
),
@@ -195,16 +192,14 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
key: const Key("addressBookAddNewContactViewButton"),
size: 36,
shadows: const [],
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.background,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
icon: SvgPicture.asset(
Assets.svg.plus,
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.accentColorDark,
width: 20,
height: 20,
),
@@ -260,38 +255,37 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
- child:
- !isDesktop
- ? TextField(
- autocorrect: Util.isDesktop ? false : true,
- enableSuggestions: Util.isDesktop ? false : true,
- controller: _searchController,
- focusNode: _searchFocusNode,
- onChanged: (value) {
- setState(() {
- _searchTerm = value;
- });
- },
- style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Search",
- _searchFocusNode,
- context,
- ).copyWith(
- prefixIcon: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 10,
- vertical: 16,
- ),
- child: SvgPicture.asset(
- Assets.svg.search,
- width: 16,
- height: 16,
+ child: !isDesktop
+ ? TextField(
+ autocorrect: Util.isDesktop ? false : true,
+ enableSuggestions: Util.isDesktop ? false : true,
+ controller: _searchController,
+ focusNode: _searchFocusNode,
+ onChanged: (value) {
+ setState(() {
+ _searchTerm = value;
+ });
+ },
+ style: STextStyles.field(context),
+ decoration:
+ standardInputDecoration(
+ "Search",
+ _searchFocusNode,
+ context,
+ ).copyWith(
+ prefixIcon: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 10,
+ vertical: 16,
+ ),
+ child: SvgPicture.asset(
+ Assets.svg.search,
+ width: 16,
+ height: 16,
+ ),
),
- ),
- suffixIcon:
- _searchController.text.isNotEmpty
- ? Padding(
+ suffixIcon: _searchController.text.isNotEmpty
+ ? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
@@ -309,10 +303,10 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
),
),
)
- : null,
- ),
- )
- : null,
+ : null,
+ ),
+ )
+ : null,
),
if (!isDesktop) const SizedBox(height: 16),
Text("Favorites", style: STextStyles.smallMed12(context)),
@@ -324,16 +318,15 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
children: [
...contacts
.where(
- (element) =>
- element.addressesSorted
- .where(
- (e) => ref.watch(
- addressBookFilterProvider.select(
- (value) => value.coins.contains(e.coin),
- ),
- ),
- )
- .isNotEmpty,
+ (element) => element.addressesSorted
+ .where(
+ (e) => ref.watch(
+ addressBookFilterProvider.select(
+ (value) => value.coins.contains(e.coin),
+ ),
+ ),
+ )
+ .isNotEmpty,
)
.where(
(e) =>
@@ -375,17 +368,15 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
children: [
...contacts
.where(
- (element) =>
- element.addressesSorted
- .where(
- (e) => ref.watch(
- addressBookFilterProvider.select(
- (value) =>
- value.coins.contains(e.coin),
- ),
- ),
- )
- .isNotEmpty,
+ (element) => element.addressesSorted
+ .where(
+ (e) => ref.watch(
+ addressBookFilterProvider.select(
+ (value) => value.coins.contains(e.coin),
+ ),
+ ),
+ )
+ .isNotEmpty,
)
.where(
(e) => ref
diff --git a/lib/pages/receive_view/receive_view.dart b/lib/pages/receive_view/receive_view.dart
index 601bd73..7ef4f26 100644
--- a/lib/pages/receive_view/receive_view.dart
+++ b/lib/pages/receive_view/receive_view.dart
@@ -178,17 +178,19 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
final Address? address;
if (wallet is Bip39HDWallet && wallet is! BCashInterface) {
DerivePathType? type;
- if (wallet.isViewOnly && wallet is ExtendedKeysInterface) {
- final voData = await wallet.getViewOnlyWalletData();
+ if (wallet.isViewOnly &&
+ wallet is ExtendedKeysInterface &&
+ wallet.viewOnlyType != .spark) {
+ final voData =
+ await wallet.getViewOnlyWalletData()
+ as ExtendedKeysViewOnlyWalletData;
for (final t in wallet.cryptoCurrency.supportedDerivationPathTypes) {
final testPath = wallet.cryptoCurrency.constructDerivePath(
derivePathType: t,
chain: 0,
index: 0,
);
- if (voData is SparkViewOnlyWalletData) {
- type = t;
- } else if (testPath.startsWith((voData as ExtendedKeysViewOnlyWalletData).xPubs.first.path)) {
+ if (testPath.startsWith(voData.xPubs.first.path)) {
type = t;
break;
}
@@ -255,10 +257,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
),
);
- final address = await wallet.generateNextSparkAddress();
- await ref.read(mainDBProvider).isar.writeTxn(() async {
- await ref.read(mainDBProvider).isar.addresses.put(address);
- });
+ final address = await wallet.generateNextSparkAddress(saveToDB: true);
shouldPop = true;
@@ -336,6 +335,9 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly) {
_showMultiType = false;
+ if (wallet.viewOnlyType == .spark) {
+ _walletAddressTypes.add(.spark);
+ }
} else {
_showMultiType =
_supportsSpark ||
@@ -345,7 +347,9 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
wallet.supportedAddressTypes.length > 1);
}
- _walletAddressTypes.add(wallet.info.mainAddressType);
+ if (_walletAddressTypes.isEmpty) {
+ _walletAddressTypes.add(wallet.info.mainAddressType);
+ }
if (_showMultiType) {
if (_supportsSpark) {
diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart
index bae20da..57808de 100644
--- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart
+++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_view.dart
@@ -30,6 +30,7 @@ import '../../../services/event_bus/events/global/wallet_sync_status_changed_eve
import '../../../services/event_bus/global_event_bus.dart';
import '../../../themes/stack_colors.dart';
import '../../../utilities/assets.dart';
+import '../../../utilities/if_not_already.dart';
import '../../../utilities/show_loading.dart';
import '../../../utilities/text_styles.dart';
import '../../../utilities/util.dart';
@@ -42,6 +43,7 @@ import '../../../wallets/wallet/impl/mimblewimblecoin_wallet.dart';
import '../../../wallets/wallet/intermediate/cryptonote_wallet.dart';
import '../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart';
import '../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
+import '../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
import '../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart';
import '../../../widgets/background.dart';
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
@@ -57,6 +59,7 @@ import 'frost_ms/frost_ms_options_view.dart';
import 'wallet_backup_views/wallet_backup_view.dart';
import 'wallet_network_settings_view/wallet_network_settings_view.dart';
import 'wallet_settings_wallet_settings/change_representative_view.dart';
+import 'wallet_settings_wallet_settings/spark_view_key_view.dart';
import 'wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart';
import 'wallet_settings_wallet_settings/xpub_view.dart';
@@ -88,6 +91,7 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
late final CryptoCurrency coin;
late String xpub;
late final bool xPubEnabled;
+ late final bool sparkViewKeyEnabled;
late final EventBus eventBus;
@@ -97,6 +101,162 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
late StreamSubscription<dynamic> _syncStatusSubscription;
// late StreamSubscription _nodeStatusSubscription;
+ late final VoidCallback _walletBackupPressed;
+ late final VoidCallback _walletXPubPressed;
+ late final VoidCallback _walletSparkViewKeyPressed;
+
+ Future<void> __walletSparkViewKeyPressedHelper() async {
+ final wallet = ref.read(pWallets).getWallet(walletId) as SparkInterface;
+ final sparkViewKeyHex = wallet.sparkViewKey!;
+
+ if (mounted) {
+ await Navigator.push(
+ context,
+ RouteGenerator.getRoute(
+ shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute,
+ builder: (_) => LockscreenView(
+ routeOnSuccessArguments: (walletId, sparkViewKeyHex),
+ showBackButton: true,
+ routeOnSuccess: SparkViewKeyView.routeName,
+ biometricsCancelButtonString: "CANCEL",
+ biometricsLocalizedReason: "Authenticate to view spark view key",
+ biometricsAuthenticationTitle: "View spark view key",
+ ),
+ settings: const RouteSettings(
+ name: "/viewSparkViewKeyDataLockscreen",
+ ),
+ ),
+ );
+ }
+ }
+
+ Future<void> _walletXPubHelper() async {
+ final xpubData = await showLoading(
+ delay: const Duration(milliseconds: 800),
+ whileFuture:
+ (ref.read(pWallets).getWallet(walletId) as ExtendedKeysInterface)
+ .getXPubs(),
+ context: context,
+ message: "Loading xpubs",
+ rootNavigator: Util.isDesktop,
+ );
+
+ if (mounted) {
+ await Navigator.push(
+ context,
+ RouteGenerator.getRoute(
+ shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute,
+ builder: (_) => LockscreenView(
+ routeOnSuccessArguments: (walletId, xpubData!),
+ showBackButton: true,
+ routeOnSuccess: XPubView.routeName,
+ biometricsCancelButtonString: "CANCEL",
+ biometricsLocalizedReason: "Authenticate to view xpub data",
+ biometricsAuthenticationTitle: "View xpub data",
+ ),
+ settings: const RouteSettings(name: "/viewXPubDataLockscreen"),
+ ),
+ );
+ }
+ }
+
+ Future<void> _walletBackupPressedHelper() async {
+ // TODO: [prio=med] take wallets that don't have a mnemonic into account
+
+ final wallet = ref.read(pWallets).getWallet(widget.walletId);
+
+ List<String>? mnemonic;
+ ({
+ String myName,
+ String config,
+ String keys,
+ ({String config, String keys})? prevGen,
+ })?
+ frostWalletData;
+ if (wallet is BitcoinFrostWallet) {
+ final futures = [
+ wallet.getSerializedKeys(),
+ wallet.getMultisigConfig(),
+ wallet.getSerializedKeysPrevGen(),
+ wallet.getMultisigConfigPrevGen(),
+ ];
+
+ final results = await Future.wait(futures);
+
+ if (results.length == 4) {
+ frostWalletData = (
+ myName: wallet.frostInfo.myName,
+ config: results[1]!,
+ keys: results[0]!,
+ prevGen: results[2] == null || results[3] == null
+ ? null
+ : (config: results[3]!, keys: results[2]!),
+ );
+ }
+ } else {
+ if (wallet is MnemonicInterface) {
+ if (wallet is ViewOnlyOptionInterface &&
+ (wallet as ViewOnlyOptionInterface).isViewOnly) {
+ // TODO: is something needed here?
+ } else {
+ mnemonic = await wallet.getMnemonicAsWords();
+ }
+ }
+ }
+
+ KeyDataInterface? keyData;
+ if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly) {
+ keyData = await wallet.getViewOnlyWalletData();
+ } else if (wallet is ExtendedKeysInterface) {
+ keyData = await wallet.getXPrivs();
+ } else if (wallet is CryptonoteWallet) {
+ keyData = await wallet.getKeys();
+ }
+
+ if (mounted) {
+ if (keyData != null &&
+ wallet is ViewOnlyOptionInterface &&
+ wallet.isViewOnly) {
+ await Navigator.push(
+ context,
+ RouteGenerator.getRoute(
+ shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute,
+ builder: (_) => LockscreenView(
+ routeOnSuccessArguments: (walletId: walletId, keyData: keyData),
+ showBackButton: true,
+ routeOnSuccess: MobileKeyDataView.routeName,
+ biometricsCancelButtonString: "CANCEL",
+ biometricsLocalizedReason: "Authenticate to view recovery data",
+ biometricsAuthenticationTitle: "View recovery data",
+ ),
+ settings: const RouteSettings(name: "/viewRecoveryDataLockscreen"),
+ ),
+ );
+ } else {
+ await Navigator.push(
+ context,
+ RouteGenerator.getRoute(
+ shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute,
+ builder: (_) => LockscreenView(
+ routeOnSuccessArguments: (
+ walletId: walletId,
+ mnemonic: mnemonic ?? [],
+ frostWalletData: frostWalletData,
+ keyData: keyData,
+ ),
+ showBackButton: true,
+ routeOnSuccess: WalletBackupView.routeName,
+ biometricsCancelButtonString: "CANCEL",
+ biometricsLocalizedReason: "Authenticate to view recovery phrase",
+ biometricsAuthenticationTitle: "View recovery phrase",
+ ),
+ settings: const RouteSettings(name: "/viewRecoverPhraseLockscreen"),
+ ),
+ );
+ }
+ }
+ }
+
@override
void initState() {
walletId = widget.walletId;
@@ -105,8 +265,10 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
final wallet = ref.read(pWallets).getWallet(walletId);
if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly) {
xPubEnabled = false;
+ sparkViewKeyEnabled = false;
} else {
xPubEnabled = wallet is ExtendedKeysInterface;
+ sparkViewKeyEnabled = wallet is SparkInterface;
}
xpub = "";
@@ -139,6 +301,14 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
}
});
+ _walletBackupPressed = IfNotAlreadyAsync<void>(
+ _walletBackupPressedHelper,
+ ).execute;
+ _walletXPubPressed = IfNotAlreadyAsync<void>(_walletXPubHelper).execute;
+ _walletSparkViewKeyPressed = IfNotAlreadyAsync<void>(
+ __walletSparkViewKeyPressedHelper,
+ ).execute;
+
// _nodeStatusSubscription =
// eventBus.on<NodeConnectionStatusChangedEvent>().listen(
// (event) async {
@@ -262,146 +432,7 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
iconAssetName: Assets.svg.lock,
iconSize: 16,
title: "Wallet backup",
- onPressed: () async {
- // TODO: [prio=med] take wallets that don't have a mnemonic into account
-
- List<String>? mnemonic;
- ({
- String myName,
- String config,
- String keys,
- ({String config, String keys})?
- prevGen,
- })?
- frostWalletData;
- if (wallet is BitcoinFrostWallet) {
- final futures = [
- wallet.getSerializedKeys(),
- wallet.getMultisigConfig(),
- wallet
- .getSerializedKeysPrevGen(),
- wallet
- .getMultisigConfigPrevGen(),
- ];
-
- final results = await Future.wait(
- futures,
- );
-
- if (results.length == 4) {
- frostWalletData = (
- myName:
- wallet.frostInfo.myName,
- config: results[1]!,
- keys: results[0]!,
- prevGen:
- results[2] == null ||
- results[3] == null
- ? null
- : (
- config: results[3]!,
- keys: results[2]!,
- ),
- );
- }
- } else {
- if (wallet is MnemonicInterface) {
- if (wallet
- is ViewOnlyOptionInterface &&
- (wallet as ViewOnlyOptionInterface)
- .isViewOnly) {
- // TODO: is something needed here?
- } else {
- mnemonic = await wallet
- .getMnemonicAsWords();
- }
- }
- }
-
- KeyDataInterface? keyData;
- if (wallet
- is ViewOnlyOptionInterface &&
- wallet.isViewOnly) {
- keyData = await wallet
- .getViewOnlyWalletData();
- } else if (wallet
- is ExtendedKeysInterface) {
- keyData = await wallet
- .getXPrivs();
- } else if (wallet
- is CryptonoteWallet) {
- keyData = await wallet.getKeys();
- }
-
- if (context.mounted) {
- if (keyData != null &&
- wallet
- is ViewOnlyOptionInterface &&
- wallet.isViewOnly) {
- await Navigator.push(
- context,
- RouteGenerator.getRoute(
- shouldUseMaterialRoute:
- RouteGenerator
- .useMaterialPageRoute,
- builder: (_) => LockscreenView(
- routeOnSuccessArguments: (
- walletId: walletId,
- keyData: keyData,
- ),
- showBackButton: true,
- routeOnSuccess:
- MobileKeyDataView
- .routeName,
- biometricsCancelButtonString:
- "CANCEL",
- biometricsLocalizedReason:
- "Authenticate to view recovery data",
- biometricsAuthenticationTitle:
- "View recovery data",
- ),
- settings: const RouteSettings(
- name:
- "/viewRecoveryDataLockscreen",
- ),
- ),
- );
- } else {
- await Navigator.push(
- context,
- RouteGenerator.getRoute(
- shouldUseMaterialRoute:
- RouteGenerator
- .useMaterialPageRoute,
- builder: (_) => LockscreenView(
- routeOnSuccessArguments: (
- walletId: walletId,
- mnemonic:
- mnemonic ?? [],
- frostWalletData:
- frostWalletData,
- keyData: keyData,
- ),
- showBackButton: true,
- routeOnSuccess:
- WalletBackupView
- .routeName,
- biometricsCancelButtonString:
- "CANCEL",
- biometricsLocalizedReason:
- "Authenticate to view recovery phrase",
- biometricsAuthenticationTitle:
- "View recovery phrase",
- ),
- settings: const RouteSettings(
- name:
- "/viewRecoverPhraseLockscreen",
- ),
- ),
- );
- }
- }
- },
+ onPressed: _walletBackupPressed,
);
},
),
@@ -435,35 +466,19 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
return SettingsListButton(
iconAssetName: Assets.svg.eye,
title: "Wallet xPub",
- onPressed: () async {
- final xpubData = await showLoading(
- delay: const Duration(
- milliseconds: 800,
- ),
- whileFuture:
- (ref
- .read(pWallets)
- .getWallet(
- walletId,
- )
- as ExtendedKeysInterface)
- .getXPubs(),
- context: context,
- message: "Loading xpubs",
- rootNavigator: Util.isDesktop,
- );
- if (context.mounted) {
- await Navigator.of(
- context,
- ).pushNamed(
- XPubView.routeName,
- arguments: (
- widget.walletId,
- xpubData,
- ),
- );
- }
- },
+ onPressed: _walletXPubPressed,
+ );
+ },
+ ),
+ if (sparkViewKeyEnabled)
+ const SizedBox(height: 8),
+ if (sparkViewKeyEnabled)
+ Consumer(
+ builder: (_, ref, __) {
+ return SettingsListButton(
+ iconAssetName: Assets.svg.eye,
+ title: "Spark view key",
+ onPressed: _walletSparkViewKeyPressed,
);
},
),
diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_view_key_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_view_key_view.dart
index 13c24d5..8364d83 100644
--- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_view_key_view.dart
+++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_view_key_view.dart
@@ -13,12 +13,11 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:flutter_svg/svg.dart';
+import '../../../../notifications/show_flush_bar.dart';
import '../../../../themes/stack_colors.dart';
import '../../../../utilities/assets.dart';
import '../../../../utilities/clipboard_interface.dart';
-import '../../../../utilities/constants.dart';
import '../../../../utilities/text_styles.dart';
import '../../../../utilities/util.dart';
import '../../../../widgets/background.dart';
@@ -27,10 +26,8 @@ import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
import '../../../../widgets/desktop/desktop_dialog.dart';
import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../../../widgets/desktop/primary_button.dart';
-import '../../../../widgets/detail_item.dart';
import '../../../../widgets/qr.dart';
import '../../../../widgets/rounded_white_container.dart';
-import '../../../../notifications/show_flush_bar.dart';
class SparkViewKeyView extends ConsumerStatefulWidget {
const SparkViewKeyView({
@@ -38,11 +35,13 @@ class SparkViewKeyView extends ConsumerStatefulWidget {
required this.walletId,
required this.sparkViewKeyHex,
this.clipboardInterface = const ClipboardWrapper(),
+ this.showDesktopDialogTitle = true,
});
final String walletId;
final String sparkViewKeyHex;
final ClipboardInterface clipboardInterface;
+ final bool showDesktopDialogTitle;
static const String routeName = "/spark_view_key";
@@ -75,7 +74,9 @@ class _SparkViewKeyViewState extends ConsumerState<SparkViewKeyView> {
condition: !isDesktop,
builder: (child) => Background(
child: Scaffold(
- backgroundColor: Theme.of(context).extension<StackColors>()!.background,
+ backgroundColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
@@ -112,7 +113,7 @@ class _SparkViewKeyViewState extends ConsumerState<SparkViewKeyView> {
),
),
child: ConditionalParent(
- condition: isDesktop,
+ condition: isDesktop && widget.showDesktopDialogTitle,
builder: (child) => DesktopDialog(
maxWidth: 600,
maxHeight: double.infinity,
@@ -130,7 +131,10 @@ class _SparkViewKeyViewState extends ConsumerState<SparkViewKeyView> {
),
),
DesktopDialogCloseButton(
- onPressedOverride: Navigator.of(context, rootNavigator: true).pop,
+ onPressedOverride: Navigator.of(
+ context,
+ rootNavigator: true,
+ ).pop,
),
],
),
@@ -150,12 +154,16 @@ class _SparkViewKeyViewState extends ConsumerState<SparkViewKeyView> {
SizedBox(height: Util.isDesktop ? 12 : 16),
QR(
data: widget.sparkViewKeyHex,
- size: Util.isDesktop ? 256 : MediaQuery.of(context).size.width / 1.5,
+ size: Util.isDesktop
+ ? 256
+ : MediaQuery.of(context).size.width / 1.5,
),
SizedBox(height: Util.isDesktop ? 12 : 16),
RoundedWhiteContainer(
borderColor: Util.isDesktop
- ? Theme.of(context).extension<StackColors>()!.textFieldDefaultBG
+ ? Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG
: null,
child: SelectableText(
widget.sparkViewKeyHex,
@@ -168,7 +176,9 @@ class _SparkViewKeyViewState extends ConsumerState<SparkViewKeyView> {
children: [
if (Util.isDesktop) const Spacer(),
if (Util.isDesktop) const SizedBox(width: 16),
- Expanded(child: PrimaryButton(label: "Copy", onPressed: _copy)),
+ Expanded(
+ child: PrimaryButton(label: "Copy", onPressed: _copy),
+ ),
],
),
],
@@ -176,4 +186,4 @@ class _SparkViewKeyViewState extends ConsumerState<SparkViewKeyView> {
),
);
}
-}
\ No newline at end of file
+}
diff --git a/lib/pages/spark_names/buy_spark_name_view.dart b/lib/pages/spark_names/buy_spark_name_view.dart
index 478893d..9eeef92 100644
--- a/lib/pages/spark_names/buy_spark_name_view.dart
+++ b/lib/pages/spark_names/buy_spark_name_view.dart
@@ -80,8 +80,7 @@ class _BuySparkNameViewState extends ConsumerState<BuySparkNameView> {
Logging.instance.t(
"Found address that already has a spark name. Generating next address...",
);
- myAddress = await wallet.generateNextSparkAddress();
- await ref.read(mainDBProvider).updateOrPutAddresses([myAddress]);
+ myAddress = await wallet.generateNextSparkAddress(saveToDB: true);
}
addressController.text = myAddress!.value;
diff --git a/lib/pages/spark_names/confirm_spark_name_transaction_view.dart b/lib/pages/spark_names/confirm_spark_name_transaction_view.dart
index 3282b1f..d1f4e68 100644
--- a/lib/pages/spark_names/confirm_spark_name_transaction_view.dart
+++ b/lib/pages/spark_names/confirm_spark_name_transaction_view.dart
@@ -16,7 +16,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
-import '../../models/isar/models/blockchain_data/address.dart';
import '../../models/isar/models/transaction_note.dart';
import '../../notifications/show_flush_bar.dart';
import '../../pages_desktop_specific/coin_control/desktop_coin_control_use_dialog.dart';
@@ -132,10 +131,7 @@ class _ConfirmSparkNameTransactionViewState
final address = txData.sparkNameInfo?.sparkAddress;
final currentReceiving = await wallet.getCurrentReceivingSparkAddress();
if (currentReceiving?.value == address?.value) {
- final address = await wallet.generateNextSparkAddress();
- await ref.read(mainDBProvider).isar.writeTxn(() async {
- await ref.read(mainDBProvider).isar.addresses.put(address);
- });
+ await wallet.generateNextSparkAddress(saveToDB: true);
}
final db = ref.read(pDrift(walletId));
@@ -219,10 +215,9 @@ class _ConfirmSparkNameTransactionViewState
child: Text(
"Ok",
style: STextStyles.button(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.accentColorDark,
),
),
onPressed: () {
@@ -267,81 +262,76 @@ class _ConfirmSparkNameTransactionViewState
return ConditionalParent(
condition: !isDesktop,
- builder:
- (child) => Background(
- child: Scaffold(
- backgroundColor:
- Theme.of(context).extension<StackColors>()!.background,
- appBar: AppBar(
- backgroundColor:
- Theme.of(context).extension<StackColors>()!.background,
- leading: AppBarBackButton(
- onPressed: () async {
- // if (FocusScope.of(context).hasFocus) {
- // FocusScope.of(context).unfocus();
- // await Future<void>.delayed(Duration(milliseconds: 50));
- // }
- Navigator.of(context).pop();
- },
- ),
- title: Text(
- "Confirm transaction",
- style: STextStyles.navBarTitle(context),
- ),
- ),
- body: SafeArea(
- child: LayoutBuilder(
- builder: (builderContext, constraints) {
- return Padding(
- padding: const EdgeInsets.only(
- left: 12,
- top: 12,
- right: 12,
+ builder: (child) => Background(
+ child: Scaffold(
+ backgroundColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
+ appBar: AppBar(
+ backgroundColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
+ leading: AppBarBackButton(
+ onPressed: () async {
+ // if (FocusScope.of(context).hasFocus) {
+ // FocusScope.of(context).unfocus();
+ // await Future<void>.delayed(Duration(milliseconds: 50));
+ // }
+ Navigator.of(context).pop();
+ },
+ ),
+ title: Text(
+ "Confirm transaction",
+ style: STextStyles.navBarTitle(context),
+ ),
+ ),
+ body: SafeArea(
+ child: LayoutBuilder(
+ builder: (builderContext, constraints) {
+ return Padding(
+ padding: const EdgeInsets.only(left: 12, top: 12, right: 12),
+ child: SingleChildScrollView(
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ minHeight: constraints.maxHeight - 24,
),
- child: SingleChildScrollView(
- child: ConstrainedBox(
- constraints: BoxConstraints(
- minHeight: constraints.maxHeight - 24,
- ),
- child: IntrinsicHeight(
- child: Padding(
- padding: const EdgeInsets.all(4),
- child: child,
- ),
- ),
+ child: IntrinsicHeight(
+ child: Padding(
+ padding: const EdgeInsets.all(4),
+ child: child,
),
),
- );
- },
- ),
- ),
+ ),
+ ),
+ );
+ },
),
),
+ ),
+ ),
child: ConditionalParent(
condition: isDesktop,
- builder:
- (child) => Column(
- crossAxisAlignment: CrossAxisAlignment.stretch,
- mainAxisSize: MainAxisSize.min,
+ builder: (child) => Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Row(
children: [
- Row(
- children: [
- AppBarBackButton(
- size: 40,
- iconSize: 24,
- onPressed:
- () =>
- Navigator.of(context, rootNavigator: true).pop(),
- ),
- Text(
- "Confirm transaction",
- style: STextStyles.desktopH3(context),
- ),
- ],
+ AppBarBackButton(
+ size: 40,
+ iconSize: 24,
+ onPressed: () =>
+ Navigator.of(context, rootNavigator: true).pop(),
+ ),
+ Text(
+ "Confirm transaction",
+ style: STextStyles.desktopH3(context),
),
- Flexible(child: SingleChildScrollView(child: child)),
],
),
+ Flexible(child: SingleChildScrollView(child: child)),
+ ],
+ ),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: isDesktop ? MainAxisSize.min : MainAxisSize.max,
@@ -487,18 +477,18 @@ class _ConfirmSparkNameTransactionViewState
),
child: RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
- borderColor:
- Theme.of(context).extension<StackColors>()!.background,
+ borderColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
decoration: BoxDecoration(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.background,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(
Constants.size.circularBorderRadius,
@@ -550,24 +540,23 @@ class _ConfirmSparkNameTransactionViewState
const SizedBox(height: 2),
SelectableText(
widget.txData.sparkNameInfo!.name,
- style: STextStyles.desktopTextExtraExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(
+ style:
+ STextStyles.desktopTextExtraExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(
context,
).extension<StackColors>()!.textDark,
- ),
+ ),
),
],
),
),
Container(
height: 1,
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.background,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
),
Padding(
padding: const EdgeInsets.all(12),
@@ -584,14 +573,14 @@ class _ConfirmSparkNameTransactionViewState
const SizedBox(height: 2),
SelectableText(
widget.txData.sparkNameInfo!.additionalInfo,
- style: STextStyles.desktopTextExtraExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(
+ style:
+ STextStyles.desktopTextExtraExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(
context,
).extension<StackColors>()!.textDark,
- ),
+ ),
),
],
),
@@ -609,14 +598,12 @@ class _ConfirmSparkNameTransactionViewState
children: [
SelectableText(
"Note (optional)",
- style: STextStyles.desktopTextExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(context)
+ style: STextStyles.desktopTextExtraSmall(context)
+ .copyWith(
+ color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveSearchIconRight,
- ),
+ ),
textAlign: TextAlign.left,
),
const SizedBox(height: 10),
@@ -631,49 +618,48 @@ class _ConfirmSparkNameTransactionViewState
enableSuggestions: isDesktop ? false : true,
controller: noteController,
focusNode: _noteFocusNode,
- style: STextStyles.desktopTextExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(
+ style: STextStyles.desktopTextExtraSmall(context)
+ .copyWith(
+ color: Theme.of(
context,
).extension<StackColors>()!.textFieldActiveText,
- height: 1.8,
- ),
+ height: 1.8,
+ ),
onChanged: (_) => setState(() {}),
- decoration: standardInputDecoration(
- "Type something...",
- _noteFocusNode,
- context,
- desktopMed: true,
- ).copyWith(
- contentPadding: const EdgeInsets.only(
- left: 16,
- top: 11,
- bottom: 12,
- right: 5,
- ),
- suffixIcon:
- noteController.text.isNotEmpty
+ decoration:
+ standardInputDecoration(
+ "Type something...",
+ _noteFocusNode,
+ context,
+ desktopMed: true,
+ ).copyWith(
+ contentPadding: const EdgeInsets.only(
+ left: 16,
+ top: 11,
+ bottom: 12,
+ right: 5,
+ ),
+ suffixIcon: noteController.text.isNotEmpty
? Padding(
- padding: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- children: [
- TextFieldIconButton(
- child: const XIcon(),
- onTap: () async {
- setState(
- () => noteController.text = "",
- );
- },
- ),
- ],
+ padding: const EdgeInsets.only(right: 0),
+ child: UnconstrainedBox(
+ child: Row(
+ children: [
+ TextFieldIconButton(
+ child: const XIcon(),
+ onTap: () async {
+ setState(
+ () =>
+ noteController.text = "",
+ );
+ },
+ ),
+ ],
+ ),
),
- ),
- )
+ )
: null,
- ),
+ ),
),
),
const SizedBox(height: 20),
@@ -697,10 +683,9 @@ class _ConfirmSparkNameTransactionViewState
horizontal: 16,
vertical: 18,
),
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultBG,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
child: Builder(
builder: (context) {
final externalCalls = ref.watch(
@@ -711,21 +696,17 @@ class _ConfirmSparkNameTransactionViewState
String fiatAmount = "N/A";
if (externalCalls) {
- final price =
- ref
- .read(priceAnd24hChangeNotifierProvider)
- .getPrice(coin)
- ?.value;
+ final price = ref
+ .read(priceAnd24hChangeNotifierProvider)
+ .getPrice(coin)
+ ?.value;
if (price != null && price > Decimal.zero) {
fiatAmount = (amountWithoutChange.decimal * price)
.toAmount(fractionDigits: 2)
.fiatString(
- locale:
- ref
- .read(
- localeServiceChangeNotifierProvider,
- )
- .locale,
+ locale: ref
+ .read(localeServiceChangeNotifierProvider)
+ .locale,
);
}
}
@@ -770,10 +751,9 @@ class _ConfirmSparkNameTransactionViewState
horizontal: 16,
vertical: 18,
),
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultBG,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
child: SelectableText(
widget.txData.recipients!.first.address,
style: STextStyles.itemSubtitle(context),
@@ -797,10 +777,9 @@ class _ConfirmSparkNameTransactionViewState
horizontal: 16,
vertical: 18,
),
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultBG,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
child: SelectableText(
ref.watch(pAmountFormatter(coin)).format(fee!),
style: STextStyles.itemSubtitle(context),
@@ -827,10 +806,9 @@ class _ConfirmSparkNameTransactionViewState
horizontal: 16,
vertical: 18,
),
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultBG,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
child: SelectableText(
"~${fee!.raw.toInt() ~/ widget.txData.vSize!}",
style: STextStyles.itemSubtitle(context),
@@ -840,64 +818,52 @@ class _ConfirmSparkNameTransactionViewState
if (!isDesktop) const Spacer(),
SizedBox(height: isDesktop ? 23 : 12),
Padding(
- padding:
- isDesktop
- ? const EdgeInsets.symmetric(horizontal: 32)
- : const EdgeInsets.all(0),
+ padding: isDesktop
+ ? const EdgeInsets.symmetric(horizontal: 32)
+ : const EdgeInsets.all(0),
child: RoundedContainer(
- padding:
- isDesktop
- ? const EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 18,
- )
- : const EdgeInsets.all(12),
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.snackBarBackSuccess,
+ padding: isDesktop
+ ? const EdgeInsets.symmetric(horizontal: 16, vertical: 18)
+ : const EdgeInsets.all(12),
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.snackBarBackSuccess,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
isDesktop ? "Total amount to send" : "Total amount",
- style:
- isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(context)
- .extension<StackColors>()!
- .textConfirmTotalAmount,
- )
- : STextStyles.titleBold12(context).copyWith(
- color:
- Theme.of(context)
- .extension<StackColors>()!
- .textConfirmTotalAmount,
- ),
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .textConfirmTotalAmount,
+ )
+ : STextStyles.titleBold12(context).copyWith(
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .textConfirmTotalAmount,
+ ),
),
SelectableText(
ref
.watch(pAmountFormatter(coin))
.format(amountWithoutChange + fee!),
- style:
- isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(context)
- .extension<StackColors>()!
- .textConfirmTotalAmount,
- )
- : STextStyles.itemSubtitle12(context).copyWith(
- color:
- Theme.of(context)
- .extension<StackColors>()!
- .textConfirmTotalAmount,
- ),
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .textConfirmTotalAmount,
+ )
+ : STextStyles.itemSubtitle12(context).copyWith(
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .textConfirmTotalAmount,
+ ),
textAlign: TextAlign.right,
),
],
@@ -906,10 +872,9 @@ class _ConfirmSparkNameTransactionViewState
),
SizedBox(height: isDesktop ? 28 : 16),
Padding(
- padding:
- isDesktop
- ? const EdgeInsets.symmetric(horizontal: 32)
- : const EdgeInsets.all(0),
+ padding: isDesktop
+ ? const EdgeInsets.symmetric(horizontal: 32)
+ : const EdgeInsets.all(0),
child: PrimaryButton(
label: "Send",
buttonHeight: isDesktop ? ButtonHeight.l : null,
@@ -919,28 +884,27 @@ class _ConfirmSparkNameTransactionViewState
if (isDesktop) {
unlocked = await showDialog<bool?>(
context: context,
- builder:
- (context) => DesktopDialog(
- maxWidth: 580,
- maxHeight: double.infinity,
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- const Row(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [DesktopDialogCloseButton()],
- ),
- Padding(
- padding: const EdgeInsets.only(
- left: 32,
- right: 32,
- bottom: 32,
- ),
- child: DesktopAuthSend(coin: coin),
- ),
- ],
+ builder: (context) => DesktopDialog(
+ maxWidth: 580,
+ maxHeight: double.infinity,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [DesktopDialogCloseButton()],
),
- ),
+ Padding(
+ padding: const EdgeInsets.only(
+ left: 32,
+ right: 32,
+ bottom: 32,
+ ),
+ child: DesktopAuthSend(coin: coin),
+ ),
+ ],
+ ),
+ ),
);
} else {
unlocked = await Navigator.push(
@@ -948,18 +912,16 @@ class _ConfirmSparkNameTransactionViewState
RouteGenerator.getRoute(
shouldUseMaterialRoute:
RouteGenerator.useMaterialPageRoute,
- builder:
- (_) => const LockscreenView(
- showBackButton: true,
- popOnSuccess: true,
- routeOnSuccessArguments: true,
- routeOnSuccess: "",
- biometricsCancelButtonString: "CANCEL",
- biometricsLocalizedReason:
- "Authenticate to send transaction",
- biometricsAuthenticationTitle:
- "Confirm Transaction",
- ),
+ builder: (_) => const LockscreenView(
+ showBackButton: true,
+ popOnSuccess: true,
+ routeOnSuccessArguments: true,
+ routeOnSuccess: "",
+ biometricsCancelButtonString: "CANCEL",
+ biometricsLocalizedReason:
+ "Authenticate to send transaction",
+ biometricsAuthenticationTitle: "Confirm Transaction",
+ ),
settings: const RouteSettings(
name: "/confirmsendlockscreen",
),
@@ -975,10 +937,9 @@ class _ConfirmSparkNameTransactionViewState
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
- message:
- Util.isDesktop
- ? "Invalid passphrase"
- : "Invalid PIN",
+ message: Util.isDesktop
+ ? "Invalid passphrase"
+ : "Invalid PIN",
context: context,
),
);
diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart
index e7c8ea1..c3c6ff4 100644
--- a/lib/pages/wallet_view/wallet_view.dart
+++ b/lib/pages/wallet_view/wallet_view.dart
@@ -104,7 +104,6 @@ import '../send_view/frost_ms/frost_send_view.dart';
import '../send_view/send_view.dart';
import '../settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart';
import '../settings_views/wallet_settings_view/wallet_settings_view.dart';
-import '../settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_view_key_view.dart';
import '../signing/signing_view.dart';
import '../spark_names/spark_names_home_view.dart';
import '../token_view/my_tokens_view.dart';
@@ -423,29 +422,6 @@ class _WalletViewState extends ConsumerState<WalletView> {
}
}
- Future<void> _onShowSparkViewKeyPressed(BuildContext context) async {
- unawaited(
- showDialog(
- context: context,
- builder: (_) => const LoadingIndicator(width: 100),
- ),
- );
-
- final wallet = ref.read(pWallets).getWallet(walletId) as SparkInterface;
- // this should not be needed here as its already called when entering a
- // wallet
- // await wallet.init();
- final sparkViewKeyHex = wallet.viewKeyHex;
-
- if (context.mounted) {
- Navigator.of(context).pop(); // Close loading dialog
- await Navigator.of(context).pushNamed(
- SparkViewKeyView.routeName,
- arguments: (walletId, sparkViewKeyHex),
- );
- }
- }
-
Future<void> attemptAnonymize() async {
bool shouldPop = false;
unawaited(
@@ -1119,22 +1095,6 @@ class _WalletViewState extends ConsumerState<WalletView> {
),
],
moreItems: <WalletNavigationBarItemData>[
- if (wallet is SparkInterface)
- WalletNavigationBarItemData(
- label: "Show Spark View Key",
- icon: SvgPicture.asset(
- Assets.svg.eye,
- height: 20,
- width: 20,
- colorFilter: ColorFilter.mode(
- Theme.of(
- context,
- ).extension<StackColors>()!.bottomNavIconIcon,
- BlendMode.srcIn,
- ),
- ),
- onTap: () => _onShowSparkViewKeyPressed(context),
- ),
if (ref.watch(
pWallets.select(
(value) => value
diff --git a/lib/pages_desktop_specific/address_book_view/desktop_address_book.dart b/lib/pages_desktop_specific/address_book_view/desktop_address_book.dart
index f27147c..87a04a3 100644
--- a/lib/pages_desktop_specific/address_book_view/desktop_address_book.dart
+++ b/lib/pages_desktop_specific/address_book_view/desktop_address_book.dart
@@ -17,7 +17,6 @@ import '../../models/isar/models/blockchain_data/address.dart';
import '../../models/isar/models/contact_entry.dart';
import '../../pages/address_book_views/subviews/add_address_book_entry_view.dart';
import '../../pages/address_book_views/subviews/address_book_filter_view.dart';
-import '../../providers/db/main_db_provider.dart';
import '../../providers/global/address_book_service_provider.dart';
import '../../providers/providers.dart';
import '../../providers/ui/address_book_providers/address_book_filter_provider.dart';
@@ -99,17 +98,18 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
// if (widget.coin == null) {
final coins = AppConfig.coins.toList();
- coins.removeWhere(
- (e) => e is Firo && e.network.isTestNet,
- );
+ coins.removeWhere((e) => e is Firo && e.network.isTestNet);
- final bool showTestNet =
- ref.read(prefsChangeNotifierProvider).showTestNetCoins;
+ final bool showTestNet = ref
+ .read(prefsChangeNotifierProvider)
+ .showTestNetCoins;
if (showTestNet) {
ref.read(addressBookFilterProvider).addAll(coins, false);
} else {
- ref.read(addressBookFilterProvider).addAll(
+ ref
+ .read(addressBookFilterProvider)
+ .addAll(
coins.where((e) => e.network != CryptoCurrencyNetwork.test),
false,
);
@@ -125,10 +125,7 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
final String addressString;
if (wallet is SparkInterface) {
Address? address = await wallet.getCurrentReceivingSparkAddress();
- if (address == null) {
- address = await wallet.generateNextSparkAddress();
- await ref.read(mainDBProvider).updateOrPutAddresses([address]);
- }
+ address ??= await wallet.generateNextSparkAddress(saveToDB: true);
addressString = address.value;
} else {
final address = await wallet.getCurrentReceivingAddress();
@@ -166,8 +163,9 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
- final contacts =
- ref.watch(addressBookServiceProvider.select((value) => value.contacts));
+ final contacts = ref.watch(
+ addressBookServiceProvider.select((value) => value.contacts),
+ );
final allContacts = contacts
.where(
@@ -176,8 +174,9 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
element.addresses
.where(
(e) => ref.watch(
- addressBookFilterProvider
- .select((value) => value.coins.contains(e.coin)),
+ addressBookFilterProvider.select(
+ (value) => value.coins.contains(e.coin),
+ ),
),
)
.isNotEmpty,
@@ -194,8 +193,9 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
element.addresses
.where(
(e) => ref.watch(
- addressBookFilterProvider
- .select((value) => value.coins.contains(e.coin)),
+ addressBookFilterProvider.select(
+ (value) => value.coins.contains(e.coin),
+ ),
),
)
.isNotEmpty,
@@ -213,22 +213,13 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
isCompactHeight: true,
leading: Row(
children: [
- const SizedBox(
- width: 24,
- ),
- Text(
- "Address Book",
- style: STextStyles.desktopH3(context),
- ),
+ const SizedBox(width: 24),
+ Text("Address Book", style: STextStyles.desktopH3(context)),
],
),
),
body: Padding(
- padding: const EdgeInsets.only(
- left: 24,
- right: 24,
- bottom: 24,
- ),
+ padding: const EdgeInsets.only(left: 24, right: 24, bottom: 24),
child: DesktopAddressBookScaffold(
controlsLeft: ClipRRect(
borderRadius: BorderRadius.circular(
@@ -245,43 +236,44 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
});
},
style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Search",
- _searchFocusNode,
- context,
- ).copyWith(
- prefixIcon: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 10,
- vertical: 20,
- ),
- child: SvgPicture.asset(
- Assets.svg.search,
- width: 16,
- height: 16,
- ),
- ),
- suffixIcon: _searchController.text.isNotEmpty
- ? Padding(
- padding: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- children: [
- TextFieldIconButton(
- child: const XIcon(),
- onTap: () async {
- setState(() {
- _searchController.text = "";
- _searchTerm = "";
- });
- },
+ decoration:
+ standardInputDecoration(
+ "Search",
+ _searchFocusNode,
+ context,
+ ).copyWith(
+ prefixIcon: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 10,
+ vertical: 20,
+ ),
+ child: SvgPicture.asset(
+ Assets.svg.search,
+ width: 16,
+ height: 16,
+ ),
+ ),
+ suffixIcon: _searchController.text.isNotEmpty
+ ? Padding(
+ padding: const EdgeInsets.only(right: 0),
+ child: UnconstrainedBox(
+ child: Row(
+ children: [
+ TextFieldIconButton(
+ child: const XIcon(),
+ onTap: () async {
+ setState(() {
+ _searchController.text = "";
+ _searchTerm = "";
+ });
+ },
+ ),
+ ],
),
- ],
- ),
- ),
- )
- : null,
- ),
+ ),
+ )
+ : null,
+ ),
),
),
controlsRight: Row(
@@ -293,24 +285,22 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
buttonHeight: ButtonHeight.l,
icon: SvgPicture.asset(
Assets.svg.filter,
- color: Theme.of(context)
- .extension<StackColors>()!
- .buttonTextSecondary,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.buttonTextSecondary,
),
onPressed: selectCryptocurrency,
),
- const SizedBox(
- width: 20,
- ),
+ const SizedBox(width: 20),
PrimaryButton(
width: 184,
label: "Add new",
buttonHeight: ButtonHeight.l,
icon: SvgPicture.asset(
Assets.svg.circlePlus,
- color: Theme.of(context)
- .extension<StackColors>()!
- .buttonTextPrimary,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.buttonTextPrimary,
),
onPressed: newContact,
),
@@ -326,10 +316,7 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
lowerLabel: favorites.isEmpty
? null
: Padding(
- padding: const EdgeInsets.only(
- top: 20,
- bottom: 12,
- ),
+ padding: const EdgeInsets.only(top: 20, bottom: 12),
child: Text(
"All contacts",
style: STextStyles.smallMed12(context),
@@ -337,15 +324,15 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
),
favorites: favorites.isEmpty
? contacts.isNotEmpty
- ? null
- : RoundedWhiteContainer(
- child: Center(
- child: Text(
- "Your favorite contacts will appear here",
- style: STextStyles.itemSubtitle(context),
+ ? null
+ : RoundedWhiteContainer(
+ child: Center(
+ child: Text(
+ "Your favorite contacts will appear here",
+ style: STextStyles.itemSubtitle(context),
+ ),
),
- ),
- )
+ )
: RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: Column(
@@ -355,9 +342,9 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
children: [
if (i > 0)
Container(
- color: Theme.of(context)
- .extension<StackColors>()!
- .background,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
height: 1,
),
Padding(
@@ -406,15 +393,15 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
),
all: allContacts.isEmpty
? contacts.isNotEmpty
- ? null
- : RoundedWhiteContainer(
- child: Center(
- child: Text(
- "Your contacts will appear here",
- style: STextStyles.itemSubtitle(context),
+ ? null
+ : RoundedWhiteContainer(
+ child: Center(
+ child: Text(
+ "Your contacts will appear here",
+ style: STextStyles.itemSubtitle(context),
+ ),
),
- ),
- )
+ )
: Column(
children: [
RoundedWhiteContainer(
@@ -426,9 +413,9 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
children: [
if (i > 0)
Container(
- color: Theme.of(context)
- .extension<StackColors>()!
- .background,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.background,
height: 1,
),
Padding(
@@ -481,9 +468,7 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
),
details: currentContactId == null
? Container()
- : DesktopContactDetails(
- contactId: currentContactId!,
- ),
+ : DesktopContactDetails(contactId: currentContactId!),
),
),
);
diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart
index d1cc854..ce8940c 100644
--- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart
+++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dart
@@ -196,17 +196,19 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
final Address? address;
if (wallet is Bip39HDWallet && wallet is! BCashInterface) {
DerivePathType? type;
- if (wallet.isViewOnly && wallet is ExtendedKeysInterface) {
- final voData = await wallet.getViewOnlyWalletData();
+ if (wallet.isViewOnly &&
+ wallet is ExtendedKeysInterface &&
+ wallet.viewOnlyType != .spark) {
+ final voData =
+ await wallet.getViewOnlyWalletData()
+ as ExtendedKeysViewOnlyWalletData;
for (final t in wallet.cryptoCurrency.supportedDerivationPathTypes) {
final testPath = wallet.cryptoCurrency.constructDerivePath(
derivePathType: t,
chain: 0,
index: 0,
);
- if (voData is SparkViewOnlyWalletData) {
- type = t;
- } else if (testPath.startsWith((voData as ExtendedKeysViewOnlyWalletData).xPubs.first.path)) {
+ if (testPath.startsWith(voData.xPubs.first.path)) {
type = t;
break;
}
@@ -270,10 +272,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
),
);
- final address = await wallet.generateNextSparkAddress();
- await ref.read(mainDBProvider).isar.writeTxn(() async {
- await ref.read(mainDBProvider).isar.addresses.put(address);
- });
+ final address = await wallet.generateNextSparkAddress(saveToDB: true);
shouldPop = true;
@@ -353,10 +352,8 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
isMimblewimblecoin = wallet is MimblewimblecoinWallet;
- if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly && wallet.viewOnlyType == ViewOnlyWalletType.spark) {
+ if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly) {
showMultiType = false;
- } else if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly) {
- showMultiType = supportsSpark;
} else {
showMultiType =
supportsSpark ||
@@ -366,11 +363,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
wallet.supportedAddressTypes.length > 1);
}
- if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly && wallet.viewOnlyType == ViewOnlyWalletType.spark) {
- _walletAddressTypes.add(AddressType.spark);
- } else {
- _walletAddressTypes.add(wallet.info.mainAddressType);
- }
+ _walletAddressTypes.add(wallet.info.mainAddressType);
if (showMultiType) {
if (supportsSpark) {
diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart
index a3c281f..fc34800 100644
--- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart
+++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart
@@ -14,11 +14,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
+import '../../../../pages/pinpad_views/pinpad_dialog.dart';
import '../../../../pages/settings_views/wallet_settings_view/frost_ms/frost_ms_options_view.dart';
import '../../../../pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/change_representative_view.dart';
import '../../../../pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/edit_refresh_height_view.dart';
-import '../../../../pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart';
import '../../../../pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/spark_view_key_view.dart';
+import '../../../../pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/xpub_view.dart';
import '../../../../providers/global/wallets_provider.dart';
import '../../../../route_generator.dart';
import '../../../../themes/stack_colors.dart';
@@ -35,6 +36,7 @@ import '../../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interfa
import '../../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
import '../../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart';
import '../../../addresses/desktop_wallet_addresses_view.dart';
+import '../../../password/request_desktop_auth_dialog.dart';
import 'desktop_delete_wallet_dialog.dart';
enum _WalletOptions {
@@ -71,6 +73,28 @@ class WalletOptionsButton extends ConsumerWidget {
final String walletId;
+ Future<void> _auth(
+ BuildContext context,
+ String message,
+ VoidCallback onAuth,
+ ) async {
+ final verified = await showDialog<String?>(
+ context: context,
+ builder: (context) => Util.isDesktop
+ ? RequestDesktopAuthDialog(title: message)
+ : PinpadDialog(
+ biometricsAuthenticationTitle: message,
+ biometricsLocalizedReason: "Authenticate to show view key",
+ biometricsCancelButtonString: "CANCEL",
+ ),
+ barrierDismissible: !Util.isDesktop,
+ );
+
+ if (verified == "verified success" && context.mounted) {
+ onAuth();
+ }
+ }
+
@override
Widget build(BuildContext context, WidgetRef ref) {
return RawMaterialButton(
@@ -142,75 +166,80 @@ class WalletOptionsButton extends ConsumerWidget {
}
break;
case _WalletOptions.showSparkKey:
- final wallet = ref.read(pWallets).getWallet(walletId) as SparkInterface;
- await wallet.init();
- final sparkViewKeyHex = wallet.viewKeyHex;
+ await _auth(context, "Show Spark view key", () async {
+ final wallet =
+ ref.read(pWallets).getWallet(walletId) as SparkInterface;
+ final sparkViewKeyHex = wallet.sparkViewKey!;
- if (context.mounted) {
- final result = await showDialog<bool?>(
- context: context,
- barrierDismissible: false,
- builder: (context) => Navigator(
- initialRoute: SparkViewKeyView.routeName,
- onGenerateRoute: RouteGenerator.generateRoute,
- onGenerateInitialRoutes: (_, __) {
- return [
- RouteGenerator.generateRoute(
- RouteSettings(
- name: SparkViewKeyView.routeName,
- arguments: (walletId, sparkViewKeyHex),
+ if (context.mounted) {
+ final result = await showDialog<bool?>(
+ context: context,
+ barrierDismissible: false,
+ builder: (context) => Navigator(
+ initialRoute: SparkViewKeyView.routeName,
+ onGenerateRoute: RouteGenerator.generateRoute,
+ onGenerateInitialRoutes: (_, __) {
+ return [
+ RouteGenerator.generateRoute(
+ RouteSettings(
+ name: SparkViewKeyView.routeName,
+ arguments: (walletId, sparkViewKeyHex),
+ ),
),
- ),
- ];
- },
- ),
- );
+ ];
+ },
+ ),
+ );
- if (result == true) {
- if (context.mounted) {
- Navigator.of(context).pop();
+ if (result == true) {
+ if (context.mounted) {
+ Navigator.of(context).pop();
+ }
}
}
- }
+ });
+
break;
case _WalletOptions.showXpub:
- final xpubData = await showLoading(
- delay: const Duration(milliseconds: 800),
- whileFuture:
- (ref.read(pWallets).getWallet(walletId)
- as ExtendedKeysInterface)
- .getXPubs(),
- context: context,
- message: "Loading xpubs",
- rootNavigator: Util.isDesktop,
- );
-
- if (context.mounted) {
- final result = await showDialog<bool?>(
+ await _auth(context, "Show xpub(s)", () async {
+ final xpubData = await showLoading(
+ delay: const Duration(milliseconds: 800),
+ whileFuture:
+ (ref.read(pWallets).getWallet(walletId)
+ as ExtendedKeysInterface)
+ .getXPubs(),
context: context,
- barrierDismissible: false,
- builder: (context) => Navigator(
- initialRoute: XPubView.routeName,
- onGenerateRoute: RouteGenerator.generateRoute,
- onGenerateInitialRoutes: (_, __) {
- return [
- RouteGenerator.generateRoute(
- RouteSettings(
- name: XPubView.routeName,
- arguments: (walletId, xpubData),
- ),
- ),
- ];
- },
- ),
+ message: "Loading xpubs",
+ rootNavigator: Util.isDesktop,
);
- if (result == true) {
- if (context.mounted) {
- Navigator.of(context).pop();
+ if (context.mounted) {
+ final result = await showDialog<bool?>(
+ context: context,
+ barrierDismissible: false,
+ builder: (context) => Navigator(
+ initialRoute: XPubView.routeName,
+ onGenerateRoute: RouteGenerator.generateRoute,
+ onGenerateInitialRoutes: (_, __) {
+ return [
+ RouteGenerator.generateRoute(
+ RouteSettings(
+ name: XPubView.routeName,
+ arguments: (walletId, xpubData),
+ ),
+ ),
+ ];
+ },
+ ),
+ );
+
+ if (result == true) {
+ if (context.mounted) {
+ Navigator.of(context).pop();
+ }
}
}
- }
+ });
break;
case _WalletOptions.changeRepresentative:
final result = await showDialog<bool?>(
@@ -315,16 +344,16 @@ class WalletOptionsPopupMenu extends ConsumerWidget {
final wallet = ref.watch(pWallets).getWallet(walletId);
bool xpubEnabled = wallet is ExtendedKeysInterface;
- if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly) {
- xpubEnabled = false;
- }
-
final bool canChangeRep = coin is NanoCurrency;
final bool isFrost = coin is FrostCurrency;
final bool isCN = wallet is CryptonoteWallet;
- final bool isSpark = wallet is SparkInterface;
+ bool isSpark = wallet is SparkInterface;
+ if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly) {
+ xpubEnabled = false;
+ isSpark = false;
+ }
return Stack(
children: [
Positioned(
@@ -519,10 +548,7 @@ class WalletOptionsPopupMenu extends ConsumerWidget {
),
),
),
- if (isSpark)
- const SizedBox(
- height: 8,
- ),
+ if (isSpark) const SizedBox(height: 8),
if (isSpark)
TransparentButton(
onPressed: () {
@@ -545,13 +571,14 @@ class WalletOptionsPopupMenu extends ConsumerWidget {
Expanded(
child: Text(
_WalletOptions.showSparkKey.prettyName,
- style: STextStyles.desktopTextExtraExtraSmall(
- context,
- ).copyWith(
- color: Theme.of(context)
- .extension<StackColors>()!
- .textDark,
- ),
+ style:
+ STextStyles.desktopTextExtraExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textDark,
+ ),
),
),
],
diff --git a/lib/wallets/isar/models/wallet_info.dart b/lib/wallets/isar/models/wallet_info.dart
index d3d762a..d65ceee 100644
--- a/lib/wallets/isar/models/wallet_info.dart
+++ b/lib/wallets/isar/models/wallet_info.dart
@@ -468,12 +468,13 @@ class WalletInfo implements IsarId {
int restoreHeight = 0,
String? walletIdOverride,
String? otherDataJsonString,
+ AddressType? overrideAddressType, // added hack for spark view only wallets
}) {
return WalletInfo(
coinName: coin.identifier,
walletId: walletIdOverride ?? const Uuid().v1(),
name: name,
- mainAddressType: coin.defaultAddressType,
+ mainAddressType: overrideAddressType ?? coin.defaultAddressType,
restoreHeight: restoreHeight,
otherDataJsonString: otherDataJsonString,
);
diff --git a/lib/wallets/wallet/impl/firo_wallet.dart b/lib/wallets/wallet/impl/firo_wallet.dart
index 550f042..3973f60 100644
--- a/lib/wallets/wallet/impl/firo_wallet.dart
+++ b/lib/wallets/wallet/impl/firo_wallet.dart
@@ -9,6 +9,7 @@ import '../../../models/isar/models/blockchain_data/v2/input_v2.dart';
import '../../../models/isar/models/blockchain_data/v2/output_v2.dart';
import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart';
import '../../../models/isar/models/isar_models.dart';
+import '../../../models/keys/view_only_wallet_data.dart';
import '../../../utilities/amount/amount.dart';
import '../../../utilities/extensions/extensions.dart';
import '../../../utilities/logger.dart';
@@ -23,7 +24,6 @@ import '../wallet_mixin_interfaces/coin_control_interface.dart';
import '../wallet_mixin_interfaces/electrumx_interface.dart';
import '../wallet_mixin_interfaces/extended_keys_interface.dart';
import '../wallet_mixin_interfaces/spark_interface.dart';
-import '../../../models/keys/view_only_wallet_data.dart';
const sparkStartBlock = 819300; // (approx 18 Jan 2024)
@@ -50,17 +50,6 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
final Set<String> _unconfirmedTxids = {};
- @override
- Set<AddressType> get supportedAddressTypes {
- if (isViewOnly && viewOnlyType == ViewOnlyWalletType.spark) {
- return {AddressType.spark};
- } else {
- final supportedAddressTypes = super.supportedAddressTypes;
- supportedAddressTypes.add(AddressType.spark);
- return supportedAddressTypes;
- }
- }
-
// ===========================================================================
@override
@@ -673,18 +662,18 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
@override
Future<List<Address>> fetchAddressesForElectrumXScan() async {
return await mainDB
- .getAddresses(walletId)
- .filter()
- .not()
- .group(
- (q) => q
- .typeEqualTo(AddressType.spark)
- .or()
- .typeEqualTo(AddressType.nonWallet)
- .or()
- .subTypeEqualTo(AddressSubType.nonWallet),
- )
- .findAll();
+ .getAddresses(walletId)
+ .filter()
+ .not()
+ .group(
+ (q) => q
+ .typeEqualTo(AddressType.spark)
+ .or()
+ .typeEqualTo(AddressType.nonWallet)
+ .or()
+ .subTypeEqualTo(AddressSubType.nonWallet),
+ )
+ .findAll();
}
@override
diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
index 7ac1fc2..860323f 100644
--- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
+++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart
@@ -109,7 +109,8 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
on Bip39HDWallet<T>, ElectrumXInterface<T> {
late Address _currentSparkAddress;
- late String viewKeyHex;
+ String? _viewKeyHex;
+ String? get sparkViewKey => _viewKeyHex!;
// Really we should just send change back to the same address.
late Address sparkChangeAddress;
@@ -133,13 +134,20 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
// private key data).
int get sparkIndex => kDefaultSparkIndex;
- Future<Address> generateSparkAddress(int diversifier) async {
- final sparkAddress = await libSpark.getAddressFromFullViewKey(
- fullViewKeyHex: viewKeyHex,
- index: sparkIndex,
- diversifier: diversifier,
- isTestNet: isTestNet,
- );
+ Future<Address> _generateSparkAddress(int diversifier) async {
+ if (isViewOnly && viewOnlyType != .spark) {
+ throw Exception(
+ "Cannot generate a spark address for a non spark view only firo wallet",
+ );
+ }
+
+ final sparkAddress =
+ await computeWithLibSparkLogging(_getAddressFromFullViewKey, (
+ fullViewKeyHex: _viewKeyHex!,
+ index: sparkIndex,
+ diversifier: diversifier,
+ isTestNet: isTestNet,
+ ));
return Address(
walletId: walletId,
@@ -165,7 +173,7 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
}) async {
return await computeWithLibSparkLogging(identifyCoinsStatic, (
walletId_: walletId,
- viewKeyHex_: viewKeyHex,
+ viewKeyHex_: _viewKeyHex!,
isTestNet_: isTestNet,
anonymitySetCoins: anonymitySetCoins,
groupId: groupId,
@@ -267,6 +275,10 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
@override
Future<void> init() async {
+ if (isViewOnly && viewOnlyType != .spark) {
+ return super.init();
+ }
+
try {
final sparkUsedTagsResetVersion =
info.otherData[WalletInfoKeys.firoSparkUsedTagsCacheResetVersion]
@@ -285,13 +297,16 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
}
if (isViewOnly) {
- final walletData =
- await getViewOnlyWalletData() as SparkViewOnlyWalletData;
- viewKeyHex = walletData.viewKey;
+ final walletData = await getViewOnlyWalletData();
+ if (walletData is SparkViewOnlyWalletData) {
+ _viewKeyHex = walletData.viewKey;
+ } else {
+ // TODO anything needed here?
+ }
} else {
final root = await getRootHDNode();
final privateKey = root.derivePath(sparkDerivationPath).privateKey.data;
- viewKeyHex = libSpark.getFullViewKeyHexFromPrivateKeyData(
+ _viewKeyHex = libSpark.getFullViewKeyHexFromPrivateKeyData(
privateKeyHex: privateKey.toHex,
index: sparkIndex,
);
@@ -299,8 +314,16 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
Address? address = await getCurrentReceivingSparkAddress();
if (address == null) {
- address = await generateSparkAddress(1);
+ address = await _generateSparkAddress(1);
await mainDB.putAddress(address);
+ if (isViewOnly &&
+ viewOnlyType == .spark &&
+ info.mainAddressType == .spark) {
+ await info.updateReceivingAddress(
+ newAddress: address.value,
+ isar: mainDB.isar,
+ );
+ }
}
if (address.derivationIndex == -1) {
@@ -308,7 +331,7 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
}
_currentSparkAddress = address;
- sparkChangeAddress = await generateSparkAddress(libSpark.sparkChange);
+ sparkChangeAddress = await _generateSparkAddress(libSpark.sparkChange);
} catch (e, s) {
// do nothing, still allow user into wallet
Logging.instance.e("$runtimeType init() failed", error: e, stackTrace: s);
@@ -349,13 +372,25 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
}
}
- Future<Address> generateNextSparkAddress() async {
+ Future<Address> generateNextSparkAddress({required bool saveToDB}) async {
int diversifier = _currentSparkAddress.derivationIndex + 1;
if (diversifier == libSpark.sparkChange) {
diversifier++; // ensure only receiving addresses are shown
}
- final newAddress = await generateSparkAddress(diversifier);
+ final newAddress = await _generateSparkAddress(diversifier);
_currentSparkAddress = newAddress;
+ if (saveToDB) {
+ await mainDB.updateOrPutAddresses([newAddress]);
+ if (isViewOnly &&
+ viewOnlyType == .spark &&
+ info.mainAddressType == .spark) {
+ await info.updateReceivingAddress(
+ newAddress: newAddress.value,
+ isar: mainDB.isar,
+ );
+ }
+ }
+
return newAddress;
}
@@ -1300,10 +1335,6 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
}
}
- Future<void> recoverViewOnlyWallet() async {
- await recoverSparkWallet(latestSparkCoinId: 0);
- }
-
Future<void> refreshSparkNames() async {
try {
Logging.instance.i("Refreshing spark names for $walletId ${info.name}");
@@ -1342,7 +1373,7 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
if (diversifier == libSpark.sparkChange) {
diversifier++;
}
- final addressString = await generateSparkAddress(diversifier);
+ final addressString = await _generateSparkAddress(diversifier);
myAddresses.add(addressString.value);
diversifier++;
@@ -2450,3 +2481,12 @@ int _estSparkFeeComputeFunc(
return est;
}
+
+Future<String> _getAddressFromFullViewKey(
+ ({String fullViewKeyHex, int index, int diversifier, bool isTestNet}) args,
+) => libSpark.getAddressFromFullViewKey(
+ fullViewKeyHex: args.fullViewKeyHex,
+ index: args.index,
+ diversifier: args.diversifier,
+ isTestNet: args.isTestNet,
+);
Why this scored 28/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.