WIP basic PoC showing firo spark address in stack wallet

This commit is contained in:
julian 2023-11-29 09:53:30 -06:00
parent 6ddef9f077
commit 734e9d90b1
14 changed files with 160 additions and 43 deletions

View file

@ -15,6 +15,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:qr_flutter/qr_flutter.dart'; import 'package:qr_flutter/qr_flutter.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart'; import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart';
import 'package:stackwallet/pages/token_view/token_view.dart'; import 'package:stackwallet/pages/token_view/token_view.dart';
@ -30,6 +31,7 @@ import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart'; import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart'; import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_loading_overlay.dart'; import 'package:stackwallet/widgets/custom_loading_overlay.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
@ -57,6 +59,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
late final Coin coin; late final Coin coin;
late final String walletId; late final String walletId;
late final ClipboardInterface clipboard; late final ClipboardInterface clipboard;
late final bool supportsSpark;
Future<void> generateNewAddress() async { Future<void> generateNewAddress() async {
final wallet = ref.read(pWallets).getWallet(walletId); final wallet = ref.read(pWallets).getWallet(walletId);
@ -98,6 +101,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
walletId = widget.walletId; walletId = widget.walletId;
coin = ref.read(pWalletInfo(walletId)).coin; coin = ref.read(pWalletInfo(walletId)).coin;
clipboard = widget.clipboard; clipboard = widget.clipboard;
supportsSpark = ref.read(pWallets).getWallet(walletId) is SparkInterface;
super.initState(); super.initState();
} }
@ -111,6 +115,106 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
if (supportsSpark)
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
clipboard.setData(
ClipboardData(text: receivingAddress),
);
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
);
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
width: 1,
),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
child: RoundedWhiteContainer(
child: Column(
children: [
Row(
children: [
Text(
"Your ${widget.contractAddress == null ? coin.ticker : ref.watch(
tokenServiceProvider.select(
(value) => value!.tokenContract.symbol,
),
)} SPARK address",
style: STextStyles.itemSubtitle(context),
),
const Spacer(),
Row(
children: [
SvgPicture.asset(
Assets.svg.copy,
width: 15,
height: 15,
color: Theme.of(context)
.extension<StackColors>()!
.infoItemIcons,
),
const SizedBox(
width: 4,
),
Text(
"Copy",
style: STextStyles.link2(context),
),
],
),
],
),
const SizedBox(
height: 8,
),
Row(
children: [
Expanded(
child: FutureBuilder<Address?>(
future: (ref.watch(pWallets).getWallet(walletId)
as SparkInterface)
.getCurrentReceivingSparkAddress(),
builder: (context, snapshot) {
String addressString = "Error";
if (snapshot.hasData) {
addressString = snapshot.data!.value;
}
return Text(
addressString,
style: STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
);
},
),
),
],
),
],
),
),
),
),
),
MouseRegion( MouseRegion(
cursor: SystemMouseCursors.click, cursor: SystemMouseCursors.click,
child: GestureDetector( child: GestureDetector(

View file

@ -28,7 +28,7 @@ class BitcoinWallet extends Bip39HDWallet
// =========================================================================== // ===========================================================================
@override @override
Future<List<Address>> fetchAllOwnAddresses() async { Future<List<Address>> fetchAddressesForElectrumXScan() async {
final allAddresses = await mainDB final allAddresses = await mainDB
.getAddresses(walletId) .getAddresses(walletId)
.filter() .filter()
@ -51,7 +51,7 @@ class BitcoinWallet extends Bip39HDWallet
// TODO: [prio=med] switch to V2 transactions // TODO: [prio=med] switch to V2 transactions
final data = await fetchTransactionsV1( final data = await fetchTransactionsV1(
addresses: await fetchAllOwnAddresses(), addresses: await fetchAddressesForElectrumXScan(),
currentChainHeight: currentChainHeight, currentChainHeight: currentChainHeight,
); );

View file

@ -63,7 +63,7 @@ class BitcoincashWallet extends Bip39HDWallet
// =========================================================================== // ===========================================================================
@override @override
Future<List<Address>> fetchAllOwnAddresses() async { Future<List<Address>> fetchAddressesForElectrumXScan() async {
final allAddresses = await mainDB final allAddresses = await mainDB
.getAddresses(walletId) .getAddresses(walletId)
.filter() .filter()
@ -94,7 +94,7 @@ class BitcoincashWallet extends Bip39HDWallet
@override @override
Future<void> updateTransactions() async { Future<void> updateTransactions() async {
List<Address> allAddressesOld = await fetchAllOwnAddresses(); List<Address> allAddressesOld = await fetchAddressesForElectrumXScan();
Set<String> receivingAddresses = allAddressesOld Set<String> receivingAddresses = allAddressesOld
.where((e) => e.subType == AddressSubType.receiving) .where((e) => e.subType == AddressSubType.receiving)

View file

@ -24,7 +24,7 @@ class DogecoinWallet extends Bip39HDWallet
// =========================================================================== // ===========================================================================
@override @override
Future<List<Address>> fetchAllOwnAddresses() async { Future<List<Address>> fetchAddressesForElectrumXScan() async {
final allAddresses = await mainDB final allAddresses = await mainDB
.getAddresses(walletId) .getAddresses(walletId)
.filter() .filter()
@ -47,7 +47,7 @@ class DogecoinWallet extends Bip39HDWallet
// TODO: [prio=med] switch to V2 transactions // TODO: [prio=med] switch to V2 transactions
final data = await fetchTransactionsV1( final data = await fetchTransactionsV1(
addresses: await fetchAllOwnAddresses(), addresses: await fetchAddressesForElectrumXScan(),
currentChainHeight: currentChainHeight, currentChainHeight: currentChainHeight,
); );

View file

@ -58,7 +58,7 @@ class EcashWallet extends Bip39HDWallet
// =========================================================================== // ===========================================================================
@override @override
Future<List<Address>> fetchAllOwnAddresses() async { Future<List<Address>> fetchAddressesForElectrumXScan() async {
final allAddresses = await mainDB final allAddresses = await mainDB
.getAddresses(walletId) .getAddresses(walletId)
.filter() .filter()
@ -87,7 +87,7 @@ class EcashWallet extends Bip39HDWallet
@override @override
Future<void> updateTransactions() async { Future<void> updateTransactions() async {
List<Address> allAddressesOld = await fetchAllOwnAddresses(); List<Address> allAddressesOld = await fetchAddressesForElectrumXScan();
Set<String> receivingAddresses = allAddressesOld Set<String> receivingAddresses = allAddressesOld
.where((e) => e.subType == AddressSubType.receiving) .where((e) => e.subType == AddressSubType.receiving)

View file

@ -37,24 +37,6 @@ class FiroWallet extends Bip39HDWallet
// =========================================================================== // ===========================================================================
@override
Future<List<Address>> fetchAllOwnAddresses() async {
final allAddresses = await mainDB
.getAddresses(walletId)
.filter()
.not()
.group(
(q) => q
.typeEqualTo(AddressType.nonWallet)
.or()
.subTypeEqualTo(AddressSubType.nonWallet),
)
.findAll();
return allAddresses;
}
// ===========================================================================
bool _duplicateTxCheck( bool _duplicateTxCheck(
List<Map<String, dynamic>> allTransactions, String txid) { List<Map<String, dynamic>> allTransactions, String txid) {
for (int i = 0; i < allTransactions.length; i++) { for (int i = 0; i < allTransactions.length; i++) {
@ -67,7 +49,7 @@ class FiroWallet extends Bip39HDWallet
@override @override
Future<void> updateTransactions() async { Future<void> updateTransactions() async {
final allAddresses = await fetchAllOwnAddresses(); final allAddresses = await fetchAddressesForElectrumXScan();
Set<String> receivingAddresses = allAddresses Set<String> receivingAddresses = allAddresses
.where((e) => e.subType == AddressSubType.receiving) .where((e) => e.subType == AddressSubType.receiving)

View file

@ -289,7 +289,10 @@ abstract class Wallet<T extends CryptoCurrency> {
// listen to changes in db and updated wallet info property as required // listen to changes in db and updated wallet info property as required
void _watchWalletInfo() { void _watchWalletInfo() {
_walletInfoStream = mainDB.isar.walletInfo.watchObject(_walletInfo.id); _walletInfoStream = mainDB.isar.walletInfo.watchObject(
_walletInfo.id,
fireImmediately: true,
);
_walletInfoStream.forEach((element) { _walletInfoStream.forEach((element) {
if (element != null) { if (element != null) {
_walletInfo = element; _walletInfo = element;

View file

@ -1641,7 +1641,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
@override @override
Future<void> updateUTXOs() async { Future<void> updateUTXOs() async {
final allAddresses = await fetchAllOwnAddresses(); final allAddresses = await fetchAddressesForElectrumXScan();
try { try {
final fetchedUtxoList = <List<Map<String, dynamic>>>[]; final fetchedUtxoList = <List<Map<String, dynamic>>>[];
@ -1856,7 +1856,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
int estimateTxFee({required int vSize, required int feeRatePerKB}); int estimateTxFee({required int vSize, required int feeRatePerKB});
Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB); Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB);
Future<List<Address>> fetchAllOwnAddresses(); Future<List<Address>> fetchAddressesForElectrumXScan();
/// Certain coins need to check if the utxo should be marked /// Certain coins need to check if the utxo should be marked
/// as blocked as well as give a reason. /// as blocked as well as give a reason.

View file

@ -10,6 +10,40 @@ import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
mixin SparkInterface on Bip39HDWallet, ElectrumXInterface { mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
@override
Future<void> init() async {
Address? address = await getCurrentReceivingSparkAddress();
if (address == null) {
address = await generateNextSparkAddress();
await mainDB.putAddress(address);
} // TODO add other address types to wallet info?
// await info.updateReceivingAddress(
// newAddress: address.value,
// isar: mainDB.isar,
// );
await super.init();
}
@override
Future<List<Address>> fetchAddressesForElectrumXScan() async {
final allAddresses = await mainDB
.getAddresses(walletId)
.filter()
.not()
.group(
(q) => q
.typeEqualTo(AddressType.spark)
.or()
.typeEqualTo(AddressType.nonWallet)
.or()
.subTypeEqualTo(AddressSubType.nonWallet),
)
.findAll();
return allAddresses;
}
Future<Address?> getCurrentReceivingSparkAddress() async { Future<Address?> getCurrentReceivingSparkAddress() async {
return await mainDB.isar.addresses return await mainDB.isar.addresses
.where() .where()
@ -29,15 +63,18 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
throw UnimplementedError(); throw UnimplementedError();
} }
Future<Address> generateNextSparkAddress({int index = 1}) async { Future<Address> generateNextSparkAddress() async {
final highestStoredDiversifier = final highestStoredDiversifier =
(await getCurrentReceivingSparkAddress())?.derivationIndex; (await getCurrentReceivingSparkAddress())?.derivationIndex;
// default to starting at 1 if none found // default to starting at 1 if none found
final int diversifier = (highestStoredDiversifier ?? 0) + 1; final int diversifier = (highestStoredDiversifier ?? 0) + 1;
// TODO: check that this stays constant and only the diversifier changes?
const index = 1;
final root = await getRootHDNode(); final root = await getRootHDNode();
final derivationPath = "$kSparkBaseDerivationPath$index"; const derivationPath = "$kSparkBaseDerivationPath$index";
final keys = root.derivePath(derivationPath); final keys = root.derivePath(derivationPath);
final String addressString = await LibSpark.getAddress( final String addressString = await LibSpark.getAddress(

View file

@ -10,7 +10,6 @@
#include <devicelocale/devicelocale_plugin.h> #include <devicelocale/devicelocale_plugin.h>
#include <flutter_libepiccash/flutter_libepiccash_plugin.h> #include <flutter_libepiccash/flutter_libepiccash_plugin.h>
#include <flutter_libmonero/flutter_libmonero_plugin.h> #include <flutter_libmonero/flutter_libmonero_plugin.h>
#include <flutter_libsparkmobile/flutter_libsparkmobile_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h> #include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <isar_flutter_libs/isar_flutter_libs_plugin.h> #include <isar_flutter_libs/isar_flutter_libs_plugin.h>
#include <stack_wallet_backup/stack_wallet_backup_plugin.h> #include <stack_wallet_backup/stack_wallet_backup_plugin.h>
@ -30,9 +29,6 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_libmonero_registrar = g_autoptr(FlPluginRegistrar) flutter_libmonero_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterLibmoneroPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterLibmoneroPlugin");
flutter_libmonero_plugin_register_with_registrar(flutter_libmonero_registrar); flutter_libmonero_plugin_register_with_registrar(flutter_libmonero_registrar);
g_autoptr(FlPluginRegistrar) flutter_libsparkmobile_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterLibsparkmobilePlugin");
flutter_libsparkmobile_plugin_register_with_registrar(flutter_libsparkmobile_registrar);
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);

View file

@ -7,7 +7,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
devicelocale devicelocale
flutter_libepiccash flutter_libepiccash
flutter_libmonero flutter_libmonero
flutter_libsparkmobile
flutter_secure_storage_linux flutter_secure_storage_linux
isar_flutter_libs isar_flutter_libs
stack_wallet_backup stack_wallet_backup
@ -17,6 +16,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST
coinlib_flutter coinlib_flutter
flutter_libsparkmobile
tor_ffi_plugin tor_ffi_plugin
) )

View file

@ -13,7 +13,6 @@ import desktop_drop
import device_info_plus import device_info_plus
import devicelocale import devicelocale
import flutter_libepiccash import flutter_libepiccash
import flutter_libsparkmobile
import flutter_local_notifications import flutter_local_notifications
import flutter_secure_storage_macos import flutter_secure_storage_macos
import isar_flutter_libs import isar_flutter_libs
@ -35,7 +34,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin"))
FlutterLibepiccashPlugin.register(with: registry.registrar(forPlugin: "FlutterLibepiccashPlugin")) FlutterLibepiccashPlugin.register(with: registry.registrar(forPlugin: "FlutterLibepiccashPlugin"))
FlutterLibsparkmobilePlugin.register(with: registry.registrar(forPlugin: "FlutterLibsparkmobilePlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin")) IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin"))

View file

@ -9,7 +9,6 @@
#include <connectivity_plus/connectivity_plus_windows_plugin.h> #include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <desktop_drop/desktop_drop_plugin.h> #include <desktop_drop/desktop_drop_plugin.h>
#include <flutter_libepiccash/flutter_libepiccash_plugin_c_api.h> #include <flutter_libepiccash/flutter_libepiccash_plugin_c_api.h>
#include <flutter_libsparkmobile/flutter_libsparkmobile_plugin_c_api.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h> #include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <isar_flutter_libs/isar_flutter_libs_plugin.h> #include <isar_flutter_libs/isar_flutter_libs_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h> #include <permission_handler_windows/permission_handler_windows_plugin.h>
@ -25,8 +24,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("DesktopDropPlugin")); registry->GetRegistrarForPlugin("DesktopDropPlugin"));
FlutterLibepiccashPluginCApiRegisterWithRegistrar( FlutterLibepiccashPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterLibepiccashPluginCApi")); registry->GetRegistrarForPlugin("FlutterLibepiccashPluginCApi"));
FlutterLibsparkmobilePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterLibsparkmobilePluginCApi"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar( FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
IsarFlutterLibsPluginRegisterWithRegistrar( IsarFlutterLibsPluginRegisterWithRegistrar(

View file

@ -6,7 +6,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus connectivity_plus
desktop_drop desktop_drop
flutter_libepiccash flutter_libepiccash
flutter_libsparkmobile
flutter_secure_storage_windows flutter_secure_storage_windows
isar_flutter_libs isar_flutter_libs
permission_handler_windows permission_handler_windows
@ -17,6 +16,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST
flutter_libsparkmobile
tor_ffi_plugin tor_ffi_plugin
) )