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_svg/flutter_svg.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/pages/receive_view/generate_receiving_uri_qr_code_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/wallets/isar/providers/wallet_info_provider.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_loading_overlay.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
@ -57,6 +59,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
late final Coin coin;
late final String walletId;
late final ClipboardInterface clipboard;
late final bool supportsSpark;
Future<void> generateNewAddress() async {
final wallet = ref.read(pWallets).getWallet(walletId);
@ -98,6 +101,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
walletId = widget.walletId;
coin = ref.read(pWalletInfo(walletId)).coin;
clipboard = widget.clipboard;
supportsSpark = ref.read(pWallets).getWallet(walletId) is SparkInterface;
super.initState();
}
@ -111,6 +115,106 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
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(
cursor: SystemMouseCursors.click,
child: GestureDetector(

View file

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

View file

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

View file

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

View file

@ -58,7 +58,7 @@ class EcashWallet extends Bip39HDWallet
// ===========================================================================
@override
Future<List<Address>> fetchAllOwnAddresses() async {
Future<List<Address>> fetchAddressesForElectrumXScan() async {
final allAddresses = await mainDB
.getAddresses(walletId)
.filter()
@ -87,7 +87,7 @@ class EcashWallet extends Bip39HDWallet
@override
Future<void> updateTransactions() async {
List<Address> allAddressesOld = await fetchAllOwnAddresses();
List<Address> allAddressesOld = await fetchAddressesForElectrumXScan();
Set<String> receivingAddresses = allAddressesOld
.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(
List<Map<String, dynamic>> allTransactions, String txid) {
for (int i = 0; i < allTransactions.length; i++) {
@ -67,7 +49,7 @@ class FiroWallet extends Bip39HDWallet
@override
Future<void> updateTransactions() async {
final allAddresses = await fetchAllOwnAddresses();
final allAddresses = await fetchAddressesForElectrumXScan();
Set<String> receivingAddresses = allAddresses
.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
void _watchWalletInfo() {
_walletInfoStream = mainDB.isar.walletInfo.watchObject(_walletInfo.id);
_walletInfoStream = mainDB.isar.walletInfo.watchObject(
_walletInfo.id,
fireImmediately: true,
);
_walletInfoStream.forEach((element) {
if (element != null) {
_walletInfo = element;

View file

@ -1641,7 +1641,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
@override
Future<void> updateUTXOs() async {
final allAddresses = await fetchAllOwnAddresses();
final allAddresses = await fetchAddressesForElectrumXScan();
try {
final fetchedUtxoList = <List<Map<String, dynamic>>>[];
@ -1856,7 +1856,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
int estimateTxFee({required int vSize, required 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
/// 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';
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 {
return await mainDB.isar.addresses
.where()
@ -29,15 +63,18 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
throw UnimplementedError();
}
Future<Address> generateNextSparkAddress({int index = 1}) async {
Future<Address> generateNextSparkAddress() async {
final highestStoredDiversifier =
(await getCurrentReceivingSparkAddress())?.derivationIndex;
// default to starting at 1 if none found
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 derivationPath = "$kSparkBaseDerivationPath$index";
const derivationPath = "$kSparkBaseDerivationPath$index";
final keys = root.derivePath(derivationPath);
final String addressString = await LibSpark.getAddress(

View file

@ -10,7 +10,6 @@
#include <devicelocale/devicelocale_plugin.h>
#include <flutter_libepiccash/flutter_libepiccash_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 <isar_flutter_libs/isar_flutter_libs_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 =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterLibmoneroPlugin");
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 =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
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
flutter_libepiccash
flutter_libmonero
flutter_libsparkmobile
flutter_secure_storage_linux
isar_flutter_libs
stack_wallet_backup
@ -17,6 +16,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
list(APPEND FLUTTER_FFI_PLUGIN_LIST
coinlib_flutter
flutter_libsparkmobile
tor_ffi_plugin
)

View file

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

View file

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

View file

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