From c6836708a83f27b65f4dd52ba40e06d78acc75ba Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 22 Jul 2024 16:50:51 -0600 Subject: [PATCH] Hook up missing spark fee estimate call. Should address https://github.com/cypherstack/stack_wallet/issues/948 --- .../spark_interface.dart | 129 ++++++++++++++++-- 1 file changed, 120 insertions(+), 9 deletions(-) diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart index 09d4dc6f1..30b753be2 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart @@ -183,14 +183,75 @@ mixin SparkInterface } Future estimateFeeForSpark(Amount amount) async { - // int spendAmount = amount.raw.toInt(); - // if (spendAmount == 0) { - return Amount( - rawValue: BigInt.from(0), - fractionDigits: cryptoCurrency.fractionDigits, - ); - // } - // TODO actual fee estimation + final spendAmount = amount.raw.toInt(); + if (spendAmount == 0) { + return Amount( + rawValue: BigInt.from(0), + fractionDigits: cryptoCurrency.fractionDigits, + ); + } else { + // fetch spendable spark coins + final coins = await mainDB.isar.sparkCoins + .where() + .walletIdEqualToAnyLTagHash(walletId) + .filter() + .isUsedEqualTo(false) + .and() + .heightIsNotNull() + .and() + .not() + .valueIntStringEqualTo("0") + .findAll(); + + final available = + coins.map((e) => e.value).fold(BigInt.zero, (p, e) => p + e); + + if (amount.raw > available) { + return Amount( + rawValue: BigInt.from(0), + fractionDigits: cryptoCurrency.fractionDigits, + ); + } + + // prepare coin data for ffi + final serializedCoins = coins + .map( + (e) => ( + serializedCoin: e.serializedCoinB64!, + serializedCoinContext: e.contextB64!, + groupId: e.groupId, + height: e.height!, + ), + ) + .toList(); + + final root = await getRootHDNode(); + final String derivationPath; + if (cryptoCurrency.network.isTestNet) { + derivationPath = "$kSparkBaseDerivationPathTestnet$kDefaultSparkIndex"; + } else { + derivationPath = "$kSparkBaseDerivationPath$kDefaultSparkIndex"; + } + final privateKey = root.derivePath(derivationPath).privateKey.data; + int estimate = await _asyncSparkFeesWrapper( + privateKeyHex: privateKey.toHex, + index: kDefaultSparkIndex, + sendAmount: spendAmount, + subtractFeeFromAmount: true, + serializedCoins: serializedCoins, + // privateRecipientsCount: (txData.sparkRecipients?.length ?? 0), + privateRecipientsCount: 1, // ROUGHLY! + ); + + if (estimate < 0) { + estimate = 0; + } + + return Amount( + rawValue: BigInt.from(estimate), + fractionDigits: cryptoCurrency.fractionDigits, + ); + } } /// Spark to Spark/Transparent (spend) creation @@ -374,7 +435,7 @@ mixin SparkInterface recipientCount + (txData.sparkRecipients?.length ?? 0); final BigInt estimatedFee; if (isSendAll) { - final estFee = LibSpark.estimateSparkFee( + final estFee = await _asyncSparkFeesWrapper( privateKeyHex: privateKey.toHex, index: kDefaultSparkIndex, sendAmount: txAmount.raw.toInt(), @@ -2002,3 +2063,53 @@ class MutableSparkRecipient { return 'MutableSparkRecipient{ address: $address, value: $value, memo: $memo }'; } } + +typedef SerializedCoinData = ({ + int groupId, + int height, + String serializedCoin, + String serializedCoinContext +}); + +Future _asyncSparkFeesWrapper({ + required String privateKeyHex, + int index = 1, + required int sendAmount, + required bool subtractFeeFromAmount, + required List serializedCoins, + required int privateRecipientsCount, +}) async { + return await compute( + _estSparkFeeComputeFunc, + ( + privateKeyHex: privateKeyHex, + index: index, + sendAmount: sendAmount, + subtractFeeFromAmount: subtractFeeFromAmount, + serializedCoins: serializedCoins, + privateRecipientsCount: privateRecipientsCount, + ), + ); +} + +int _estSparkFeeComputeFunc( + ({ + String privateKeyHex, + int index, + int sendAmount, + bool subtractFeeFromAmount, + List serializedCoins, + int privateRecipientsCount, + }) args, +) { + final est = LibSpark.estimateSparkFee( + privateKeyHex: args.privateKeyHex, + index: args.index, + sendAmount: args.sendAmount, + subtractFeeFromAmount: args.subtractFeeFromAmount, + serializedCoins: args.serializedCoins, + privateRecipientsCount: args.privateRecipientsCount, + ); + + return est; +}