What changed, and why it matters
This commit adds a user-facing toggle that lets Bitcoin wallet owners choose whether to generate older-style "legacy" (P2PKH) addresses. Previously, the app hid legacy addresses for Bitcoin wallets when other address types were available. The change is a feature addition, not a bug fix, and does not appear to address any security vulnerability.
No security action required. Treat as a normal feature review: verify the toggle state persists correctly, that disabling legacy addresses again removes p2pkh from the receive UI, and that the new _sub helper correctly limits results to receiving addresses.
Security signals we found
Feature addition: opt-in legacy Bitcoin address generation
UI state synchronization for address-type list and active subscriptions
Refactoring of address stream query helper
No cryptographic, permission, or input-validation changes observed
Evidence from the diff
The patch introduces a new WalletInfo key enableLegacyAddressesKey and an isLegacyAddressesEnabled getter. It wires UI toggles in mobile and desktop wallet settings, and updates receive-view logic so that AddressType.p2pkh is only filtered out when the new flag is false. It also refactors duplicated address-stream subscription code into a helper _sub(AddressType) and changes the subtype filter from not().subTypeEqualTo(AddressSubType.change) to subTypeEqualTo(AddressSubType.receiving).
Changed components
lib/pages/receive_view/receive_view.dartlib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dartlib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_receive.dartlib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dartlib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dartlib/wallets/isar/models/wallet_info.dartInspect captured patch +420 / −285
diff --git a/lib/pages/receive_view/receive_view.dart b/lib/pages/receive_view/receive_view.dart
index 5fa78b3..6d7f7e1 100644
--- a/lib/pages/receive_view/receive_view.dart
+++ b/lib/pages/receive_view/receive_view.dart
@@ -112,12 +112,10 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
if (mounted) {
await showDialog<void>(
context: context,
- builder:
- (context) => StackOkDialog(
- title: "Slatepack receive error",
- message:
- ex?.toString() ?? "Unexpected result without exception",
- ),
+ builder: (context) => StackOkDialog(
+ title: "Slatepack receive error",
+ message: ex?.toString() ?? "Unexpected result without exception",
+ ),
);
}
return;
@@ -127,27 +125,25 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
final response =
await showDialog<({String responseSlatepack, bool wasEncrypted})>(
context: context,
- builder:
- (context) => SDialog(
- child: MwcSlatepackImportDialog(
- walletId: widget.walletId,
- clipboard: widget.clipboard,
- rawSlatepack: result.raw,
- decoded: result.result,
- slatepackType: result.type,
- ),
- ),
+ builder: (context) => SDialog(
+ child: MwcSlatepackImportDialog(
+ walletId: widget.walletId,
+ clipboard: widget.clipboard,
+ rawSlatepack: result.raw,
+ decoded: result.result,
+ slatepackType: result.type,
+ ),
+ ),
);
if (mounted && response != null) {
await showDialog<void>(
context: context,
barrierDismissible: false,
- builder:
- (context) => SlatepackResponseDialog(
- responseSlatepack: response.responseSlatepack,
- wasEncrypted: response.wasEncrypted,
- ),
+ builder: (context) => SlatepackResponseDialog(
+ responseSlatepack: response.responseSlatepack,
+ wasEncrypted: response.wasEncrypted,
+ ),
);
}
}
@@ -300,6 +296,32 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
}
}
+ StreamSubscription<Address?> _sub(AddressType type) {
+ return ref
+ .read(mainDBProvider)
+ .isar
+ .addresses
+ .where()
+ .walletIdEqualTo(walletId)
+ .filter()
+ .typeEqualTo(type)
+ .and()
+ .subTypeEqualTo(AddressSubType.receiving)
+ .sortByDerivationIndexDesc()
+ .findFirst()
+ .asStream()
+ .listen((event) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (mounted) {
+ setState(() {
+ _addressMap[type] =
+ event?.value ?? _addressMap[type] ?? "[No address yet]";
+ });
+ }
+ });
+ });
+ }
+
@override
void initState() {
walletId = widget.walletId;
@@ -341,7 +363,9 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
}
}
- if (_walletAddressTypes.length > 1 && wallet is BitcoinWallet) {
+ if (_walletAddressTypes.length > 1 &&
+ wallet is BitcoinWallet &&
+ !wallet.info.isLegacyAddressesEnabled) {
_walletAddressTypes.removeWhere((e) => e == AddressType.p2pkh);
}
@@ -351,30 +375,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
if (_showMultiType) {
for (final type in _walletAddressTypes) {
- _addressSubMap[type] = ref
- .read(mainDBProvider)
- .isar
- .addresses
- .where()
- .walletIdEqualTo(walletId)
- .filter()
- .typeEqualTo(type)
- .and()
- .not()
- .subTypeEqualTo(AddressSubType.change)
- .sortByDerivationIndexDesc()
- .findFirst()
- .asStream()
- .listen((event) {
- WidgetsBinding.instance.addPostFrameCallback((_) {
- if (mounted) {
- setState(() {
- _addressMap[type] =
- event?.value ?? _addressMap[type] ?? "[No address yet]";
- });
- }
- });
- });
+ _addressSubMap[type] = _sub(type);
}
}
@@ -399,42 +400,40 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
if (prev?.isMwebEnabled != next.isMwebEnabled) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
+ const type = AddressType.mweb;
setState(() {
supportsMweb = next.isMwebEnabled;
- if (supportsMweb &&
- !_walletAddressTypes.contains(AddressType.mweb)) {
- _walletAddressTypes.insert(0, AddressType.mweb);
- _addressSubMap[AddressType.mweb] = ref
- .read(mainDBProvider)
- .isar
- .addresses
- .where()
- .walletIdEqualTo(walletId)
- .filter()
- .typeEqualTo(AddressType.mweb)
- .and()
- .not()
- .subTypeEqualTo(AddressSubType.change)
- .sortByDerivationIndexDesc()
- .findFirst()
- .asStream()
- .listen((event) {
- WidgetsBinding.instance.addPostFrameCallback((_) {
- if (mounted) {
- setState(() {
- _addressMap[AddressType.mweb] =
- event?.value ??
- _addressMap[AddressType.mweb] ??
- "[No address yet]";
- });
- }
- });
- });
+ if (supportsMweb && !_walletAddressTypes.contains(type)) {
+ _walletAddressTypes.insert(0, type);
+
+ _addressSubMap[type] = _sub(type);
+ } else {
+ _walletAddressTypes.remove(type);
+ _addressSubMap[type]?.cancel();
+ _addressSubMap.remove(type);
+ }
+
+ if (_currentIndex >= _walletAddressTypes.length) {
+ _currentIndex = _walletAddressTypes.length - 1;
+ }
+ });
+ }
+ });
+ }
+
+ if (prev?.isLegacyAddressesEnabled != next.isLegacyAddressesEnabled) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (mounted) {
+ const type = AddressType.p2pkh;
+ setState(() {
+ if (!_walletAddressTypes.contains(type)) {
+ _walletAddressTypes.insert(0, type);
+ _addressSubMap[type] = _sub(type);
} else {
- _walletAddressTypes.remove(AddressType.mweb);
- _addressSubMap[AddressType.mweb]?.cancel();
- _addressSubMap.remove(AddressType.mweb);
+ _walletAddressTypes.remove(type);
+ _addressSubMap[type]?.cancel();
+ _addressSubMap.remove(type);
}
if (_currentIndex >= _walletAddressTypes.length) {
@@ -494,10 +493,9 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
color: Theme.of(context).extension<StackColors>()!.background,
icon: SvgPicture.asset(
Assets.svg.verticalEllipsis,
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.accentColorDark,
width: 20,
height: 20,
),
@@ -514,10 +512,9 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
right: 10,
child: Container(
decoration: BoxDecoration(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.popupBG,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.popupBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
@@ -580,100 +577,94 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
children: [
ConditionalParent(
condition: _showMultiType,
- builder:
- (child) => Column(
- crossAxisAlignment: CrossAxisAlignment.stretch,
- children: [
- Text(
- "Address type",
- style: STextStyles.w500_14(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.infoItemLabel,
- ),
- ),
- const SizedBox(height: 10),
- DropdownButtonHideUnderline(
- child: DropdownButton2<int>(
- value: _currentIndex,
- items: [
- for (
- int i = 0;
- i < _walletAddressTypes.length;
- i++
- )
- DropdownMenuItem(
- value: i,
- child: Text(
- _supportsSpark &&
- _walletAddressTypes[i] ==
- AddressType.p2pkh
- ? "Transparent address"
- : "${_walletAddressTypes[i].readableName} address",
- style: STextStyles.w500_14(context),
- ),
- ),
- ],
- onChanged: (value) {
- if (value != null &&
- value != _currentIndex) {
- setState(() {
- _currentIndex = value;
- });
- }
- },
- isExpanded: true,
- iconStyleData: IconStyleData(
- icon: Padding(
- padding: const EdgeInsets.only(right: 10),
- child: SvgPicture.asset(
- Assets.svg.chevronDown,
- width: 12,
- height: 6,
- color:
- Theme.of(context)
- .extension<StackColors>()!
- .textFieldActiveSearchIconRight,
- ),
+ builder: (child) => Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Text(
+ "Address type",
+ style: STextStyles.w500_14(context).copyWith(
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.infoItemLabel,
+ ),
+ ),
+ const SizedBox(height: 10),
+ DropdownButtonHideUnderline(
+ child: DropdownButton2<int>(
+ value: _currentIndex,
+ items: [
+ for (
+ int i = 0;
+ i < _walletAddressTypes.length;
+ i++
+ )
+ DropdownMenuItem(
+ value: i,
+ child: Text(
+ _supportsSpark &&
+ _walletAddressTypes[i] ==
+ AddressType.p2pkh
+ ? "Transparent address"
+ : "${_walletAddressTypes[i].readableName} address",
+ style: STextStyles.w500_14(context),
),
),
- buttonStyleData: ButtonStyleData(
- decoration: BoxDecoration(
- color:
- Theme.of(context)
- .extension<StackColors>()!
- .textFieldDefaultBG,
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- ),
+ ],
+ onChanged: (value) {
+ if (value != null && value != _currentIndex) {
+ setState(() {
+ _currentIndex = value;
+ });
+ }
+ },
+ isExpanded: true,
+ iconStyleData: IconStyleData(
+ icon: Padding(
+ padding: const EdgeInsets.only(right: 10),
+ child: SvgPicture.asset(
+ Assets.svg.chevronDown,
+ width: 12,
+ height: 6,
+ 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,
- borderRadius: BorderRadius.circular(
- Constants.size.circularBorderRadius,
- ),
- ),
+ ),
+ ),
+ buttonStyleData: ButtonStyleData(
+ decoration: BoxDecoration(
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .textFieldDefaultBG,
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
),
- menuItemStyleData: const MenuItemStyleData(
- padding: EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 8,
- ),
+ ),
+ ),
+ dropdownStyleData: DropdownStyleData(
+ offset: const Offset(0, -10),
+ elevation: 0,
+ decoration: BoxDecoration(
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .textFieldDefaultBG,
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
),
),
),
- const SizedBox(height: 12),
- child,
- ],
+ menuItemStyleData: const MenuItemStyleData(
+ padding: EdgeInsets.symmetric(
+ horizontal: 16,
+ vertical: 8,
+ ),
+ ),
+ ),
),
+ const SizedBox(height: 12),
+ child,
+ ],
+ ),
child: GestureDetector(
onTap: () {
HapticFeedback.lightImpact();
@@ -701,10 +692,9 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
Assets.svg.copy,
width: 10,
height: 10,
- color:
- Theme.of(context)
- .extension<StackColors>()!
- .infoItemIcons,
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .infoItemIcons,
),
const SizedBox(width: 4),
Text(
@@ -753,14 +743,14 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
label: "Generate new address",
onPressed:
supportsMweb &&
- _walletAddressTypes[_currentIndex] ==
- AddressType.mweb
- ? generateNewMwebAddress
- : _supportsSpark &&
- _walletAddressTypes[_currentIndex] ==
- AddressType.spark
- ? generateNewSparkAddress
- : generateNewAddress,
+ _walletAddressTypes[_currentIndex] ==
+ AddressType.mweb
+ ? generateNewMwebAddress
+ : _supportsSpark &&
+ _walletAddressTypes[_currentIndex] ==
+ AddressType.spark
+ ? generateNewSparkAddress
+ : generateNewAddress,
),
// MWC Slatepack import button.
if (coin is Mimblewimblecoin) ...[
@@ -794,11 +784,10 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
RouteGenerator.getRoute(
shouldUseMaterialRoute:
RouteGenerator.useMaterialPageRoute,
- builder:
- (_) => GenerateUriQrCodeView(
- coin: coin,
- receivingAddress: address,
- ),
+ builder: (_) => GenerateUriQrCodeView(
+ coin: coin,
+ receivingAddress: address,
+ ),
settings: const RouteSettings(
name: GenerateUriQrCodeView.routeName,
),
diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart
index f030856..0490795 100644
--- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart
+++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart
@@ -22,6 +22,7 @@ import '../../../../utilities/logger.dart';
import '../../../../utilities/text_styles.dart';
import '../../../../wallets/isar/models/wallet_info.dart';
import '../../../../wallets/isar/providers/wallet_info_provider.dart';
+import '../../../../wallets/wallet/impl/bitcoin_wallet.dart';
import '../../../../wallets/wallet/intermediate/cryptonote_wallet.dart';
import '../../../../wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart';
import '../../../../wallets/wallet/wallet_mixin_interfaces/mweb_interface.dart';
@@ -87,6 +88,37 @@ class _WalletSettingsWalletSettingsViewState
}
}
+ bool _switchLegacyToggledLock = false; // Mutex.
+ Future<void> _switchLegacyToggled() async {
+ if (_switchLegacyToggledLock) {
+ return;
+ }
+ _switchLegacyToggledLock = true; // Lock mutex.
+
+ try {
+ // Toggle enableLegacyAddresses in wallet info.
+ await ref
+ .read(pWalletInfo(widget.walletId))
+ .updateOtherData(
+ newEntries: {
+ WalletInfoKeys.enableLegacyAddresses: !ref
+ .read(pWalletInfo(widget.walletId))
+ .isLegacyAddressesEnabled,
+ },
+ isar: ref.read(mainDBProvider).isar,
+ );
+ } catch (e, s) {
+ Logging.instance.f(
+ "Failed to update enableLegacyAddresses for wallet",
+ error: e,
+ stackTrace: s,
+ );
+ } finally {
+ // ensure _switchLegacyToggledLock is set to false no matter what
+ _switchLegacyToggledLock = false;
+ }
+ }
+
bool _switchReuseAddressToggledLock = false; // Mutex.
Future<void> _switchReuseAddressToggled() async {
if (_switchReuseAddressToggledLock) {
@@ -475,6 +507,56 @@ class _WalletSettingsWalletSettingsViewState
),
),
),
+ if (wallet is BitcoinWallet) const SizedBox(height: 8),
+ if (wallet is BitcoinWallet)
+ RoundedWhiteContainer(
+ padding: const EdgeInsets.all(0),
+ child: RawMaterialButton(
+ materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ ),
+ onPressed: _switchLegacyToggled,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 12.0,
+ vertical: 20,
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ "Enable legacy addresses",
+ style: STextStyles.titleBold12(context),
+ textAlign: TextAlign.left,
+ ),
+ SizedBox(
+ height: 20,
+ width: 40,
+ child: IgnorePointer(
+ child: DraggableSwitch(
+ value:
+ ref.watch(
+ pWalletInfo(
+ widget.walletId,
+ ).select(
+ (value) => value.otherData,
+ ),
+ )[WalletInfoKeys
+ .enableLegacyAddresses]
+ as bool? ??
+ false,
+ onChanged: (_) => (),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
if (wallet is SparkInterface && !wallet.isViewOnly)
const SizedBox(height: 8),
if (wallet is SparkInterface && !wallet.isViewOnly)
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 607bf3f..30d92b5 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
@@ -312,6 +312,32 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
}
}
+ StreamSubscription<Address?> _sub(AddressType type) {
+ return ref
+ .read(mainDBProvider)
+ .isar
+ .addresses
+ .where()
+ .walletIdEqualTo(walletId)
+ .filter()
+ .typeEqualTo(type)
+ .and()
+ .subTypeEqualTo(AddressSubType.receiving)
+ .sortByDerivationIndexDesc()
+ .findFirst()
+ .asStream()
+ .listen((event) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (mounted) {
+ setState(() {
+ _addressMap[type] =
+ event?.value ?? _addressMap[type] ?? "[No address yet]";
+ });
+ }
+ });
+ });
+ }
+
@override
void initState() {
_receiveSlateController = TextEditingController();
@@ -356,7 +382,9 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
}
}
- if (_walletAddressTypes.length > 1 && wallet is BitcoinWallet) {
+ if (_walletAddressTypes.length > 1 &&
+ wallet is BitcoinWallet &&
+ !wallet.info.isLegacyAddressesEnabled) {
_walletAddressTypes.removeWhere((e) => e == AddressType.p2pkh);
}
@@ -366,30 +394,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
if (showMultiType) {
for (final type in _walletAddressTypes) {
- _addressSubMap[type] = ref
- .read(mainDBProvider)
- .isar
- .addresses
- .where()
- .walletIdEqualTo(walletId)
- .filter()
- .typeEqualTo(type)
- .and()
- .not()
- .subTypeEqualTo(AddressSubType.change)
- .sortByDerivationIndexDesc()
- .findFirst()
- .asStream()
- .listen((event) {
- WidgetsBinding.instance.addPostFrameCallback((_) {
- if (mounted) {
- setState(() {
- _addressMap[type] =
- event?.value ?? _addressMap[type] ?? "[No address yet]";
- });
- }
- });
- });
+ _addressSubMap[type] = _sub(type);
}
}
@@ -413,42 +418,39 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
if (prev?.isMwebEnabled != next.isMwebEnabled) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
+ const type = AddressType.mweb;
setState(() {
supportsMweb = next.isMwebEnabled;
- if (supportsMweb &&
- !_walletAddressTypes.contains(AddressType.mweb)) {
- _walletAddressTypes.insert(0, AddressType.mweb);
- _addressSubMap[AddressType.mweb] = ref
- .read(mainDBProvider)
- .isar
- .addresses
- .where()
- .walletIdEqualTo(walletId)
- .filter()
- .typeEqualTo(AddressType.mweb)
- .and()
- .not()
- .subTypeEqualTo(AddressSubType.change)
- .sortByDerivationIndexDesc()
- .findFirst()
- .asStream()
- .listen((event) {
- WidgetsBinding.instance.addPostFrameCallback((_) {
- if (mounted) {
- setState(() {
- _addressMap[AddressType.mweb] =
- event?.value ??
- _addressMap[AddressType.mweb] ??
- "[No address yet]";
- });
- }
- });
- });
+ if (supportsMweb && !_walletAddressTypes.contains(type)) {
+ _walletAddressTypes.insert(0, type);
+ _addressSubMap[type] = _sub(type);
+ } else {
+ _walletAddressTypes.remove(type);
+ _addressSubMap[type]?.cancel();
+ _addressSubMap.remove(type);
+ }
+
+ if (_currentIndex >= _walletAddressTypes.length) {
+ _currentIndex = _walletAddressTypes.length - 1;
+ }
+ });
+ }
+ });
+ }
+
+ if (prev?.isLegacyAddressesEnabled != next.isLegacyAddressesEnabled) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (mounted) {
+ const type = AddressType.p2pkh;
+ setState(() {
+ if (!_walletAddressTypes.contains(type)) {
+ _walletAddressTypes.insert(0, type);
+ _addressSubMap[type] = _sub(type);
} else {
- _walletAddressTypes.remove(AddressType.mweb);
- _addressSubMap[AddressType.mweb]?.cancel();
- _addressSubMap.remove(AddressType.mweb);
+ _walletAddressTypes.remove(type);
+ _addressSubMap[type]?.cancel();
+ _addressSubMap.remove(type);
}
if (_currentIndex >= _walletAddressTypes.length) {
diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart
index 4a709a0..dd8869c 100644
--- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart
+++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_features.dart
@@ -40,6 +40,7 @@ import '../../../../utilities/logger.dart';
import '../../../../utilities/text_styles.dart';
import '../../../../wallets/crypto_currency/coins/banano.dart';
import '../../../../wallets/crypto_currency/coins/firo.dart';
+import '../../../../wallets/wallet/impl/bitcoin_wallet.dart';
import '../../../../wallets/wallet/impl/firo_wallet.dart';
import '../../../../wallets/wallet/impl/namecoin_wallet.dart';
import '../../../../wallets/wallet/intermediate/cryptonote_wallet.dart';
@@ -96,7 +97,8 @@ enum WalletFeature {
clearSparkCache("", ""),
rbf("", ""),
reuseAddress("", ""),
- enableMweb("", "");
+ enableMweb("", ""),
+ enableLegacyAddresses("", "");
final String label;
final String description;
@@ -582,6 +584,9 @@ class _DesktopWalletFeaturesState extends ConsumerState<DesktopWalletFeatures> {
if (wallet is RbfInterface) (WalletFeature.rbf, Assets.svg.key, () => ()),
+ if (wallet is BitcoinWallet)
+ (WalletFeature.enableLegacyAddresses, Assets.svg.key, () => ()),
+
if (canGen) (WalletFeature.reuseAddress, Assets.svg.key, () => ()),
if (showMwebOption) (WalletFeature.enableMweb, Assets.svg.key, () => ()),
diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart
index 20f2524..74be058 100644
--- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart
+++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart
@@ -69,6 +69,27 @@ class _MoreFeaturesDialogState extends ConsumerState<MoreFeaturesDialog> {
}
}
+ bool _switchLegacyToggledLock = false; // Mutex.
+ Future<void> _switchLegacyToggled(bool newValue) async {
+ if (_switchLegacyToggledLock) {
+ return;
+ }
+ _switchLegacyToggledLock = true; // Lock mutex.
+
+ try {
+ // Toggle enableLegacyAddresses in wallet info.
+ await ref
+ .read(pWalletInfo(widget.walletId))
+ .updateOtherData(
+ newEntries: {WalletInfoKeys.enableLegacyAddresses: newValue},
+ isar: ref.read(mainDBProvider).isar,
+ );
+ } finally {
+ // ensure _switchLegacyToggledLock is set to false no matter what
+ _switchLegacyToggledLock = false;
+ }
+ }
+
late final DSBController _switchControllerAddressReuse;
late final DSBController _switchControllerMwebToggle;
@@ -382,6 +403,40 @@ class _MoreFeaturesDialogState extends ConsumerState<MoreFeaturesDialog> {
),
);
+ case WalletFeature.enableLegacyAddresses:
+ return _MoreFeaturesItemBase(
+ child: Row(
+ children: [
+ const SizedBox(width: 3),
+ SizedBox(
+ height: 20,
+ width: 40,
+ child: DraggableSwitchButton(
+ isOn:
+ ref.watch(
+ pWalletInfo(
+ widget.walletId,
+ ).select((value) => value.otherData),
+ )[WalletInfoKeys.enableLegacyAddresses]
+ as bool? ??
+ false,
+ onValueChanged: _switchLegacyToggled,
+ ),
+ ),
+ const SizedBox(width: 16),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ "Enable legacy (P2PKH) address generation",
+ style: STextStyles.w600_20(context),
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+
case WalletFeature.reuseAddress:
return _MoreFeaturesItemBase(
onPressed: _switchReuseAddressToggled,
@@ -525,26 +580,23 @@ class _MoreFeaturesItemState extends State<_MoreFeaturesItem> {
height: _MoreFeaturesItem.iconSizeBG,
radiusMultiplier: _MoreFeaturesItem.iconSizeBG,
child: Center(
- child:
- widget.isSvgFile
- ? SvgPicture.file(
- File(widget.iconAsset),
- width: _MoreFeaturesItem.iconSize,
- height: _MoreFeaturesItem.iconSize,
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.settingsIconIcon,
- )
- : SvgPicture.asset(
- widget.iconAsset,
- width: _MoreFeaturesItem.iconSize,
- height: _MoreFeaturesItem.iconSize,
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.settingsIconIcon,
- ),
+ child: widget.isSvgFile
+ ? SvgPicture.file(
+ File(widget.iconAsset),
+ width: _MoreFeaturesItem.iconSize,
+ height: _MoreFeaturesItem.iconSize,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.settingsIconIcon,
+ )
+ : SvgPicture.asset(
+ widget.iconAsset,
+ width: _MoreFeaturesItem.iconSize,
+ height: _MoreFeaturesItem.iconSize,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.settingsIconIcon,
+ ),
),
),
const SizedBox(width: 16),
@@ -576,8 +628,9 @@ class _MoreFeaturesItemBase extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 32),
child: RoundedContainer(
color: Colors.transparent,
- borderColor:
- Theme.of(context).extension<StackColors>()!.textFieldDefaultBG,
+ borderColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG,
onPressed: onPressed,
child: child,
),
@@ -636,10 +689,9 @@ class _MoreFeaturesClearSparkCacheItemState
Assets.svg.x,
width: _MoreFeaturesItem.iconSize,
height: _MoreFeaturesItem.iconSize,
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.settingsIconIcon,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.settingsIconIcon,
),
),
),
diff --git a/lib/wallets/isar/models/wallet_info.dart b/lib/wallets/isar/models/wallet_info.dart
index 5b2d656..d3d762a 100644
--- a/lib/wallets/isar/models/wallet_info.dart
+++ b/lib/wallets/isar/models/wallet_info.dart
@@ -114,10 +114,9 @@ class WalletInfo implements IsarId {
}
@ignore
- Map<String, dynamic> get otherData =>
- otherDataJsonString == null
- ? {}
- : Map<String, dynamic>.from(jsonDecode(otherDataJsonString!) as Map);
+ Map<String, dynamic> get otherData => otherDataJsonString == null
+ ? {}
+ : Map<String, dynamic>.from(jsonDecode(otherDataJsonString!) as Map);
@ignore
bool get isViewOnly =>
@@ -143,6 +142,10 @@ class WalletInfo implements IsarId {
bool get isMwebEnabled =>
otherData[WalletInfoKeys.mwebEnabled] as bool? ?? false;
+ @ignore
+ bool get isLegacyAddressesEnabled =>
+ otherData[WalletInfoKeys.enableLegacyAddresses] as bool? ?? false;
+
//============================================================================
//============= Updaters ================================================
@@ -248,12 +251,11 @@ class WalletInfo implements IsarId {
if (customIndexOverride != null) {
index = customIndexOverride;
} else if (flag) {
- final highest =
- await isar.walletInfo
- .where()
- .sortByFavouriteOrderIndexDesc()
- .favouriteOrderIndexProperty()
- .findFirst();
+ final highest = await isar.walletInfo
+ .where()
+ .sortByFavouriteOrderIndexDesc()
+ .favouriteOrderIndexProperty()
+ .findFirst();
index = (highest ?? 0) + 1;
} else {
index = -1;
@@ -336,8 +338,10 @@ class WalletInfo implements IsarId {
/// Can be dangerous. Don't use unless you know the consequences
Future<void> setMnemonicVerified({required Isar isar}) async {
- final meta =
- await isar.walletInfoMeta.where().walletIdEqualTo(walletId).findFirst();
+ final meta = await isar.walletInfoMeta
+ .where()
+ .walletIdEqualTo(walletId)
+ .findFirst();
if (meta == null) {
await isar.writeTxn(() async {
await isar.walletInfoMeta.put(
@@ -524,4 +528,5 @@ abstract class WalletInfoKeys {
static const String mwebScanHeight = "mwebScanHeightKey";
static const String firoSparkUsedTagsCacheResetVersion =
"firoSparkUsedTagsCacheResetVersionKey";
+ static const String enableLegacyAddresses = "enableLegacyAddressesKey";
}
Why this scored 19/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.