Skip to content

Commit 63310d2

Browse files
Implement Firo view key functionality.
1 parent c316428 commit 63310d2

File tree

16 files changed

+1135
-397
lines changed

16 files changed

+1135
-397
lines changed

lib/models/keys/view_only_wallet_data.dart

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import 'key_data_interface.dart';
77
enum ViewOnlyWalletType {
88
cryptonote,
99
addressOnly,
10-
xPub;
10+
xPub,
11+
spark;
1112
}
1213

1314
sealed class ViewOnlyWalletData with KeyDataInterface {
@@ -46,6 +47,12 @@ sealed class ViewOnlyWalletData with KeyDataInterface {
4647
jsonEncodedString,
4748
walletId: walletId,
4849
);
50+
51+
case ViewOnlyWalletType.spark:
52+
return SparkViewOnlyWalletData.fromJsonEncodedString(
53+
jsonEncodedString,
54+
walletId: walletId,
55+
);
4956
}
5057
}
5158

@@ -162,3 +169,34 @@ class ExtendedKeysViewOnlyWalletData extends ViewOnlyWalletData {
162169
],
163170
});
164171
}
172+
173+
class SparkViewOnlyWalletData extends ViewOnlyWalletData {
174+
@override
175+
final type = ViewOnlyWalletType.spark;
176+
177+
final String viewKey;
178+
179+
SparkViewOnlyWalletData({
180+
required super.walletId,
181+
required this.viewKey,
182+
});
183+
184+
static SparkViewOnlyWalletData fromJsonEncodedString(
185+
String jsonEncodedString, {
186+
required String walletId,
187+
}) {
188+
final map = jsonDecode(jsonEncodedString) as Map;
189+
final json = Map<String, dynamic>.from(map);
190+
191+
return SparkViewOnlyWalletData(
192+
walletId: walletId,
193+
viewKey: json["viewKey"] as String,
194+
);
195+
}
196+
197+
@override
198+
String toJsonEncodedString() => jsonEncode({
199+
"type": type.index,
200+
"viewKey": viewKey,
201+
});
202+
}

lib/pages/add_wallet_views/restore_wallet_view/restore_view_only_wallet_view.dart

Lines changed: 74 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,13 @@ import '../../../widgets/desktop/desktop_app_bar.dart';
3636
import '../../../widgets/desktop/desktop_scaffold.dart';
3737
import '../../../widgets/desktop/primary_button.dart';
3838
import '../../../widgets/stack_text_field.dart';
39-
import '../../../widgets/toggle.dart';
39+
import '../../../widgets/options.dart';
4040
import '../../home_view/home_view.dart';
4141
import 'confirm_recovery_dialog.dart';
4242
import 'sub_widgets/restore_failed_dialog.dart';
4343
import 'sub_widgets/restore_succeeded_dialog.dart';
4444
import 'sub_widgets/restoring_dialog.dart';
45+
import '../../../wallets/wallet/impl/firo_wallet.dart';
4546

4647
class RestoreViewOnlyWalletView extends ConsumerStatefulWidget {
4748
const RestoreViewOnlyWalletView({
@@ -68,13 +69,13 @@ class _RestoreViewOnlyWalletViewState
6869
extends ConsumerState<RestoreViewOnlyWalletView> {
6970
late final TextEditingController addressController;
7071
late final TextEditingController viewKeyController;
72+
late final TextEditingController sparkViewKeyController;
7173

72-
late String _currentDropDownValue;
74+
late ViewOnlyWalletType _walletType;
7375

7476
bool _enableRestoreButton = false;
75-
bool _addressOnly = false;
76-
7777
bool _buttonLock = false;
78+
late String _currentDropDownValue;
7879

7980
Future<void> _requestRestore() async {
8081
if (_buttonLock) return;
@@ -107,21 +108,16 @@ class _RestoreViewOnlyWalletViewState
107108
WalletInfoKeys.isViewOnlyKey: true,
108109
};
109110

110-
final ViewOnlyWalletType viewOnlyWalletType;
111+
ViewOnlyWalletType viewOnlyWalletType = _walletType;
111112
if (widget.coin is Bip39HDCurrency) {
112-
viewOnlyWalletType =
113-
_addressOnly
114-
? ViewOnlyWalletType.addressOnly
115-
: ViewOnlyWalletType.xPub;
116113
} else if (widget.coin is CryptonoteCurrency) {
117114
viewOnlyWalletType = ViewOnlyWalletType.cryptonote;
118115
} else {
119116
throw Exception(
120117
"Unsupported view only wallet currency type found: ${widget.coin.runtimeType}",
121118
);
122119
}
123-
otherDataJson[WalletInfoKeys.viewOnlyTypeIndexKey] =
124-
viewOnlyWalletType.index;
120+
otherDataJson[WalletInfoKeys.viewOnlyTypeIndexKey] = _walletType.index;
125121

126122
if (!Platform.isLinux && !Util.isDesktop) await WakelockPlus.enable();
127123

@@ -192,6 +188,16 @@ class _RestoreViewOnlyWalletViewState
192188
],
193189
);
194190
break;
191+
192+
case ViewOnlyWalletType.spark:
193+
if (sparkViewKeyController.text.isEmpty) {
194+
throw Exception("Spark View Key is empty");
195+
}
196+
viewOnlyData = SparkViewOnlyWalletData(
197+
walletId: info.walletId,
198+
viewKey: sparkViewKeyController.text,
199+
);
200+
break;
195201
}
196202

197203
var node = ref
@@ -237,6 +243,10 @@ class _RestoreViewOnlyWalletViewState
237243
await (wallet as XelisWallet).init(isRestore: true);
238244
break;
239245

246+
case const (FiroWallet):
247+
await (wallet as FiroWallet).init();
248+
break;
249+
240250
default:
241251
await wallet.init();
242252
}
@@ -314,19 +324,23 @@ class _RestoreViewOnlyWalletViewState
314324
super.initState();
315325
addressController = TextEditingController();
316326
viewKeyController = TextEditingController();
327+
sparkViewKeyController = TextEditingController();
317328

318329
if (widget.coin is Bip39HDCurrency) {
319-
_currentDropDownValue =
320-
(widget.coin as Bip39HDCurrency)
321-
.supportedHardenedDerivationPaths
322-
.last;
330+
_currentDropDownValue = (widget.coin as Bip39HDCurrency)
331+
.supportedHardenedDerivationPaths
332+
.last;
333+
_walletType = ViewOnlyWalletType.xPub;
334+
} else if (widget.coin is CryptonoteCurrency) {
335+
_walletType = ViewOnlyWalletType.cryptonote;
323336
}
324337
}
325338

326339
@override
327340
void dispose() {
328341
addressController.dispose();
329342
viewKeyController.dispose();
343+
sparkViewKeyController.dispose();
330344
super.dispose();
331345
}
332346

@@ -393,23 +407,25 @@ class _RestoreViewOnlyWalletViewState
393407
if (isElectrumX)
394408
SizedBox(
395409
height: isDesktop ? 56 : 48,
396-
width: isDesktop ? 490 : null,
397-
child: Toggle(
410+
width: isDesktop ? 490 : double.infinity,
411+
child: Options(
398412
key: UniqueKey(),
399-
onText: "Extended pub key",
400-
offText: "Single address",
401-
onColor:
402-
Theme.of(
403-
context,
404-
).extension<StackColors>()!.popupBG,
405-
offColor:
406-
Theme.of(context)
407-
.extension<StackColors>()!
408-
.textFieldDefaultBG,
409-
isOn: _addressOnly,
413+
texts: [
414+
"Single address",
415+
"Extended pub key",
416+
if (widget.coin is Firo)
417+
isDesktop ? "Spark View Key" : "View Key"
418+
],
419+
onColor: Theme.of(context)
420+
.extension<StackColors>()!
421+
.popupBG,
422+
offColor: Theme.of(context)
423+
.extension<StackColors>()!
424+
.textFieldDefaultBG,
425+
selectedIndex: _walletType.index-1,
410426
onValueChanged: (value) {
411427
setState(() {
412-
_addressOnly = value;
428+
_walletType = ViewOnlyWalletType.values[value+1];
413429
});
414430
},
415431
decoration: BoxDecoration(
@@ -420,8 +436,10 @@ class _RestoreViewOnlyWalletViewState
420436
),
421437
),
422438
),
423-
SizedBox(height: isDesktop ? 24 : 16),
424-
if (!isElectrumX || _addressOnly)
439+
SizedBox(
440+
height: isDesktop ? 24 : 16,
441+
),
442+
if (!isElectrumX || _walletType == ViewOnlyWalletType.addressOnly)
425443
FullTextField(
426444
key: const Key("viewOnlyAddressRestoreFieldKey"),
427445
label: "Address",
@@ -441,8 +459,11 @@ class _RestoreViewOnlyWalletViewState
441459
}
442460
},
443461
),
444-
if (!isElectrumX) SizedBox(height: isDesktop ? 16 : 12),
445-
if (isElectrumX && !_addressOnly)
462+
if (!isElectrumX)
463+
SizedBox(
464+
height: isDesktop ? 16 : 12,
465+
),
466+
if (isElectrumX && _walletType == ViewOnlyWalletType.xPub)
446467
DropdownButtonHideUnderline(
447468
child: DropdownButton2<String>(
448469
value: _currentDropDownValue,
@@ -513,9 +534,11 @@ class _RestoreViewOnlyWalletViewState
513534
),
514535
),
515536
),
516-
if (isElectrumX && !_addressOnly)
517-
SizedBox(height: isDesktop ? 16 : 12),
518-
if (!isElectrumX || !_addressOnly)
537+
if (isElectrumX && _walletType == ViewOnlyWalletType.xPub)
538+
SizedBox(
539+
height: isDesktop ? 16 : 12,
540+
),
541+
if (!isElectrumX || _walletType == ViewOnlyWalletType.xPub)
519542
FullTextField(
520543
key: const Key("viewOnlyKeyRestoreFieldKey"),
521544
label:
@@ -536,6 +559,21 @@ class _RestoreViewOnlyWalletViewState
536559
}
537560
},
538561
),
562+
if (_walletType == ViewOnlyWalletType.spark)
563+
SizedBox(
564+
height: isDesktop ? 16 : 12,
565+
),
566+
if (_walletType == ViewOnlyWalletType.spark)
567+
FullTextField(
568+
key: const Key("viewOnlySparkViewKeyRestoreFieldKey"),
569+
label: "Spark View Key",
570+
controller: sparkViewKeyController,
571+
onChanged: (value) {
572+
setState(() {
573+
_enableRestoreButton = value.isNotEmpty;
574+
});
575+
},
576+
),
539577
if (!isDesktop) const Spacer(),
540578
SizedBox(height: isDesktop ? 24 : 16),
541579
PrimaryButton(

lib/pages/receive_view/receive_view.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,16 +183,16 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
183183
if (wallet is Bip39HDWallet && wallet is! BCashInterface) {
184184
DerivePathType? type;
185185
if (wallet.isViewOnly && wallet is ExtendedKeysInterface) {
186-
final voData =
187-
await wallet.getViewOnlyWalletData()
188-
as ExtendedKeysViewOnlyWalletData;
186+
final voData = await wallet.getViewOnlyWalletData();
189187
for (final t in wallet.cryptoCurrency.supportedDerivationPathTypes) {
190188
final testPath = wallet.cryptoCurrency.constructDerivePath(
191189
derivePathType: t,
192190
chain: 0,
193191
index: 0,
194192
);
195-
if (testPath.startsWith(voData.xPubs.first.path)) {
193+
if (voData is SparkViewOnlyWalletData) {
194+
type = t;
195+
} else if (testPath.startsWith((voData as ExtendedKeysViewOnlyWalletData).xPubs.first.path)) {
196196
type = t;
197197
break;
198198
}

lib/pages/settings_views/sub_widgets/view_only_wallet_data_widget.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,22 @@ class ViewOnlyWalletDataWidget extends StatelessWidget {
8181
),
8282
],
8383
),
84+
final SparkViewOnlyWalletData e => Column(
85+
crossAxisAlignment: CrossAxisAlignment.stretch,
86+
children: [
87+
DetailItem(
88+
title: "View Key",
89+
detail: e.viewKey,
90+
button: Util.isDesktop
91+
? IconCopyButton(
92+
data: e.viewKey,
93+
)
94+
: SimpleCopyButton(
95+
data: e.viewKey,
96+
),
97+
),
98+
],
99+
),
84100
};
85101
}
86102
}

0 commit comments

Comments
 (0)