Merge pull request #398 from cypherstack/coin_control

Coin control
This commit is contained in:
Diego Salazar 2023-03-09 15:45:01 -07:00 committed by GitHub
commit 57e1818586
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 1243 additions and 2262 deletions

View file

@ -22,10 +22,10 @@ Highlights include:
- The only OS supported for building is Ubuntu 20.04 - The only OS supported for building is Ubuntu 20.04
- A machine with at least 100 GB of Storage - A machine with at least 100 GB of Storage
The following prerequisities can be installed with the setup script `scripts/setup.sh` or manually as described below: The following prerequisites can be installed with the setup script `scripts/setup.sh` or manually as described below:
- Flutter 3.3.4 [(install manually or with git, do not install with snap)](https://docs.flutter.dev/get-started/install) - Flutter 3.7.6 [(install manually or with git, do not install with snap)](https://docs.flutter.dev/get-started/install)
- Dart SDK Requirement (>=2.17.0, up until <3.0.0) - Dart SDK Requirement (>=2.19.0, up until <3.0.0) (normally included with a flutter install)
- Android setup ([Android Studio](https://developer.android.com/studio) and subsequent dependencies) - Android setup ([Android Studio](https://developer.android.com/studio) and subsequent dependencies)
### Scripted setup ### Scripted setup

View file

@ -21,6 +21,6 @@
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>10.0</string> <string>15.0</string>
</dict> </dict>
</plist> </plist>

View file

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project # Uncomment this line to define a global platform for your project
platform :ios, '10.0' platform :ios, '15.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View file

@ -439,7 +439,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@ -574,7 +574,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -624,7 +624,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;

View file

@ -0,0 +1,21 @@
import 'dart:typed_data';
import 'package:bitcoindart/bitcoindart.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart';
class SigningData {
SigningData({
required this.derivePathType,
required this.utxo,
this.output,
this.keyPair,
this.redeemScript,
});
final DerivePathType derivePathType;
final UTXO utxo;
Uint8List? output;
ECPair? keyPair;
Uint8List? redeemScript;
}

View file

@ -146,16 +146,58 @@ class _SendViewState extends ConsumerState<SendView> {
_updatePreviewButtonState(_address, _amountToSend); _updatePreviewButtonState(_address, _amountToSend);
// if (_amountToSend == null) { _cryptoAmountChangedFeeUpdateTimer?.cancel();
// setState(() { _cryptoAmountChangedFeeUpdateTimer = Timer(updateFeesTimerDuration, () {
// _calculateFeesFuture = calculateFees(0); if (coin != Coin.epicCash && !_baseFocus.hasFocus) {
// }); setState(() {
// } else { _calculateFeesFuture = calculateFees(
// setState(() { _amountToSend == null
// _calculateFeesFuture = ? 0
// calculateFees(Format.decimalAmountToSatoshis(_amountToSend!)); : Format.decimalAmountToSatoshis(
// }); _amountToSend!,
// } coin,
),
);
});
}
});
}
}
final updateFeesTimerDuration = const Duration(milliseconds: 500);
Timer? _cryptoAmountChangedFeeUpdateTimer;
Timer? _baseAmountChangedFeeUpdateTimer;
void _baseAmountChanged() {
_baseAmountChangedFeeUpdateTimer?.cancel();
_baseAmountChangedFeeUpdateTimer = Timer(updateFeesTimerDuration, () {
if (coin != Coin.epicCash && !_cryptoFocus.hasFocus) {
setState(() {
_calculateFeesFuture = calculateFees(
_amountToSend == null
? 0
: Format.decimalAmountToSatoshis(
_amountToSend!,
coin,
),
);
});
}
});
}
int _currentFee = 0;
void _setCurrentFee(String fee, bool shouldSetState) {
final value = Format.decimalAmountToSatoshis(
Decimal.parse(fee),
coin,
);
if (shouldSetState) {
setState(() => _currentFee = value);
} else {
_currentFee = value;
} }
} }
@ -328,45 +370,48 @@ class _SendViewState extends ConsumerState<SendView> {
selectedUTXOs.isEmpty)) { selectedUTXOs.isEmpty)) {
// confirm send all // confirm send all
if (amount == availableBalance) { if (amount == availableBalance) {
final bool? shouldSendAll = await showDialog<bool>( bool? shouldSendAll;
context: context, if (mounted) {
useSafeArea: false, shouldSendAll = await showDialog<bool>(
barrierDismissible: true, context: context,
builder: (context) { useSafeArea: false,
return StackDialog( barrierDismissible: true,
title: "Confirm send all", builder: (context) {
message: return StackDialog(
"You are about to send your entire balance. Would you like to continue?", title: "Confirm send all",
leftButton: TextButton( message:
style: Theme.of(context) "You are about to send your entire balance. Would you like to continue?",
.extension<StackColors>()! leftButton: TextButton(
.getSecondaryEnabledButtonStyle(context), style: Theme.of(context)
child: Text( .extension<StackColors>()!
"Cancel", .getSecondaryEnabledButtonStyle(context),
style: STextStyles.button(context).copyWith( child: Text(
color: Theme.of(context) "Cancel",
.extension<StackColors>()! style: STextStyles.button(context).copyWith(
.accentColorDark), color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
onPressed: () {
Navigator.of(context).pop(false);
},
), ),
onPressed: () { rightButton: TextButton(
Navigator.of(context).pop(false); style: Theme.of(context)
}, .extension<StackColors>()!
), .getPrimaryEnabledButtonStyle(context),
rightButton: TextButton( child: Text(
style: Theme.of(context) "Yes",
.extension<StackColors>()! style: STextStyles.button(context),
.getPrimaryEnabledButtonStyle(context), ),
child: Text( onPressed: () {
"Yes", Navigator.of(context).pop(true);
style: STextStyles.button(context), },
), ),
onPressed: () { );
Navigator.of(context).pop(true); },
}, );
), }
);
},
);
if (shouldSendAll == null || shouldSendAll == false) { if (shouldSendAll == null || shouldSendAll == false) {
// cancel preview // cancel preview
@ -378,22 +423,24 @@ class _SendViewState extends ConsumerState<SendView> {
try { try {
bool wasCancelled = false; bool wasCancelled = false;
unawaited( if (mounted) {
showDialog<dynamic>( unawaited(
context: context, showDialog<void>(
useSafeArea: false, context: context,
barrierDismissible: false, useSafeArea: false,
builder: (context) { barrierDismissible: false,
return BuildingTransactionDialog( builder: (context) {
onCancel: () { return BuildingTransactionDialog(
wasCancelled = true; onCancel: () {
wasCancelled = true;
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
); );
}, },
), ),
); );
}
Map<String, dynamic> txData; Map<String, dynamic> txData;
@ -519,6 +566,7 @@ class _SendViewState extends ConsumerState<SendView> {
onCryptoAmountChanged = _cryptoAmountChanged; onCryptoAmountChanged = _cryptoAmountChanged;
cryptoAmountController.addListener(onCryptoAmountChanged); cryptoAmountController.addListener(onCryptoAmountChanged);
baseAmountController.addListener(_baseAmountChanged);
if (_data != null) { if (_data != null) {
if (_data!.amount != null) { if (_data!.amount != null) {
@ -534,43 +582,47 @@ class _SendViewState extends ConsumerState<SendView> {
noteController.text = "PayNym send"; noteController.text = "PayNym send";
} }
if (coin != Coin.epicCash) { // if (coin != Coin.epicCash) {
_cryptoFocus.addListener(() { // _cryptoFocus.addListener(() {
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) { // if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
if (_amountToSend == null) { // if (_amountToSend == null) {
setState(() { // setState(() {
_calculateFeesFuture = calculateFees(0); // _calculateFeesFuture = calculateFees(0);
}); // });
} else { // } else {
setState(() { // setState(() {
_calculateFeesFuture = calculateFees( // _calculateFeesFuture = calculateFees(
Format.decimalAmountToSatoshis(_amountToSend!, coin)); // Format.decimalAmountToSatoshis(_amountToSend!, coin));
}); // });
} // }
} // }
}); // });
_baseFocus.addListener(() { // _baseFocus.addListener(() {
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) { // if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
if (_amountToSend == null) { // if (_amountToSend == null) {
setState(() { // setState(() {
_calculateFeesFuture = calculateFees(0); // _calculateFeesFuture = calculateFees(0);
}); // });
} else { // } else {
setState(() { // setState(() {
_calculateFeesFuture = calculateFees( // _calculateFeesFuture = calculateFees(
Format.decimalAmountToSatoshis(_amountToSend!, coin)); // Format.decimalAmountToSatoshis(_amountToSend!, coin));
}); // });
} // }
} // }
}); // });
} // }
super.initState(); super.initState();
} }
@override @override
void dispose() { void dispose() {
_cryptoAmountChangedFeeUpdateTimer?.cancel();
_baseAmountChangedFeeUpdateTimer?.cancel();
cryptoAmountController.removeListener(onCryptoAmountChanged); cryptoAmountController.removeListener(onCryptoAmountChanged);
baseAmountController.removeListener(_baseAmountChanged);
sendToController.dispose(); sendToController.dispose();
cryptoAmountController.dispose(); cryptoAmountController.dispose();
@ -1569,26 +1621,52 @@ class _SendViewState extends ConsumerState<SendView> {
? "Select coins" ? "Select coins"
: "Selected coins (${selectedUTXOs.length})", : "Selected coins (${selectedUTXOs.length})",
onTap: () async { onTap: () async {
final result = if (FocusScope.of(context).hasFocus) {
await Navigator.of(context).pushNamed( FocusScope.of(context).unfocus();
CoinControlView.routeName, await Future<void>.delayed(
arguments: Tuple4( const Duration(milliseconds: 100),
walletId, );
CoinControlViewType.use, }
_amountToSend != null
? Format.decimalAmountToSatoshis(
_amountToSend!,
coin,
)
: null,
selectedUTXOs,
),
);
if (result is Set<UTXO>) { if (mounted) {
setState(() { final spendable = ref
selectedUTXOs = result; .read(walletsChangeNotifierProvider)
}); .getManager(widget.walletId)
.balance
.spendable;
int? amount;
if (_amountToSend != null) {
amount =
Format.decimalAmountToSatoshis(
_amountToSend!,
coin,
);
if (spendable == amount) {
// this is now a send all
} else {
amount += _currentFee;
}
}
final result =
await Navigator.of(context)
.pushNamed(
CoinControlView.routeName,
arguments: Tuple4(
walletId,
CoinControlViewType.use,
amount,
selectedUTXOs,
),
);
if (result is Set<UTXO>) {
setState(() {
selectedUTXOs = result;
});
}
} }
}, },
), ),
@ -1711,6 +1789,10 @@ class _SendViewState extends ConsumerState<SendView> {
.text) ?? .text) ??
Decimal.zero, Decimal.zero,
updateChosen: (String fee) { updateChosen: (String fee) {
_setCurrentFee(
fee,
true,
);
setState(() { setState(() {
_calculateFeesFuture = _calculateFeesFuture =
Future(() => fee); Future(() => fee);
@ -1736,6 +1818,10 @@ class _SendViewState extends ConsumerState<SendView> {
ConnectionState ConnectionState
.done && .done &&
snapshot.hasData) { snapshot.hasData) {
_setCurrentFee(
snapshot.data! as String,
false,
);
return Text( return Text(
"~${snapshot.data! as String} ${coin.ticker}", "~${snapshot.data! as String} ${coin.ticker}",
style: STextStyles style: STextStyles
@ -1788,6 +1874,11 @@ class _SendViewState extends ConsumerState<SendView> {
ConnectionState ConnectionState
.done && .done &&
snapshot.hasData) { snapshot.hasData) {
_setCurrentFee(
snapshot.data!
as String,
false,
);
return Text( return Text(
"~${snapshot.data! as String} ${coin.ticker}", "~${snapshot.data! as String} ${coin.ticker}",
style: STextStyles style: STextStyles

View file

@ -18,6 +18,7 @@ import 'package:stackwallet/exceptions/electrumx/no_such_transaction.dart';
import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models; import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
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/services/coins/coin_service.dart'; import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
@ -1151,13 +1152,15 @@ class BitcoinWallet extends CoinServiceAPI
isSendAll = true; isSendAll = true;
} }
final bool coinControl = utxos != null;
final txData = await coinSelection( final txData = await coinSelection(
satoshiAmountToSend: satoshiAmount, satoshiAmountToSend: satoshiAmount,
selectedTxFeeRate: rate, selectedTxFeeRate: rate,
recipientAddress: address, recipientAddress: address,
isSendAll: isSendAll, isSendAll: isSendAll,
utxos: utxos?.toList(), utxos: utxos?.toList(),
coinControl: utxos is List<isar_models.UTXO>, coinControl: coinControl,
); );
Logging.instance.log("prepare send: $txData", level: LogLevel.Info); Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
@ -1841,14 +1844,16 @@ class BitcoinWallet extends CoinServiceAPI
String? blockReason; String? blockReason;
if (storedTx?.subType == if (storedTx?.subType ==
isar_models.TransactionSubType.bip47Notification && isar_models.TransactionSubType.bip47Notification) {
storedTx?.type == isar_models.TransactionType.incoming) { if (storedTx?.type == isar_models.TransactionType.incoming) {
// probably safe to assume this is an incoming tx as it is a utxo shouldBlock = true;
// belonging to this wallet. The extra check may be redundant but blockReason = "Incoming paynym notification transaction.";
// just in case... } else if (storedTx?.type == isar_models.TransactionType.outgoing) {
shouldBlock = true;
shouldBlock = true; blockReason = "Paynym notification change output. Incautious "
blockReason = "Incoming paynym notification transaction."; "handling of change outputs from notification transactions "
"may cause unintended loss of privacy.";
}
} }
final vout = jsonUTXO["tx_pos"] as int; final vout = jsonUTXO["tx_pos"] as int;
@ -2360,6 +2365,7 @@ class BitcoinWallet extends CoinServiceAPI
} else { } else {
satoshisBeingUsed = spendableSatoshiValue; satoshisBeingUsed = spendableSatoshiValue;
utxoObjectsToUse = spendableOutputs; utxoObjectsToUse = spendableOutputs;
inputsBeingConsumed = spendableOutputs.length;
} }
Logging.instance Logging.instance
@ -2381,7 +2387,6 @@ class BitcoinWallet extends CoinServiceAPI
.log("Attempting to send all $coin", level: LogLevel.Info); .log("Attempting to send all $coin", level: LogLevel.Info);
final int vSizeForOneOutput = (await buildTransaction( final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [recipientAddress], recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1], satoshiAmounts: [satoshisBeingUsed - 1],
@ -2399,7 +2404,6 @@ class BitcoinWallet extends CoinServiceAPI
final int amount = satoshiAmountToSend - feeForOneOutput; final int amount = satoshiAmountToSend - feeForOneOutput;
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: [amount], satoshiAmounts: [amount],
@ -2410,7 +2414,7 @@ class BitcoinWallet extends CoinServiceAPI
"recipientAmt": amount, "recipientAmt": amount,
"fee": feeForOneOutput, "fee": feeForOneOutput,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
@ -2418,7 +2422,6 @@ class BitcoinWallet extends CoinServiceAPI
final int vSizeForOneOutput; final int vSizeForOneOutput;
try { try {
vSizeForOneOutput = (await buildTransaction( vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [recipientAddress], recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1], satoshiAmounts: [satoshisBeingUsed - 1],
@ -2431,7 +2434,6 @@ class BitcoinWallet extends CoinServiceAPI
final int vSizeForTwoOutPuts; final int vSizeForTwoOutPuts;
try { try {
vSizeForTwoOutPuts = (await buildTransaction( vSizeForTwoOutPuts = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [ recipients: [
recipientAddress, recipientAddress,
@ -2502,7 +2504,6 @@ class BitcoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info); .log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2530,7 +2531,6 @@ class BitcoinWallet extends CoinServiceAPI
Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs', Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs',
level: LogLevel.Info); level: LogLevel.Info);
txn = await buildTransaction( txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2543,7 +2543,7 @@ class BitcoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": feeBeingPaid, "fee": feeBeingPaid,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} else { } else {
@ -2560,7 +2560,6 @@ class BitcoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2571,7 +2570,7 @@ class BitcoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend, "fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
@ -2590,7 +2589,6 @@ class BitcoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2601,7 +2599,7 @@ class BitcoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend, "fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
@ -2620,7 +2618,6 @@ class BitcoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2631,7 +2628,7 @@ class BitcoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": feeForOneOutput, "fee": feeForOneOutput,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} else { } else {
@ -2657,241 +2654,144 @@ class BitcoinWallet extends CoinServiceAPI
} }
} }
Future<Map<String, dynamic>> fetchBuildTxData( Future<List<SigningData>> fetchBuildTxData(
List<isar_models.UTXO> utxosToUse, List<isar_models.UTXO> utxosToUse,
) async { ) async {
// return data // return data
Map<String, dynamic> results = {}; List<SigningData> signingData = [];
Map<String, List<String>> addressTxid = {};
// addresses to check
List<String> addressesP2PKH = [];
List<String> addressesP2SH = [];
List<String> addressesP2WPKH = [];
try { try {
// Populating the addresses to check // Populating the addresses to check
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid; if (utxosToUse[i].address == null) {
final tx = await _cachedElectrumXClient.getTransaction( final txid = utxosToUse[i].txid;
txHash: txid, final tx = await _cachedElectrumXClient.getTransaction(
coin: coin, txHash: txid,
); coin: coin,
);
for (final output in tx["vout"] as List) { for (final output in tx["vout"] as List) {
final n = output["n"]; final n = output["n"];
if (n != null && n == utxosToUse[i].vout) { if (n != null && n == utxosToUse[i].vout) {
final address = output["scriptPubKey"]["address"] as String; utxosToUse[i] = utxosToUse[i].copyWith(
if (!addressTxid.containsKey(address)) { address: output["scriptPubKey"]?["addresses"]?[0] as String? ??
addressTxid[address] = <String>[]; output["scriptPubKey"]["address"] as String,
} );
(addressTxid[address] as List).add(txid);
switch (addressType(address: address)) {
case DerivePathType.bip44:
addressesP2PKH.add(address);
break;
case DerivePathType.bip49:
addressesP2SH.add(address);
break;
case DerivePathType.bip84:
addressesP2WPKH.add(address);
break;
default:
throw Exception("DerivePathType unsupported");
} }
} }
} }
final derivePathType = addressType(address: utxosToUse[i].address!);
signingData.add(
SigningData(
derivePathType: derivePathType,
utxo: utxosToUse[i],
),
);
} }
// p2pkh / bip44 Map<DerivePathType, Map<String, dynamic>> receiveDerivations = {};
final p2pkhLength = addressesP2PKH.length; Map<DerivePathType, Map<String, dynamic>> changeDerivations = {};
if (p2pkhLength > 0) {
final receiveDerivations = await _fetchDerivations(
chain: 0,
derivePathType: DerivePathType.bip44,
);
final changeDerivations = await _fetchDerivations(
chain: 1,
derivePathType: DerivePathType.bip44,
);
for (int i = 0; i < p2pkhLength; i++) {
// receives
final receiveDerivation = receiveDerivations[addressesP2PKH[i]];
// if a match exists it will not be null
if (receiveDerivation != null) {
final data = P2PKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
receiveDerivation["pubKey"] as String)),
network: _network,
).data;
for (String tx in addressTxid[addressesP2PKH[i]]!) { for (final sd in signingData) {
results[tx] = { String? pubKey;
"output": data.output, String? wif;
"keyPair": ECPair.fromWIF(
receiveDerivation["wif"] as String, // fetch receiving derivations if null
network: _network, receiveDerivations[sd.derivePathType] ??= await _fetchDerivations(
), chain: 0,
}; derivePathType: sd.derivePathType,
} );
} else { final receiveDerivation =
// if its not a receive, check change receiveDerivations[sd.derivePathType]![sd.utxo.address!];
final changeDerivation = changeDerivations[addressesP2PKH[i]];
// if a match exists it will not be null if (receiveDerivation != null) {
if (changeDerivation != null) { pubKey = receiveDerivation["pubKey"] as String;
final data = P2PKH( wif = receiveDerivation["wif"] as String;
} else {
// fetch change derivations if null
changeDerivations[sd.derivePathType] ??= await _fetchDerivations(
chain: 1,
derivePathType: sd.derivePathType,
);
final changeDerivation =
changeDerivations[sd.derivePathType]![sd.utxo.address!];
if (changeDerivation != null) {
pubKey = changeDerivation["pubKey"] as String;
wif = changeDerivation["wif"] as String;
}
}
if (wif == null || pubKey == null) {
final address = await db.getAddress(walletId, sd.utxo.address!);
if (address?.derivationPath != null) {
final node = await Bip32Utils.getBip32Node(
(await mnemonicString)!,
(await mnemonicPassphrase)!,
_network,
address!.derivationPath!.value,
);
wif = node.toWIF();
pubKey = Format.uint8listToString(node.publicKey);
}
}
if (wif != null && pubKey != null) {
final PaymentData data;
final Uint8List? redeemScript;
switch (sd.derivePathType) {
case DerivePathType.bip44:
data = P2PKH(
data: PaymentData( data: PaymentData(
pubkey: Format.stringToUint8List( pubkey: Format.stringToUint8List(pubKey),
changeDerivation["pubKey"] as String)), ),
network: _network, network: _network,
).data; ).data;
redeemScript = null;
break;
for (String tx in addressTxid[addressesP2PKH[i]]!) { case DerivePathType.bip49:
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
),
};
}
}
}
}
}
// p2sh / bip49
final p2shLength = addressesP2SH.length;
if (p2shLength > 0) {
final receiveDerivations = await _fetchDerivations(
chain: 0,
derivePathType: DerivePathType.bip49,
);
final changeDerivations = await _fetchDerivations(
chain: 1,
derivePathType: DerivePathType.bip49,
);
for (int i = 0; i < p2shLength; i++) {
// receives
final receiveDerivation = receiveDerivations[addressesP2SH[i]];
// if a match exists it will not be null
if (receiveDerivation != null) {
final p2wpkh = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
receiveDerivation["pubKey"] as String)),
network: _network)
.data;
final redeemScript = p2wpkh.output;
final data =
P2SH(data: PaymentData(redeem: p2wpkh), network: _network).data;
for (String tx in addressTxid[addressesP2SH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
receiveDerivation["wif"] as String,
network: _network,
),
"redeemScript": redeemScript,
};
}
} else {
// if its not a receive, check change
final changeDerivation = changeDerivations[addressesP2SH[i]];
// if a match exists it will not be null
if (changeDerivation != null) {
final p2wpkh = P2WPKH( final p2wpkh = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
changeDerivation["pubKey"] as String)),
network: _network)
.data;
final redeemScript = p2wpkh.output;
final data =
P2SH(data: PaymentData(redeem: p2wpkh), network: _network)
.data;
for (String tx in addressTxid[addressesP2SH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
),
"redeemScript": redeemScript,
};
}
}
}
}
}
// p2wpkh / bip84
final p2wpkhLength = addressesP2WPKH.length;
if (p2wpkhLength > 0) {
final receiveDerivations = await _fetchDerivations(
chain: 0,
derivePathType: DerivePathType.bip84,
);
final changeDerivations = await _fetchDerivations(
chain: 1,
derivePathType: DerivePathType.bip84,
);
for (int i = 0; i < p2wpkhLength; i++) {
// receives
final receiveDerivation = receiveDerivations[addressesP2WPKH[i]];
// if a match exists it will not be null
if (receiveDerivation != null) {
final data = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
receiveDerivation["pubKey"] as String)),
network: _network,
).data;
for (String tx in addressTxid[addressesP2WPKH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
receiveDerivation["wif"] as String,
network: _network,
),
};
}
} else {
// if its not a receive, check change
final changeDerivation = changeDerivations[addressesP2WPKH[i]];
// if a match exists it will not be null
if (changeDerivation != null) {
final data = P2WPKH(
data: PaymentData( data: PaymentData(
pubkey: Format.stringToUint8List( pubkey: Format.stringToUint8List(pubKey),
changeDerivation["pubKey"] as String)), ),
network: _network, network: _network,
).data; ).data;
redeemScript = p2wpkh.output;
data = P2SH(
data: PaymentData(redeem: p2wpkh),
network: _network,
).data;
break;
for (String tx in addressTxid[addressesP2WPKH[i]]!) { case DerivePathType.bip84:
results[tx] = { data = P2WPKH(
"output": data.output, data: PaymentData(
"keyPair": ECPair.fromWIF( pubkey: Format.stringToUint8List(pubKey),
changeDerivation["wif"] as String, ),
network: _network, network: _network,
), ).data;
}; redeemScript = null;
} break;
}
default:
throw Exception("DerivePathType unsupported");
} }
final keyPair = ECPair.fromWIF(
wif,
network: _network,
);
sd.redeemScript = redeemScript;
sd.output = data.output;
sd.keyPair = keyPair;
} }
} }
return results; return signingData;
} catch (e, s) { } catch (e, s) {
Logging.instance Logging.instance
.log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error); .log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error);
@ -2901,8 +2801,7 @@ class BitcoinWallet extends CoinServiceAPI
/// Builds and signs a transaction /// Builds and signs a transaction
Future<Map<String, dynamic>> buildTransaction({ Future<Map<String, dynamic>> buildTransaction({
required List<isar_models.UTXO> utxosToUse, required List<SigningData> utxoSigningData,
required Map<String, dynamic> utxoSigningData,
required List<String> recipients, required List<String> recipients,
required List<int> satoshiAmounts, required List<int> satoshiAmounts,
}) async { }) async {
@ -2913,10 +2812,14 @@ class BitcoinWallet extends CoinServiceAPI
txb.setVersion(1); txb.setVersion(1);
// Add transaction inputs // Add transaction inputs
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxosToUse[i].txid; final txid = utxoSigningData[i].utxo.txid;
txb.addInput(txid, utxosToUse[i].vout, null, txb.addInput(
utxoSigningData[txid]["output"] as Uint8List); txid,
utxoSigningData[i].utxo.vout,
null,
utxoSigningData[i].output!,
);
} }
// Add transaction output // Add transaction output
@ -2926,13 +2829,12 @@ class BitcoinWallet extends CoinServiceAPI
try { try {
// Sign the transaction accordingly // Sign the transaction accordingly
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxosToUse[i].txid;
txb.sign( txb.sign(
vin: i, vin: i,
keyPair: utxoSigningData[txid]["keyPair"] as ECPair, keyPair: utxoSigningData[i].keyPair!,
witnessValue: utxosToUse[i].value, witnessValue: utxoSigningData[i].utxo.value,
redeemScript: utxoSigningData[txid]["redeemScript"] as Uint8List?, redeemScript: utxoSigningData[i].redeemScript,
); );
} }
} catch (e, s) { } catch (e, s) {

View file

@ -1081,13 +1081,15 @@ class BitcoinCashWallet extends CoinServiceAPI
isSendAll = true; isSendAll = true;
} }
final bool coinControl = utxos != null;
final result = await coinSelection( final result = await coinSelection(
satoshiAmountToSend: satoshiAmount, satoshiAmountToSend: satoshiAmount,
selectedTxFeeRate: rate, selectedTxFeeRate: rate,
recipientAddress: address, recipientAddress: address,
isSendAll: isSendAll, isSendAll: isSendAll,
utxos: utxos?.toList(), utxos: utxos?.toList(),
coinControl: utxos is List<isar_models.UTXO>, coinControl: coinControl,
); );
Logging.instance Logging.instance
@ -2426,6 +2428,7 @@ class BitcoinCashWallet extends CoinServiceAPI
} else { } else {
satoshisBeingUsed = spendableSatoshiValue; satoshisBeingUsed = spendableSatoshiValue;
utxoObjectsToUse = spendableOutputs; utxoObjectsToUse = spendableOutputs;
inputsBeingConsumed = spendableOutputs.length;
} }
Logging.instance Logging.instance

View file

@ -1,815 +0,0 @@
// import 'dart:convert';
// import 'dart:typed_data';
//
// import 'package:bip32/bip32.dart' as bip32;
// import 'package:bip47/bip47.dart';
// import 'package:bip47/src/util.dart';
// import 'package:bitcoindart/bitcoindart.dart' as btc_dart;
// import 'package:bitcoindart/src/utils/constants/op.dart' as op;
// import 'package:bitcoindart/src/utils/script.dart' as bscript;
// import 'package:isar/isar.dart';
// import 'package:pointycastle/digests/sha256.dart';
// import 'package:stackwallet/exceptions/wallet/insufficient_balance_exception.dart';
// import 'package:stackwallet/exceptions/wallet/paynym_send_exception.dart';
// import 'package:stackwallet/models/isar/models/isar_models.dart';
// import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
// import 'package:stackwallet/utilities/bip32_utils.dart';
// import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart';
// import 'package:stackwallet/utilities/format.dart';
// import 'package:stackwallet/utilities/logger.dart';
// import 'package:tuple/tuple.dart';
//
// const kPaynymDerivePath = "m/47'/0'/0'";
//
// extension PayNym on DogecoinWallet {
// // generate bip32 payment code root
// Future<bip32.BIP32> getRootNode({
// required List<String> mnemonic,
// }) async {
// final root = await Bip32Utils.getBip32Root(mnemonic.join(" "), network);
// return root;
// }
//
// Future<Uint8List> deriveNotificationPrivateKey({
// required List<String> mnemonic,
// }) async {
// final root = await getRootNode(mnemonic: mnemonic);
// final node = root.derivePath(kPaynymDerivePath).derive(0);
// return node.privateKey!;
// }
//
// /// fetch or generate this wallet's bip47 payment code
// Future<PaymentCode> getPaymentCode(
// DerivePathType derivePathType,
// ) async {
// final address = await getMyNotificationAddress(derivePathType);
// final paymentCode = PaymentCode.fromPaymentCode(
// address.otherData!,
// network,
// );
// return paymentCode;
// }
//
// Future<Uint8List> signWithNotificationKey(Uint8List data) async {
// final privateKey =
// await deriveNotificationPrivateKey(mnemonic: await mnemonic);
// final pair = btc_dart.ECPair.fromPrivateKey(privateKey, network: network);
// final signed = pair.sign(SHA256Digest().process(data));
// return signed;
// }
//
// Future<String> signStringWithNotificationKey(String data) async {
// final bytes =
// await signWithNotificationKey(Uint8List.fromList(utf8.encode(data)));
// return Format.uint8listToString(bytes);
// }
//
// Future<Future<Map<String, dynamic>>> preparePaymentCodeSend(
// {required PaymentCode paymentCode,
// required int satoshiAmount,
// Map<String, dynamic>? args}) async {
// if (!(await hasConnected(paymentCode.notificationAddressP2PKH()))) {
// throw PaynymSendException(
// "No notification transaction sent to $paymentCode");
// } else {
// final myPrivateKey =
// await deriveNotificationPrivateKey(mnemonic: await mnemonic);
// final sendToAddress = await nextUnusedSendAddressFrom(
// pCode: paymentCode,
// privateKey: myPrivateKey,
// );
//
// return prepareSend(
// address: sendToAddress.value, satoshiAmount: satoshiAmount);
// }
// }
//
// /// get the next unused address to send to given the receiver's payment code
// /// and your own private key
// Future<Address> nextUnusedSendAddressFrom({
// required PaymentCode pCode,
// required Uint8List privateKey,
// int startIndex = 0,
// }) async {
// // https://en.bitcoin.it/wiki/BIP_0047#Path_levels
// const maxCount = 2147483647;
//
// for (int i = startIndex; i < maxCount; i++) {
// final address = await db
// .getAddresses(walletId)
// .filter()
// .subTypeEqualTo(AddressSubType.paynymSend)
// .and()
// .otherDataEqualTo(pCode.toString())
// .and()
// .derivationIndexEqualTo(i)
// .findFirst();
//
// if (address != null) {
// final count = await getTxCount(address: address.value);
// // return address if unused, otherwise continue to next index
// if (count == 0) {
// return address;
// }
// } else {
// final pair = PaymentAddress.initWithPrivateKey(
// privateKey,
// pCode,
// i, // index to use
// ).getSendAddressKeyPair();
//
// // add address to local db
// final address = generatePaynymSendAddressFromKeyPair(
// pair: pair,
// derivationIndex: i,
// derivePathType: DerivePathType.bip44,
// toPaymentCode: pCode,
// );
// await db.putAddress(address);
//
// final count = await getTxCount(address: address.value);
// // return address if unused, otherwise continue to next index
// if (count == 0) {
// return address;
// }
// }
// }
//
// throw PaynymSendException("Exhausted unused send addresses!");
// }
//
// Future<Map<String, dynamic>> prepareNotificationTx({
// required int selectedTxFeeRate,
// required String targetPaymentCodeString,
// int additionalOutputs = 0,
// List<UTXO>? utxos,
// }) async {
// const amountToSend = DUST_LIMIT;
// final List<UTXO> availableOutputs = utxos ?? await this.utxos;
// final List<UTXO> spendableOutputs = [];
// int spendableSatoshiValue = 0;
//
// // Build list of spendable outputs and totaling their satoshi amount
// for (var i = 0; i < availableOutputs.length; i++) {
// if (availableOutputs[i].isBlocked == false &&
// availableOutputs[i]
// .isConfirmed(await chainHeight, MINIMUM_CONFIRMATIONS) ==
// true) {
// spendableOutputs.add(availableOutputs[i]);
// spendableSatoshiValue += availableOutputs[i].value;
// }
// }
//
// if (spendableSatoshiValue < amountToSend) {
// // insufficient balance
// throw InsufficientBalanceException(
// "Spendable balance is less than the minimum required for a notification transaction.");
// } else if (spendableSatoshiValue == amountToSend) {
// // insufficient balance due to missing amount to cover fee
// throw InsufficientBalanceException(
// "Remaining balance does not cover the network fee.");
// }
//
// // sort spendable by age (oldest first)
// spendableOutputs.sort((a, b) => b.blockTime!.compareTo(a.blockTime!));
//
// int satoshisBeingUsed = 0;
// int outputsBeingUsed = 0;
// List<UTXO> utxoObjectsToUse = [];
//
// for (int i = 0;
// satoshisBeingUsed < amountToSend && i < spendableOutputs.length;
// i++) {
// utxoObjectsToUse.add(spendableOutputs[i]);
// satoshisBeingUsed += spendableOutputs[i].value;
// outputsBeingUsed += 1;
// }
//
// // add additional outputs if required
// for (int i = 0;
// i < additionalOutputs && outputsBeingUsed < spendableOutputs.length;
// i++) {
// utxoObjectsToUse.add(spendableOutputs[outputsBeingUsed]);
// satoshisBeingUsed += spendableOutputs[outputsBeingUsed].value;
// outputsBeingUsed += 1;
// }
//
// // gather required signing data
// final utxoSigningData = await fetchBuildTxData(utxoObjectsToUse);
//
// final int vSizeForNoChange = (await _createNotificationTx(
// targetPaymentCodeString: targetPaymentCodeString,
// utxosToUse: utxoObjectsToUse,
// utxoSigningData: utxoSigningData,
// change: 0))
// .item2;
//
// final int vSizeForWithChange = (await _createNotificationTx(
// targetPaymentCodeString: targetPaymentCodeString,
// utxosToUse: utxoObjectsToUse,
// utxoSigningData: utxoSigningData,
// change: satoshisBeingUsed - amountToSend))
// .item2;
//
// // Assume 2 outputs, for recipient and payment code script
// int feeForNoChange = estimateTxFee(
// vSize: vSizeForNoChange,
// feeRatePerKB: selectedTxFeeRate,
// );
//
// // Assume 3 outputs, for recipient, payment code script, and change
// int feeForWithChange = estimateTxFee(
// vSize: vSizeForWithChange,
// feeRatePerKB: selectedTxFeeRate,
// );
//
// if (feeForNoChange < vSizeForNoChange * 1000) {
// feeForNoChange = vSizeForNoChange * 1000;
// }
// if (feeForWithChange < vSizeForWithChange * 1000) {
// feeForWithChange = vSizeForWithChange * 1000;
// }
//
// if (satoshisBeingUsed - amountToSend > feeForNoChange + DUST_LIMIT) {
// // try to add change output due to "left over" amount being greater than
// // the estimated fee + the dust limit
// int changeAmount = satoshisBeingUsed - amountToSend - feeForWithChange;
//
// // check estimates are correct and build notification tx
// if (changeAmount >= DUST_LIMIT &&
// satoshisBeingUsed - amountToSend - changeAmount == feeForWithChange) {
// final txn = await _createNotificationTx(
// targetPaymentCodeString: targetPaymentCodeString,
// utxosToUse: utxoObjectsToUse,
// utxoSigningData: utxoSigningData,
// change: changeAmount,
// );
//
// int feeBeingPaid = satoshisBeingUsed - amountToSend - changeAmount;
//
// Map<String, dynamic> transactionObject = {
// "hex": txn.item1,
// "recipientPaynym": targetPaymentCodeString,
// "amount": amountToSend,
// "fee": feeBeingPaid,
// "vSize": txn.item2,
// };
// return transactionObject;
// } else {
// // something broke during fee estimation or the change amount is smaller
// // than the dust limit. Try without change
// final txn = await _createNotificationTx(
// targetPaymentCodeString: targetPaymentCodeString,
// utxosToUse: utxoObjectsToUse,
// utxoSigningData: utxoSigningData,
// change: 0,
// );
//
// int feeBeingPaid = satoshisBeingUsed - amountToSend;
//
// Map<String, dynamic> transactionObject = {
// "hex": txn.item1,
// "recipientPaynym": targetPaymentCodeString,
// "amount": amountToSend,
// "fee": feeBeingPaid,
// "vSize": txn.item2,
// };
// return transactionObject;
// }
// } else if (satoshisBeingUsed - amountToSend >= feeForNoChange) {
// // since we already checked if we need to add a change output we can just
// // build without change here
// final txn = await _createNotificationTx(
// targetPaymentCodeString: targetPaymentCodeString,
// utxosToUse: utxoObjectsToUse,
// utxoSigningData: utxoSigningData,
// change: 0,
// );
//
// int feeBeingPaid = satoshisBeingUsed - amountToSend;
//
// Map<String, dynamic> transactionObject = {
// "hex": txn.item1,
// "recipientPaynym": targetPaymentCodeString,
// "amount": amountToSend,
// "fee": feeBeingPaid,
// "vSize": txn.item2,
// };
// return transactionObject;
// } else {
// // if we get here we do not have enough funds to cover the tx total so we
// // check if we have any more available outputs and try again
// if (spendableOutputs.length > outputsBeingUsed) {
// return prepareNotificationTx(
// selectedTxFeeRate: selectedTxFeeRate,
// targetPaymentCodeString: targetPaymentCodeString,
// additionalOutputs: additionalOutputs + 1,
// );
// } else {
// throw InsufficientBalanceException(
// "Remaining balance does not cover the network fee.");
// }
// }
// }
//
// // return tuple with string value equal to the raw tx hex and the int value
// // equal to its vSize
// Future<Tuple2<String, int>> _createNotificationTx({
// required String targetPaymentCodeString,
// required List<UTXO> utxosToUse,
// required Map<String, dynamic> utxoSigningData,
// required int change,
// }) async {
// final targetPaymentCode =
// PaymentCode.fromPaymentCode(targetPaymentCodeString, network);
// final myCode = await getPaymentCode(DerivePathType.bip44);
//
// final utxo = utxosToUse.first;
// final txPoint = utxo.txid.fromHex.toList();
// final txPointIndex = utxo.vout;
//
// final rev = Uint8List(txPoint.length + 4);
// Util.copyBytes(Uint8List.fromList(txPoint), 0, rev, 0, txPoint.length);
// final buffer = rev.buffer.asByteData();
// buffer.setUint32(txPoint.length, txPointIndex, Endian.little);
//
// final myKeyPair = utxoSigningData[utxo.txid]["keyPair"] as btc_dart.ECPair;
//
// final S = SecretPoint(
// myKeyPair.privateKey!,
// targetPaymentCode.notificationPublicKey(),
// );
//
// final blindingMask = PaymentCode.getMask(S.ecdhSecret(), rev);
//
// final blindedPaymentCode = PaymentCode.blind(
// myCode.getPayload(),
// blindingMask,
// );
//
// final opReturnScript = bscript.compile([
// (op.OPS["OP_RETURN"] as int),
// blindedPaymentCode,
// ]);
//
// // build a notification tx
// final txb = btc_dart.TransactionBuilder(network: network);
// txb.setVersion(1);
//
// txb.addInput(
// utxo.txid,
// txPointIndex,
// );
//
// // todo: modify address once segwit support is in our bip47
// txb.addOutput(targetPaymentCode.notificationAddressP2PKH(), DUST_LIMIT);
// txb.addOutput(opReturnScript, 0);
//
// // TODO: add possible change output and mark output as dangerous
// if (change > 0) {
// // generate new change address if current change address has been used
// await checkChangeAddressForTransactions();
// final String changeAddress = await currentChangeAddress;
// txb.addOutput(changeAddress, change);
// }
//
// txb.sign(
// vin: 0,
// keyPair: myKeyPair,
// );
//
// // sign rest of possible inputs
// for (var i = 1; i < utxosToUse.length - 1; i++) {
// final txid = utxosToUse[i].txid;
// txb.sign(
// vin: i,
// keyPair: utxoSigningData[txid]["keyPair"] as btc_dart.ECPair,
// // witnessValue: utxosToUse[i].value,
// );
// }
//
// final builtTx = txb.build();
//
// return Tuple2(builtTx.toHex(), builtTx.virtualSize());
// }
//
// Future<String> broadcastNotificationTx(
// {required Map<String, dynamic> preparedTx}) async {
// try {
// Logging.instance.log("confirmNotificationTx txData: $preparedTx",
// level: LogLevel.Info);
// final txHash = await electrumXClient.broadcastTransaction(
// rawTx: preparedTx["hex"] as String);
// Logging.instance.log("Sent txHash: $txHash", level: LogLevel.Info);
//
// // TODO: only refresh transaction data
// try {
// await refresh();
// } catch (e) {
// Logging.instance.log(
// "refresh() failed in confirmNotificationTx ($walletName::$walletId): $e",
// level: LogLevel.Error,
// );
// }
//
// return txHash;
// } catch (e, s) {
// Logging.instance.log("Exception rethrown from confirmSend(): $e\n$s",
// level: LogLevel.Error);
// rethrow;
// }
// }
//
// // TODO optimize
// Future<bool> hasConnected(String paymentCodeString) async {
// final myNotificationAddress =
// await getMyNotificationAddress(DerivePathTypeExt.primaryFor(coin));
//
// final txns = await db
// .getTransactions(walletId)
// .filter()
// .subTypeEqualTo(TransactionSubType.bip47Notification)
// .findAll();
//
// for (final tx in txns) {
// // quick check that may cause problems?
// if (tx.address.value?.value == myNotificationAddress.value) {
// return true;
// }
//
// final unBlindedPaymentCode = await unBlindedPaymentCodeFromTransaction(
// transaction: tx,
// myNotificationAddress: myNotificationAddress,
// );
//
// if (paymentCodeString == unBlindedPaymentCode.toString()) {
// return true;
// }
// }
//
// // otherwise return no
// return false;
// }
//
// Future<PaymentCode?> unBlindedPaymentCodeFromTransaction({
// required Transaction transaction,
// required Address myNotificationAddress,
// }) async {
// if (transaction.address.value != null &&
// transaction.address.value!.value != myNotificationAddress.value) {
// return null;
// }
//
// try {
// final blindedCode =
// transaction.outputs.elementAt(1).scriptPubKeyAsm!.split(" ")[1];
//
// final designatedInput = transaction.inputs.first;
//
// final txPoint = designatedInput.txid.fromHex.toList();
// final txPointIndex = designatedInput.vout;
//
// final rev = Uint8List(txPoint.length + 4);
// Util.copyBytes(Uint8List.fromList(txPoint), 0, rev, 0, txPoint.length);
// final buffer = rev.buffer.asByteData();
// buffer.setUint32(txPoint.length, txPointIndex, Endian.little);
//
// final pubKey = designatedInput.scriptSigAsm!.split(" ")[1].fromHex;
//
// final myPrivateKey =
// await deriveNotificationPrivateKey(mnemonic: await mnemonic);
//
// final S = SecretPoint(myPrivateKey, pubKey);
//
// final mask = PaymentCode.getMask(S.ecdhSecret(), rev);
//
// final unBlindedPayload = PaymentCode.blind(blindedCode.fromHex, mask);
//
// final unBlindedPaymentCode =
// PaymentCode.initFromPayload(unBlindedPayload);
//
// return unBlindedPaymentCode;
// } catch (e) {
// Logging.instance.log(
// "unBlindedPaymentCodeFromTransaction() failed: $e",
// level: LogLevel.Warning,
// );
// return null;
// }
// }
//
// Future<List<PaymentCode>>
// getAllPaymentCodesFromNotificationTransactions() async {
// final myAddress =
// await getMyNotificationAddress(DerivePathTypeExt.primaryFor(coin));
// final txns = await db
// .getTransactions(walletId)
// .filter()
// .subTypeEqualTo(TransactionSubType.bip47Notification)
// .findAll();
//
// List<PaymentCode> unBlindedList = [];
//
// for (final tx in txns) {
// final unBlinded = await unBlindedPaymentCodeFromTransaction(
// transaction: tx,
// myNotificationAddress: myAddress,
// );
// if (unBlinded != null) {
// unBlindedList.add(unBlinded);
// }
// }
//
// return unBlindedList;
// }
//
// Future<void> restoreHistoryWith(
// PaymentCode other,
// int maxUnusedAddressGap,
// int maxNumberOfIndexesToCheck,
// ) async {
// // https://en.bitcoin.it/wiki/BIP_0047#Path_levels
// const maxCount = 2147483647;
// assert(maxNumberOfIndexesToCheck < maxCount);
//
// final myPrivateKey =
// await deriveNotificationPrivateKey(mnemonic: await mnemonic);
//
// List<Address> addresses = [];
// int receivingGapCounter = 0;
// int outgoingGapCounter = 0;
//
// for (int i = 0;
// i < maxNumberOfIndexesToCheck &&
// (receivingGapCounter < maxUnusedAddressGap ||
// outgoingGapCounter < maxUnusedAddressGap);
// i++) {
// final paymentAddress = PaymentAddress.initWithPrivateKey(
// myPrivateKey,
// other,
// i, // index to use
// );
//
// if (receivingGapCounter < maxUnusedAddressGap) {
// final pair = paymentAddress.getSendAddressKeyPair();
// final address = generatePaynymSendAddressFromKeyPair(
// pair: pair,
// derivationIndex: i,
// derivePathType: DerivePathType.bip44,
// toPaymentCode: other,
// );
// addresses.add(address);
//
// final count = await getTxCount(address: address.value);
//
// if (count > 0) {
// receivingGapCounter++;
// } else {
// receivingGapCounter = 0;
// }
// }
//
// if (outgoingGapCounter < maxUnusedAddressGap) {
// final pair = paymentAddress.getReceiveAddressKeyPair();
// final address = generatePaynymReceivingAddressFromKeyPair(
// pair: pair,
// derivationIndex: i,
// derivePathType: DerivePathType.bip44,
// fromPaymentCode: other,
// );
// addresses.add(address);
//
// final count = await getTxCount(address: address.value);
//
// if (count > 0) {
// outgoingGapCounter++;
// } else {
// outgoingGapCounter = 0;
// }
// }
// }
// await db.putAddresses(addresses);
// }
//
// Address generatePaynymSendAddressFromKeyPair({
// required btc_dart.ECPair pair,
// required int derivationIndex,
// required DerivePathType derivePathType,
// required PaymentCode toPaymentCode,
// }) {
// final data = btc_dart.PaymentData(pubkey: pair.publicKey);
//
// String addressString;
// switch (derivePathType) {
// case DerivePathType.bip44:
// addressString =
// btc_dart.P2PKH(data: data, network: network).data.address!;
// break;
//
// // The following doesn't apply currently
// // case DerivePathType.bip49:
// // addressString = btc_dart
// // .P2SH(
// // data: btc_dart.PaymentData(
// // redeem: btc_dart
// // .P2WPKH(
// // data: data,
// // network: network,
// // )
// // .data),
// // network: network,
// // )
// // .data
// // .address!;
// // break;
// //
// // case DerivePathType.bip84:
// // addressString = btc_dart
// // .P2WPKH(
// // network: network,
// // data: data,
// // )
// // .data
// // .address!;
// // break;
// default:
// throw UnimplementedError("segwit paynyms not implemented yet");
// }
//
// final address = Address(
// walletId: walletId,
// value: addressString,
// publicKey: pair.publicKey,
// derivationIndex: derivationIndex,
// type: AddressType.nonWallet,
// subType: AddressSubType.paynymSend,
// otherData: toPaymentCode.toString(),
// );
//
// return address;
// }
//
// Address generatePaynymReceivingAddressFromKeyPair({
// required btc_dart.ECPair pair,
// required int derivationIndex,
// required DerivePathType derivePathType,
// required PaymentCode fromPaymentCode,
// }) {
// final data = btc_dart.PaymentData(pubkey: pair.publicKey);
//
// String addressString;
// AddressType addrType;
// switch (derivePathType) {
// case DerivePathType.bip44:
// addressString = btc_dart
// .P2PKH(
// data: data,
// network: network,
// )
// .data
// .address!;
// addrType = AddressType.p2pkh;
// break;
//
// // The following doesn't apply currently
// // case DerivePathType.bip49:
// // addressString = btc_dart
// // .P2SH(
// // data: btc_dart.PaymentData(
// // redeem: btc_dart
// // .P2WPKH(
// // data: data,
// // network: network,
// // )
// // .data),
// // network: network,
// // )
// // .data
// // .address!;
// // addrType = AddressType.p2sh;
// // break;
// //
// // case DerivePathType.bip84:
// // addressString = btc_dart
// // .P2WPKH(
// // network: network,
// // data: data,
// // )
// // .data
// // .address!;
// // addrType = AddressType.p2wpkh;
// // break;
// default:
// throw UnimplementedError("segwit paynyms not implemented yet");
// }
//
// final address = Address(
// walletId: walletId,
// value: addressString,
// publicKey: pair.publicKey,
// derivationIndex: derivationIndex,
// type: addrType,
// subType: AddressSubType.paynymReceive,
// otherData: fromPaymentCode.toString(),
// );
//
// return address;
// }
//
// Future<Address> getMyNotificationAddress(
// DerivePathType derivePathType,
// ) async {
// // TODO: fix when segwit is here
// derivePathType = DerivePathType.bip44;
//
// AddressType type;
// switch (derivePathType) {
// case DerivePathType.bip44:
// type = AddressType.p2pkh;
// break;
// case DerivePathType.bip49:
// type = AddressType.p2sh;
// break;
// case DerivePathType.bip84:
// type = AddressType.p2wpkh;
// break;
// }
//
// final storedAddress = await db
// .getAddresses(walletId)
// .filter()
// .subTypeEqualTo(AddressSubType.paynymNotification)
// .and()
// .typeEqualTo(type)
// .and()
// .not()
// .typeEqualTo(AddressType.nonWallet)
// .findFirst();
//
// if (storedAddress != null) {
// return storedAddress;
// } else {
// final root = await getRootNode(mnemonic: await mnemonic);
// final node = root.derivePath(kPaynymDerivePath);
// final paymentCode = PaymentCode.initFromPubKey(
// node.publicKey,
// node.chainCode,
// network,
// );
//
// String addressString;
// final data =
// btc_dart.PaymentData(pubkey: paymentCode.notificationPublicKey());
// switch (derivePathType) {
// case DerivePathType.bip44:
// addressString = btc_dart
// .P2PKH(
// data: data,
// network: network,
// )
// .data
// .address!;
// break;
// // case DerivePathType.bip49:
// // addressString = btc_dart
// // .P2SH(
// // data: btc_dart.PaymentData(
// // redeem: btc_dart
// // .P2WPKH(
// // data: data,
// // network: network,
// // )
// // .data),
// // network: network,
// // )
// // .data
// // .address!;
// // break;
// // case DerivePathType.bip84:
// // addressString = btc_dart
// // .P2WPKH(
// // network: network,
// // data: data,
// // )
// // .data
// // .address!;
// // break;
// default:
// throw UnimplementedError("segwit paynyms not implemented yet");
// }
//
// final address = Address(
// walletId: walletId,
// value: addressString,
// publicKey: paymentCode.getPubKey(),
// derivationIndex: 0,
// type: type,
// subType: AddressSubType.paynymNotification,
// otherData: paymentCode.toString(),
// );
//
// await db.putAddress(address);
// return address;
// }
// }
// }

View file

@ -18,6 +18,7 @@ import 'package:stackwallet/exceptions/electrumx/no_such_transaction.dart';
import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models; import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
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/services/coins/coin_service.dart'; import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
@ -946,13 +947,15 @@ class DogecoinWallet extends CoinServiceAPI
isSendAll = true; isSendAll = true;
} }
final bool coinControl = utxos != null;
final result = await coinSelection( final result = await coinSelection(
satoshiAmountToSend: satoshiAmount, satoshiAmountToSend: satoshiAmount,
selectedTxFeeRate: rate, selectedTxFeeRate: rate,
recipientAddress: address, recipientAddress: address,
isSendAll: isSendAll, isSendAll: isSendAll,
utxos: utxos?.toList(), utxos: utxos?.toList(),
coinControl: utxos is List<isar_models.UTXO>, coinControl: coinControl,
); );
Logging.instance Logging.instance
@ -1586,14 +1589,16 @@ class DogecoinWallet extends CoinServiceAPI
String? blockReason; String? blockReason;
if (storedTx?.subType == if (storedTx?.subType ==
isar_models.TransactionSubType.bip47Notification && isar_models.TransactionSubType.bip47Notification) {
storedTx?.type == isar_models.TransactionType.incoming) { if (storedTx?.type == isar_models.TransactionType.incoming) {
// probably safe to assume this is an incoming tx as it is a utxo shouldBlock = true;
// belonging to this wallet. The extra check may be redundant but blockReason = "Incoming paynym notification transaction.";
// just in case... } else if (storedTx?.type == isar_models.TransactionType.outgoing) {
shouldBlock = true;
shouldBlock = true; blockReason = "Paynym notification change output. Incautious "
blockReason = "Incoming paynym notification transaction."; "handling of change outputs from notification transactions "
"may cause unintended loss of privacy.";
}
} }
final vout = jsonUTXO["tx_pos"] as int; final vout = jsonUTXO["tx_pos"] as int;
@ -2126,6 +2131,7 @@ class DogecoinWallet extends CoinServiceAPI
} else { } else {
satoshisBeingUsed = spendableSatoshiValue; satoshisBeingUsed = spendableSatoshiValue;
utxoObjectsToUse = spendableOutputs; utxoObjectsToUse = spendableOutputs;
inputsBeingConsumed = spendableOutputs.length;
} }
Logging.instance Logging.instance
@ -2149,7 +2155,6 @@ class DogecoinWallet extends CoinServiceAPI
.log("Attempting to send all $coin", level: LogLevel.Info); .log("Attempting to send all $coin", level: LogLevel.Info);
final int vSizeForOneOutput = (await buildTransaction( final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [recipientAddress], recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1], satoshiAmounts: [satoshisBeingUsed - 1],
@ -2164,7 +2169,6 @@ class DogecoinWallet extends CoinServiceAPI
final int amount = satoshiAmountToSend - feeForOneOutput; final int amount = satoshiAmountToSend - feeForOneOutput;
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: [amount], satoshiAmounts: [amount],
@ -2175,19 +2179,17 @@ class DogecoinWallet extends CoinServiceAPI
"recipientAmt": amount, "recipientAmt": amount,
"fee": feeForOneOutput, "fee": feeForOneOutput,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
final int vSizeForOneOutput = (await buildTransaction( final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [recipientAddress], recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1], satoshiAmounts: [satoshisBeingUsed - 1],
))["vSize"] as int; ))["vSize"] as int;
final int vSizeForTwoOutPuts = (await buildTransaction( final int vSizeForTwoOutPuts = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [ recipients: [
recipientAddress, recipientAddress,
@ -2267,7 +2269,6 @@ class DogecoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info); .log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2295,7 +2296,6 @@ class DogecoinWallet extends CoinServiceAPI
Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs', Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs',
level: LogLevel.Info); level: LogLevel.Info);
txn = await buildTransaction( txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2308,7 +2308,7 @@ class DogecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": feeBeingPaid, "fee": feeBeingPaid,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} else { } else {
@ -2325,7 +2325,6 @@ class DogecoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2336,7 +2335,7 @@ class DogecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend, "fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
@ -2355,7 +2354,6 @@ class DogecoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2366,7 +2364,7 @@ class DogecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend, "fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
@ -2385,7 +2383,6 @@ class DogecoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2396,7 +2393,7 @@ class DogecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": feeForOneOutput, "fee": feeForOneOutput,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} else { } else {
@ -2422,103 +2419,120 @@ class DogecoinWallet extends CoinServiceAPI
} }
} }
Future<Map<String, dynamic>> fetchBuildTxData( Future<List<SigningData>> fetchBuildTxData(
List<isar_models.UTXO> utxosToUse, List<isar_models.UTXO> utxosToUse,
) async { ) async {
// return data // return data
Map<String, dynamic> results = {}; List<SigningData> signingData = [];
Map<String, List<String>> addressTxid = {};
// addresses to check
List<String> addressesP2PKH = [];
try { try {
// Populating the addresses to check // Populating the addresses to check
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid; if (utxosToUse[i].address == null) {
final tx = await _cachedElectrumXClient.getTransaction( final txid = utxosToUse[i].txid;
txHash: txid, final tx = await _cachedElectrumXClient.getTransaction(
coin: coin, txHash: txid,
); coin: coin,
);
for (final output in tx["vout"] as List) { for (final output in tx["vout"] as List) {
final n = output["n"]; final n = output["n"];
if (n != null && n == utxosToUse[i].vout) { if (n != null && n == utxosToUse[i].vout) {
final address = output["scriptPubKey"]["addresses"][0] as String; utxosToUse[i] = utxosToUse[i].copyWith(
if (!addressTxid.containsKey(address)) { address: output["scriptPubKey"]?["addresses"]?[0] as String? ??
addressTxid[address] = <String>[]; output["scriptPubKey"]["address"] as String,
} );
(addressTxid[address] as List).add(txid);
switch (addressType(address: address)) {
case DerivePathType.bip44:
addressesP2PKH.add(address);
break;
default:
throw Exception("Unsupported DerivePathType");
} }
} }
} }
final derivePathType = addressType(address: utxosToUse[i].address!);
signingData.add(
SigningData(
derivePathType: derivePathType,
utxo: utxosToUse[i],
),
);
} }
// p2pkh / bip44 Map<DerivePathType, Map<String, dynamic>> receiveDerivations = {};
final p2pkhLength = addressesP2PKH.length; Map<DerivePathType, Map<String, dynamic>> changeDerivations = {};
if (p2pkhLength > 0) {
final receiveDerivations = await _fetchDerivations(
chain: 0,
derivePathType: DerivePathType.bip44,
);
final changeDerivations = await _fetchDerivations(
chain: 1,
derivePathType: DerivePathType.bip44,
);
for (int i = 0; i < p2pkhLength; i++) {
// receives
final receiveDerivation = receiveDerivations[addressesP2PKH[i]];
// if a match exists it will not be null
if (receiveDerivation != null) {
final data = P2PKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
receiveDerivation["pubKey"] as String)),
network: network,
).data;
for (String tx in addressTxid[addressesP2PKH[i]]!) { for (final sd in signingData) {
results[tx] = { String? pubKey;
"output": data.output, String? wif;
"keyPair": ECPair.fromWIF(
receiveDerivation["wif"] as String, // fetch receiving derivations if null
network: network, receiveDerivations[sd.derivePathType] ??= await _fetchDerivations(
), chain: 0,
}; derivePathType: sd.derivePathType,
} );
} else { final receiveDerivation =
// if its not a receive, check change receiveDerivations[sd.derivePathType]![sd.utxo.address!];
final changeDerivation = changeDerivations[addressesP2PKH[i]];
// if a match exists it will not be null if (receiveDerivation != null) {
if (changeDerivation != null) { pubKey = receiveDerivation["pubKey"] as String;
final data = P2PKH( wif = receiveDerivation["wif"] as String;
} else {
// fetch change derivations if null
changeDerivations[sd.derivePathType] ??= await _fetchDerivations(
chain: 1,
derivePathType: sd.derivePathType,
);
final changeDerivation =
changeDerivations[sd.derivePathType]![sd.utxo.address!];
if (changeDerivation != null) {
pubKey = changeDerivation["pubKey"] as String;
wif = changeDerivation["wif"] as String;
}
}
if (wif == null || pubKey == null) {
final address = await db.getAddress(walletId, sd.utxo.address!);
if (address?.derivationPath != null) {
final node = await Bip32Utils.getBip32Node(
(await mnemonicString)!,
(await mnemonicPassphrase)!,
network,
address!.derivationPath!.value,
);
wif = node.toWIF();
pubKey = Format.uint8listToString(node.publicKey);
}
}
if (wif != null && pubKey != null) {
final PaymentData data;
final Uint8List? redeemScript;
switch (sd.derivePathType) {
case DerivePathType.bip44:
data = P2PKH(
data: PaymentData( data: PaymentData(
pubkey: Format.stringToUint8List( pubkey: Format.stringToUint8List(pubKey),
changeDerivation["pubKey"] as String)), ),
network: network, network: network,
).data; ).data;
redeemScript = null;
break;
for (String tx in addressTxid[addressesP2PKH[i]]!) { default:
results[tx] = { throw Exception("DerivePathType unsupported");
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: network,
),
};
}
}
} }
final keyPair = ECPair.fromWIF(
wif,
network: network,
);
sd.redeemScript = redeemScript;
sd.output = data.output;
sd.keyPair = keyPair;
} }
} }
return results; return signingData;
} catch (e, s) { } catch (e, s) {
Logging.instance Logging.instance
.log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error); .log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error);
@ -2528,8 +2542,7 @@ class DogecoinWallet extends CoinServiceAPI
/// Builds and signs a transaction /// Builds and signs a transaction
Future<Map<String, dynamic>> buildTransaction({ Future<Map<String, dynamic>> buildTransaction({
required List<isar_models.UTXO> utxosToUse, required List<SigningData> utxoSigningData,
required Map<String, dynamic> utxoSigningData,
required List<String> recipients, required List<String> recipients,
required List<int> satoshiAmounts, required List<int> satoshiAmounts,
}) async { }) async {
@ -2540,10 +2553,14 @@ class DogecoinWallet extends CoinServiceAPI
txb.setVersion(1); txb.setVersion(1);
// Add transaction inputs // Add transaction inputs
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxosToUse[i].txid; final txid = utxoSigningData[i].utxo.txid;
txb.addInput(txid, utxosToUse[i].vout, null, txb.addInput(
utxoSigningData[txid]["output"] as Uint8List); txid,
utxoSigningData[i].utxo.vout,
null,
utxoSigningData[i].output!,
);
} }
// Add transaction output // Add transaction output
@ -2553,13 +2570,12 @@ class DogecoinWallet extends CoinServiceAPI
try { try {
// Sign the transaction accordingly // Sign the transaction accordingly
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxosToUse[i].txid;
txb.sign( txb.sign(
vin: i, vin: i,
keyPair: utxoSigningData[txid]["keyPair"] as ECPair, keyPair: utxoSigningData[i].keyPair!,
witnessValue: utxosToUse[i].value, witnessValue: utxoSigningData[i].utxo.value,
redeemScript: utxoSigningData[txid]["redeemScript"] as Uint8List?, redeemScript: utxoSigningData[i].redeemScript,
); );
} }
} catch (e, s) { } catch (e, s) {

View file

@ -18,6 +18,7 @@ import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
import 'package:stackwallet/models/lelantus_coin.dart'; import 'package:stackwallet/models/lelantus_coin.dart';
import 'package:stackwallet/models/lelantus_fee_data.dart'; import 'package:stackwallet/models/lelantus_fee_data.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/services/coins/coin_service.dart'; import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
@ -36,6 +37,7 @@ import 'package:stackwallet/utilities/bip32_utils.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/default_nodes.dart'; import 'package:stackwallet/utilities/default_nodes.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/format.dart'; import 'package:stackwallet/utilities/format.dart';
@ -1367,7 +1369,6 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
.log("Attempting to send all $coin", level: LogLevel.Info); .log("Attempting to send all $coin", level: LogLevel.Info);
final int vSizeForOneOutput = (await buildTransaction( final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [_recipientAddress], recipients: [_recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1], satoshiAmounts: [satoshisBeingUsed - 1],
@ -1383,7 +1384,6 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
final int amount = satoshiAmountToSend - feeForOneOutput; final int amount = satoshiAmountToSend - feeForOneOutput;
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: [amount], satoshiAmounts: [amount],
@ -1399,13 +1399,11 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
} }
final int vSizeForOneOutput = (await buildTransaction( final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [_recipientAddress], recipients: [_recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1], satoshiAmounts: [satoshisBeingUsed - 1],
))["vSize"] as int; ))["vSize"] as int;
final int vSizeForTwoOutPuts = (await buildTransaction( final int vSizeForTwoOutPuts = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [ recipients: [
_recipientAddress, _recipientAddress,
@ -1484,7 +1482,6 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
Logging.instance Logging.instance
.log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info); .log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -1512,7 +1509,6 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs', Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs',
level: LogLevel.Info); level: LogLevel.Info);
txn = await buildTransaction( txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -1541,7 +1537,6 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -1570,7 +1565,6 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -1599,7 +1593,6 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -1629,119 +1622,141 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
} }
} }
Future<Map<String, dynamic>> fetchBuildTxData( Future<List<SigningData>> fetchBuildTxData(
List<isar_models.UTXO> utxosToUse, List<isar_models.UTXO> utxosToUse,
) async { ) async {
// return data // return data
Map<String, dynamic> results = {}; List<SigningData> signingData = [];
Map<String, List<String>> addressTxid = {};
// addresses to check
List<String> addresses = [];
try { try {
// Populating the addresses to check // Populating the addresses to check
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid; if (utxosToUse[i].address == null) {
final tx = await _cachedElectrumXClient.getTransaction( final txid = utxosToUse[i].txid;
txHash: txid, final tx = await _cachedElectrumXClient.getTransaction(
coin: coin, txHash: txid,
coin: coin,
);
for (final output in tx["vout"] as List) {
final n = output["n"];
if (n != null && n == utxosToUse[i].vout) {
utxosToUse[i] = utxosToUse[i].copyWith(
address: output["scriptPubKey"]?["addresses"]?[0] as String? ??
output["scriptPubKey"]["address"] as String,
);
}
}
}
signingData.add(
SigningData(
derivePathType: DerivePathType.bip44,
utxo: utxosToUse[i],
),
);
}
Map<DerivePathType, Map<String, dynamic>> receiveDerivations = {};
Map<DerivePathType, Map<String, dynamic>> changeDerivations = {};
for (final sd in signingData) {
String? pubKey;
String? wif;
// fetch receiving derivations if null
receiveDerivations[sd.derivePathType] ??= Map<String, dynamic>.from(
jsonDecode((await _secureStore.read(
key: "${walletId}_receiveDerivations",
)) ??
"{}") as Map,
); );
for (final output in tx["vout"] as List) { dynamic receiveDerivation;
final n = output["n"]; for (int j = 0;
if (n != null && n == utxosToUse[i].vout) { j < receiveDerivations[sd.derivePathType]!.length &&
final address = output["scriptPubKey"]["addresses"][0] as String; receiveDerivation == null;
j++) {
if (!addressTxid.containsKey(address)) { if (receiveDerivations[sd.derivePathType]!["$j"]["address"] ==
addressTxid[address] = <String>[]; sd.utxo.address!) {
} receiveDerivation = receiveDerivations[sd.derivePathType]!["$j"];
(addressTxid[address] as List).add(txid);
addresses.add(address);
} }
} }
}
// p2pkh / bip44 if (receiveDerivation != null) {
final addressesLength = addresses.length; pubKey = receiveDerivation["publicKey"] as String;
if (addressesLength > 0) { wif = receiveDerivation["wif"] as String;
final receiveDerivationsString = } else {
await _secureStore.read(key: "${walletId}_receiveDerivations"); // fetch change derivations if null
final receiveDerivations = Map<String, dynamic>.from( changeDerivations[sd.derivePathType] ??= Map<String, dynamic>.from(
jsonDecode(receiveDerivationsString ?? "{}") as Map); jsonDecode((await _secureStore.read(
key: "${walletId}_changeDerivations",
)) ??
"{}") as Map,
);
final changeDerivationsString = dynamic changeDerivation;
await _secureStore.read(key: "${walletId}_changeDerivations"); for (int j = 0;
final changeDerivations = Map<String, dynamic>.from( j < changeDerivations[sd.derivePathType]!.length &&
jsonDecode(changeDerivationsString ?? "{}") as Map); changeDerivation == null;
j++) {
for (int i = 0; i < addressesLength; i++) { if (changeDerivations[sd.derivePathType]!["$j"]["address"] ==
// receives sd.utxo.address!) {
changeDerivation = changeDerivations[sd.derivePathType]!["$j"];
dynamic receiveDerivation;
for (int j = 0; j < receiveDerivations.length; j++) {
if (receiveDerivations["$j"]["address"] == addresses[i]) {
receiveDerivation = receiveDerivations["$j"];
} }
} }
// receiveDerivation = receiveDerivations[addresses[i]]; if (changeDerivation != null) {
// if a match exists it will not be null pubKey = changeDerivation["publicKey"] as String;
if (receiveDerivation != null) { wif = changeDerivation["wif"] as String;
final data = P2PKH( }
data: PaymentData( }
pubkey: Format.stringToUint8List(
receiveDerivation["publicKey"] as String)),
network: _network,
).data;
for (String tx in addressTxid[addresses[i]]!) { if (wif == null || pubKey == null) {
results[tx] = { final address = await db.getAddress(walletId, sd.utxo.address!);
"output": data.output, if (address?.derivationPath != null) {
"keyPair": ECPair.fromWIF( final node = await Bip32Utils.getBip32Node(
receiveDerivation["wif"] as String, (await mnemonicString)!,
network: _network, (await mnemonicPassphrase)!,
), _network,
}; address!.derivationPath!.value,
} );
} else {
// if its not a receive, check change
dynamic changeDerivation; wif = node.toWIF();
pubKey = Format.uint8listToString(node.publicKey);
}
}
for (int j = 0; j < changeDerivations.length; j++) { if (wif != null && pubKey != null) {
if (changeDerivations["$j"]["address"] == addresses[i]) { final PaymentData data;
changeDerivation = changeDerivations["$j"]; final Uint8List? redeemScript;
}
}
// final changeDerivation = changeDerivations[addresses[i]]; switch (sd.derivePathType) {
// if a match exists it will not be null case DerivePathType.bip44:
if (changeDerivation != null) { data = P2PKH(
final data = P2PKH(
data: PaymentData( data: PaymentData(
pubkey: Format.stringToUint8List( pubkey: Format.stringToUint8List(pubKey),
changeDerivation["publicKey"] as String)), ),
network: _network, network: _network,
).data; ).data;
redeemScript = null;
break;
for (String tx in addressTxid[addresses[i]]!) { default:
results[tx] = { throw Exception("DerivePathType unsupported");
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
),
};
}
}
} }
final keyPair = ECPair.fromWIF(
wif,
network: _network,
);
sd.redeemScript = redeemScript;
sd.output = data.output;
sd.keyPair = keyPair;
} }
} }
return results; return signingData;
} catch (e, s) { } catch (e, s) {
Logging.instance Logging.instance
.log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error); .log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error);
@ -1751,8 +1766,7 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
/// Builds and signs a transaction /// Builds and signs a transaction
Future<Map<String, dynamic>> buildTransaction({ Future<Map<String, dynamic>> buildTransaction({
required List<isar_models.UTXO> utxosToUse, required List<SigningData> utxoSigningData,
required Map<String, dynamic> utxoSigningData,
required List<String> recipients, required List<String> recipients,
required List<int> satoshiAmounts, required List<int> satoshiAmounts,
}) async { }) async {
@ -1763,10 +1777,14 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
txb.setVersion(1); txb.setVersion(1);
// Add transaction inputs // Add transaction inputs
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxosToUse[i].txid; final txid = utxoSigningData[i].utxo.txid;
txb.addInput(txid, utxosToUse[i].vout, null, txb.addInput(
utxoSigningData[txid]["output"] as Uint8List); txid,
utxoSigningData[i].utxo.vout,
null,
utxoSigningData[i].output!,
);
} }
// Add transaction output // Add transaction output
@ -1776,13 +1794,12 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
try { try {
// Sign the transaction accordingly // Sign the transaction accordingly
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxosToUse[i].txid;
txb.sign( txb.sign(
vin: i, vin: i,
keyPair: utxoSigningData[txid]["keyPair"] as ECPair, keyPair: utxoSigningData[i].keyPair!,
witnessValue: utxosToUse[i].value, witnessValue: utxoSigningData[i].utxo.value,
redeemScript: utxoSigningData[txid]["redeemScript"] as Uint8List?, redeemScript: utxoSigningData[i].redeemScript,
); );
} }
} catch (e, s) { } catch (e, s) {

View file

@ -17,6 +17,7 @@ import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models; import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
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/services/coins/coin_service.dart'; import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
@ -1058,13 +1059,15 @@ class LitecoinWallet extends CoinServiceAPI
isSendAll = true; isSendAll = true;
} }
final bool coinControl = utxos != null;
final txData = await coinSelection( final txData = await coinSelection(
satoshiAmountToSend: satoshiAmount, satoshiAmountToSend: satoshiAmount,
selectedTxFeeRate: rate, selectedTxFeeRate: rate,
recipientAddress: address, recipientAddress: address,
isSendAll: isSendAll, isSendAll: isSendAll,
utxos: utxos?.toList(), utxos: utxos?.toList(),
coinControl: utxos is List<isar_models.UTXO>, coinControl: coinControl,
); );
Logging.instance.log("prepare send: $txData", level: LogLevel.Info); Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
@ -2314,6 +2317,7 @@ class LitecoinWallet extends CoinServiceAPI
} else { } else {
satoshisBeingUsed = spendableSatoshiValue; satoshisBeingUsed = spendableSatoshiValue;
utxoObjectsToUse = spendableOutputs; utxoObjectsToUse = spendableOutputs;
inputsBeingConsumed = spendableOutputs.length;
} }
Logging.instance Logging.instance
@ -2335,7 +2339,6 @@ class LitecoinWallet extends CoinServiceAPI
.log("Attempting to send all $coin", level: LogLevel.Info); .log("Attempting to send all $coin", level: LogLevel.Info);
final int vSizeForOneOutput = (await buildTransaction( final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [recipientAddress], recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1], satoshiAmounts: [satoshisBeingUsed - 1],
@ -2353,7 +2356,6 @@ class LitecoinWallet extends CoinServiceAPI
final int amount = satoshiAmountToSend - feeForOneOutput; final int amount = satoshiAmountToSend - feeForOneOutput;
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: [amount], satoshiAmounts: [amount],
@ -2364,19 +2366,17 @@ class LitecoinWallet extends CoinServiceAPI
"recipientAmt": amount, "recipientAmt": amount,
"fee": feeForOneOutput, "fee": feeForOneOutput,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
final int vSizeForOneOutput = (await buildTransaction( final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [recipientAddress], recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1], satoshiAmounts: [satoshisBeingUsed - 1],
))["vSize"] as int; ))["vSize"] as int;
final int vSizeForTwoOutPuts = (await buildTransaction( final int vSizeForTwoOutPuts = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [ recipients: [
recipientAddress, recipientAddress,
@ -2442,7 +2442,6 @@ class LitecoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info); .log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2470,7 +2469,6 @@ class LitecoinWallet extends CoinServiceAPI
Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs', Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs',
level: LogLevel.Info); level: LogLevel.Info);
txn = await buildTransaction( txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2483,7 +2481,7 @@ class LitecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": feeBeingPaid, "fee": feeBeingPaid,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} else { } else {
@ -2500,7 +2498,6 @@ class LitecoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2511,7 +2508,7 @@ class LitecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend, "fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
@ -2530,7 +2527,6 @@ class LitecoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2541,7 +2537,7 @@ class LitecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend, "fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
@ -2560,7 +2556,6 @@ class LitecoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2571,7 +2566,7 @@ class LitecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": feeForOneOutput, "fee": feeForOneOutput,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} else { } else {
@ -2597,245 +2592,146 @@ class LitecoinWallet extends CoinServiceAPI
} }
} }
Future<Map<String, dynamic>> fetchBuildTxData( Future<List<SigningData>> fetchBuildTxData(
List<isar_models.UTXO> utxosToUse, List<isar_models.UTXO> utxosToUse,
) async { ) async {
// return data // return data
Map<String, dynamic> results = {}; List<SigningData> signingData = [];
Map<String, List<String>> addressTxid = {};
// addresses to check
List<String> addressesP2PKH = [];
List<String> addressesP2SH = [];
List<String> addressesP2WPKH = [];
try { try {
// Populating the addresses to check // Populating the addresses to check
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid; if (utxosToUse[i].address == null) {
final tx = await _cachedElectrumXClient.getTransaction( final txid = utxosToUse[i].txid;
txHash: txid, final tx = await _cachedElectrumXClient.getTransaction(
coin: coin, txHash: txid,
); coin: coin,
);
for (final output in tx["vout"] as List) { for (final output in tx["vout"] as List) {
final n = output["n"]; final n = output["n"];
if (n != null && n == utxosToUse[i].vout) { if (n != null && n == utxosToUse[i].vout) {
final address = output["scriptPubKey"]["addresses"][0] as String; utxosToUse[i] = utxosToUse[i].copyWith(
if (!addressTxid.containsKey(address)) { address: output["scriptPubKey"]?["addresses"]?[0] as String? ??
addressTxid[address] = <String>[]; output["scriptPubKey"]["address"] as String,
} );
(addressTxid[address] as List).add(txid);
switch (addressType(address: address)) {
case DerivePathType.bip44:
addressesP2PKH.add(address);
break;
case DerivePathType.bip49:
addressesP2SH.add(address);
break;
case DerivePathType.bip84:
addressesP2WPKH.add(address);
break;
default:
throw Exception("DerivePathType unsupported");
} }
} }
} }
final derivePathType = addressType(address: utxosToUse[i].address!);
signingData.add(
SigningData(
derivePathType: derivePathType,
utxo: utxosToUse[i],
),
);
} }
// p2pkh / bip44 Map<DerivePathType, Map<String, dynamic>> receiveDerivations = {};
final p2pkhLength = addressesP2PKH.length; Map<DerivePathType, Map<String, dynamic>> changeDerivations = {};
if (p2pkhLength > 0) {
final receiveDerivations = await _fetchDerivations(
chain: 0,
derivePathType: DerivePathType.bip44,
);
final changeDerivations = await _fetchDerivations(
chain: 1,
derivePathType: DerivePathType.bip44,
);
for (int i = 0; i < p2pkhLength; i++) {
// receives
final receiveDerivation = receiveDerivations[addressesP2PKH[i]];
// if a match exists it will not be null
if (receiveDerivation != null) {
final data = P2PKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
receiveDerivation["pubKey"] as String)),
network: _network,
).data;
for (String tx in addressTxid[addressesP2PKH[i]]!) { for (final sd in signingData) {
results[tx] = { String? pubKey;
"output": data.output, String? wif;
"keyPair": ECPair.fromWIF(
receiveDerivation["wif"] as String, // fetch receiving derivations if null
network: _network, receiveDerivations[sd.derivePathType] ??= await _fetchDerivations(
), chain: 0,
}; derivePathType: sd.derivePathType,
} );
} else { final receiveDerivation =
// if its not a receive, check change receiveDerivations[sd.derivePathType]![sd.utxo.address!];
final changeDerivation = changeDerivations[addressesP2PKH[i]];
// if a match exists it will not be null if (receiveDerivation != null) {
if (changeDerivation != null) { pubKey = receiveDerivation["pubKey"] as String;
final data = P2PKH( wif = receiveDerivation["wif"] as String;
} else {
// fetch change derivations if null
changeDerivations[sd.derivePathType] ??= await _fetchDerivations(
chain: 1,
derivePathType: sd.derivePathType,
);
final changeDerivation =
changeDerivations[sd.derivePathType]![sd.utxo.address!];
if (changeDerivation != null) {
pubKey = changeDerivation["pubKey"] as String;
wif = changeDerivation["wif"] as String;
}
}
if (wif == null || pubKey == null) {
final address = await db.getAddress(walletId, sd.utxo.address!);
if (address?.derivationPath != null) {
final node = await Bip32Utils.getBip32Node(
(await mnemonicString)!,
(await mnemonicPassphrase)!,
_network,
address!.derivationPath!.value,
);
wif = node.toWIF();
pubKey = Format.uint8listToString(node.publicKey);
}
}
if (wif != null && pubKey != null) {
final PaymentData data;
final Uint8List? redeemScript;
switch (sd.derivePathType) {
case DerivePathType.bip44:
data = P2PKH(
data: PaymentData( data: PaymentData(
pubkey: Format.stringToUint8List( pubkey: Format.stringToUint8List(pubKey),
changeDerivation["pubKey"] as String)), ),
network: _network, network: _network,
).data; ).data;
redeemScript = null;
break;
for (String tx in addressTxid[addressesP2PKH[i]]!) { case DerivePathType.bip49:
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
),
};
}
}
}
}
}
// p2sh / bip49
final p2shLength = addressesP2SH.length;
if (p2shLength > 0) {
final receiveDerivations = await _fetchDerivations(
chain: 0,
derivePathType: DerivePathType.bip49,
);
final changeDerivations = await _fetchDerivations(
chain: 1,
derivePathType: DerivePathType.bip49,
);
for (int i = 0; i < p2shLength; i++) {
// receives
final receiveDerivation = receiveDerivations[addressesP2SH[i]];
// if a match exists it will not be null
if (receiveDerivation != null) {
final p2wpkh = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
receiveDerivation["pubKey"] as String)),
network: _network,
overridePrefix: _network.bech32!)
.data;
final redeemScript = p2wpkh.output;
final data =
P2SH(data: PaymentData(redeem: p2wpkh), network: _network).data;
for (String tx in addressTxid[addressesP2SH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
receiveDerivation["wif"] as String,
network: _network,
),
"redeemScript": redeemScript,
};
}
} else {
// if its not a receive, check change
final changeDerivation = changeDerivations[addressesP2SH[i]];
// if a match exists it will not be null
if (changeDerivation != null) {
final p2wpkh = P2WPKH( final p2wpkh = P2WPKH(
data: PaymentData( data: PaymentData(
pubkey: Format.stringToUint8List( pubkey: Format.stringToUint8List(pubKey),
changeDerivation["pubKey"] as String)),
network: _network,
overridePrefix: _network.bech32!)
.data;
final redeemScript = p2wpkh.output;
final data =
P2SH(data: PaymentData(redeem: p2wpkh), network: _network)
.data;
for (String tx in addressTxid[addressesP2SH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
),
"redeemScript": redeemScript,
};
}
}
}
}
}
// p2wpkh / bip84
final p2wpkhLength = addressesP2WPKH.length;
if (p2wpkhLength > 0) {
final receiveDerivations = await _fetchDerivations(
chain: 0,
derivePathType: DerivePathType.bip84,
);
final changeDerivations = await _fetchDerivations(
chain: 1,
derivePathType: DerivePathType.bip84,
);
for (int i = 0; i < p2wpkhLength; i++) {
// receives
final receiveDerivation = receiveDerivations[addressesP2WPKH[i]];
// if a match exists it will not be null
if (receiveDerivation != null) {
final data = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
receiveDerivation["pubKey"] as String)),
network: _network,
overridePrefix: _network.bech32!)
.data;
for (String tx in addressTxid[addressesP2WPKH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
receiveDerivation["wif"] as String,
network: _network,
), ),
}; network: _network,
} overridePrefix: _network.bech32!,
} else { ).data;
// if its not a receive, check change redeemScript = p2wpkh.output;
final changeDerivation = changeDerivations[addressesP2WPKH[i]]; data = P2SH(
// if a match exists it will not be null data: PaymentData(redeem: p2wpkh),
if (changeDerivation != null) { network: _network,
final data = P2WPKH( ).data;
data: PaymentData( break;
pubkey: Format.stringToUint8List(
changeDerivation["pubKey"] as String)),
network: _network,
overridePrefix: _network.bech32!)
.data;
for (String tx in addressTxid[addressesP2WPKH[i]]!) { case DerivePathType.bip84:
results[tx] = { data = P2WPKH(
"output": data.output, data: PaymentData(
"keyPair": ECPair.fromWIF( pubkey: Format.stringToUint8List(pubKey),
changeDerivation["wif"] as String, ),
network: _network, network: _network,
), overridePrefix: _network.bech32!,
}; ).data;
} redeemScript = null;
} break;
default:
throw Exception("DerivePathType unsupported");
} }
final keyPair = ECPair.fromWIF(
wif,
network: _network,
);
sd.redeemScript = redeemScript;
sd.output = data.output;
sd.keyPair = keyPair;
} }
} }
return results; return signingData;
} catch (e, s) { } catch (e, s) {
Logging.instance Logging.instance
.log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error); .log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error);
@ -2845,8 +2741,7 @@ class LitecoinWallet extends CoinServiceAPI
/// Builds and signs a transaction /// Builds and signs a transaction
Future<Map<String, dynamic>> buildTransaction({ Future<Map<String, dynamic>> buildTransaction({
required List<isar_models.UTXO> utxosToUse, required List<SigningData> utxoSigningData,
required Map<String, dynamic> utxoSigningData,
required List<String> recipients, required List<String> recipients,
required List<int> satoshiAmounts, required List<int> satoshiAmounts,
}) async { }) async {
@ -2857,10 +2752,15 @@ class LitecoinWallet extends CoinServiceAPI
txb.setVersion(1); txb.setVersion(1);
// Add transaction inputs // Add transaction inputs
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxosToUse[i].txid; final txid = utxoSigningData[i].utxo.txid;
txb.addInput(txid, utxosToUse[i].vout, null, txb.addInput(
utxoSigningData[txid]["output"] as Uint8List, _network.bech32!); txid,
utxoSigningData[i].utxo.vout,
null,
utxoSigningData[i].output!,
_network.bech32!,
);
} }
// Add transaction output // Add transaction output
@ -2870,14 +2770,14 @@ class LitecoinWallet extends CoinServiceAPI
try { try {
// Sign the transaction accordingly // Sign the transaction accordingly
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxosToUse[i].txid;
txb.sign( txb.sign(
vin: i, vin: i,
keyPair: utxoSigningData[txid]["keyPair"] as ECPair, keyPair: utxoSigningData[i].keyPair!,
witnessValue: utxosToUse[i].value, witnessValue: utxoSigningData[i].utxo.value,
redeemScript: utxoSigningData[txid]["redeemScript"] as Uint8List?, redeemScript: utxoSigningData[i].redeemScript,
overridePrefix: _network.bech32!); overridePrefix: _network.bech32!,
);
} }
} catch (e, s) { } catch (e, s) {
Logging.instance.log("Caught exception while signing transaction: $e\n$s", Logging.instance.log("Caught exception while signing transaction: $e\n$s",

View file

@ -17,6 +17,7 @@ import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models; import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
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/services/coins/coin_service.dart'; import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
@ -1049,13 +1050,15 @@ class NamecoinWallet extends CoinServiceAPI
isSendAll = true; isSendAll = true;
} }
final bool coinControl = utxos != null;
final txData = await coinSelection( final txData = await coinSelection(
satoshiAmountToSend: satoshiAmount, satoshiAmountToSend: satoshiAmount,
selectedTxFeeRate: rate, selectedTxFeeRate: rate,
recipientAddress: address, recipientAddress: address,
isSendAll: isSendAll, isSendAll: isSendAll,
utxos: utxos?.toList(), utxos: utxos?.toList(),
coinControl: utxos is List<isar_models.UTXO>, coinControl: coinControl,
); );
Logging.instance.log("prepare send: $txData", level: LogLevel.Info); Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
@ -2307,6 +2310,7 @@ class NamecoinWallet extends CoinServiceAPI
} else { } else {
satoshisBeingUsed = spendableSatoshiValue; satoshisBeingUsed = spendableSatoshiValue;
utxoObjectsToUse = spendableOutputs; utxoObjectsToUse = spendableOutputs;
inputsBeingConsumed = spendableOutputs.length;
} }
Logging.instance Logging.instance
@ -2328,7 +2332,6 @@ class NamecoinWallet extends CoinServiceAPI
.log("Attempting to send all $coin", level: LogLevel.Info); .log("Attempting to send all $coin", level: LogLevel.Info);
final int vSizeForOneOutput = (await buildTransaction( final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [recipientAddress], recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1], satoshiAmounts: [satoshisBeingUsed - 1],
@ -2346,7 +2349,6 @@ class NamecoinWallet extends CoinServiceAPI
final int amount = satoshiAmountToSend - feeForOneOutput; final int amount = satoshiAmountToSend - feeForOneOutput;
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: [amount], satoshiAmounts: [amount],
@ -2357,19 +2359,17 @@ class NamecoinWallet extends CoinServiceAPI
"recipientAmt": amount, "recipientAmt": amount,
"fee": feeForOneOutput, "fee": feeForOneOutput,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
final int vSizeForOneOutput = (await buildTransaction( final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [recipientAddress], recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1], satoshiAmounts: [satoshisBeingUsed - 1],
))["vSize"] as int; ))["vSize"] as int;
final int vSizeForTwoOutPuts = (await buildTransaction( final int vSizeForTwoOutPuts = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [ recipients: [
recipientAddress, recipientAddress,
@ -2435,7 +2435,6 @@ class NamecoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info); .log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2463,7 +2462,6 @@ class NamecoinWallet extends CoinServiceAPI
Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs', Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs',
level: LogLevel.Info); level: LogLevel.Info);
txn = await buildTransaction( txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2476,7 +2474,7 @@ class NamecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": feeBeingPaid, "fee": feeBeingPaid,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} else { } else {
@ -2493,7 +2491,6 @@ class NamecoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2504,7 +2501,7 @@ class NamecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend, "fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
@ -2523,7 +2520,6 @@ class NamecoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2534,7 +2530,7 @@ class NamecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend, "fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
@ -2553,7 +2549,6 @@ class NamecoinWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2564,7 +2559,7 @@ class NamecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": feeForOneOutput, "fee": feeForOneOutput,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} else { } else {
@ -2590,248 +2585,144 @@ class NamecoinWallet extends CoinServiceAPI
} }
} }
Future<Map<String, dynamic>> fetchBuildTxData( Future<List<SigningData>> fetchBuildTxData(
List<isar_models.UTXO> utxosToUse, List<isar_models.UTXO> utxosToUse,
) async { ) async {
// return data // return data
Map<String, dynamic> results = {}; List<SigningData> signingData = [];
Map<String, List<String>> addressTxid = {};
// addresses to check
List<String> addressesP2PKH = [];
List<String> addressesP2SH = [];
List<String> addressesP2WPKH = [];
Logging.instance.log("utxos: $utxosToUse", level: LogLevel.Info);
try { try {
// Populating the addresses to check // Populating the addresses to check
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid; if (utxosToUse[i].address == null) {
final tx = await _cachedElectrumXClient.getTransaction( final txid = utxosToUse[i].txid;
txHash: txid, final tx = await _cachedElectrumXClient.getTransaction(
coin: coin, txHash: txid,
); coin: coin,
Logging.instance.log("tx: ${json.encode(tx)}", );
level: LogLevel.Info, printFullLength: true); for (final output in tx["vout"] as List) {
final n = output["n"];
for (final output in tx["vout"] as List) { if (n != null && n == utxosToUse[i].vout) {
final n = output["n"]; utxosToUse[i] = utxosToUse[i].copyWith(
if (n != null && n == utxosToUse[i].vout) { address: output["scriptPubKey"]?["addresses"]?[0] as String? ??
final address = output["scriptPubKey"]["address"] as String; output["scriptPubKey"]["address"] as String,
if (!addressTxid.containsKey(address)) { );
addressTxid[address] = <String>[];
}
(addressTxid[address] as List).add(txid);
switch (addressType(address: address)) {
case DerivePathType.bip44:
addressesP2PKH.add(address);
break;
case DerivePathType.bip49:
addressesP2SH.add(address);
break;
case DerivePathType.bip84:
addressesP2WPKH.add(address);
break;
default:
throw Exception("DerivePathType unsupported");
} }
} }
} }
final derivePathType = addressType(address: utxosToUse[i].address!);
signingData.add(
SigningData(
derivePathType: derivePathType,
utxo: utxosToUse[i],
),
);
} }
// p2pkh / bip44 Map<DerivePathType, Map<String, dynamic>> receiveDerivations = {};
final p2pkhLength = addressesP2PKH.length; Map<DerivePathType, Map<String, dynamic>> changeDerivations = {};
if (p2pkhLength > 0) {
final receiveDerivations = await _fetchDerivations(
chain: 0,
derivePathType: DerivePathType.bip44,
);
final changeDerivations = await _fetchDerivations(
chain: 1,
derivePathType: DerivePathType.bip44,
);
for (int i = 0; i < p2pkhLength; i++) {
// receives
final receiveDerivation = receiveDerivations[addressesP2PKH[i]];
// if a match exists it will not be null
if (receiveDerivation != null) {
final data = P2PKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
receiveDerivation["pubKey"] as String)),
network: _network,
).data;
for (String tx in addressTxid[addressesP2PKH[i]]!) { for (final sd in signingData) {
results[tx] = { String? pubKey;
"output": data.output, String? wif;
"keyPair": ECPair.fromWIF(
receiveDerivation["wif"] as String, // fetch receiving derivations if null
network: _network, receiveDerivations[sd.derivePathType] ??= await _fetchDerivations(
), chain: 0,
}; derivePathType: sd.derivePathType,
} );
} else { final receiveDerivation =
// if its not a receive, check change receiveDerivations[sd.derivePathType]![sd.utxo.address!];
final changeDerivation = changeDerivations[addressesP2PKH[i]];
// if a match exists it will not be null if (receiveDerivation != null) {
if (changeDerivation != null) { pubKey = receiveDerivation["pubKey"] as String;
final data = P2PKH( wif = receiveDerivation["wif"] as String;
} else {
// fetch change derivations if null
changeDerivations[sd.derivePathType] ??= await _fetchDerivations(
chain: 1,
derivePathType: sd.derivePathType,
);
final changeDerivation =
changeDerivations[sd.derivePathType]![sd.utxo.address!];
if (changeDerivation != null) {
pubKey = changeDerivation["pubKey"] as String;
wif = changeDerivation["wif"] as String;
}
}
if (wif == null || pubKey == null) {
final address = await db.getAddress(walletId, sd.utxo.address!);
if (address?.derivationPath != null) {
final node = await Bip32Utils.getBip32Node(
(await mnemonicString)!,
(await mnemonicPassphrase)!,
_network,
address!.derivationPath!.value,
);
wif = node.toWIF();
pubKey = Format.uint8listToString(node.publicKey);
}
}
if (wif != null && pubKey != null) {
final PaymentData data;
final Uint8List? redeemScript;
switch (sd.derivePathType) {
case DerivePathType.bip44:
data = P2PKH(
data: PaymentData( data: PaymentData(
pubkey: Format.stringToUint8List( pubkey: Format.stringToUint8List(pubKey),
changeDerivation["pubKey"] as String)), ),
network: _network, network: _network,
).data; ).data;
redeemScript = null;
break;
for (String tx in addressTxid[addressesP2PKH[i]]!) { case DerivePathType.bip49:
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
),
};
}
}
}
}
}
// p2sh / bip49
final p2shLength = addressesP2SH.length;
if (p2shLength > 0) {
final receiveDerivations = await _fetchDerivations(
chain: 0,
derivePathType: DerivePathType.bip49,
);
final changeDerivations = await _fetchDerivations(
chain: 1,
derivePathType: DerivePathType.bip49,
);
for (int i = 0; i < p2shLength; i++) {
// receives
final receiveDerivation = receiveDerivations[addressesP2SH[i]];
// if a match exists it will not be null
if (receiveDerivation != null) {
final p2wpkh = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
receiveDerivation["pubKey"] as String)),
network: _network,
overridePrefix: namecoin.bech32!)
.data;
final redeemScript = p2wpkh.output;
final data =
P2SH(data: PaymentData(redeem: p2wpkh), network: _network).data;
for (String tx in addressTxid[addressesP2SH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
receiveDerivation["wif"] as String,
network: _network,
),
"redeemScript": redeemScript,
};
}
} else {
// if its not a receive, check change
final changeDerivation = changeDerivations[addressesP2SH[i]];
// if a match exists it will not be null
if (changeDerivation != null) {
final p2wpkh = P2WPKH( final p2wpkh = P2WPKH(
data: PaymentData( data: PaymentData(
pubkey: Format.stringToUint8List( pubkey: Format.stringToUint8List(pubKey),
changeDerivation["pubKey"] as String)),
network: _network,
overridePrefix: namecoin.bech32!)
.data;
final redeemScript = p2wpkh.output;
final data =
P2SH(data: PaymentData(redeem: p2wpkh), network: _network)
.data;
for (String tx in addressTxid[addressesP2SH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
),
"redeemScript": redeemScript,
};
}
}
}
}
}
// p2wpkh / bip84
final p2wpkhLength = addressesP2WPKH.length;
if (p2wpkhLength > 0) {
final receiveDerivations = await _fetchDerivations(
chain: 0,
derivePathType: DerivePathType.bip84,
);
final changeDerivations = await _fetchDerivations(
chain: 1,
derivePathType: DerivePathType.bip84,
);
for (int i = 0; i < p2wpkhLength; i++) {
// receives
final receiveDerivation = receiveDerivations[addressesP2WPKH[i]];
// if a match exists it will not be null
if (receiveDerivation != null) {
final data = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
receiveDerivation["pubKey"] as String)),
network: _network,
overridePrefix: namecoin.bech32!)
.data;
for (String tx in addressTxid[addressesP2WPKH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
receiveDerivation["wif"] as String,
network: _network,
), ),
}; network: _network,
} overridePrefix: _network.bech32!,
} else { ).data;
// if its not a receive, check change redeemScript = p2wpkh.output;
final changeDerivation = changeDerivations[addressesP2WPKH[i]]; data = P2SH(data: PaymentData(redeem: p2wpkh), network: _network)
// if a match exists it will not be null
if (changeDerivation != null) {
final data = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
changeDerivation["pubKey"] as String)),
network: _network,
overridePrefix: namecoin.bech32!)
.data; .data;
break;
for (String tx in addressTxid[addressesP2WPKH[i]]!) { case DerivePathType.bip84:
results[tx] = { data = P2WPKH(
"output": data.output, data: PaymentData(
"keyPair": ECPair.fromWIF( pubkey: Format.stringToUint8List(pubKey),
changeDerivation["wif"] as String, ),
network: _network, network: _network,
), overridePrefix: _network.bech32!,
}; ).data;
} redeemScript = null;
} break;
default:
throw Exception("DerivePathType unsupported");
} }
final keyPair = ECPair.fromWIF(
wif,
network: _network,
);
sd.redeemScript = redeemScript;
sd.output = data.output;
sd.keyPair = keyPair;
} }
} }
return results; return signingData;
} catch (e, s) { } catch (e, s) {
Logging.instance Logging.instance
.log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error); .log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error);
@ -2841,8 +2732,7 @@ class NamecoinWallet extends CoinServiceAPI
/// Builds and signs a transaction /// Builds and signs a transaction
Future<Map<String, dynamic>> buildTransaction({ Future<Map<String, dynamic>> buildTransaction({
required List<isar_models.UTXO> utxosToUse, required List<SigningData> utxoSigningData,
required Map<String, dynamic> utxoSigningData,
required List<String> recipients, required List<String> recipients,
required List<int> satoshiAmounts, required List<int> satoshiAmounts,
}) async { }) async {
@ -2853,10 +2743,15 @@ class NamecoinWallet extends CoinServiceAPI
txb.setVersion(2); txb.setVersion(2);
// Add transaction inputs // Add transaction inputs
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxosToUse[i].txid; final txid = utxoSigningData[i].utxo.txid;
txb.addInput(txid, utxosToUse[i].vout, null, txb.addInput(
utxoSigningData[txid]["output"] as Uint8List, namecoin.bech32!); txid,
utxoSigningData[i].utxo.vout,
null,
utxoSigningData[i].output!,
_network.bech32!,
);
} }
// Add transaction output // Add transaction output
@ -2866,14 +2761,15 @@ class NamecoinWallet extends CoinServiceAPI
try { try {
// Sign the transaction accordingly // Sign the transaction accordingly
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxosToUse[i].txid; final txid = utxoSigningData[i].utxo.txid;
txb.sign( txb.addInput(
vin: i, txid,
keyPair: utxoSigningData[txid]["keyPair"] as ECPair, utxoSigningData[i].utxo.vout,
witnessValue: utxosToUse[i].value, null,
redeemScript: utxoSigningData[txid]["redeemScript"] as Uint8List?, utxoSigningData[i].output!,
overridePrefix: namecoin.bech32!); _network.bech32!,
);
} }
} catch (e, s) { } catch (e, s) {
Logging.instance.log("Caught exception while signing transaction: $e\n$s", Logging.instance.log("Caught exception while signing transaction: $e\n$s",

View file

@ -17,6 +17,7 @@ import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models; import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
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/services/coins/coin_service.dart'; import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
@ -976,13 +977,15 @@ class ParticlWallet extends CoinServiceAPI
isSendAll = true; isSendAll = true;
} }
final bool coinControl = utxos != null;
final txData = await coinSelection( final txData = await coinSelection(
satoshiAmountToSend: satoshiAmount, satoshiAmountToSend: satoshiAmount,
selectedTxFeeRate: rate, selectedTxFeeRate: rate,
recipientAddress: address, recipientAddress: address,
isSendAll: isSendAll, isSendAll: isSendAll,
utxos: utxos?.toList(), utxos: utxos?.toList(),
coinControl: utxos is List<isar_models.UTXO>, coinControl: coinControl,
); );
Logging.instance.log("prepare send: $txData", level: LogLevel.Info); Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
@ -2468,6 +2471,7 @@ class ParticlWallet extends CoinServiceAPI
} else { } else {
satoshisBeingUsed = spendableSatoshiValue; satoshisBeingUsed = spendableSatoshiValue;
utxoObjectsToUse = spendableOutputs; utxoObjectsToUse = spendableOutputs;
inputsBeingConsumed = spendableOutputs.length;
} }
Logging.instance Logging.instance
@ -2489,7 +2493,6 @@ class ParticlWallet extends CoinServiceAPI
.log("Attempting to send all $coin", level: LogLevel.Info); .log("Attempting to send all $coin", level: LogLevel.Info);
final int vSizeForOneOutput = (await buildTransaction( final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [recipientAddress], recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1], satoshiAmounts: [satoshisBeingUsed - 1],
@ -2507,7 +2510,6 @@ class ParticlWallet extends CoinServiceAPI
final int amount = satoshiAmountToSend - feeForOneOutput; final int amount = satoshiAmountToSend - feeForOneOutput;
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: [amount], satoshiAmounts: [amount],
@ -2518,19 +2520,17 @@ class ParticlWallet extends CoinServiceAPI
"recipientAmt": amount, "recipientAmt": amount,
"fee": feeForOneOutput, "fee": feeForOneOutput,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
final int vSizeForOneOutput = (await buildTransaction( final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [recipientAddress], recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1], satoshiAmounts: [satoshisBeingUsed - 1],
))["vSize"] as int; ))["vSize"] as int;
final int vSizeForTwoOutPuts = (await buildTransaction( final int vSizeForTwoOutPuts = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: [ recipients: [
recipientAddress, recipientAddress,
@ -2596,7 +2596,6 @@ class ParticlWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info); .log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2624,7 +2623,6 @@ class ParticlWallet extends CoinServiceAPI
Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs', Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs',
level: LogLevel.Info); level: LogLevel.Info);
txn = await buildTransaction( txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2637,7 +2635,7 @@ class ParticlWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": feeBeingPaid, "fee": feeBeingPaid,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} else { } else {
@ -2654,7 +2652,6 @@ class ParticlWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2665,7 +2662,7 @@ class ParticlWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend, "fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
@ -2684,7 +2681,6 @@ class ParticlWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2695,7 +2691,7 @@ class ParticlWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend, "fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
@ -2714,7 +2710,6 @@ class ParticlWallet extends CoinServiceAPI
Logging.instance Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info); .log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction( dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
recipients: recipientsArray, recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray, satoshiAmounts: recipientsAmtArray,
@ -2725,7 +2720,7 @@ class ParticlWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0], "recipientAmt": recipientsAmtArray[0],
"fee": feeForOneOutput, "fee": feeForOneOutput,
"vSize": txn["vSize"], "vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse, "usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} else { } else {
@ -2751,168 +2746,130 @@ class ParticlWallet extends CoinServiceAPI
} }
} }
Future<Map<String, dynamic>> fetchBuildTxData( Future<List<SigningData>> fetchBuildTxData(
List<isar_models.UTXO> utxosToUse, List<isar_models.UTXO> utxosToUse,
) async { ) async {
// return data // return data
Map<String, dynamic> results = {}; List<SigningData> signingData = [];
Map<String, List<String>> addressTxid = {};
// addresses to check
List<String> addressesP2PKH = [];
List<String> addressesP2WPKH = [];
try { try {
// Populating the addresses to check // Populating the addresses to check
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid; if (utxosToUse[i].address == null) {
final tx = await _cachedElectrumXClient.getTransaction( final txid = utxosToUse[i].txid;
txHash: txid, final tx = await _cachedElectrumXClient.getTransaction(
coin: coin, txHash: txid,
); coin: coin,
);
for (final output in tx["vout"] as List) { for (final output in tx["vout"] as List) {
final n = output["n"]; final n = output["n"];
if (n != null && n == utxosToUse[i].vout) { if (n != null && n == utxosToUse[i].vout) {
final address = output["scriptPubKey"]["addresses"][0] as String; utxosToUse[i] = utxosToUse[i].copyWith(
if (!addressTxid.containsKey(address)) { address: output["scriptPubKey"]?["addresses"]?[0] as String? ??
addressTxid[address] = <String>[]; output["scriptPubKey"]["address"] as String,
} );
(addressTxid[address] as List).add(txid);
switch (addressType(address: address)) {
case DerivePathType.bip44:
addressesP2PKH.add(address);
break;
case DerivePathType.bip84:
addressesP2WPKH.add(address);
break;
default:
throw Exception(
"DerivePathType ${addressType(address: address)} not supported");
} }
} }
} }
final derivePathType = addressType(address: utxosToUse[i].address!);
signingData.add(
SigningData(
derivePathType: derivePathType,
utxo: utxosToUse[i],
),
);
} }
// p2pkh / bip44 Map<DerivePathType, Map<String, dynamic>> receiveDerivations = {};
final p2pkhLength = addressesP2PKH.length; Map<DerivePathType, Map<String, dynamic>> changeDerivations = {};
if (p2pkhLength > 0) {
final receiveDerivations = await _fetchDerivations(
chain: 0,
derivePathType: DerivePathType.bip44,
);
final changeDerivations = await _fetchDerivations(
chain: 1,
derivePathType: DerivePathType.bip44,
);
for (int i = 0; i < p2pkhLength; i++) {
// receives
final receiveDerivation = receiveDerivations[addressesP2PKH[i]];
// if a match exists it will not be null
if (receiveDerivation != null) {
final data = P2PKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
receiveDerivation["pubKey"] as String)),
network: _network,
).data;
for (String tx in addressTxid[addressesP2PKH[i]]!) { for (final sd in signingData) {
results[tx] = { String? pubKey;
"output": data.output, String? wif;
"keyPair": ECPair.fromWIF(
receiveDerivation["wif"] as String, // fetch receiving derivations if null
network: _network, receiveDerivations[sd.derivePathType] ??= await _fetchDerivations(
), chain: 0,
}; derivePathType: sd.derivePathType,
} );
} else { final receiveDerivation =
// if its not a receive, check change receiveDerivations[sd.derivePathType]![sd.utxo.address!];
final changeDerivation = changeDerivations[addressesP2PKH[i]];
// if a match exists it will not be null if (receiveDerivation != null) {
if (changeDerivation != null) { pubKey = receiveDerivation["pubKey"] as String;
final data = P2PKH( wif = receiveDerivation["wif"] as String;
} else {
// fetch change derivations if null
changeDerivations[sd.derivePathType] ??= await _fetchDerivations(
chain: 1,
derivePathType: sd.derivePathType,
);
final changeDerivation =
changeDerivations[sd.derivePathType]![sd.utxo.address!];
if (changeDerivation != null) {
pubKey = changeDerivation["pubKey"] as String;
wif = changeDerivation["wif"] as String;
}
}
if (wif == null || pubKey == null) {
final address = await db.getAddress(walletId, sd.utxo.address!);
if (address?.derivationPath != null) {
final node = await Bip32Utils.getBip32Node(
(await mnemonicString)!,
(await mnemonicPassphrase)!,
_network,
address!.derivationPath!.value,
);
wif = node.toWIF();
pubKey = Format.uint8listToString(node.publicKey);
}
}
if (wif != null && pubKey != null) {
final PaymentData data;
final Uint8List? redeemScript;
switch (sd.derivePathType) {
case DerivePathType.bip44:
data = P2PKH(
data: PaymentData( data: PaymentData(
pubkey: Format.stringToUint8List( pubkey: Format.stringToUint8List(pubKey),
changeDerivation["pubKey"] as String)), ),
network: _network, network: _network,
).data; ).data;
redeemScript = null;
break;
for (String tx in addressTxid[addressesP2PKH[i]]!) { case DerivePathType.bip84:
results[tx] = { data = P2WPKH(
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
),
};
}
}
}
}
}
// p2wpkh / bip84
final p2wpkhLength = addressesP2WPKH.length;
if (p2wpkhLength > 0) {
final receiveDerivations = await _fetchDerivations(
chain: 0,
derivePathType: DerivePathType.bip84,
);
final changeDerivations = await _fetchDerivations(
chain: 1,
derivePathType: DerivePathType.bip84,
);
for (int i = 0; i < p2wpkhLength; i++) {
// receives
final receiveDerivation = receiveDerivations[addressesP2WPKH[i]];
// if a match exists it will not be null
if (receiveDerivation != null) {
final data = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
receiveDerivation["pubKey"] as String)),
network: _network,
).data;
for (String tx in addressTxid[addressesP2WPKH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
receiveDerivation["wif"] as String,
network: _network,
),
};
}
} else {
// if its not a receive, check change
final changeDerivation = changeDerivations[addressesP2WPKH[i]];
// if a match exists it will not be null
if (changeDerivation != null) {
final data = P2WPKH(
data: PaymentData( data: PaymentData(
pubkey: Format.stringToUint8List( pubkey: Format.stringToUint8List(pubKey),
changeDerivation["pubKey"] as String)), ),
network: _network, network: _network,
).data; ).data;
redeemScript = null;
break;
for (String tx in addressTxid[addressesP2WPKH[i]]!) { default:
results[tx] = { throw Exception("DerivePathType unsupported");
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
),
};
}
}
} }
final keyPair = ECPair.fromWIF(
wif,
network: _network,
);
sd.redeemScript = redeemScript;
sd.output = data.output;
sd.keyPair = keyPair;
} }
} }
Logging.instance.log("FETCHED TX BUILD DATA IS -----$results",
level: LogLevel.Info, printFullLength: true); return signingData;
return results;
} catch (e, s) { } catch (e, s) {
Logging.instance Logging.instance
.log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error); .log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error);
@ -2922,8 +2879,7 @@ class ParticlWallet extends CoinServiceAPI
/// Builds and signs a transaction /// Builds and signs a transaction
Future<Map<String, dynamic>> buildTransaction({ Future<Map<String, dynamic>> buildTransaction({
required List<isar_models.UTXO> utxosToUse, required List<SigningData> utxoSigningData,
required Map<String, dynamic> utxoSigningData,
required List<String> recipients, required List<String> recipients,
required List<int> satoshiAmounts, required List<int> satoshiAmounts,
}) async { }) async {
@ -2937,11 +2893,15 @@ class ParticlWallet extends CoinServiceAPI
txb.setVersion(160); txb.setVersion(160);
// Add transaction inputs // Add transaction inputs
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxosToUse[i].txid; final txid = utxoSigningData[i].utxo.txid;
txb.addInput(
txb.addInput(txid, utxosToUse[i].vout, null, txid,
utxoSigningData[txid]["output"] as Uint8List, ''); utxoSigningData[i].utxo.vout,
null,
utxoSigningData[i].output!,
'',
);
} }
// Add transaction output // Add transaction output
@ -2951,13 +2911,13 @@ class ParticlWallet extends CoinServiceAPI
try { try {
// Sign the transaction accordingly // Sign the transaction accordingly
for (var i = 0; i < utxosToUse.length; i++) { for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxosToUse[i].txid;
txb.sign( txb.sign(
vin: i, vin: i,
keyPair: utxoSigningData[txid]["keyPair"] as ECPair, keyPair: utxoSigningData[i].keyPair!,
witnessValue: utxosToUse[i].value, witnessValue: utxoSigningData[i].utxo.value,
redeemScript: utxoSigningData[txid]["redeemScript"] as Uint8List?); redeemScript: utxoSigningData[i].redeemScript,
);
} }
} catch (e, s) { } catch (e, s) {
Logging.instance.log("Caught exception while signing transaction: $e\n$s", Logging.instance.log("Caught exception while signing transaction: $e\n$s",

View file

@ -15,6 +15,7 @@ import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/exceptions/wallet/insufficient_balance_exception.dart'; import 'package:stackwallet/exceptions/wallet/insufficient_balance_exception.dart';
import 'package:stackwallet/exceptions/wallet/paynym_send_exception.dart'; import 'package:stackwallet/exceptions/wallet/paynym_send_exception.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/utilities/bip32_utils.dart'; import 'package:stackwallet/utilities/bip32_utils.dart';
import 'package:stackwallet/utilities/bip47_utils.dart'; import 'package:stackwallet/utilities/bip47_utils.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -57,7 +58,7 @@ mixin PaynymWalletInterface {
late final Future<int> Function({ late final Future<int> Function({
required String address, required String address,
}) _getTxCount; }) _getTxCount;
late final Future<Map<String, dynamic>> Function( late final Future<List<SigningData>> Function(
List<UTXO> utxosToUse, List<UTXO> utxosToUse,
) _fetchBuildTxData; ) _fetchBuildTxData;
late final Future<void> Function() _refresh; late final Future<void> Function() _refresh;
@ -100,7 +101,7 @@ mixin PaynymWalletInterface {
required String address, required String address,
}) })
getTxCount, getTxCount,
required Future<Map<String, dynamic>> Function( required Future<List<SigningData>> Function(
List<UTXO> utxosToUse, List<UTXO> utxosToUse,
) )
fetchBuildTxData, fetchBuildTxData,
@ -455,7 +456,6 @@ mixin PaynymWalletInterface {
final int vSizeForNoChange = (await _createNotificationTx( final int vSizeForNoChange = (await _createNotificationTx(
targetPaymentCodeString: targetPaymentCodeString, targetPaymentCodeString: targetPaymentCodeString,
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
change: 0, change: 0,
dustLimit: dustLimit:
@ -465,7 +465,6 @@ mixin PaynymWalletInterface {
final int vSizeForWithChange = (await _createNotificationTx( final int vSizeForWithChange = (await _createNotificationTx(
targetPaymentCodeString: targetPaymentCodeString, targetPaymentCodeString: targetPaymentCodeString,
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
change: satoshisBeingUsed - amountToSend, change: satoshisBeingUsed - amountToSend,
)) ))
@ -503,7 +502,6 @@ mixin PaynymWalletInterface {
feeForWithChange) { feeForWithChange) {
var txn = await _createNotificationTx( var txn = await _createNotificationTx(
targetPaymentCodeString: targetPaymentCodeString, targetPaymentCodeString: targetPaymentCodeString,
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
change: changeAmount, change: changeAmount,
); );
@ -516,7 +514,6 @@ mixin PaynymWalletInterface {
feeBeingPaid += 1; feeBeingPaid += 1;
txn = await _createNotificationTx( txn = await _createNotificationTx(
targetPaymentCodeString: targetPaymentCodeString, targetPaymentCodeString: targetPaymentCodeString,
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
change: changeAmount, change: changeAmount,
); );
@ -528,6 +525,7 @@ mixin PaynymWalletInterface {
"amount": amountToSend, "amount": amountToSend,
"fee": feeBeingPaid, "fee": feeBeingPaid,
"vSize": txn.item2, "vSize": txn.item2,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} else { } else {
@ -535,7 +533,6 @@ mixin PaynymWalletInterface {
// than the dust limit. Try without change // than the dust limit. Try without change
final txn = await _createNotificationTx( final txn = await _createNotificationTx(
targetPaymentCodeString: targetPaymentCodeString, targetPaymentCodeString: targetPaymentCodeString,
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
change: 0, change: 0,
); );
@ -548,6 +545,7 @@ mixin PaynymWalletInterface {
"amount": amountToSend, "amount": amountToSend,
"fee": feeBeingPaid, "fee": feeBeingPaid,
"vSize": txn.item2, "vSize": txn.item2,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} }
@ -556,7 +554,6 @@ mixin PaynymWalletInterface {
// build without change here // build without change here
final txn = await _createNotificationTx( final txn = await _createNotificationTx(
targetPaymentCodeString: targetPaymentCodeString, targetPaymentCodeString: targetPaymentCodeString,
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData, utxoSigningData: utxoSigningData,
change: 0, change: 0,
); );
@ -569,6 +566,7 @@ mixin PaynymWalletInterface {
"amount": amountToSend, "amount": amountToSend,
"fee": feeBeingPaid, "fee": feeBeingPaid,
"vSize": txn.item2, "vSize": txn.item2,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
}; };
return transactionObject; return transactionObject;
} else { } else {
@ -594,8 +592,7 @@ mixin PaynymWalletInterface {
// equal to its vSize // equal to its vSize
Future<Tuple2<String, int>> _createNotificationTx({ Future<Tuple2<String, int>> _createNotificationTx({
required String targetPaymentCodeString, required String targetPaymentCodeString,
required List<UTXO> utxosToUse, required List<SigningData> utxoSigningData,
required Map<String, dynamic> utxoSigningData,
required int change, required int change,
int? dustLimit, int? dustLimit,
}) async { }) async {
@ -604,7 +601,7 @@ mixin PaynymWalletInterface {
PaymentCode.fromPaymentCode(targetPaymentCodeString, _network); PaymentCode.fromPaymentCode(targetPaymentCodeString, _network);
final myCode = await getPaymentCode(DerivePathType.bip44); final myCode = await getPaymentCode(DerivePathType.bip44);
final utxo = utxosToUse.first; final utxo = utxoSigningData.first.utxo;
final txPoint = utxo.txid.fromHex.reversed.toList(); final txPoint = utxo.txid.fromHex.reversed.toList();
final txPointIndex = utxo.vout; final txPointIndex = utxo.vout;
@ -613,8 +610,7 @@ mixin PaynymWalletInterface {
final buffer = rev.buffer.asByteData(); final buffer = rev.buffer.asByteData();
buffer.setUint32(txPoint.length, txPointIndex, Endian.little); buffer.setUint32(txPoint.length, txPointIndex, Endian.little);
final myKeyPair = final myKeyPair = utxoSigningData.first.keyPair!;
utxoSigningData[utxo.txid]["keyPair"] as btc_dart.ECPair;
final S = SecretPoint( final S = SecretPoint(
myKeyPair.privateKey!, myKeyPair.privateKey!,
@ -642,17 +638,17 @@ mixin PaynymWalletInterface {
utxo.txid, utxo.txid,
txPointIndex, txPointIndex,
null, null,
utxoSigningData[utxo.txid]["output"] as Uint8List, utxoSigningData.first.output!,
); );
// add rest of possible inputs // add rest of possible inputs
for (var i = 1; i < utxosToUse.length; i++) { for (var i = 1; i < utxoSigningData.length; i++) {
final utxo = utxosToUse[i]; final utxo = utxoSigningData[i].utxo;
txb.addInput( txb.addInput(
utxo.txid, utxo.txid,
utxo.vout, utxo.vout,
null, null,
utxoSigningData[utxo.txid]["output"] as Uint8List, utxoSigningData[i].output!,
); );
} }
@ -675,18 +671,16 @@ mixin PaynymWalletInterface {
vin: 0, vin: 0,
keyPair: myKeyPair, keyPair: myKeyPair,
witnessValue: utxo.value, witnessValue: utxo.value,
witnessScript: utxoSigningData[utxo.txid]["redeemScript"] as Uint8List?, witnessScript: utxoSigningData.first.redeemScript,
); );
// sign rest of possible inputs // sign rest of possible inputs
for (var i = 1; i < utxosToUse.length; i++) { for (var i = 1; i < utxoSigningData.length; i++) {
final txid = utxosToUse[i].txid;
txb.sign( txb.sign(
vin: i, vin: i,
keyPair: utxoSigningData[txid]["keyPair"] as btc_dart.ECPair, keyPair: utxoSigningData[i].keyPair!,
witnessValue: utxosToUse[i].value, witnessValue: utxoSigningData[i].utxo.value,
witnessScript: witnessScript: utxoSigningData[i].redeemScript,
utxoSigningData[utxo.txid]["redeemScript"] as Uint8List?,
); );
} }

View file

@ -21,20 +21,24 @@ class StackDialogBase extends StatelessWidget {
mainAxisAlignment: mainAxisAlignment:
!Util.isDesktop ? MainAxisAlignment.end : MainAxisAlignment.center, !Util.isDesktop ? MainAxisAlignment.end : MainAxisAlignment.center,
children: [ children: [
Material( Flexible(
borderRadius: BorderRadius.circular( child: SingleChildScrollView(
20, child: Material(
),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.popupBG,
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
20, 20,
), ),
), child: Container(
child: Padding( decoration: BoxDecoration(
padding: padding, color: Theme.of(context).extension<StackColors>()!.popupBG,
child: child, borderRadius: BorderRadius.circular(
20,
),
),
child: Padding(
padding: padding,
child: child,
),
),
), ),
), ),
), ),

View file

@ -12,7 +12,7 @@ sudo apt install -y unzip pkg-config clang cmake ninja-build libgtk-3-dev
cd $DEVELOPMENT cd $DEVELOPMENT
git clone https://github.com/flutter/flutter.git git clone https://github.com/flutter/flutter.git
cd flutter cd flutter
git checkout 3.3.4 git checkout 3.7.6
export FLUTTER_DIR=$(pwd)/bin export FLUTTER_DIR=$(pwd)/bin
echo 'export PATH="$PATH:'${FLUTTER_DIR}'"' >> ~/.bashrc echo 'export PATH="$PATH:'${FLUTTER_DIR}'"' >> ~/.bashrc
source ~/.bashrc source ~/.bashrc

View file

@ -4,7 +4,7 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes // ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i22; import 'dart:async' as _i22;
import 'dart:typed_data' as _i28; import 'dart:typed_data' as _i29;
import 'dart:ui' as _i24; import 'dart:ui' as _i24;
import 'package:bip32/bip32.dart' as _i17; import 'package:bip32/bip32.dart' as _i17;
@ -20,19 +20,20 @@ import 'package:stackwallet/models/balance.dart' as _i12;
import 'package:stackwallet/models/isar/models/isar_models.dart' as _i16; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i16;
import 'package:stackwallet/models/node_model.dart' as _i25; import 'package:stackwallet/models/node_model.dart' as _i25;
import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i9; import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i9;
import 'package:stackwallet/models/signing_data.dart' as _i28;
import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart' as _i26; import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart' as _i26;
import 'package:stackwallet/services/coins/coin_service.dart' as _i19; import 'package:stackwallet/services/coins/coin_service.dart' as _i19;
import 'package:stackwallet/services/coins/manager.dart' as _i6; import 'package:stackwallet/services/coins/manager.dart' as _i6;
import 'package:stackwallet/services/locale_service.dart' as _i29; import 'package:stackwallet/services/locale_service.dart' as _i30;
import 'package:stackwallet/services/node_service.dart' as _i3; import 'package:stackwallet/services/node_service.dart' as _i3;
import 'package:stackwallet/services/transaction_notification_tracker.dart' import 'package:stackwallet/services/transaction_notification_tracker.dart'
as _i8; as _i8;
import 'package:stackwallet/services/wallets.dart' as _i20; import 'package:stackwallet/services/wallets.dart' as _i20;
import 'package:stackwallet/services/wallets_service.dart' as _i2; import 'package:stackwallet/services/wallets_service.dart' as _i2;
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i31; import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i32;
import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i21; import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i21;
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart' as _i27; import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart' as _i27;
import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i30; import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i31;
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart' import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
as _i7; as _i7;
import 'package:stackwallet/utilities/prefs.dart' as _i23; import 'package:stackwallet/utilities/prefs.dart' as _i23;
@ -1438,7 +1439,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
}, },
)); ));
@override @override
_i22.Future<Map<String, dynamic>> fetchBuildTxData( _i22.Future<List<_i28.SigningData>> fetchBuildTxData(
List<_i16.UTXO>? utxosToUse) => List<_i16.UTXO>? utxosToUse) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
@ -1446,12 +1447,11 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
[utxosToUse], [utxosToUse],
), ),
returnValue: returnValue:
_i22.Future<Map<String, dynamic>>.value(<String, dynamic>{}), _i22.Future<List<_i28.SigningData>>.value(<_i28.SigningData>[]),
) as _i22.Future<Map<String, dynamic>>); ) as _i22.Future<List<_i28.SigningData>>);
@override @override
_i22.Future<Map<String, dynamic>> buildTransaction({ _i22.Future<Map<String, dynamic>> buildTransaction({
required List<_i16.UTXO>? utxosToUse, required List<_i28.SigningData>? utxoSigningData,
required Map<String, dynamic>? utxoSigningData,
required List<String>? recipients, required List<String>? recipients,
required List<int>? satoshiAmounts, required List<int>? satoshiAmounts,
}) => }) =>
@ -1460,7 +1460,6 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
#buildTransaction, #buildTransaction,
[], [],
{ {
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData, #utxoSigningData: utxoSigningData,
#recipients: recipients, #recipients: recipients,
#satoshiAmounts: satoshiAmounts, #satoshiAmounts: satoshiAmounts,
@ -1714,7 +1713,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
})? })?
prepareSend, prepareSend,
required _i22.Future<int> Function({required String address})? getTxCount, required _i22.Future<int> Function({required String address})? getTxCount,
required _i22.Future<Map<String, dynamic>> Function(List<_i16.UTXO>)? required _i22.Future<List<_i28.SigningData>> Function(List<_i16.UTXO>)?
fetchBuildTxData, fetchBuildTxData,
required _i22.Future<void> Function()? refresh, required _i22.Future<void> Function()? refresh,
required _i22.Future<void> Function()? checkChangeAddressForTransactions, required _i22.Future<void> Function()? checkChangeAddressForTransactions,
@ -1866,14 +1865,14 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
)), )),
) as _i22.Future<_i18.PaymentCode>); ) as _i22.Future<_i18.PaymentCode>);
@override @override
_i22.Future<_i28.Uint8List> signWithNotificationKey(_i28.Uint8List? data) => _i22.Future<_i29.Uint8List> signWithNotificationKey(_i29.Uint8List? data) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
#signWithNotificationKey, #signWithNotificationKey,
[data], [data],
), ),
returnValue: _i22.Future<_i28.Uint8List>.value(_i28.Uint8List(0)), returnValue: _i22.Future<_i29.Uint8List>.value(_i29.Uint8List(0)),
) as _i22.Future<_i28.Uint8List>); ) as _i22.Future<_i29.Uint8List>);
@override @override
_i22.Future<String> signStringWithNotificationKey(String? data) => _i22.Future<String> signStringWithNotificationKey(String? data) =>
(super.noSuchMethod( (super.noSuchMethod(
@ -2209,7 +2208,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
/// A class which mocks [LocaleService]. /// A class which mocks [LocaleService].
/// ///
/// See the documentation for Mockito's code generation for more information. /// See the documentation for Mockito's code generation for more information.
class MockLocaleService extends _i1.Mock implements _i29.LocaleService { class MockLocaleService extends _i1.Mock implements _i30.LocaleService {
MockLocaleService() { MockLocaleService() {
_i1.throwOnMissingStub(this); _i1.throwOnMissingStub(this);
} }
@ -2327,12 +2326,12 @@ class MockPrefs extends _i1.Mock implements _i23.Prefs {
returnValueForMissingStub: null, returnValueForMissingStub: null,
); );
@override @override
_i30.SyncingType get syncType => (super.noSuchMethod( _i31.SyncingType get syncType => (super.noSuchMethod(
Invocation.getter(#syncType), Invocation.getter(#syncType),
returnValue: _i30.SyncingType.currentWalletOnly, returnValue: _i31.SyncingType.currentWalletOnly,
) as _i30.SyncingType); ) as _i31.SyncingType);
@override @override
set syncType(_i30.SyncingType? syncType) => super.noSuchMethod( set syncType(_i31.SyncingType? syncType) => super.noSuchMethod(
Invocation.setter( Invocation.setter(
#syncType, #syncType,
syncType, syncType,
@ -2465,12 +2464,12 @@ class MockPrefs extends _i1.Mock implements _i23.Prefs {
returnValueForMissingStub: null, returnValueForMissingStub: null,
); );
@override @override
_i31.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( _i32.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod(
Invocation.getter(#backupFrequencyType), Invocation.getter(#backupFrequencyType),
returnValue: _i31.BackupFrequencyType.everyTenMinutes, returnValue: _i32.BackupFrequencyType.everyTenMinutes,
) as _i31.BackupFrequencyType); ) as _i32.BackupFrequencyType);
@override @override
set backupFrequencyType(_i31.BackupFrequencyType? backupFrequencyType) => set backupFrequencyType(_i32.BackupFrequencyType? backupFrequencyType) =>
super.noSuchMethod( super.noSuchMethod(
Invocation.setter( Invocation.setter(
#backupFrequencyType, #backupFrequencyType,

View file

@ -12,8 +12,9 @@ import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart' as _i5;
import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i4; import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i4;
import 'package:stackwallet/models/balance.dart' as _i6; import 'package:stackwallet/models/balance.dart' as _i6;
import 'package:stackwallet/models/isar/models/isar_models.dart' as _i12; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i12;
import 'package:stackwallet/models/lelantus_coin.dart' as _i13; import 'package:stackwallet/models/lelantus_coin.dart' as _i14;
import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i3; import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i3;
import 'package:stackwallet/models/signing_data.dart' as _i13;
import 'package:stackwallet/services/coins/firo/firo_wallet.dart' as _i9; import 'package:stackwallet/services/coins/firo/firo_wallet.dart' as _i9;
import 'package:stackwallet/services/transaction_notification_tracker.dart' import 'package:stackwallet/services/transaction_notification_tracker.dart'
as _i2; as _i2;
@ -485,7 +486,7 @@ class MockFiroWallet extends _i1.Mock implements _i9.FiroWallet {
}, },
)); ));
@override @override
_i10.Future<Map<String, dynamic>> fetchBuildTxData( _i10.Future<List<_i13.SigningData>> fetchBuildTxData(
List<_i12.UTXO>? utxosToUse) => List<_i12.UTXO>? utxosToUse) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
@ -493,12 +494,11 @@ class MockFiroWallet extends _i1.Mock implements _i9.FiroWallet {
[utxosToUse], [utxosToUse],
), ),
returnValue: returnValue:
_i10.Future<Map<String, dynamic>>.value(<String, dynamic>{}), _i10.Future<List<_i13.SigningData>>.value(<_i13.SigningData>[]),
) as _i10.Future<Map<String, dynamic>>); ) as _i10.Future<List<_i13.SigningData>>);
@override @override
_i10.Future<Map<String, dynamic>> buildTransaction({ _i10.Future<Map<String, dynamic>> buildTransaction({
required List<_i12.UTXO>? utxosToUse, required List<_i13.SigningData>? utxoSigningData,
required Map<String, dynamic>? utxoSigningData,
required List<String>? recipients, required List<String>? recipients,
required List<int>? satoshiAmounts, required List<int>? satoshiAmounts,
}) => }) =>
@ -507,7 +507,6 @@ class MockFiroWallet extends _i1.Mock implements _i9.FiroWallet {
#buildTransaction, #buildTransaction,
[], [],
{ {
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData, #utxoSigningData: utxoSigningData,
#recipients: recipients, #recipients: recipients,
#satoshiAmounts: satoshiAmounts, #satoshiAmounts: satoshiAmounts,
@ -570,14 +569,14 @@ class MockFiroWallet extends _i1.Mock implements _i9.FiroWallet {
returnValueForMissingStub: _i10.Future<void>.value(), returnValueForMissingStub: _i10.Future<void>.value(),
) as _i10.Future<void>); ) as _i10.Future<void>);
@override @override
List<Map<dynamic, _i13.LelantusCoin>> getLelantusCoinMap() => List<Map<dynamic, _i14.LelantusCoin>> getLelantusCoinMap() =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
#getLelantusCoinMap, #getLelantusCoinMap,
[], [],
), ),
returnValue: <Map<dynamic, _i13.LelantusCoin>>[], returnValue: <Map<dynamic, _i14.LelantusCoin>>[],
) as List<Map<dynamic, _i13.LelantusCoin>>); ) as List<Map<dynamic, _i14.LelantusCoin>>);
@override @override
_i10.Future<void> anonymizeAllPublicFunds() => (super.noSuchMethod( _i10.Future<void> anonymizeAllPublicFunds() => (super.noSuchMethod(
Invocation.method( Invocation.method(

View file

@ -4,7 +4,7 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes // ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i22; import 'dart:async' as _i22;
import 'dart:typed_data' as _i27; import 'dart:typed_data' as _i28;
import 'dart:ui' as _i24; import 'dart:ui' as _i24;
import 'package:bip32/bip32.dart' as _i16; import 'package:bip32/bip32.dart' as _i16;
@ -18,12 +18,13 @@ import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart' as _i10;
import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i9; import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i9;
import 'package:stackwallet/models/balance.dart' as _i11; import 'package:stackwallet/models/balance.dart' as _i11;
import 'package:stackwallet/models/isar/models/isar_models.dart' as _i15; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i15;
import 'package:stackwallet/models/node_model.dart' as _i29; import 'package:stackwallet/models/node_model.dart' as _i30;
import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i8; import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i8;
import 'package:stackwallet/models/signing_data.dart' as _i27;
import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart' as _i25; import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart' as _i25;
import 'package:stackwallet/services/coins/coin_service.dart' as _i19; import 'package:stackwallet/services/coins/coin_service.dart' as _i19;
import 'package:stackwallet/services/coins/manager.dart' as _i6; import 'package:stackwallet/services/coins/manager.dart' as _i6;
import 'package:stackwallet/services/locale_service.dart' as _i28; import 'package:stackwallet/services/locale_service.dart' as _i29;
import 'package:stackwallet/services/node_service.dart' as _i3; import 'package:stackwallet/services/node_service.dart' as _i3;
import 'package:stackwallet/services/transaction_notification_tracker.dart' import 'package:stackwallet/services/transaction_notification_tracker.dart'
as _i7; as _i7;
@ -1230,7 +1231,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
}, },
)); ));
@override @override
_i22.Future<Map<String, dynamic>> fetchBuildTxData( _i22.Future<List<_i27.SigningData>> fetchBuildTxData(
List<_i15.UTXO>? utxosToUse) => List<_i15.UTXO>? utxosToUse) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
@ -1238,12 +1239,11 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
[utxosToUse], [utxosToUse],
), ),
returnValue: returnValue:
_i22.Future<Map<String, dynamic>>.value(<String, dynamic>{}), _i22.Future<List<_i27.SigningData>>.value(<_i27.SigningData>[]),
) as _i22.Future<Map<String, dynamic>>); ) as _i22.Future<List<_i27.SigningData>>);
@override @override
_i22.Future<Map<String, dynamic>> buildTransaction({ _i22.Future<Map<String, dynamic>> buildTransaction({
required List<_i15.UTXO>? utxosToUse, required List<_i27.SigningData>? utxoSigningData,
required Map<String, dynamic>? utxoSigningData,
required List<String>? recipients, required List<String>? recipients,
required List<int>? satoshiAmounts, required List<int>? satoshiAmounts,
}) => }) =>
@ -1252,7 +1252,6 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
#buildTransaction, #buildTransaction,
[], [],
{ {
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData, #utxoSigningData: utxoSigningData,
#recipients: recipients, #recipients: recipients,
#satoshiAmounts: satoshiAmounts, #satoshiAmounts: satoshiAmounts,
@ -1506,7 +1505,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
})? })?
prepareSend, prepareSend,
required _i22.Future<int> Function({required String address})? getTxCount, required _i22.Future<int> Function({required String address})? getTxCount,
required _i22.Future<Map<String, dynamic>> Function(List<_i15.UTXO>)? required _i22.Future<List<_i27.SigningData>> Function(List<_i15.UTXO>)?
fetchBuildTxData, fetchBuildTxData,
required _i22.Future<void> Function()? refresh, required _i22.Future<void> Function()? refresh,
required _i22.Future<void> Function()? checkChangeAddressForTransactions, required _i22.Future<void> Function()? checkChangeAddressForTransactions,
@ -1658,14 +1657,14 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
)), )),
) as _i22.Future<_i17.PaymentCode>); ) as _i22.Future<_i17.PaymentCode>);
@override @override
_i22.Future<_i27.Uint8List> signWithNotificationKey(_i27.Uint8List? data) => _i22.Future<_i28.Uint8List> signWithNotificationKey(_i28.Uint8List? data) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
#signWithNotificationKey, #signWithNotificationKey,
[data], [data],
), ),
returnValue: _i22.Future<_i27.Uint8List>.value(_i27.Uint8List(0)), returnValue: _i22.Future<_i28.Uint8List>.value(_i28.Uint8List(0)),
) as _i22.Future<_i27.Uint8List>); ) as _i22.Future<_i28.Uint8List>);
@override @override
_i22.Future<String> signStringWithNotificationKey(String? data) => _i22.Future<String> signStringWithNotificationKey(String? data) =>
(super.noSuchMethod( (super.noSuchMethod(
@ -2001,7 +2000,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
/// A class which mocks [LocaleService]. /// A class which mocks [LocaleService].
/// ///
/// See the documentation for Mockito's code generation for more information. /// See the documentation for Mockito's code generation for more information.
class MockLocaleService extends _i1.Mock implements _i28.LocaleService { class MockLocaleService extends _i1.Mock implements _i29.LocaleService {
MockLocaleService() { MockLocaleService() {
_i1.throwOnMissingStub(this); _i1.throwOnMissingStub(this);
} }
@ -2073,15 +2072,15 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
), ),
) as _i18.SecureStorageInterface); ) as _i18.SecureStorageInterface);
@override @override
List<_i29.NodeModel> get primaryNodes => (super.noSuchMethod( List<_i30.NodeModel> get primaryNodes => (super.noSuchMethod(
Invocation.getter(#primaryNodes), Invocation.getter(#primaryNodes),
returnValue: <_i29.NodeModel>[], returnValue: <_i30.NodeModel>[],
) as List<_i29.NodeModel>); ) as List<_i30.NodeModel>);
@override @override
List<_i29.NodeModel> get nodes => (super.noSuchMethod( List<_i30.NodeModel> get nodes => (super.noSuchMethod(
Invocation.getter(#nodes), Invocation.getter(#nodes),
returnValue: <_i29.NodeModel>[], returnValue: <_i30.NodeModel>[],
) as List<_i29.NodeModel>); ) as List<_i30.NodeModel>);
@override @override
bool get hasListeners => (super.noSuchMethod( bool get hasListeners => (super.noSuchMethod(
Invocation.getter(#hasListeners), Invocation.getter(#hasListeners),
@ -2099,7 +2098,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
@override @override
_i22.Future<void> setPrimaryNodeFor({ _i22.Future<void> setPrimaryNodeFor({
required _i21.Coin? coin, required _i21.Coin? coin,
required _i29.NodeModel? node, required _i30.NodeModel? node,
bool? shouldNotifyListeners = false, bool? shouldNotifyListeners = false,
}) => }) =>
(super.noSuchMethod( (super.noSuchMethod(
@ -2116,40 +2115,40 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
returnValueForMissingStub: _i22.Future<void>.value(), returnValueForMissingStub: _i22.Future<void>.value(),
) as _i22.Future<void>); ) as _i22.Future<void>);
@override @override
_i29.NodeModel? getPrimaryNodeFor({required _i21.Coin? coin}) => _i30.NodeModel? getPrimaryNodeFor({required _i21.Coin? coin}) =>
(super.noSuchMethod(Invocation.method( (super.noSuchMethod(Invocation.method(
#getPrimaryNodeFor, #getPrimaryNodeFor,
[], [],
{#coin: coin}, {#coin: coin},
)) as _i29.NodeModel?); )) as _i30.NodeModel?);
@override @override
List<_i29.NodeModel> getNodesFor(_i21.Coin? coin) => (super.noSuchMethod( List<_i30.NodeModel> getNodesFor(_i21.Coin? coin) => (super.noSuchMethod(
Invocation.method( Invocation.method(
#getNodesFor, #getNodesFor,
[coin], [coin],
), ),
returnValue: <_i29.NodeModel>[], returnValue: <_i30.NodeModel>[],
) as List<_i29.NodeModel>); ) as List<_i30.NodeModel>);
@override @override
_i29.NodeModel? getNodeById({required String? id}) => _i30.NodeModel? getNodeById({required String? id}) =>
(super.noSuchMethod(Invocation.method( (super.noSuchMethod(Invocation.method(
#getNodeById, #getNodeById,
[], [],
{#id: id}, {#id: id},
)) as _i29.NodeModel?); )) as _i30.NodeModel?);
@override @override
List<_i29.NodeModel> failoverNodesFor({required _i21.Coin? coin}) => List<_i30.NodeModel> failoverNodesFor({required _i21.Coin? coin}) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
#failoverNodesFor, #failoverNodesFor,
[], [],
{#coin: coin}, {#coin: coin},
), ),
returnValue: <_i29.NodeModel>[], returnValue: <_i30.NodeModel>[],
) as List<_i29.NodeModel>); ) as List<_i30.NodeModel>);
@override @override
_i22.Future<void> add( _i22.Future<void> add(
_i29.NodeModel? node, _i30.NodeModel? node,
String? password, String? password,
bool? shouldNotifyListeners, bool? shouldNotifyListeners,
) => ) =>
@ -2201,7 +2200,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
) as _i22.Future<void>); ) as _i22.Future<void>);
@override @override
_i22.Future<void> edit( _i22.Future<void> edit(
_i29.NodeModel? editedNode, _i30.NodeModel? editedNode,
String? password, String? password,
bool? shouldNotifyListeners, bool? shouldNotifyListeners,
) => ) =>

View file

@ -4,7 +4,7 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes // ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i21; import 'dart:async' as _i21;
import 'dart:typed_data' as _i27; import 'dart:typed_data' as _i28;
import 'dart:ui' as _i23; import 'dart:ui' as _i23;
import 'package:bip32/bip32.dart' as _i16; import 'package:bip32/bip32.dart' as _i16;
@ -19,6 +19,7 @@ import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i9;
import 'package:stackwallet/models/balance.dart' as _i11; import 'package:stackwallet/models/balance.dart' as _i11;
import 'package:stackwallet/models/isar/models/isar_models.dart' as _i15; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i15;
import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i8; import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i8;
import 'package:stackwallet/models/signing_data.dart' as _i26;
import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart' as _i24; import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart' as _i24;
import 'package:stackwallet/services/coins/coin_service.dart' as _i18; import 'package:stackwallet/services/coins/coin_service.dart' as _i18;
import 'package:stackwallet/services/coins/manager.dart' as _i6; import 'package:stackwallet/services/coins/manager.dart' as _i6;
@ -30,7 +31,7 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2;
import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i20; import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i20;
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart' as _i25; import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart' as _i25;
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart' import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
as _i26; as _i27;
import 'package:stackwallet/utilities/prefs.dart' as _i22; import 'package:stackwallet/utilities/prefs.dart' as _i22;
import 'package:tuple/tuple.dart' as _i14; import 'package:tuple/tuple.dart' as _i14;
@ -1217,7 +1218,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
}, },
)); ));
@override @override
_i21.Future<Map<String, dynamic>> fetchBuildTxData( _i21.Future<List<_i26.SigningData>> fetchBuildTxData(
List<_i15.UTXO>? utxosToUse) => List<_i15.UTXO>? utxosToUse) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
@ -1225,12 +1226,11 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
[utxosToUse], [utxosToUse],
), ),
returnValue: returnValue:
_i21.Future<Map<String, dynamic>>.value(<String, dynamic>{}), _i21.Future<List<_i26.SigningData>>.value(<_i26.SigningData>[]),
) as _i21.Future<Map<String, dynamic>>); ) as _i21.Future<List<_i26.SigningData>>);
@override @override
_i21.Future<Map<String, dynamic>> buildTransaction({ _i21.Future<Map<String, dynamic>> buildTransaction({
required List<_i15.UTXO>? utxosToUse, required List<_i26.SigningData>? utxoSigningData,
required Map<String, dynamic>? utxoSigningData,
required List<String>? recipients, required List<String>? recipients,
required List<int>? satoshiAmounts, required List<int>? satoshiAmounts,
}) => }) =>
@ -1239,7 +1239,6 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
#buildTransaction, #buildTransaction,
[], [],
{ {
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData, #utxoSigningData: utxoSigningData,
#recipients: recipients, #recipients: recipients,
#satoshiAmounts: satoshiAmounts, #satoshiAmounts: satoshiAmounts,
@ -1474,7 +1473,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
required _i20.Coin? coin, required _i20.Coin? coin,
required _i12.MainDB? db, required _i12.MainDB? db,
required _i9.ElectrumX? electrumXClient, required _i9.ElectrumX? electrumXClient,
required _i26.SecureStorageInterface? secureStorage, required _i27.SecureStorageInterface? secureStorage,
required int? dustLimitP2PKH, required int? dustLimitP2PKH,
required int? minConfirms, required int? minConfirms,
required _i21.Future<String?> Function()? getMnemonicString, required _i21.Future<String?> Function()? getMnemonicString,
@ -1493,7 +1492,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
})? })?
prepareSend, prepareSend,
required _i21.Future<int> Function({required String address})? getTxCount, required _i21.Future<int> Function({required String address})? getTxCount,
required _i21.Future<Map<String, dynamic>> Function(List<_i15.UTXO>)? required _i21.Future<List<_i26.SigningData>> Function(List<_i15.UTXO>)?
fetchBuildTxData, fetchBuildTxData,
required _i21.Future<void> Function()? refresh, required _i21.Future<void> Function()? refresh,
required _i21.Future<void> Function()? checkChangeAddressForTransactions, required _i21.Future<void> Function()? checkChangeAddressForTransactions,
@ -1645,14 +1644,14 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
)), )),
) as _i21.Future<_i17.PaymentCode>); ) as _i21.Future<_i17.PaymentCode>);
@override @override
_i21.Future<_i27.Uint8List> signWithNotificationKey(_i27.Uint8List? data) => _i21.Future<_i28.Uint8List> signWithNotificationKey(_i28.Uint8List? data) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
#signWithNotificationKey, #signWithNotificationKey,
[data], [data],
), ),
returnValue: _i21.Future<_i27.Uint8List>.value(_i27.Uint8List(0)), returnValue: _i21.Future<_i28.Uint8List>.value(_i28.Uint8List(0)),
) as _i21.Future<_i27.Uint8List>); ) as _i21.Future<_i28.Uint8List>);
@override @override
_i21.Future<String> signStringWithNotificationKey(String? data) => _i21.Future<String> signStringWithNotificationKey(String? data) =>
(super.noSuchMethod( (super.noSuchMethod(

View file

@ -16,20 +16,21 @@ import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i11;
import 'package:stackwallet/models/balance.dart' as _i9; import 'package:stackwallet/models/balance.dart' as _i9;
import 'package:stackwallet/models/isar/models/isar_models.dart' as _i21; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i21;
import 'package:stackwallet/models/models.dart' as _i8; import 'package:stackwallet/models/models.dart' as _i8;
import 'package:stackwallet/models/signing_data.dart' as _i23;
import 'package:stackwallet/services/coins/coin_service.dart' as _i7; import 'package:stackwallet/services/coins/coin_service.dart' as _i7;
import 'package:stackwallet/services/coins/firo/firo_wallet.dart' as _i22; import 'package:stackwallet/services/coins/firo/firo_wallet.dart' as _i22;
import 'package:stackwallet/services/coins/manager.dart' as _i6; import 'package:stackwallet/services/coins/manager.dart' as _i6;
import 'package:stackwallet/services/locale_service.dart' as _i23; import 'package:stackwallet/services/locale_service.dart' as _i24;
import 'package:stackwallet/services/node_service.dart' as _i3; import 'package:stackwallet/services/node_service.dart' as _i3;
import 'package:stackwallet/services/notes_service.dart' as _i27; import 'package:stackwallet/services/notes_service.dart' as _i28;
import 'package:stackwallet/services/price_service.dart' as _i26; import 'package:stackwallet/services/price_service.dart' as _i27;
import 'package:stackwallet/services/transaction_notification_tracker.dart' import 'package:stackwallet/services/transaction_notification_tracker.dart'
as _i10; as _i10;
import 'package:stackwallet/services/wallets.dart' as _i16; import 'package:stackwallet/services/wallets.dart' as _i16;
import 'package:stackwallet/services/wallets_service.dart' as _i2; import 'package:stackwallet/services/wallets_service.dart' as _i2;
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i25; import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i26;
import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i17; import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i17;
import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i24; import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i25;
import 'package:stackwallet/utilities/prefs.dart' as _i19; import 'package:stackwallet/utilities/prefs.dart' as _i19;
import 'package:tuple/tuple.dart' as _i15; import 'package:tuple/tuple.dart' as _i15;
@ -1443,7 +1444,7 @@ class MockFiroWallet extends _i1.Mock implements _i22.FiroWallet {
}, },
)); ));
@override @override
_i18.Future<Map<String, dynamic>> fetchBuildTxData( _i18.Future<List<_i23.SigningData>> fetchBuildTxData(
List<_i21.UTXO>? utxosToUse) => List<_i21.UTXO>? utxosToUse) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
@ -1451,12 +1452,11 @@ class MockFiroWallet extends _i1.Mock implements _i22.FiroWallet {
[utxosToUse], [utxosToUse],
), ),
returnValue: returnValue:
_i18.Future<Map<String, dynamic>>.value(<String, dynamic>{}), _i18.Future<List<_i23.SigningData>>.value(<_i23.SigningData>[]),
) as _i18.Future<Map<String, dynamic>>); ) as _i18.Future<List<_i23.SigningData>>);
@override @override
_i18.Future<Map<String, dynamic>> buildTransaction({ _i18.Future<Map<String, dynamic>> buildTransaction({
required List<_i21.UTXO>? utxosToUse, required List<_i23.SigningData>? utxoSigningData,
required Map<String, dynamic>? utxoSigningData,
required List<String>? recipients, required List<String>? recipients,
required List<int>? satoshiAmounts, required List<int>? satoshiAmounts,
}) => }) =>
@ -1465,7 +1465,6 @@ class MockFiroWallet extends _i1.Mock implements _i22.FiroWallet {
#buildTransaction, #buildTransaction,
[], [],
{ {
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData, #utxoSigningData: utxoSigningData,
#recipients: recipients, #recipients: recipients,
#satoshiAmounts: satoshiAmounts, #satoshiAmounts: satoshiAmounts,
@ -2001,7 +2000,7 @@ class MockFiroWallet extends _i1.Mock implements _i22.FiroWallet {
/// A class which mocks [LocaleService]. /// A class which mocks [LocaleService].
/// ///
/// See the documentation for Mockito's code generation for more information. /// See the documentation for Mockito's code generation for more information.
class MockLocaleService extends _i1.Mock implements _i23.LocaleService { class MockLocaleService extends _i1.Mock implements _i24.LocaleService {
MockLocaleService() { MockLocaleService() {
_i1.throwOnMissingStub(this); _i1.throwOnMissingStub(this);
} }
@ -2119,12 +2118,12 @@ class MockPrefs extends _i1.Mock implements _i19.Prefs {
returnValueForMissingStub: null, returnValueForMissingStub: null,
); );
@override @override
_i24.SyncingType get syncType => (super.noSuchMethod( _i25.SyncingType get syncType => (super.noSuchMethod(
Invocation.getter(#syncType), Invocation.getter(#syncType),
returnValue: _i24.SyncingType.currentWalletOnly, returnValue: _i25.SyncingType.currentWalletOnly,
) as _i24.SyncingType); ) as _i25.SyncingType);
@override @override
set syncType(_i24.SyncingType? syncType) => super.noSuchMethod( set syncType(_i25.SyncingType? syncType) => super.noSuchMethod(
Invocation.setter( Invocation.setter(
#syncType, #syncType,
syncType, syncType,
@ -2257,12 +2256,12 @@ class MockPrefs extends _i1.Mock implements _i19.Prefs {
returnValueForMissingStub: null, returnValueForMissingStub: null,
); );
@override @override
_i25.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( _i26.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod(
Invocation.getter(#backupFrequencyType), Invocation.getter(#backupFrequencyType),
returnValue: _i25.BackupFrequencyType.everyTenMinutes, returnValue: _i26.BackupFrequencyType.everyTenMinutes,
) as _i25.BackupFrequencyType); ) as _i26.BackupFrequencyType);
@override @override
set backupFrequencyType(_i25.BackupFrequencyType? backupFrequencyType) => set backupFrequencyType(_i26.BackupFrequencyType? backupFrequencyType) =>
super.noSuchMethod( super.noSuchMethod(
Invocation.setter( Invocation.setter(
#backupFrequencyType, #backupFrequencyType,
@ -2425,7 +2424,7 @@ class MockPrefs extends _i1.Mock implements _i19.Prefs {
/// A class which mocks [PriceService]. /// A class which mocks [PriceService].
/// ///
/// See the documentation for Mockito's code generation for more information. /// See the documentation for Mockito's code generation for more information.
class MockPriceService extends _i1.Mock implements _i26.PriceService { class MockPriceService extends _i1.Mock implements _i27.PriceService {
MockPriceService() { MockPriceService() {
_i1.throwOnMissingStub(this); _i1.throwOnMissingStub(this);
} }
@ -2533,7 +2532,7 @@ class MockPriceService extends _i1.Mock implements _i26.PriceService {
/// A class which mocks [NotesService]. /// A class which mocks [NotesService].
/// ///
/// See the documentation for Mockito's code generation for more information. /// See the documentation for Mockito's code generation for more information.
class MockNotesService extends _i1.Mock implements _i27.NotesService { class MockNotesService extends _i1.Mock implements _i28.NotesService {
MockNotesService() { MockNotesService() {
_i1.throwOnMissingStub(this); _i1.throwOnMissingStub(this);
} }

View file

@ -4,7 +4,7 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes // ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i20; import 'dart:async' as _i20;
import 'dart:typed_data' as _i26; import 'dart:typed_data' as _i27;
import 'dart:ui' as _i22; import 'dart:ui' as _i22;
import 'package:bip32/bip32.dart' as _i16; import 'package:bip32/bip32.dart' as _i16;
@ -19,9 +19,10 @@ import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i9;
import 'package:stackwallet/models/balance.dart' as _i11; import 'package:stackwallet/models/balance.dart' as _i11;
import 'package:stackwallet/models/isar/models/isar_models.dart' as _i15; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i15;
import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i8; import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i8;
import 'package:stackwallet/models/signing_data.dart' as _i25;
import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart' as _i23; import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart' as _i23;
import 'package:stackwallet/services/coins/manager.dart' as _i6; import 'package:stackwallet/services/coins/manager.dart' as _i6;
import 'package:stackwallet/services/locale_service.dart' as _i27; import 'package:stackwallet/services/locale_service.dart' as _i28;
import 'package:stackwallet/services/node_service.dart' as _i3; import 'package:stackwallet/services/node_service.dart' as _i3;
import 'package:stackwallet/services/transaction_notification_tracker.dart' import 'package:stackwallet/services/transaction_notification_tracker.dart'
as _i7; as _i7;
@ -30,7 +31,7 @@ import 'package:stackwallet/services/wallets_service.dart' as _i2;
import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i19; import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i19;
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart' as _i24; import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart' as _i24;
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart' import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
as _i25; as _i26;
import 'package:stackwallet/utilities/prefs.dart' as _i21; import 'package:stackwallet/utilities/prefs.dart' as _i21;
import 'package:tuple/tuple.dart' as _i14; import 'package:tuple/tuple.dart' as _i14;
@ -980,7 +981,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i23.BitcoinWallet {
}, },
)); ));
@override @override
_i20.Future<Map<String, dynamic>> fetchBuildTxData( _i20.Future<List<_i25.SigningData>> fetchBuildTxData(
List<_i15.UTXO>? utxosToUse) => List<_i15.UTXO>? utxosToUse) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
@ -988,12 +989,11 @@ class MockBitcoinWallet extends _i1.Mock implements _i23.BitcoinWallet {
[utxosToUse], [utxosToUse],
), ),
returnValue: returnValue:
_i20.Future<Map<String, dynamic>>.value(<String, dynamic>{}), _i20.Future<List<_i25.SigningData>>.value(<_i25.SigningData>[]),
) as _i20.Future<Map<String, dynamic>>); ) as _i20.Future<List<_i25.SigningData>>);
@override @override
_i20.Future<Map<String, dynamic>> buildTransaction({ _i20.Future<Map<String, dynamic>> buildTransaction({
required List<_i15.UTXO>? utxosToUse, required List<_i25.SigningData>? utxoSigningData,
required Map<String, dynamic>? utxoSigningData,
required List<String>? recipients, required List<String>? recipients,
required List<int>? satoshiAmounts, required List<int>? satoshiAmounts,
}) => }) =>
@ -1002,7 +1002,6 @@ class MockBitcoinWallet extends _i1.Mock implements _i23.BitcoinWallet {
#buildTransaction, #buildTransaction,
[], [],
{ {
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData, #utxoSigningData: utxoSigningData,
#recipients: recipients, #recipients: recipients,
#satoshiAmounts: satoshiAmounts, #satoshiAmounts: satoshiAmounts,
@ -1237,7 +1236,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i23.BitcoinWallet {
required _i19.Coin? coin, required _i19.Coin? coin,
required _i12.MainDB? db, required _i12.MainDB? db,
required _i9.ElectrumX? electrumXClient, required _i9.ElectrumX? electrumXClient,
required _i25.SecureStorageInterface? secureStorage, required _i26.SecureStorageInterface? secureStorage,
required int? dustLimitP2PKH, required int? dustLimitP2PKH,
required int? minConfirms, required int? minConfirms,
required _i20.Future<String?> Function()? getMnemonicString, required _i20.Future<String?> Function()? getMnemonicString,
@ -1256,7 +1255,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i23.BitcoinWallet {
})? })?
prepareSend, prepareSend,
required _i20.Future<int> Function({required String address})? getTxCount, required _i20.Future<int> Function({required String address})? getTxCount,
required _i20.Future<Map<String, dynamic>> Function(List<_i15.UTXO>)? required _i20.Future<List<_i25.SigningData>> Function(List<_i15.UTXO>)?
fetchBuildTxData, fetchBuildTxData,
required _i20.Future<void> Function()? refresh, required _i20.Future<void> Function()? refresh,
required _i20.Future<void> Function()? checkChangeAddressForTransactions, required _i20.Future<void> Function()? checkChangeAddressForTransactions,
@ -1408,14 +1407,14 @@ class MockBitcoinWallet extends _i1.Mock implements _i23.BitcoinWallet {
)), )),
) as _i20.Future<_i17.PaymentCode>); ) as _i20.Future<_i17.PaymentCode>);
@override @override
_i20.Future<_i26.Uint8List> signWithNotificationKey(_i26.Uint8List? data) => _i20.Future<_i27.Uint8List> signWithNotificationKey(_i27.Uint8List? data) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
#signWithNotificationKey, #signWithNotificationKey,
[data], [data],
), ),
returnValue: _i20.Future<_i26.Uint8List>.value(_i26.Uint8List(0)), returnValue: _i20.Future<_i27.Uint8List>.value(_i27.Uint8List(0)),
) as _i20.Future<_i26.Uint8List>); ) as _i20.Future<_i27.Uint8List>);
@override @override
_i20.Future<String> signStringWithNotificationKey(String? data) => _i20.Future<String> signStringWithNotificationKey(String? data) =>
(super.noSuchMethod( (super.noSuchMethod(
@ -1751,7 +1750,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i23.BitcoinWallet {
/// A class which mocks [LocaleService]. /// A class which mocks [LocaleService].
/// ///
/// See the documentation for Mockito's code generation for more information. /// See the documentation for Mockito's code generation for more information.
class MockLocaleService extends _i1.Mock implements _i27.LocaleService { class MockLocaleService extends _i1.Mock implements _i28.LocaleService {
MockLocaleService() { MockLocaleService() {
_i1.throwOnMissingStub(this); _i1.throwOnMissingStub(this);
} }

View file

@ -4,7 +4,7 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes // ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i22; import 'dart:async' as _i22;
import 'dart:typed_data' as _i27; import 'dart:typed_data' as _i28;
import 'dart:ui' as _i24; import 'dart:ui' as _i24;
import 'package:bip32/bip32.dart' as _i16; import 'package:bip32/bip32.dart' as _i16;
@ -18,8 +18,9 @@ import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart' as _i10;
import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i9; import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i9;
import 'package:stackwallet/models/balance.dart' as _i11; import 'package:stackwallet/models/balance.dart' as _i11;
import 'package:stackwallet/models/isar/models/isar_models.dart' as _i15; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i15;
import 'package:stackwallet/models/node_model.dart' as _i28; import 'package:stackwallet/models/node_model.dart' as _i29;
import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i8; import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i8;
import 'package:stackwallet/models/signing_data.dart' as _i27;
import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart' as _i25; import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart' as _i25;
import 'package:stackwallet/services/coins/coin_service.dart' as _i19; import 'package:stackwallet/services/coins/coin_service.dart' as _i19;
import 'package:stackwallet/services/coins/manager.dart' as _i6; import 'package:stackwallet/services/coins/manager.dart' as _i6;
@ -1229,7 +1230,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
}, },
)); ));
@override @override
_i22.Future<Map<String, dynamic>> fetchBuildTxData( _i22.Future<List<_i27.SigningData>> fetchBuildTxData(
List<_i15.UTXO>? utxosToUse) => List<_i15.UTXO>? utxosToUse) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
@ -1237,12 +1238,11 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
[utxosToUse], [utxosToUse],
), ),
returnValue: returnValue:
_i22.Future<Map<String, dynamic>>.value(<String, dynamic>{}), _i22.Future<List<_i27.SigningData>>.value(<_i27.SigningData>[]),
) as _i22.Future<Map<String, dynamic>>); ) as _i22.Future<List<_i27.SigningData>>);
@override @override
_i22.Future<Map<String, dynamic>> buildTransaction({ _i22.Future<Map<String, dynamic>> buildTransaction({
required List<_i15.UTXO>? utxosToUse, required List<_i27.SigningData>? utxoSigningData,
required Map<String, dynamic>? utxoSigningData,
required List<String>? recipients, required List<String>? recipients,
required List<int>? satoshiAmounts, required List<int>? satoshiAmounts,
}) => }) =>
@ -1251,7 +1251,6 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
#buildTransaction, #buildTransaction,
[], [],
{ {
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData, #utxoSigningData: utxoSigningData,
#recipients: recipients, #recipients: recipients,
#satoshiAmounts: satoshiAmounts, #satoshiAmounts: satoshiAmounts,
@ -1505,7 +1504,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
})? })?
prepareSend, prepareSend,
required _i22.Future<int> Function({required String address})? getTxCount, required _i22.Future<int> Function({required String address})? getTxCount,
required _i22.Future<Map<String, dynamic>> Function(List<_i15.UTXO>)? required _i22.Future<List<_i27.SigningData>> Function(List<_i15.UTXO>)?
fetchBuildTxData, fetchBuildTxData,
required _i22.Future<void> Function()? refresh, required _i22.Future<void> Function()? refresh,
required _i22.Future<void> Function()? checkChangeAddressForTransactions, required _i22.Future<void> Function()? checkChangeAddressForTransactions,
@ -1657,14 +1656,14 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
)), )),
) as _i22.Future<_i17.PaymentCode>); ) as _i22.Future<_i17.PaymentCode>);
@override @override
_i22.Future<_i27.Uint8List> signWithNotificationKey(_i27.Uint8List? data) => _i22.Future<_i28.Uint8List> signWithNotificationKey(_i28.Uint8List? data) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
#signWithNotificationKey, #signWithNotificationKey,
[data], [data],
), ),
returnValue: _i22.Future<_i27.Uint8List>.value(_i27.Uint8List(0)), returnValue: _i22.Future<_i28.Uint8List>.value(_i28.Uint8List(0)),
) as _i22.Future<_i27.Uint8List>); ) as _i22.Future<_i28.Uint8List>);
@override @override
_i22.Future<String> signStringWithNotificationKey(String? data) => _i22.Future<String> signStringWithNotificationKey(String? data) =>
(super.noSuchMethod( (super.noSuchMethod(
@ -2010,15 +2009,15 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
), ),
) as _i18.SecureStorageInterface); ) as _i18.SecureStorageInterface);
@override @override
List<_i28.NodeModel> get primaryNodes => (super.noSuchMethod( List<_i29.NodeModel> get primaryNodes => (super.noSuchMethod(
Invocation.getter(#primaryNodes), Invocation.getter(#primaryNodes),
returnValue: <_i28.NodeModel>[], returnValue: <_i29.NodeModel>[],
) as List<_i28.NodeModel>); ) as List<_i29.NodeModel>);
@override @override
List<_i28.NodeModel> get nodes => (super.noSuchMethod( List<_i29.NodeModel> get nodes => (super.noSuchMethod(
Invocation.getter(#nodes), Invocation.getter(#nodes),
returnValue: <_i28.NodeModel>[], returnValue: <_i29.NodeModel>[],
) as List<_i28.NodeModel>); ) as List<_i29.NodeModel>);
@override @override
bool get hasListeners => (super.noSuchMethod( bool get hasListeners => (super.noSuchMethod(
Invocation.getter(#hasListeners), Invocation.getter(#hasListeners),
@ -2036,7 +2035,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
@override @override
_i22.Future<void> setPrimaryNodeFor({ _i22.Future<void> setPrimaryNodeFor({
required _i21.Coin? coin, required _i21.Coin? coin,
required _i28.NodeModel? node, required _i29.NodeModel? node,
bool? shouldNotifyListeners = false, bool? shouldNotifyListeners = false,
}) => }) =>
(super.noSuchMethod( (super.noSuchMethod(
@ -2053,40 +2052,40 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
returnValueForMissingStub: _i22.Future<void>.value(), returnValueForMissingStub: _i22.Future<void>.value(),
) as _i22.Future<void>); ) as _i22.Future<void>);
@override @override
_i28.NodeModel? getPrimaryNodeFor({required _i21.Coin? coin}) => _i29.NodeModel? getPrimaryNodeFor({required _i21.Coin? coin}) =>
(super.noSuchMethod(Invocation.method( (super.noSuchMethod(Invocation.method(
#getPrimaryNodeFor, #getPrimaryNodeFor,
[], [],
{#coin: coin}, {#coin: coin},
)) as _i28.NodeModel?); )) as _i29.NodeModel?);
@override @override
List<_i28.NodeModel> getNodesFor(_i21.Coin? coin) => (super.noSuchMethod( List<_i29.NodeModel> getNodesFor(_i21.Coin? coin) => (super.noSuchMethod(
Invocation.method( Invocation.method(
#getNodesFor, #getNodesFor,
[coin], [coin],
), ),
returnValue: <_i28.NodeModel>[], returnValue: <_i29.NodeModel>[],
) as List<_i28.NodeModel>); ) as List<_i29.NodeModel>);
@override @override
_i28.NodeModel? getNodeById({required String? id}) => _i29.NodeModel? getNodeById({required String? id}) =>
(super.noSuchMethod(Invocation.method( (super.noSuchMethod(Invocation.method(
#getNodeById, #getNodeById,
[], [],
{#id: id}, {#id: id},
)) as _i28.NodeModel?); )) as _i29.NodeModel?);
@override @override
List<_i28.NodeModel> failoverNodesFor({required _i21.Coin? coin}) => List<_i29.NodeModel> failoverNodesFor({required _i21.Coin? coin}) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
#failoverNodesFor, #failoverNodesFor,
[], [],
{#coin: coin}, {#coin: coin},
), ),
returnValue: <_i28.NodeModel>[], returnValue: <_i29.NodeModel>[],
) as List<_i28.NodeModel>); ) as List<_i29.NodeModel>);
@override @override
_i22.Future<void> add( _i22.Future<void> add(
_i28.NodeModel? node, _i29.NodeModel? node,
String? password, String? password,
bool? shouldNotifyListeners, bool? shouldNotifyListeners,
) => ) =>
@ -2138,7 +2137,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
) as _i22.Future<void>); ) as _i22.Future<void>);
@override @override
_i22.Future<void> edit( _i22.Future<void> edit(
_i28.NodeModel? editedNode, _i29.NodeModel? editedNode,
String? password, String? password,
bool? shouldNotifyListeners, bool? shouldNotifyListeners,
) => ) =>

View file

@ -4,7 +4,7 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes // ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i22; import 'dart:async' as _i22;
import 'dart:typed_data' as _i27; import 'dart:typed_data' as _i28;
import 'dart:ui' as _i24; import 'dart:ui' as _i24;
import 'package:bip32/bip32.dart' as _i16; import 'package:bip32/bip32.dart' as _i16;
@ -18,8 +18,9 @@ import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart' as _i10;
import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i9; import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i9;
import 'package:stackwallet/models/balance.dart' as _i11; import 'package:stackwallet/models/balance.dart' as _i11;
import 'package:stackwallet/models/isar/models/isar_models.dart' as _i15; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i15;
import 'package:stackwallet/models/node_model.dart' as _i28; import 'package:stackwallet/models/node_model.dart' as _i29;
import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i8; import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i8;
import 'package:stackwallet/models/signing_data.dart' as _i27;
import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart' as _i25; import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart' as _i25;
import 'package:stackwallet/services/coins/coin_service.dart' as _i19; import 'package:stackwallet/services/coins/coin_service.dart' as _i19;
import 'package:stackwallet/services/coins/manager.dart' as _i6; import 'package:stackwallet/services/coins/manager.dart' as _i6;
@ -1229,7 +1230,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
}, },
)); ));
@override @override
_i22.Future<Map<String, dynamic>> fetchBuildTxData( _i22.Future<List<_i27.SigningData>> fetchBuildTxData(
List<_i15.UTXO>? utxosToUse) => List<_i15.UTXO>? utxosToUse) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
@ -1237,12 +1238,11 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
[utxosToUse], [utxosToUse],
), ),
returnValue: returnValue:
_i22.Future<Map<String, dynamic>>.value(<String, dynamic>{}), _i22.Future<List<_i27.SigningData>>.value(<_i27.SigningData>[]),
) as _i22.Future<Map<String, dynamic>>); ) as _i22.Future<List<_i27.SigningData>>);
@override @override
_i22.Future<Map<String, dynamic>> buildTransaction({ _i22.Future<Map<String, dynamic>> buildTransaction({
required List<_i15.UTXO>? utxosToUse, required List<_i27.SigningData>? utxoSigningData,
required Map<String, dynamic>? utxoSigningData,
required List<String>? recipients, required List<String>? recipients,
required List<int>? satoshiAmounts, required List<int>? satoshiAmounts,
}) => }) =>
@ -1251,7 +1251,6 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
#buildTransaction, #buildTransaction,
[], [],
{ {
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData, #utxoSigningData: utxoSigningData,
#recipients: recipients, #recipients: recipients,
#satoshiAmounts: satoshiAmounts, #satoshiAmounts: satoshiAmounts,
@ -1505,7 +1504,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
})? })?
prepareSend, prepareSend,
required _i22.Future<int> Function({required String address})? getTxCount, required _i22.Future<int> Function({required String address})? getTxCount,
required _i22.Future<Map<String, dynamic>> Function(List<_i15.UTXO>)? required _i22.Future<List<_i27.SigningData>> Function(List<_i15.UTXO>)?
fetchBuildTxData, fetchBuildTxData,
required _i22.Future<void> Function()? refresh, required _i22.Future<void> Function()? refresh,
required _i22.Future<void> Function()? checkChangeAddressForTransactions, required _i22.Future<void> Function()? checkChangeAddressForTransactions,
@ -1657,14 +1656,14 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
)), )),
) as _i22.Future<_i17.PaymentCode>); ) as _i22.Future<_i17.PaymentCode>);
@override @override
_i22.Future<_i27.Uint8List> signWithNotificationKey(_i27.Uint8List? data) => _i22.Future<_i28.Uint8List> signWithNotificationKey(_i28.Uint8List? data) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
#signWithNotificationKey, #signWithNotificationKey,
[data], [data],
), ),
returnValue: _i22.Future<_i27.Uint8List>.value(_i27.Uint8List(0)), returnValue: _i22.Future<_i28.Uint8List>.value(_i28.Uint8List(0)),
) as _i22.Future<_i27.Uint8List>); ) as _i22.Future<_i28.Uint8List>);
@override @override
_i22.Future<String> signStringWithNotificationKey(String? data) => _i22.Future<String> signStringWithNotificationKey(String? data) =>
(super.noSuchMethod( (super.noSuchMethod(
@ -2010,15 +2009,15 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
), ),
) as _i18.SecureStorageInterface); ) as _i18.SecureStorageInterface);
@override @override
List<_i28.NodeModel> get primaryNodes => (super.noSuchMethod( List<_i29.NodeModel> get primaryNodes => (super.noSuchMethod(
Invocation.getter(#primaryNodes), Invocation.getter(#primaryNodes),
returnValue: <_i28.NodeModel>[], returnValue: <_i29.NodeModel>[],
) as List<_i28.NodeModel>); ) as List<_i29.NodeModel>);
@override @override
List<_i28.NodeModel> get nodes => (super.noSuchMethod( List<_i29.NodeModel> get nodes => (super.noSuchMethod(
Invocation.getter(#nodes), Invocation.getter(#nodes),
returnValue: <_i28.NodeModel>[], returnValue: <_i29.NodeModel>[],
) as List<_i28.NodeModel>); ) as List<_i29.NodeModel>);
@override @override
bool get hasListeners => (super.noSuchMethod( bool get hasListeners => (super.noSuchMethod(
Invocation.getter(#hasListeners), Invocation.getter(#hasListeners),
@ -2036,7 +2035,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
@override @override
_i22.Future<void> setPrimaryNodeFor({ _i22.Future<void> setPrimaryNodeFor({
required _i21.Coin? coin, required _i21.Coin? coin,
required _i28.NodeModel? node, required _i29.NodeModel? node,
bool? shouldNotifyListeners = false, bool? shouldNotifyListeners = false,
}) => }) =>
(super.noSuchMethod( (super.noSuchMethod(
@ -2053,40 +2052,40 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
returnValueForMissingStub: _i22.Future<void>.value(), returnValueForMissingStub: _i22.Future<void>.value(),
) as _i22.Future<void>); ) as _i22.Future<void>);
@override @override
_i28.NodeModel? getPrimaryNodeFor({required _i21.Coin? coin}) => _i29.NodeModel? getPrimaryNodeFor({required _i21.Coin? coin}) =>
(super.noSuchMethod(Invocation.method( (super.noSuchMethod(Invocation.method(
#getPrimaryNodeFor, #getPrimaryNodeFor,
[], [],
{#coin: coin}, {#coin: coin},
)) as _i28.NodeModel?); )) as _i29.NodeModel?);
@override @override
List<_i28.NodeModel> getNodesFor(_i21.Coin? coin) => (super.noSuchMethod( List<_i29.NodeModel> getNodesFor(_i21.Coin? coin) => (super.noSuchMethod(
Invocation.method( Invocation.method(
#getNodesFor, #getNodesFor,
[coin], [coin],
), ),
returnValue: <_i28.NodeModel>[], returnValue: <_i29.NodeModel>[],
) as List<_i28.NodeModel>); ) as List<_i29.NodeModel>);
@override @override
_i28.NodeModel? getNodeById({required String? id}) => _i29.NodeModel? getNodeById({required String? id}) =>
(super.noSuchMethod(Invocation.method( (super.noSuchMethod(Invocation.method(
#getNodeById, #getNodeById,
[], [],
{#id: id}, {#id: id},
)) as _i28.NodeModel?); )) as _i29.NodeModel?);
@override @override
List<_i28.NodeModel> failoverNodesFor({required _i21.Coin? coin}) => List<_i29.NodeModel> failoverNodesFor({required _i21.Coin? coin}) =>
(super.noSuchMethod( (super.noSuchMethod(
Invocation.method( Invocation.method(
#failoverNodesFor, #failoverNodesFor,
[], [],
{#coin: coin}, {#coin: coin},
), ),
returnValue: <_i28.NodeModel>[], returnValue: <_i29.NodeModel>[],
) as List<_i28.NodeModel>); ) as List<_i29.NodeModel>);
@override @override
_i22.Future<void> add( _i22.Future<void> add(
_i28.NodeModel? node, _i29.NodeModel? node,
String? password, String? password,
bool? shouldNotifyListeners, bool? shouldNotifyListeners,
) => ) =>
@ -2138,7 +2137,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
) as _i22.Future<void>); ) as _i22.Future<void>);
@override @override
_i22.Future<void> edit( _i22.Future<void> edit(
_i28.NodeModel? editedNode, _i29.NodeModel? editedNode,
String? password, String? password,
bool? shouldNotifyListeners, bool? shouldNotifyListeners,
) => ) =>