dirty hack for showing firo transactions right away until we can add functionality to sparkmobile

This commit is contained in:
julian 2024-01-05 12:59:01 -06:00
parent 3cf0d82085
commit b11694220b
5 changed files with 282 additions and 28 deletions

View file

@ -1,4 +1,5 @@
import 'package:cw_wownero/pending_wownero_transaction.dart'; import 'package:cw_wownero/pending_wownero_transaction.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/models/paynym/paynym_account_lite.dart'; import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/amount/amount.dart';
@ -64,6 +65,8 @@ class TxData {
})>? sparkRecipients; })>? sparkRecipients;
final List<TxData>? sparkMints; final List<TxData>? sparkMints;
final TransactionV2? tempTx;
TxData({ TxData({
this.feeRateType, this.feeRateType,
this.feeRateAmount, this.feeRateAmount,
@ -96,6 +99,7 @@ class TxData {
this.tezosOperationsList, this.tezosOperationsList,
this.sparkRecipients, this.sparkRecipients,
this.sparkMints, this.sparkMints,
this.tempTx,
}); });
Amount? get amount => recipients != null && recipients!.isNotEmpty Amount? get amount => recipients != null && recipients!.isNotEmpty
@ -153,6 +157,7 @@ class TxData {
})>? })>?
sparkRecipients, sparkRecipients,
List<TxData>? sparkMints, List<TxData>? sparkMints,
TransactionV2? tempTx,
}) { }) {
return TxData( return TxData(
feeRateType: feeRateType ?? this.feeRateType, feeRateType: feeRateType ?? this.feeRateType,
@ -187,6 +192,7 @@ class TxData {
tezosOperationsList: tezosOperationsList ?? this.tezosOperationsList, tezosOperationsList: tezosOperationsList ?? this.tezosOperationsList,
sparkRecipients: sparkRecipients ?? this.sparkRecipients, sparkRecipients: sparkRecipients ?? this.sparkRecipients,
sparkMints: sparkMints ?? this.sparkMints, sparkMints: sparkMints ?? this.sparkMints,
tempTx: tempTx ?? this.tempTx,
); );
} }
@ -223,5 +229,6 @@ class TxData {
'tezosOperationsList: $tezosOperationsList, ' 'tezosOperationsList: $tezosOperationsList, '
'sparkRecipients: $sparkRecipients, ' 'sparkRecipients: $sparkRecipients, '
'sparkMints: $sparkMints, ' 'sparkMints: $sparkMints, '
'tempTx: $tempTx, '
'}'; '}';
} }

View file

@ -16,6 +16,7 @@ import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/crypto_currency/coins/firo.dart'; import 'package:stackwallet/wallets/crypto_currency/coins/firo.dart';
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
import 'package:stackwallet/wallets/isar/models/spark_coin.dart'; import 'package:stackwallet/wallets/isar/models/spark_coin.dart';
import 'package:stackwallet/wallets/models/tx_data.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/electrumx_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart';
@ -41,8 +42,23 @@ class FiroWallet extends Bip39HDWallet
FilterOperation? get receivingAddressFilterOperation => FilterOperation? get receivingAddressFilterOperation =>
FilterGroup.and(standardReceivingAddressFilters); FilterGroup.and(standardReceivingAddressFilters);
final Set<String> _unconfirmedTxids = {};
// =========================================================================== // ===========================================================================
@override
Future<TxData> updateSentCachedTxData({required TxData txData}) async {
if (txData.tempTx != null) {
await mainDB.updateOrPutTransactionV2s([txData.tempTx!]);
_unconfirmedTxids.add(txData.tempTx!.txid);
Logging.instance.log(
"Added firo unconfirmed: ${txData.tempTx!.txid}",
level: LogLevel.Info,
);
}
return txData;
}
@override @override
Future<void> updateTransactions() async { Future<void> updateTransactions() async {
List<Address> allAddressesOld = await fetchAddressesForElectrumXScan(); List<Address> allAddressesOld = await fetchAddressesForElectrumXScan();
@ -487,7 +503,16 @@ class FiroWallet extends Bip39HDWallet
otherData: otherData, otherData: otherData,
); );
if (_unconfirmedTxids.contains(tx.txid)) {
if (tx.isConfirmed(await chainHeight, cryptoCurrency.minConfirms)) {
txns.add(tx); txns.add(tx);
_unconfirmedTxids.removeWhere((e) => e == tx.txid);
} else {
// don't update in db until confirmed
}
} else {
txns.add(tx);
}
} }
await mainDB.updateOrPutTransactionV2s(txns); await mainDB.updateOrPutTransactionV2s(txns);

View file

@ -368,7 +368,7 @@ abstract class Wallet<T extends CryptoCurrency> {
Future<void> updateTransactions(); Future<void> updateTransactions();
Future<void> updateBalance(); Future<void> updateBalance();
// returns true if new utxos were added to local db /// returns true if new utxos were added to local db
Future<bool> updateUTXOs(); Future<bool> updateUTXOs();
/// updates the wallet info's cachedChainHeight /// updates the wallet info's cachedChainHeight
@ -381,6 +381,14 @@ abstract class Wallet<T extends CryptoCurrency> {
Future<bool> pingCheck(); Future<bool> pingCheck();
//=========================================== //===========================================
/// add transaction to local db temporarily. Used for quickly updating ui
/// before refresh can fetch data from server
Future<TxData> updateSentCachedTxData({required TxData txData}) async {
if (txData.tempTx != null) {
await mainDB.updateOrPutTransactionV2s([txData.tempTx!]);
}
return txData;
}
NodeModel getCurrentNode() { NodeModel getCurrentNode() {
final node = nodeService.getPrimaryNodeFor(coin: cryptoCurrency.coin) ?? final node = nodeService.getPrimaryNodeFor(coin: cryptoCurrency.coin) ??

View file

@ -8,6 +8,9 @@ import 'package:decimal/decimal.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart'; import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart';
import 'package:stackwallet/electrumx_rpc/electrumx_client.dart'; import 'package:stackwallet/electrumx_rpc/electrumx_client.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/input_v2.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/output_v2.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/models/paymint/fee_object_model.dart'; import 'package:stackwallet/models/paymint/fee_object_model.dart';
import 'package:stackwallet/models/signing_data.dart'; import 'package:stackwallet/models/signing_data.dart';
@ -172,7 +175,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
); );
} }
final int vSizeForOneOutput = buildTransaction( final int vSizeForOneOutput = (await buildTransaction(
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
txData: txData.copyWith( txData: txData.copyWith(
recipients: _helperRecipientsConvert( recipients: _helperRecipientsConvert(
@ -180,7 +183,8 @@ mixin ElectrumXInterface on Bip39HDWallet {
[satoshisBeingUsed - 1], [satoshisBeingUsed - 1],
), ),
), ),
).vSize!; ))
.vSize!;
int feeForOneOutput = satsPerVByte != null int feeForOneOutput = satsPerVByte != null
? (satsPerVByte * vSizeForOneOutput) ? (satsPerVByte * vSizeForOneOutput)
: estimateTxFee( : estimateTxFee(
@ -200,7 +204,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
} }
final int amount = satoshiAmountToSend - feeForOneOutput; final int amount = satoshiAmountToSend - feeForOneOutput;
final data = buildTransaction( final data = await buildTransaction(
txData: txData.copyWith( txData: txData.copyWith(
recipients: _helperRecipientsConvert( recipients: _helperRecipientsConvert(
[recipientAddress], [recipientAddress],
@ -221,7 +225,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
final int vSizeForOneOutput; final int vSizeForOneOutput;
try { try {
vSizeForOneOutput = buildTransaction( vSizeForOneOutput = (await buildTransaction(
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
txData: txData.copyWith( txData: txData.copyWith(
recipients: _helperRecipientsConvert( recipients: _helperRecipientsConvert(
@ -229,7 +233,8 @@ mixin ElectrumXInterface on Bip39HDWallet {
[satoshisBeingUsed - 1], [satoshisBeingUsed - 1],
), ),
), ),
).vSize!; ))
.vSize!;
} catch (e) { } catch (e) {
Logging.instance.log("vSizeForOneOutput: $e", level: LogLevel.Error); Logging.instance.log("vSizeForOneOutput: $e", level: LogLevel.Error);
rethrow; rethrow;
@ -237,7 +242,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
final int vSizeForTwoOutPuts; final int vSizeForTwoOutPuts;
try { try {
vSizeForTwoOutPuts = buildTransaction( vSizeForTwoOutPuts = (await buildTransaction(
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
txData: txData.copyWith( txData: txData.copyWith(
recipients: _helperRecipientsConvert( recipients: _helperRecipientsConvert(
@ -248,7 +253,8 @@ mixin ElectrumXInterface on Bip39HDWallet {
], ],
), ),
), ),
).vSize!; ))
.vSize!;
} catch (e) { } catch (e) {
Logging.instance.log("vSizeForTwoOutPuts: $e", level: LogLevel.Error); Logging.instance.log("vSizeForTwoOutPuts: $e", level: LogLevel.Error);
rethrow; rethrow;
@ -312,7 +318,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
Logging.instance Logging.instance
.log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info); .log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info);
var txn = buildTransaction( var txn = await buildTransaction(
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
txData: txData.copyWith( txData: txData.copyWith(
recipients: _helperRecipientsConvert( recipients: _helperRecipientsConvert(
@ -343,7 +349,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
level: LogLevel.Info); level: LogLevel.Info);
Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs', Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs',
level: LogLevel.Info); level: LogLevel.Info);
txn = buildTransaction( txn = await buildTransaction(
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
txData: txData.copyWith( txData: txData.copyWith(
recipients: _helperRecipientsConvert( recipients: _helperRecipientsConvert(
@ -374,7 +380,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
level: LogLevel.Info); level: LogLevel.Info);
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
final txn = buildTransaction( final txn = await buildTransaction(
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
txData: txData.copyWith( txData: txData.copyWith(
recipients: _helperRecipientsConvert( recipients: _helperRecipientsConvert(
@ -406,7 +412,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
level: LogLevel.Info); level: LogLevel.Info);
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
final txn = buildTransaction( final txn = await buildTransaction(
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
txData: txData.copyWith( txData: txData.copyWith(
recipients: _helperRecipientsConvert( recipients: _helperRecipientsConvert(
@ -438,7 +444,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
level: LogLevel.Info); level: LogLevel.Info);
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
final txn = buildTransaction( final txn = await buildTransaction(
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
txData: txData.copyWith( txData: txData.copyWith(
recipients: _helperRecipientsConvert( recipients: _helperRecipientsConvert(
@ -615,10 +621,10 @@ mixin ElectrumXInterface on Bip39HDWallet {
} }
/// Builds and signs a transaction /// Builds and signs a transaction
TxData buildTransaction({ Future<TxData> buildTransaction({
required TxData txData, required TxData txData,
required List<SigningData> utxoSigningData, required List<SigningData> utxoSigningData,
}) { }) async {
Logging.instance Logging.instance
.log("Starting buildTransaction ----------", level: LogLevel.Info); .log("Starting buildTransaction ----------", level: LogLevel.Info);
@ -637,7 +643,12 @@ mixin ElectrumXInterface on Bip39HDWallet {
wif: cryptoCurrency.networkParams.wifPrefix, wif: cryptoCurrency.networkParams.wifPrefix,
), ),
); );
txb.setVersion(1); // TODO possibly override this for certain coins? const version = 1; // TODO possibly override this for certain coins?
txb.setVersion(version);
// temp tx data to show in gui while waiting for real data from server
final List<InputV2> tempInputs = [];
final List<OutputV2> tempOutputs = [];
// Add transaction inputs // Add transaction inputs
for (var i = 0; i < utxoSigningData.length; i++) { for (var i = 0; i < utxoSigningData.length; i++) {
@ -649,6 +660,25 @@ mixin ElectrumXInterface on Bip39HDWallet {
utxoSigningData[i].output!, utxoSigningData[i].output!,
cryptoCurrency.networkParams.bech32Hrp, cryptoCurrency.networkParams.bech32Hrp,
); );
tempInputs.add(
InputV2.isarCantDoRequiredInDefaultConstructor(
scriptSigHex: txb.inputs.first.script?.toHex,
sequence: 0xffffffff - 1,
outpoint: OutpointV2.isarCantDoRequiredInDefaultConstructor(
txid: utxoSigningData[i].utxo.txid,
vout: utxoSigningData[i].utxo.vout,
),
addresses: utxoSigningData[i].utxo.address == null
? []
: [utxoSigningData[i].utxo.address!],
valueStringSats: utxoSigningData[i].utxo.value.toString(),
witness: null,
innerRedeemScriptAsm: null,
coinbase: null,
walletOwns: true,
),
);
} }
// Add transaction output // Add transaction output
@ -658,6 +688,24 @@ mixin ElectrumXInterface on Bip39HDWallet {
txData.recipients![i].amount.raw.toInt(), txData.recipients![i].amount.raw.toInt(),
cryptoCurrency.networkParams.bech32Hrp, cryptoCurrency.networkParams.bech32Hrp,
); );
tempOutputs.add(
OutputV2.isarCantDoRequiredInDefaultConstructor(
scriptPubKeyHex: "000000",
valueStringSats: txData.recipients![i].amount.raw.toString(),
addresses: [
txData.recipients![i].address.toString(),
],
walletOwns: (await mainDB.isar.addresses
.where()
.walletIdEqualTo(walletId)
.filter()
.valueEqualTo(txData.recipients![i].address)
.valueProperty()
.findFirst()) !=
null,
),
);
} }
try { try {
@ -683,6 +731,22 @@ mixin ElectrumXInterface on Bip39HDWallet {
return txData.copyWith( return txData.copyWith(
raw: builtTx.toHex(), raw: builtTx.toHex(),
vSize: vSize, vSize: vSize,
tempTx: TransactionV2(
walletId: walletId,
blockHash: null,
hash: builtTx.getId(),
txid: builtTx.getId(),
height: null,
timestamp: DateTime.timestamp().millisecondsSinceEpoch ~/ 1000,
inputs: List.unmodifiable(tempInputs),
outputs: List.unmodifiable(tempOutputs),
version: version,
type: tempOutputs.map((e) => e.walletOwns).fold(true, (p, e) => p &= e)
? TransactionType.sentToSelf
: TransactionType.outgoing,
subType: TransactionSubType.none,
otherData: null,
),
); );
} }
@ -1749,7 +1813,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
// mark utxos as used // mark utxos as used
await mainDB.putUTXOs(txData.usedUTXOs!); await mainDB.putUTXOs(txData.usedUTXOs!);
return txData; return await updateSentCachedTxData(txData: txData);
} catch (e, s) { } catch (e, s) {
Logging.instance.log("Exception rethrown from confirmSend(): $e\n$s", Logging.instance.log("Exception rethrown from confirmSend(): $e\n$s",
level: LogLevel.Error); level: LogLevel.Error);

View file

@ -7,6 +7,9 @@ import 'package:flutter/foundation.dart';
import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart'; import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/input_v2.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/output_v2.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/models/signing_data.dart'; import 'package:stackwallet/models/signing_data.dart';
import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/amount/amount.dart';
@ -331,6 +334,10 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
); );
} }
// temp tx data to show in gui while waiting for real data from server
final List<InputV2> tempInputs = [];
final List<OutputV2> tempOutputs = [];
for (int i = 0; i < (txData.recipients?.length ?? 0); i++) { for (int i = 0; i < (txData.recipients?.length ?? 0); i++) {
if (txData.recipients![i].amount.raw == BigInt.zero) { if (txData.recipients![i].amount.raw == BigInt.zero) {
continue; continue;
@ -354,8 +361,62 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
scriptPubKey, scriptPubKey,
recipientsWithFeeSubtracted[i].amount.raw.toInt(), recipientsWithFeeSubtracted[i].amount.raw.toInt(),
); );
tempOutputs.add(
OutputV2.isarCantDoRequiredInDefaultConstructor(
scriptPubKeyHex: scriptPubKey.toHex,
valueStringSats: recipientsWithFeeSubtracted[i].amount.raw.toString(),
addresses: [
recipientsWithFeeSubtracted[i].address.toString(),
],
walletOwns: (await mainDB.isar.addresses
.where()
.walletIdEqualTo(walletId)
.filter()
.valueEqualTo(recipientsWithFeeSubtracted[i].address)
.valueProperty()
.findFirst()) !=
null,
),
);
} }
if (sparkRecipientsWithFeeSubtracted != null) {
for (final recip in sparkRecipientsWithFeeSubtracted) {
tempOutputs.add(
OutputV2.isarCantDoRequiredInDefaultConstructor(
scriptPubKeyHex: Uint8List.fromList([OP_SPARKSMINT]).toHex,
valueStringSats: recip.amount.raw.toString(),
addresses: [
recip.address.toString(),
],
walletOwns: (await mainDB.isar.addresses
.where()
.walletIdEqualTo(walletId)
.filter()
.valueEqualTo(recip.address)
.valueProperty()
.findFirst()) !=
null,
),
);
}
}
tempInputs.add(
InputV2.isarCantDoRequiredInDefaultConstructor(
scriptSigHex: "d3",
sequence: 0xffffffff,
outpoint: null,
addresses: [],
valueStringSats: "0",
witness: null,
innerRedeemScriptAsm: null,
coinbase: null,
walletOwns: true,
),
);
final extractedTx = txb.buildIncomplete(); final extractedTx = txb.buildIncomplete();
extractedTx.addInput( extractedTx.addInput(
'0000000000000000000000000000000000000000000000000000000000000000' '0000000000000000000000000000000000000000000000000000000000000000'
@ -412,12 +473,33 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
); );
} }
final fee = Amount(
rawValue: BigInt.from(spend.fee),
fractionDigits: cryptoCurrency.fractionDigits,
);
return txData.copyWith( return txData.copyWith(
raw: rawTxHex, raw: rawTxHex,
vSize: extractedTx.virtualSize(), vSize: extractedTx.virtualSize(),
fee: Amount( fee: fee,
rawValue: BigInt.from(spend.fee), tempTx: TransactionV2(
fractionDigits: cryptoCurrency.fractionDigits, walletId: walletId,
blockHash: null,
hash: extractedTx.getId(),
txid: extractedTx.getId(),
timestamp: DateTime.timestamp().millisecondsSinceEpoch ~/ 1000,
inputs: List.unmodifiable(tempInputs),
outputs: List.unmodifiable(tempOutputs),
type: tempOutputs.map((e) => e.walletOwns).fold(true, (p, e) => p &= e)
? TransactionType.sentToSelf
: TransactionType.outgoing,
subType: TransactionSubType.sparkSpend,
otherData: jsonEncode(
{
"anonFees": fee.toJsonString(),
},
),
height: null,
version: 3,
), ),
// TODO used coins // TODO used coins
); );
@ -448,7 +530,7 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
// // mark utxos as used // // mark utxos as used
// await mainDB.putUTXOs(txData.usedUTXOs!); // await mainDB.putUTXOs(txData.usedUTXOs!);
return txData; return await updateSentCachedTxData(txData: txData);
} catch (e, s) { } catch (e, s) {
Logging.instance.log("Exception rethrown from confirmSend(): $e\n$s", Logging.instance.log("Exception rethrown from confirmSend(): $e\n$s",
level: LogLevel.Error); level: LogLevel.Error);
@ -702,7 +784,7 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
: currentHeight; : currentHeight;
const txVersion = 1; const txVersion = 1;
final List<SigningData> vin = []; final List<SigningData> vin = [];
final List<(dynamic, int)> vout = []; final List<(dynamic, int, String?)> vout = [];
BigInt nFeeRet = BigInt.zero; BigInt nFeeRet = BigInt.zero;
@ -821,13 +903,15 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
final dummyTxb = btc.TransactionBuilder(network: _bitcoinDartNetwork); final dummyTxb = btc.TransactionBuilder(network: _bitcoinDartNetwork);
dummyTxb.setVersion(txVersion); dummyTxb.setVersion(txVersion);
dummyTxb.setLockTime(lockTime); dummyTxb.setLockTime(lockTime);
for (final recipient in dummyRecipients) { for (int i = 0; i < dummyRecipients.length; i++) {
final recipient = dummyRecipients[i];
if (recipient.amount < cryptoCurrency.dustLimit.raw.toInt()) { if (recipient.amount < cryptoCurrency.dustLimit.raw.toInt()) {
throw Exception("Output amount too small"); throw Exception("Output amount too small");
} }
vout.add(( vout.add((
recipient.scriptPubKey, recipient.scriptPubKey,
recipient.amount, recipient.amount,
singleTxOutputs[i].address,
)); ));
} }
@ -860,7 +944,7 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
final changeAddress = await getCurrentChangeAddress(); final changeAddress = await getCurrentChangeAddress();
vout.insert( vout.insert(
nChangePosInOut, nChangePosInOut,
(changeAddress!.value, nChange.toInt()), (changeAddress!.value, nChange.toInt(), null),
); );
} }
} }
@ -942,8 +1026,13 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
); );
int i = 0; int i = 0;
for (final recipient in recipients) { for (int i = 0; i < recipients.length; i++) {
final out = (recipient.scriptPubKey, recipient.amount); final recipient = recipients[i];
final out = (
recipient.scriptPubKey,
recipient.amount,
singleTxOutputs[i].address,
);
while (i < vout.length) { while (i < vout.length) {
if (vout[i].$1 is Uint8List && if (vout[i].$1 is Uint8List &&
(vout[i].$1 as Uint8List).isNotEmpty && (vout[i].$1 as Uint8List).isNotEmpty &&
@ -973,6 +1062,10 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
continue; continue;
} }
// temp tx data to show in gui while waiting for real data from server
final List<InputV2> tempInputs = [];
final List<OutputV2> tempOutputs = [];
// sign // sign
final txb = btc.TransactionBuilder(network: _bitcoinDartNetwork); final txb = btc.TransactionBuilder(network: _bitcoinDartNetwork);
txb.setVersion(txVersion); txb.setVersion(txVersion);
@ -985,10 +1078,50 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
1, // minus 1 is important. 0xffffffff on its own will burn funds 1, // minus 1 is important. 0xffffffff on its own will burn funds
input.output, input.output,
); );
tempInputs.add(
InputV2.isarCantDoRequiredInDefaultConstructor(
scriptSigHex: txb.inputs.first.script?.toHex,
sequence: 0xffffffff - 1,
outpoint: OutpointV2.isarCantDoRequiredInDefaultConstructor(
txid: input.utxo.txid,
vout: input.utxo.vout,
),
addresses: input.utxo.address == null ? [] : [input.utxo.address!],
valueStringSats: input.utxo.value.toString(),
witness: null,
innerRedeemScriptAsm: null,
coinbase: null,
walletOwns: true,
),
);
} }
for (final output in vout) { for (final output in vout) {
txb.addOutput(output.$1, output.$2); final addressOrScript = output.$1;
final value = output.$2;
txb.addOutput(addressOrScript, value);
tempOutputs.add(
OutputV2.isarCantDoRequiredInDefaultConstructor(
scriptPubKeyHex:
addressOrScript is Uint8List ? addressOrScript.toHex : "000000",
valueStringSats: value.toString(),
addresses: [
if (addressOrScript is String) addressOrScript.toString(),
],
walletOwns: (await mainDB.isar.addresses
.where()
.walletIdEqualTo(walletId)
.filter()
.valueEqualTo(addressOrScript is Uint8List
? output.$3!
: addressOrScript as String)
.valueProperty()
.findFirst()) !=
null,
),
);
} }
try { try {
@ -1035,6 +1168,23 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
fractionDigits: cryptoCurrency.fractionDigits, fractionDigits: cryptoCurrency.fractionDigits,
), ),
usedUTXOs: vin.map((e) => e.utxo).toList(), usedUTXOs: vin.map((e) => e.utxo).toList(),
tempTx: TransactionV2(
walletId: walletId,
blockHash: null,
hash: builtTx.getId(),
txid: builtTx.getId(),
timestamp: DateTime.timestamp().millisecondsSinceEpoch ~/ 1000,
inputs: List.unmodifiable(tempInputs),
outputs: List.unmodifiable(tempOutputs),
type:
tempOutputs.map((e) => e.walletOwns).fold(true, (p, e) => p &= e)
? TransactionType.sentToSelf
: TransactionType.outgoing,
subType: TransactionSubType.sparkMint,
otherData: null,
height: null,
version: 3,
),
); );
if (nFeeRet.toInt() < data.vSize!) { if (nFeeRet.toInt() < data.vSize!) {