mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-08 03:39:23 +00:00
feat: WIP UI updates, add a MultisigCoordinator view
TODO: disable "Continue" button on setup view unless all inputs have values. Auto-fill first input in Coordinator view. Add walletId params as necessary in order to get relevant metadata
This commit is contained in:
parent
47525fb301
commit
53901ef6a5
7 changed files with 450 additions and 112 deletions
|
@ -0,0 +1,373 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
import '../../../../themes/stack_colors.dart';
|
||||||
|
import '../../../../utilities/text_styles.dart';
|
||||||
|
import '../../../../widgets/background.dart';
|
||||||
|
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
|
import '../../../widgets/desktop/primary_button.dart';
|
||||||
|
import '../../../widgets/desktop/secondary_button.dart';
|
||||||
|
import '../../../widgets/icon_widgets/copy_icon.dart';
|
||||||
|
import '../../../widgets/icon_widgets/qrcode_icon.dart';
|
||||||
|
|
||||||
|
final multisigCoordinatorStateProvider =
|
||||||
|
StateNotifierProvider<MultisigCoordinatorState, MultisigCoordinatorData>(
|
||||||
|
(ref) {
|
||||||
|
return MultisigCoordinatorState();
|
||||||
|
});
|
||||||
|
|
||||||
|
class MultisigCoordinatorData {
|
||||||
|
const MultisigCoordinatorData({
|
||||||
|
this.threshold = 2,
|
||||||
|
this.totalCosigners = 3,
|
||||||
|
this.coinType = 0, // Bitcoin mainnet.
|
||||||
|
this.accountIndex = 0,
|
||||||
|
this.scriptType = MultisigScriptType.nativeSegwit,
|
||||||
|
this.cosignerXpubs = const [],
|
||||||
|
});
|
||||||
|
|
||||||
|
final int threshold;
|
||||||
|
final int totalCosigners;
|
||||||
|
final int coinType;
|
||||||
|
final int accountIndex;
|
||||||
|
final MultisigScriptType scriptType;
|
||||||
|
final List<String> cosignerXpubs;
|
||||||
|
|
||||||
|
MultisigCoordinatorData copyWith({
|
||||||
|
int? threshold,
|
||||||
|
int? totalCosigners,
|
||||||
|
int? coinType,
|
||||||
|
int? accountIndex,
|
||||||
|
MultisigScriptType? scriptType,
|
||||||
|
List<String>? cosignerXpubs,
|
||||||
|
}) {
|
||||||
|
return MultisigCoordinatorData(
|
||||||
|
threshold: threshold ?? this.threshold,
|
||||||
|
totalCosigners: totalCosigners ?? this.totalCosigners,
|
||||||
|
coinType: coinType ?? this.coinType,
|
||||||
|
accountIndex: accountIndex ?? this.accountIndex,
|
||||||
|
scriptType: scriptType ?? this.scriptType,
|
||||||
|
cosignerXpubs: cosignerXpubs ?? this.cosignerXpubs,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'threshold': threshold,
|
||||||
|
'totalCosigners': totalCosigners,
|
||||||
|
'coinType': coinType,
|
||||||
|
'accountIndex': accountIndex,
|
||||||
|
'scriptType': scriptType.index,
|
||||||
|
'cosignerXpubs': cosignerXpubs,
|
||||||
|
};
|
||||||
|
|
||||||
|
factory MultisigCoordinatorData.fromJson(Map<String, dynamic> json) {
|
||||||
|
return MultisigCoordinatorData(
|
||||||
|
threshold: json['threshold'] as int,
|
||||||
|
totalCosigners: json['totalCosigners'] as int,
|
||||||
|
coinType: json['coinType'] as int,
|
||||||
|
accountIndex: json['accountIndex'] as int,
|
||||||
|
scriptType: MultisigScriptType.values[json['scriptType'] as int],
|
||||||
|
cosignerXpubs: (json['cosignerXpubs'] as List).cast<String>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MultisigScriptType {
|
||||||
|
legacy, // P2SH.
|
||||||
|
segwit, // P2SH-P2WSH.
|
||||||
|
nativeSegwit, // P2WSH.
|
||||||
|
}
|
||||||
|
|
||||||
|
class MultisigCoordinatorState extends StateNotifier<MultisigCoordinatorData> {
|
||||||
|
MultisigCoordinatorState() : super(const MultisigCoordinatorData());
|
||||||
|
|
||||||
|
void updateThreshold(int threshold) {
|
||||||
|
state = state.copyWith(threshold: threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateTotalCosigners(int total) {
|
||||||
|
state = state.copyWith(totalCosigners: total);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateScriptType(MultisigScriptType type) {
|
||||||
|
state = state.copyWith(scriptType: type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addCosignerXpub(String xpub) {
|
||||||
|
if (state.cosignerXpubs.length < state.totalCosigners) {
|
||||||
|
state = state.copyWith(
|
||||||
|
cosignerXpubs: [...state.cosignerXpubs, xpub],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MultisigCoordinatorView extends ConsumerStatefulWidget {
|
||||||
|
const MultisigCoordinatorView({
|
||||||
|
super.key,
|
||||||
|
required this.totalCosigners,
|
||||||
|
required this.threshold,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int totalCosigners;
|
||||||
|
final int threshold;
|
||||||
|
|
||||||
|
static const String routeName = "/multisigCoordinator";
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<MultisigCoordinatorView> createState() =>
|
||||||
|
_MultisigSetupViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MultisigSetupViewState extends ConsumerState<MultisigCoordinatorView> {
|
||||||
|
final List<TextEditingController> xpubControllers = [];
|
||||||
|
// bool _isNfcAvailable = false;
|
||||||
|
// String _nfcStatus = 'Checking NFC availability...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
// Initialize controllers.
|
||||||
|
for (int i = 0; i < widget.totalCosigners; i++) {
|
||||||
|
xpubControllers.add(TextEditingController());
|
||||||
|
}
|
||||||
|
|
||||||
|
// _checkNfcAvailability();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
for (final controller in xpubControllers) {
|
||||||
|
controller.dispose();
|
||||||
|
}
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Future<void> _checkNfcAvailability() async {
|
||||||
|
// try {
|
||||||
|
// final availability = await NfcManager.instance.isAvailable();
|
||||||
|
// setState(() {
|
||||||
|
// _isNfcAvailable = availability;
|
||||||
|
// _nfcStatus = _isNfcAvailable
|
||||||
|
// ? 'NFC is available'
|
||||||
|
// : 'NFC is not available on this device';
|
||||||
|
// });
|
||||||
|
// } catch (e) {
|
||||||
|
// setState(() {
|
||||||
|
// _nfcStatus = 'Error checking NFC: $e';
|
||||||
|
// _isNfcAvailable = false;
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Future<void> _startNfcSession() async {
|
||||||
|
// if (!_isNfcAvailable) return;
|
||||||
|
//
|
||||||
|
// setState(() => _nfcStatus = 'Ready to exchange information...');
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// await NfcManager.instance.startSession(
|
||||||
|
// onDiscovered: (tag) async {
|
||||||
|
// try {
|
||||||
|
// final ndef = Ndef.from(tag);
|
||||||
|
//
|
||||||
|
// if (ndef == null) {
|
||||||
|
// setState(() => _nfcStatus = 'Tag is not NDEF compatible');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// final setupData = ref.watch(multisigSetupStateProvider);
|
||||||
|
//
|
||||||
|
// if (ndef.isWritable) {
|
||||||
|
// final message = NdefMessage([
|
||||||
|
// NdefRecord.createMime(
|
||||||
|
// 'application/x-multisig-setup',
|
||||||
|
// Uint8List.fromList(
|
||||||
|
// utf8.encode(jsonEncode(setupData.toJson()))),
|
||||||
|
// ),
|
||||||
|
// ]);
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// await ndef.write(message);
|
||||||
|
// setState(
|
||||||
|
// () => _nfcStatus = 'Configuration shared successfully');
|
||||||
|
// } catch (e) {
|
||||||
|
// setState(
|
||||||
|
// () => _nfcStatus = 'Failed to share configuration: $e');
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// await NfcManager.instance.stopSession();
|
||||||
|
// } catch (e) {
|
||||||
|
// setState(() => _nfcStatus = 'Error during NFC exchange: $e');
|
||||||
|
// await NfcManager.instance.stopSession();
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// } catch (e) {
|
||||||
|
// setState(() => _nfcStatus = 'Error: $e');
|
||||||
|
// await NfcManager.instance.stopSession();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Background(
|
||||||
|
child: SafeArea(
|
||||||
|
child: Scaffold(
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(context).extension<StackColors>()!.background,
|
||||||
|
appBar: AppBar(
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
leading: AppBarBackButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (FocusScope.of(context).hasFocus) {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
await Future<void>.delayed(const Duration(milliseconds: 75));
|
||||||
|
}
|
||||||
|
if (context.mounted) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
"Enter cosigner xpubs",
|
||||||
|
style: STextStyles.navBarTitle(context),
|
||||||
|
),
|
||||||
|
titleSpacing: 0,
|
||||||
|
),
|
||||||
|
body: LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minHeight: constraints.maxHeight,
|
||||||
|
),
|
||||||
|
child: IntrinsicHeight(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Enter the extended public key (xpub) for each cosigner. "
|
||||||
|
"These can be obtained from each participant's wallet.",
|
||||||
|
style: STextStyles.itemSubtitle(context),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
// Generate input fields for each cosigner
|
||||||
|
for (int i = 0; i < widget.totalCosigners; i++)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Cosigner ${i + 1} xpub",
|
||||||
|
style:
|
||||||
|
STextStyles.w500_14(context).copyWith(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textDark3,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
controller: xpubControllers[i],
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: "Enter xpub",
|
||||||
|
hintStyle:
|
||||||
|
STextStyles.fieldLabel(context),
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value.isNotEmpty) {
|
||||||
|
ref
|
||||||
|
.read(
|
||||||
|
multisigCoordinatorStateProvider
|
||||||
|
.notifier)
|
||||||
|
.addCosignerXpub(value);
|
||||||
|
}
|
||||||
|
setState(
|
||||||
|
() {}); // Trigger rebuild to update button state.
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
SecondaryButton(
|
||||||
|
width: 44,
|
||||||
|
buttonHeight: ButtonHeight.xl,
|
||||||
|
icon: QrCodeIcon(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.buttonTextSecondary,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
// TODO: Implement QR code scanning
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
SecondaryButton(
|
||||||
|
width: 44,
|
||||||
|
buttonHeight: ButtonHeight.xl,
|
||||||
|
icon: CopyIcon(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.buttonTextSecondary,
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
final data = await Clipboard.getData(
|
||||||
|
'text/plain');
|
||||||
|
if (data?.text != null) {
|
||||||
|
xpubControllers[i].text =
|
||||||
|
data!.text!;
|
||||||
|
ref
|
||||||
|
.read(
|
||||||
|
multisigCoordinatorStateProvider
|
||||||
|
.notifier)
|
||||||
|
.addCosignerXpub(data.text!);
|
||||||
|
setState(
|
||||||
|
() {}); // Trigger rebuild to update button state.
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const Spacer(),
|
||||||
|
|
||||||
|
PrimaryButton(
|
||||||
|
label: "Create multisignature account",
|
||||||
|
enabled: xpubControllers.every(
|
||||||
|
(controller) => controller.text.isNotEmpty),
|
||||||
|
onPressed: () {
|
||||||
|
// TODO.
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import '../../../widgets/custom_buttons/blue_text_button.dart';
|
||||||
import '../../../widgets/desktop/primary_button.dart';
|
import '../../../widgets/desktop/primary_button.dart';
|
||||||
import '../../../widgets/dialogs/simple_mobile_dialog.dart';
|
import '../../../widgets/dialogs/simple_mobile_dialog.dart';
|
||||||
import '../../../widgets/stack_dialog.dart';
|
import '../../../widgets/stack_dialog.dart';
|
||||||
|
import 'multisig_coordinator_view.dart';
|
||||||
|
|
||||||
final multisigSetupStateProvider =
|
final multisigSetupStateProvider =
|
||||||
StateNotifierProvider<MultisigSetupState, MultisigSetupData>((ref) {
|
StateNotifierProvider<MultisigSetupState, MultisigSetupData>((ref) {
|
||||||
|
@ -110,8 +111,13 @@ class MultisigSetupState extends StateNotifier<MultisigSetupData> {
|
||||||
class MultisigSetupView extends ConsumerStatefulWidget {
|
class MultisigSetupView extends ConsumerStatefulWidget {
|
||||||
const MultisigSetupView({
|
const MultisigSetupView({
|
||||||
super.key,
|
super.key,
|
||||||
|
this.totalCosigners,
|
||||||
|
this.threshold,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final int? totalCosigners;
|
||||||
|
final int? threshold;
|
||||||
|
|
||||||
static const String routeName = "/multisigSetup";
|
static const String routeName = "/multisigSetup";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -119,81 +125,25 @@ class MultisigSetupView extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MultisigSetupViewState extends ConsumerState<MultisigSetupView> {
|
class _MultisigSetupViewState extends ConsumerState<MultisigSetupView> {
|
||||||
// bool _isNfcAvailable = false;
|
|
||||||
// String _nfcStatus = 'Checking NFC availability...';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// _checkNfcAvailability();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Future<void> _checkNfcAvailability() async {
|
// Initialize participants count if provided.
|
||||||
// try {
|
if (widget.totalCosigners != null) {
|
||||||
// final availability = await NfcManager.instance.isAvailable();
|
_participantsCount = widget.totalCosigners!;
|
||||||
// setState(() {
|
_participantsController.text = widget.totalCosigners!.toString();
|
||||||
// _isNfcAvailable = availability;
|
// Initialize the controllers list.
|
||||||
// _nfcStatus = _isNfcAvailable
|
for (int i = 0; i < widget.totalCosigners!; i++) {
|
||||||
// ? 'NFC is available'
|
controllers.add(TextEditingController());
|
||||||
// : 'NFC is not available on this device';
|
}
|
||||||
// });
|
}
|
||||||
// } catch (e) {
|
|
||||||
// setState(() {
|
// Initialize threshold if provided.
|
||||||
// _nfcStatus = 'Error checking NFC: $e';
|
if (widget.threshold != null) {
|
||||||
// _isNfcAvailable = false;
|
_thresholdController.text = widget.threshold!.toString();
|
||||||
// });
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Future<void> _startNfcSession() async {
|
|
||||||
// if (!_isNfcAvailable) return;
|
|
||||||
//
|
|
||||||
// setState(() => _nfcStatus = 'Ready to exchange information...');
|
|
||||||
//
|
|
||||||
// try {
|
|
||||||
// await NfcManager.instance.startSession(
|
|
||||||
// onDiscovered: (tag) async {
|
|
||||||
// try {
|
|
||||||
// final ndef = Ndef.from(tag);
|
|
||||||
//
|
|
||||||
// if (ndef == null) {
|
|
||||||
// setState(() => _nfcStatus = 'Tag is not NDEF compatible');
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// final setupData = ref.watch(multisigSetupStateProvider);
|
|
||||||
//
|
|
||||||
// if (ndef.isWritable) {
|
|
||||||
// final message = NdefMessage([
|
|
||||||
// NdefRecord.createMime(
|
|
||||||
// 'application/x-multisig-setup',
|
|
||||||
// Uint8List.fromList(
|
|
||||||
// utf8.encode(jsonEncode(setupData.toJson()))),
|
|
||||||
// ),
|
|
||||||
// ]);
|
|
||||||
//
|
|
||||||
// try {
|
|
||||||
// await ndef.write(message);
|
|
||||||
// setState(
|
|
||||||
// () => _nfcStatus = 'Configuration shared successfully');
|
|
||||||
// } catch (e) {
|
|
||||||
// setState(
|
|
||||||
// () => _nfcStatus = 'Failed to share configuration: $e');
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// await NfcManager.instance.stopSession();
|
|
||||||
// } catch (e) {
|
|
||||||
// setState(() => _nfcStatus = 'Error during NFC exchange: $e');
|
|
||||||
// await NfcManager.instance.stopSession();
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
// } catch (e) {
|
|
||||||
// setState(() => _nfcStatus = 'Error: $e');
|
|
||||||
// await NfcManager.instance.stopSession();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Displays a short explanation dialog about musig.
|
/// Displays a short explanation dialog about musig.
|
||||||
Future<void> _showMultisigInfoDialog() async {
|
Future<void> _showMultisigInfoDialog() async {
|
||||||
|
@ -557,7 +507,7 @@ class _MultisigSetupViewState extends ConsumerState<MultisigSetupView> {
|
||||||
|
|
||||||
// TODO: Push button to bottom of page.
|
// TODO: Push button to bottom of page.
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
label: "Create multisignature account",
|
label: "Continue",
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (FocusScope.of(context).hasFocus) {
|
if (FocusScope.of(context).hasFocus) {
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
|
@ -575,34 +525,15 @@ class _MultisigSetupViewState extends ConsumerState<MultisigSetupView> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Adapt the FROST config steps UI.
|
await Navigator.of(context).push(
|
||||||
// final config = Frost.createMultisigConfig(
|
MaterialPageRoute<void>(
|
||||||
// name: controllers.first.text.trim(),
|
builder: (context) => MultisigCoordinatorView(
|
||||||
// threshold: int.parse(_thresholdController.text),
|
totalCosigners:
|
||||||
// participants:
|
int.parse(_participantsController.text),
|
||||||
// controllers.map((e) => e.text.trim()).toList(),
|
threshold: int.parse(_thresholdController.text),
|
||||||
// );
|
),
|
||||||
//
|
),
|
||||||
// ref.read(pFrostMyName.notifier).state =
|
);
|
||||||
// controllers.first.text.trim();
|
|
||||||
// ref.read(pFrostMultisigConfig.notifier).state = config;
|
|
||||||
//
|
|
||||||
// ref.read(pFrostScaffoldArgs.state).state = (
|
|
||||||
// info: (
|
|
||||||
// walletName: widget.walletName,
|
|
||||||
// frostCurrency: widget.frostCurrency,
|
|
||||||
// ),
|
|
||||||
// walletId: null,
|
|
||||||
// stepRoutes: FrostRouteGenerator.createNewConfigStepRoutes,
|
|
||||||
// frostInterruptionDialogType:
|
|
||||||
// FrostInterruptionDialogType.walletCreation,
|
|
||||||
// parentNav: Navigator.of(context),
|
|
||||||
// callerRouteName: CreateNewFrostMsWalletView.routeName,
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// await Navigator.of(context).pushNamed(
|
|
||||||
// FrostStepScaffold.routeName,
|
|
||||||
// );
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
|
@ -98,7 +98,7 @@ import '../settings_views/wallet_settings_view/wallet_network_settings_view/wall
|
||||||
import '../settings_views/wallet_settings_view/wallet_settings_view.dart';
|
import '../settings_views/wallet_settings_view/wallet_settings_view.dart';
|
||||||
import '../special/firo_rescan_recovery_error_dialog.dart';
|
import '../special/firo_rescan_recovery_error_dialog.dart';
|
||||||
import '../token_view/my_tokens_view.dart';
|
import '../token_view/my_tokens_view.dart';
|
||||||
import 'multisig_setup_view/multisig_setup_view.dart';
|
import 'multisig_coordinator_view/multisig_setup_view.dart';
|
||||||
import 'sub_widgets/transactions_list.dart';
|
import 'sub_widgets/transactions_list.dart';
|
||||||
import 'sub_widgets/wallet_summary.dart';
|
import 'sub_widgets/wallet_summary.dart';
|
||||||
import 'transaction_views/all_transactions_view.dart';
|
import 'transaction_views/all_transactions_view.dart';
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:stackwallet/pages/wallet_view/multisig_setup_view/multisig_setup_view.dart';
|
import 'package:stackwallet/pages/wallet_view/multisig_coordinator_view/multisig_coordinator_view.dart';
|
||||||
|
import 'package:stackwallet/pages/wallet_view/multisig_coordinator_view/multisig_setup_view.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
import 'models/add_wallet_list_entity/add_wallet_list_entity.dart';
|
import 'models/add_wallet_list_entity/add_wallet_list_entity.dart';
|
||||||
|
@ -2157,13 +2158,41 @@ class RouteGenerator {
|
||||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||||
|
|
||||||
case MultisigSetupView.routeName:
|
case MultisigSetupView.routeName:
|
||||||
return getRoute(
|
if (args is Tuple2<int?, int?>) {
|
||||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
return getRoute(
|
||||||
builder: (_) => const MultisigSetupView(),
|
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||||
settings: RouteSettings(
|
builder: (_) => MultisigSetupView(
|
||||||
name: settings.name,
|
totalCosigners: args.item1,
|
||||||
),
|
threshold: args.item2,
|
||||||
);
|
),
|
||||||
|
settings: RouteSettings(
|
||||||
|
name: settings.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return getRoute(
|
||||||
|
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||||
|
builder: (_) => const MultisigSetupView(),
|
||||||
|
settings: RouteSettings(
|
||||||
|
name: settings.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||||
|
|
||||||
|
case MultisigCoordinatorView.routeName:
|
||||||
|
if (args is Tuple2<int, int>) {
|
||||||
|
return getRoute(
|
||||||
|
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||||
|
builder: (_) => MultisigCoordinatorView(
|
||||||
|
totalCosigners: args.item1,
|
||||||
|
threshold: args.item2,
|
||||||
|
),
|
||||||
|
settings: RouteSettings(
|
||||||
|
name: settings.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||||
|
|
||||||
// == Desktop specific routes ============================================
|
// == Desktop specific routes ============================================
|
||||||
|
|
|
@ -6,12 +6,16 @@ import '../../../utilities/amount/amount.dart';
|
||||||
import '../../../utilities/default_nodes.dart';
|
import '../../../utilities/default_nodes.dart';
|
||||||
import '../../../utilities/enums/derive_path_type_enum.dart';
|
import '../../../utilities/enums/derive_path_type_enum.dart';
|
||||||
import '../crypto_currency.dart';
|
import '../crypto_currency.dart';
|
||||||
|
import '../interfaces/bip48_currency_interface.dart';
|
||||||
import '../interfaces/electrumx_currency_interface.dart';
|
import '../interfaces/electrumx_currency_interface.dart';
|
||||||
import '../interfaces/paynym_currency_interface.dart';
|
import '../interfaces/paynym_currency_interface.dart';
|
||||||
import '../intermediate/bip39_hd_currency.dart';
|
import '../intermediate/bip39_hd_currency.dart';
|
||||||
|
|
||||||
class Bitcoin extends Bip39HDCurrency
|
class Bitcoin extends Bip39HDCurrency
|
||||||
with ElectrumXCurrencyInterface, PaynymCurrencyInterface {
|
with
|
||||||
|
ElectrumXCurrencyInterface,
|
||||||
|
PaynymCurrencyInterface,
|
||||||
|
BIP48CurrencyInterface {
|
||||||
Bitcoin(super.network) {
|
Bitcoin(super.network) {
|
||||||
_idMain = "bitcoin";
|
_idMain = "bitcoin";
|
||||||
_uriScheme = "bitcoin";
|
_uriScheme = "bitcoin";
|
||||||
|
|
|
@ -273,6 +273,8 @@ class BIP48Wallet<T extends Bip39HDCurrency> extends Wallet<T>
|
||||||
|
|
||||||
TransactionType type;
|
TransactionType type;
|
||||||
TransactionSubType subType = TransactionSubType.none;
|
TransactionSubType subType = TransactionSubType.none;
|
||||||
|
// Will BIP48 wallets enjoy BIP47 compatibility? We should add vectors
|
||||||
|
// for this if so--do any wallets implement such functionality?
|
||||||
if (outputs.length > 1 && inputs.isNotEmpty) {
|
if (outputs.length > 1 && inputs.isNotEmpty) {
|
||||||
for (int i = 0; i < outputs.length; i++) {
|
for (int i = 0; i < outputs.length; i++) {
|
||||||
final List<String>? scriptChunks =
|
final List<String>? scriptChunks =
|
||||||
|
@ -465,14 +467,13 @@ class BIP48Wallet<T extends Bip39HDCurrency> extends Wallet<T>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<TxData> prepareSend({required TxData txData}) {
|
Future<TxData> prepareSend({required TxData txData}) {
|
||||||
// TODO: implement prepareSendpu
|
// TODO: implement prepareSend
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> recover({
|
Future<void> recover({
|
||||||
required bool isRescan,
|
required bool isRescan,
|
||||||
String? serializedKeys,
|
|
||||||
String? multisigConfig,
|
String? multisigConfig,
|
||||||
}) async {
|
}) async {
|
||||||
// TODO.
|
// TODO.
|
||||||
|
|
Loading…
Reference in a new issue