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
- 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)
- Dart SDK Requirement (>=2.17.0, up until <3.0.0)
- 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.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)
### Scripted setup

View file

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

View file

@ -1,5 +1,5 @@
# 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.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View file

@ -439,7 +439,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -574,7 +574,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -624,7 +624,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = 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);
// if (_amountToSend == null) {
// setState(() {
// _calculateFeesFuture = calculateFees(0);
// });
// } else {
// setState(() {
// _calculateFeesFuture =
// calculateFees(Format.decimalAmountToSatoshis(_amountToSend!));
// });
// }
_cryptoAmountChangedFeeUpdateTimer?.cancel();
_cryptoAmountChangedFeeUpdateTimer = Timer(updateFeesTimerDuration, () {
if (coin != Coin.epicCash && !_baseFocus.hasFocus) {
setState(() {
_calculateFeesFuture = calculateFees(
_amountToSend == null
? 0
: 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)) {
// confirm send all
if (amount == availableBalance) {
final bool? shouldSendAll = await showDialog<bool>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return StackDialog(
title: "Confirm send all",
message:
"You are about to send your entire balance. Would you like to continue?",
leftButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonStyle(context),
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
bool? shouldSendAll;
if (mounted) {
shouldSendAll = await showDialog<bool>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return StackDialog(
title: "Confirm send all",
message:
"You are about to send your entire balance. Would you like to continue?",
leftButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonStyle(context),
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
onPressed: () {
Navigator.of(context).pop(false);
},
),
onPressed: () {
Navigator.of(context).pop(false);
},
),
rightButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonStyle(context),
child: Text(
"Yes",
style: STextStyles.button(context),
rightButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonStyle(context),
child: Text(
"Yes",
style: STextStyles.button(context),
),
onPressed: () {
Navigator.of(context).pop(true);
},
),
onPressed: () {
Navigator.of(context).pop(true);
},
),
);
},
);
);
},
);
}
if (shouldSendAll == null || shouldSendAll == false) {
// cancel preview
@ -378,22 +423,24 @@ class _SendViewState extends ConsumerState<SendView> {
try {
bool wasCancelled = false;
unawaited(
showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: false,
builder: (context) {
return BuildingTransactionDialog(
onCancel: () {
wasCancelled = true;
if (mounted) {
unawaited(
showDialog<void>(
context: context,
useSafeArea: false,
barrierDismissible: false,
builder: (context) {
return BuildingTransactionDialog(
onCancel: () {
wasCancelled = true;
Navigator.of(context).pop();
},
);
},
),
);
Navigator.of(context).pop();
},
);
},
),
);
}
Map<String, dynamic> txData;
@ -519,6 +566,7 @@ class _SendViewState extends ConsumerState<SendView> {
onCryptoAmountChanged = _cryptoAmountChanged;
cryptoAmountController.addListener(onCryptoAmountChanged);
baseAmountController.addListener(_baseAmountChanged);
if (_data != null) {
if (_data!.amount != null) {
@ -534,43 +582,47 @@ class _SendViewState extends ConsumerState<SendView> {
noteController.text = "PayNym send";
}
if (coin != Coin.epicCash) {
_cryptoFocus.addListener(() {
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
if (_amountToSend == null) {
setState(() {
_calculateFeesFuture = calculateFees(0);
});
} else {
setState(() {
_calculateFeesFuture = calculateFees(
Format.decimalAmountToSatoshis(_amountToSend!, coin));
});
}
}
});
// if (coin != Coin.epicCash) {
// _cryptoFocus.addListener(() {
// if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
// if (_amountToSend == null) {
// setState(() {
// _calculateFeesFuture = calculateFees(0);
// });
// } else {
// setState(() {
// _calculateFeesFuture = calculateFees(
// Format.decimalAmountToSatoshis(_amountToSend!, coin));
// });
// }
// }
// });
_baseFocus.addListener(() {
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
if (_amountToSend == null) {
setState(() {
_calculateFeesFuture = calculateFees(0);
});
} else {
setState(() {
_calculateFeesFuture = calculateFees(
Format.decimalAmountToSatoshis(_amountToSend!, coin));
});
}
}
});
}
// _baseFocus.addListener(() {
// if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
// if (_amountToSend == null) {
// setState(() {
// _calculateFeesFuture = calculateFees(0);
// });
// } else {
// setState(() {
// _calculateFeesFuture = calculateFees(
// Format.decimalAmountToSatoshis(_amountToSend!, coin));
// });
// }
// }
// });
// }
super.initState();
}
@override
void dispose() {
_cryptoAmountChangedFeeUpdateTimer?.cancel();
_baseAmountChangedFeeUpdateTimer?.cancel();
cryptoAmountController.removeListener(onCryptoAmountChanged);
baseAmountController.removeListener(_baseAmountChanged);
sendToController.dispose();
cryptoAmountController.dispose();
@ -1569,26 +1621,52 @@ class _SendViewState extends ConsumerState<SendView> {
? "Select coins"
: "Selected coins (${selectedUTXOs.length})",
onTap: () async {
final result =
await Navigator.of(context).pushNamed(
CoinControlView.routeName,
arguments: Tuple4(
walletId,
CoinControlViewType.use,
_amountToSend != null
? Format.decimalAmountToSatoshis(
_amountToSend!,
coin,
)
: null,
selectedUTXOs,
),
);
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 100),
);
}
if (result is Set<UTXO>) {
setState(() {
selectedUTXOs = result;
});
if (mounted) {
final spendable = ref
.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) ??
Decimal.zero,
updateChosen: (String fee) {
_setCurrentFee(
fee,
true,
);
setState(() {
_calculateFeesFuture =
Future(() => fee);
@ -1736,6 +1818,10 @@ class _SendViewState extends ConsumerState<SendView> {
ConnectionState
.done &&
snapshot.hasData) {
_setCurrentFee(
snapshot.data! as String,
false,
);
return Text(
"~${snapshot.data! as String} ${coin.ticker}",
style: STextStyles
@ -1788,6 +1874,11 @@ class _SendViewState extends ConsumerState<SendView> {
ConnectionState
.done &&
snapshot.hasData) {
_setCurrentFee(
snapshot.data!
as String,
false,
);
return Text(
"~${snapshot.data! as String} ${coin.ticker}",
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/isar/models/isar_models.dart' as isar_models;
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/event_bus/events/global/node_connection_status_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;
}
final bool coinControl = utxos != null;
final txData = await coinSelection(
satoshiAmountToSend: satoshiAmount,
selectedTxFeeRate: rate,
recipientAddress: address,
isSendAll: isSendAll,
utxos: utxos?.toList(),
coinControl: utxos is List<isar_models.UTXO>,
coinControl: coinControl,
);
Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
@ -1841,14 +1844,16 @@ class BitcoinWallet extends CoinServiceAPI
String? blockReason;
if (storedTx?.subType ==
isar_models.TransactionSubType.bip47Notification &&
storedTx?.type == isar_models.TransactionType.incoming) {
// probably safe to assume this is an incoming tx as it is a utxo
// belonging to this wallet. The extra check may be redundant but
// just in case...
shouldBlock = true;
blockReason = "Incoming paynym notification transaction.";
isar_models.TransactionSubType.bip47Notification) {
if (storedTx?.type == isar_models.TransactionType.incoming) {
shouldBlock = true;
blockReason = "Incoming paynym notification transaction.";
} else if (storedTx?.type == isar_models.TransactionType.outgoing) {
shouldBlock = true;
blockReason = "Paynym notification change output. Incautious "
"handling of change outputs from notification transactions "
"may cause unintended loss of privacy.";
}
}
final vout = jsonUTXO["tx_pos"] as int;
@ -2360,6 +2365,7 @@ class BitcoinWallet extends CoinServiceAPI
} else {
satoshisBeingUsed = spendableSatoshiValue;
utxoObjectsToUse = spendableOutputs;
inputsBeingConsumed = spendableOutputs.length;
}
Logging.instance
@ -2381,7 +2387,6 @@ class BitcoinWallet extends CoinServiceAPI
.log("Attempting to send all $coin", level: LogLevel.Info);
final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1],
@ -2399,7 +2404,6 @@ class BitcoinWallet extends CoinServiceAPI
final int amount = satoshiAmountToSend - feeForOneOutput;
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: [amount],
@ -2410,7 +2414,7 @@ class BitcoinWallet extends CoinServiceAPI
"recipientAmt": amount,
"fee": feeForOneOutput,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
@ -2418,7 +2422,6 @@ class BitcoinWallet extends CoinServiceAPI
final int vSizeForOneOutput;
try {
vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1],
@ -2431,7 +2434,6 @@ class BitcoinWallet extends CoinServiceAPI
final int vSizeForTwoOutPuts;
try {
vSizeForTwoOutPuts = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [
recipientAddress,
@ -2502,7 +2504,6 @@ class BitcoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2530,7 +2531,6 @@ class BitcoinWallet extends CoinServiceAPI
Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs',
level: LogLevel.Info);
txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2543,7 +2543,7 @@ class BitcoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": feeBeingPaid,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
} else {
@ -2560,7 +2560,6 @@ class BitcoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2571,7 +2570,7 @@ class BitcoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
@ -2590,7 +2589,6 @@ class BitcoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2601,7 +2599,7 @@ class BitcoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
@ -2620,7 +2618,6 @@ class BitcoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2631,7 +2628,7 @@ class BitcoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": feeForOneOutput,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
} else {
@ -2657,241 +2654,144 @@ class BitcoinWallet extends CoinServiceAPI
}
}
Future<Map<String, dynamic>> fetchBuildTxData(
Future<List<SigningData>> fetchBuildTxData(
List<isar_models.UTXO> utxosToUse,
) async {
// return data
Map<String, dynamic> results = {};
Map<String, List<String>> addressTxid = {};
// addresses to check
List<String> addressesP2PKH = [];
List<String> addressesP2SH = [];
List<String> addressesP2WPKH = [];
List<SigningData> signingData = [];
try {
// Populating the addresses to check
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
final tx = await _cachedElectrumXClient.getTransaction(
txHash: txid,
coin: coin,
);
for (final output in tx["vout"] as List) {
final n = output["n"];
if (n != null && n == utxosToUse[i].vout) {
final address = 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");
if (utxosToUse[i].address == null) {
final txid = utxosToUse[i].txid;
final tx = await _cachedElectrumXClient.getTransaction(
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,
);
}
}
}
final derivePathType = addressType(address: utxosToUse[i].address!);
signingData.add(
SigningData(
derivePathType: derivePathType,
utxo: utxosToUse[i],
),
);
}
// p2pkh / bip44
final p2pkhLength = addressesP2PKH.length;
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;
Map<DerivePathType, Map<String, dynamic>> receiveDerivations = {};
Map<DerivePathType, Map<String, dynamic>> changeDerivations = {};
for (String tx in addressTxid[addressesP2PKH[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[addressesP2PKH[i]];
// if a match exists it will not be null
if (changeDerivation != null) {
final data = P2PKH(
for (final sd in signingData) {
String? pubKey;
String? wif;
// fetch receiving derivations if null
receiveDerivations[sd.derivePathType] ??= await _fetchDerivations(
chain: 0,
derivePathType: sd.derivePathType,
);
final receiveDerivation =
receiveDerivations[sd.derivePathType]![sd.utxo.address!];
if (receiveDerivation != null) {
pubKey = receiveDerivation["pubKey"] as String;
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(
pubkey: Format.stringToUint8List(
changeDerivation["pubKey"] as String)),
pubkey: Format.stringToUint8List(pubKey),
),
network: _network,
).data;
redeemScript = null;
break;
for (String tx in addressTxid[addressesP2PKH[i]]!) {
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) {
case DerivePathType.bip49:
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(
pubkey: Format.stringToUint8List(
changeDerivation["pubKey"] as String)),
pubkey: Format.stringToUint8List(pubKey),
),
network: _network,
).data;
redeemScript = p2wpkh.output;
data = P2SH(
data: PaymentData(redeem: p2wpkh),
network: _network,
).data;
break;
for (String tx in addressTxid[addressesP2WPKH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
),
};
}
}
case DerivePathType.bip84:
data = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(pubKey),
),
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) {
Logging.instance
.log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error);
@ -2901,8 +2801,7 @@ class BitcoinWallet extends CoinServiceAPI
/// Builds and signs a transaction
Future<Map<String, dynamic>> buildTransaction({
required List<isar_models.UTXO> utxosToUse,
required Map<String, dynamic> utxoSigningData,
required List<SigningData> utxoSigningData,
required List<String> recipients,
required List<int> satoshiAmounts,
}) async {
@ -2913,10 +2812,14 @@ class BitcoinWallet extends CoinServiceAPI
txb.setVersion(1);
// Add transaction inputs
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
txb.addInput(txid, utxosToUse[i].vout, null,
utxoSigningData[txid]["output"] as Uint8List);
for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxoSigningData[i].utxo.txid;
txb.addInput(
txid,
utxoSigningData[i].utxo.vout,
null,
utxoSigningData[i].output!,
);
}
// Add transaction output
@ -2926,13 +2829,12 @@ class BitcoinWallet extends CoinServiceAPI
try {
// Sign the transaction accordingly
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
for (var i = 0; i < utxoSigningData.length; i++) {
txb.sign(
vin: i,
keyPair: utxoSigningData[txid]["keyPair"] as ECPair,
witnessValue: utxosToUse[i].value,
redeemScript: utxoSigningData[txid]["redeemScript"] as Uint8List?,
keyPair: utxoSigningData[i].keyPair!,
witnessValue: utxoSigningData[i].utxo.value,
redeemScript: utxoSigningData[i].redeemScript,
);
}
} catch (e, s) {

View file

@ -1081,13 +1081,15 @@ class BitcoinCashWallet extends CoinServiceAPI
isSendAll = true;
}
final bool coinControl = utxos != null;
final result = await coinSelection(
satoshiAmountToSend: satoshiAmount,
selectedTxFeeRate: rate,
recipientAddress: address,
isSendAll: isSendAll,
utxos: utxos?.toList(),
coinControl: utxos is List<isar_models.UTXO>,
coinControl: coinControl,
);
Logging.instance
@ -2426,6 +2428,7 @@ class BitcoinCashWallet extends CoinServiceAPI
} else {
satoshisBeingUsed = spendableSatoshiValue;
utxoObjectsToUse = spendableOutputs;
inputsBeingConsumed = spendableOutputs.length;
}
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/isar/models/isar_models.dart' as isar_models;
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/event_bus/events/global/node_connection_status_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;
}
final bool coinControl = utxos != null;
final result = await coinSelection(
satoshiAmountToSend: satoshiAmount,
selectedTxFeeRate: rate,
recipientAddress: address,
isSendAll: isSendAll,
utxos: utxos?.toList(),
coinControl: utxos is List<isar_models.UTXO>,
coinControl: coinControl,
);
Logging.instance
@ -1586,14 +1589,16 @@ class DogecoinWallet extends CoinServiceAPI
String? blockReason;
if (storedTx?.subType ==
isar_models.TransactionSubType.bip47Notification &&
storedTx?.type == isar_models.TransactionType.incoming) {
// probably safe to assume this is an incoming tx as it is a utxo
// belonging to this wallet. The extra check may be redundant but
// just in case...
shouldBlock = true;
blockReason = "Incoming paynym notification transaction.";
isar_models.TransactionSubType.bip47Notification) {
if (storedTx?.type == isar_models.TransactionType.incoming) {
shouldBlock = true;
blockReason = "Incoming paynym notification transaction.";
} else if (storedTx?.type == isar_models.TransactionType.outgoing) {
shouldBlock = true;
blockReason = "Paynym notification change output. Incautious "
"handling of change outputs from notification transactions "
"may cause unintended loss of privacy.";
}
}
final vout = jsonUTXO["tx_pos"] as int;
@ -2126,6 +2131,7 @@ class DogecoinWallet extends CoinServiceAPI
} else {
satoshisBeingUsed = spendableSatoshiValue;
utxoObjectsToUse = spendableOutputs;
inputsBeingConsumed = spendableOutputs.length;
}
Logging.instance
@ -2149,7 +2155,6 @@ class DogecoinWallet extends CoinServiceAPI
.log("Attempting to send all $coin", level: LogLevel.Info);
final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1],
@ -2164,7 +2169,6 @@ class DogecoinWallet extends CoinServiceAPI
final int amount = satoshiAmountToSend - feeForOneOutput;
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: [amount],
@ -2175,19 +2179,17 @@ class DogecoinWallet extends CoinServiceAPI
"recipientAmt": amount,
"fee": feeForOneOutput,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1],
))["vSize"] as int;
final int vSizeForTwoOutPuts = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [
recipientAddress,
@ -2267,7 +2269,6 @@ class DogecoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2295,7 +2296,6 @@ class DogecoinWallet extends CoinServiceAPI
Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs',
level: LogLevel.Info);
txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2308,7 +2308,7 @@ class DogecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": feeBeingPaid,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
} else {
@ -2325,7 +2325,6 @@ class DogecoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2336,7 +2335,7 @@ class DogecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
@ -2355,7 +2354,6 @@ class DogecoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2366,7 +2364,7 @@ class DogecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
@ -2385,7 +2383,6 @@ class DogecoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2396,7 +2393,7 @@ class DogecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": feeForOneOutput,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
} else {
@ -2422,103 +2419,120 @@ class DogecoinWallet extends CoinServiceAPI
}
}
Future<Map<String, dynamic>> fetchBuildTxData(
Future<List<SigningData>> fetchBuildTxData(
List<isar_models.UTXO> utxosToUse,
) async {
// return data
Map<String, dynamic> results = {};
Map<String, List<String>> addressTxid = {};
// addresses to check
List<String> addressesP2PKH = [];
List<SigningData> signingData = [];
try {
// Populating the addresses to check
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
final tx = await _cachedElectrumXClient.getTransaction(
txHash: txid,
coin: coin,
);
for (final output in tx["vout"] as List) {
final n = output["n"];
if (n != null && n == utxosToUse[i].vout) {
final address = output["scriptPubKey"]["addresses"][0] 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;
default:
throw Exception("Unsupported DerivePathType");
if (utxosToUse[i].address == null) {
final txid = utxosToUse[i].txid;
final tx = await _cachedElectrumXClient.getTransaction(
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,
);
}
}
}
final derivePathType = addressType(address: utxosToUse[i].address!);
signingData.add(
SigningData(
derivePathType: derivePathType,
utxo: utxosToUse[i],
),
);
}
// p2pkh / bip44
final p2pkhLength = addressesP2PKH.length;
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;
Map<DerivePathType, Map<String, dynamic>> receiveDerivations = {};
Map<DerivePathType, Map<String, dynamic>> changeDerivations = {};
for (String tx in addressTxid[addressesP2PKH[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[addressesP2PKH[i]];
// if a match exists it will not be null
if (changeDerivation != null) {
final data = P2PKH(
for (final sd in signingData) {
String? pubKey;
String? wif;
// fetch receiving derivations if null
receiveDerivations[sd.derivePathType] ??= await _fetchDerivations(
chain: 0,
derivePathType: sd.derivePathType,
);
final receiveDerivation =
receiveDerivations[sd.derivePathType]![sd.utxo.address!];
if (receiveDerivation != null) {
pubKey = receiveDerivation["pubKey"] as String;
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(
pubkey: Format.stringToUint8List(
changeDerivation["pubKey"] as String)),
pubkey: Format.stringToUint8List(pubKey),
),
network: network,
).data;
redeemScript = null;
break;
for (String tx in addressTxid[addressesP2PKH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: network,
),
};
}
}
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) {
Logging.instance
.log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error);
@ -2528,8 +2542,7 @@ class DogecoinWallet extends CoinServiceAPI
/// Builds and signs a transaction
Future<Map<String, dynamic>> buildTransaction({
required List<isar_models.UTXO> utxosToUse,
required Map<String, dynamic> utxoSigningData,
required List<SigningData> utxoSigningData,
required List<String> recipients,
required List<int> satoshiAmounts,
}) async {
@ -2540,10 +2553,14 @@ class DogecoinWallet extends CoinServiceAPI
txb.setVersion(1);
// Add transaction inputs
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
txb.addInput(txid, utxosToUse[i].vout, null,
utxoSigningData[txid]["output"] as Uint8List);
for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxoSigningData[i].utxo.txid;
txb.addInput(
txid,
utxoSigningData[i].utxo.vout,
null,
utxoSigningData[i].output!,
);
}
// Add transaction output
@ -2553,13 +2570,12 @@ class DogecoinWallet extends CoinServiceAPI
try {
// Sign the transaction accordingly
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
for (var i = 0; i < utxoSigningData.length; i++) {
txb.sign(
vin: i,
keyPair: utxoSigningData[txid]["keyPair"] as ECPair,
witnessValue: utxosToUse[i].value,
redeemScript: utxoSigningData[txid]["redeemScript"] as Uint8List?,
keyPair: utxoSigningData[i].keyPair!,
witnessValue: utxoSigningData[i].utxo.value,
redeemScript: utxoSigningData[i].redeemScript,
);
}
} 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_fee_data.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/event_bus/events/global/node_connection_status_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/default_nodes.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/flutter_secure_storage_interface.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);
final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [_recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1],
@ -1383,7 +1384,6 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
final int amount = satoshiAmountToSend - feeForOneOutput;
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: [amount],
@ -1399,13 +1399,11 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
}
final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [_recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1],
))["vSize"] as int;
final int vSizeForTwoOutPuts = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [
_recipientAddress,
@ -1484,7 +1482,6 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
Logging.instance
.log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -1512,7 +1509,6 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs',
level: LogLevel.Info);
txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -1541,7 +1537,6 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -1570,7 +1565,6 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -1599,7 +1593,6 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
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,
) async {
// return data
Map<String, dynamic> results = {};
Map<String, List<String>> addressTxid = {};
// addresses to check
List<String> addresses = [];
List<SigningData> signingData = [];
try {
// Populating the addresses to check
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
final tx = await _cachedElectrumXClient.getTransaction(
txHash: txid,
coin: coin,
if (utxosToUse[i].address == null) {
final txid = utxosToUse[i].txid;
final tx = await _cachedElectrumXClient.getTransaction(
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) {
final n = output["n"];
if (n != null && n == utxosToUse[i].vout) {
final address = output["scriptPubKey"]["addresses"][0] as String;
if (!addressTxid.containsKey(address)) {
addressTxid[address] = <String>[];
}
(addressTxid[address] as List).add(txid);
addresses.add(address);
dynamic receiveDerivation;
for (int j = 0;
j < receiveDerivations[sd.derivePathType]!.length &&
receiveDerivation == null;
j++) {
if (receiveDerivations[sd.derivePathType]!["$j"]["address"] ==
sd.utxo.address!) {
receiveDerivation = receiveDerivations[sd.derivePathType]!["$j"];
}
}
}
// p2pkh / bip44
final addressesLength = addresses.length;
if (addressesLength > 0) {
final receiveDerivationsString =
await _secureStore.read(key: "${walletId}_receiveDerivations");
final receiveDerivations = Map<String, dynamic>.from(
jsonDecode(receiveDerivationsString ?? "{}") as Map);
if (receiveDerivation != null) {
pubKey = receiveDerivation["publicKey"] as String;
wif = receiveDerivation["wif"] as String;
} else {
// fetch change derivations if null
changeDerivations[sd.derivePathType] ??= Map<String, dynamic>.from(
jsonDecode((await _secureStore.read(
key: "${walletId}_changeDerivations",
)) ??
"{}") as Map,
);
final changeDerivationsString =
await _secureStore.read(key: "${walletId}_changeDerivations");
final changeDerivations = Map<String, dynamic>.from(
jsonDecode(changeDerivationsString ?? "{}") as Map);
for (int i = 0; i < addressesLength; i++) {
// receives
dynamic receiveDerivation;
for (int j = 0; j < receiveDerivations.length; j++) {
if (receiveDerivations["$j"]["address"] == addresses[i]) {
receiveDerivation = receiveDerivations["$j"];
dynamic changeDerivation;
for (int j = 0;
j < changeDerivations[sd.derivePathType]!.length &&
changeDerivation == null;
j++) {
if (changeDerivations[sd.derivePathType]!["$j"]["address"] ==
sd.utxo.address!) {
changeDerivation = changeDerivations[sd.derivePathType]!["$j"];
}
}
// receiveDerivation = receiveDerivations[addresses[i]];
// if a match exists it will not be null
if (receiveDerivation != null) {
final data = P2PKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
receiveDerivation["publicKey"] as String)),
network: _network,
).data;
if (changeDerivation != null) {
pubKey = changeDerivation["publicKey"] as String;
wif = changeDerivation["wif"] as String;
}
}
for (String tx in addressTxid[addresses[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
receiveDerivation["wif"] as String,
network: _network,
),
};
}
} else {
// if its not a receive, check change
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,
);
dynamic changeDerivation;
wif = node.toWIF();
pubKey = Format.uint8listToString(node.publicKey);
}
}
for (int j = 0; j < changeDerivations.length; j++) {
if (changeDerivations["$j"]["address"] == addresses[i]) {
changeDerivation = changeDerivations["$j"];
}
}
if (wif != null && pubKey != null) {
final PaymentData data;
final Uint8List? redeemScript;
// final changeDerivation = changeDerivations[addresses[i]];
// if a match exists it will not be null
if (changeDerivation != null) {
final data = P2PKH(
switch (sd.derivePathType) {
case DerivePathType.bip44:
data = P2PKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
changeDerivation["publicKey"] as String)),
pubkey: Format.stringToUint8List(pubKey),
),
network: _network,
).data;
redeemScript = null;
break;
for (String tx in addressTxid[addresses[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
),
};
}
}
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) {
Logging.instance
.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
Future<Map<String, dynamic>> buildTransaction({
required List<isar_models.UTXO> utxosToUse,
required Map<String, dynamic> utxoSigningData,
required List<SigningData> utxoSigningData,
required List<String> recipients,
required List<int> satoshiAmounts,
}) async {
@ -1763,10 +1777,14 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
txb.setVersion(1);
// Add transaction inputs
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
txb.addInput(txid, utxosToUse[i].vout, null,
utxoSigningData[txid]["output"] as Uint8List);
for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxoSigningData[i].utxo.txid;
txb.addInput(
txid,
utxoSigningData[i].utxo.vout,
null,
utxoSigningData[i].output!,
);
}
// Add transaction output
@ -1776,13 +1794,12 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
try {
// Sign the transaction accordingly
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
for (var i = 0; i < utxoSigningData.length; i++) {
txb.sign(
vin: i,
keyPair: utxoSigningData[txid]["keyPair"] as ECPair,
witnessValue: utxosToUse[i].value,
redeemScript: utxoSigningData[txid]["redeemScript"] as Uint8List?,
keyPair: utxoSigningData[i].keyPair!,
witnessValue: utxoSigningData[i].utxo.value,
redeemScript: utxoSigningData[i].redeemScript,
);
}
} 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/isar/models/isar_models.dart' as isar_models;
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/event_bus/events/global/node_connection_status_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;
}
final bool coinControl = utxos != null;
final txData = await coinSelection(
satoshiAmountToSend: satoshiAmount,
selectedTxFeeRate: rate,
recipientAddress: address,
isSendAll: isSendAll,
utxos: utxos?.toList(),
coinControl: utxos is List<isar_models.UTXO>,
coinControl: coinControl,
);
Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
@ -2314,6 +2317,7 @@ class LitecoinWallet extends CoinServiceAPI
} else {
satoshisBeingUsed = spendableSatoshiValue;
utxoObjectsToUse = spendableOutputs;
inputsBeingConsumed = spendableOutputs.length;
}
Logging.instance
@ -2335,7 +2339,6 @@ class LitecoinWallet extends CoinServiceAPI
.log("Attempting to send all $coin", level: LogLevel.Info);
final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1],
@ -2353,7 +2356,6 @@ class LitecoinWallet extends CoinServiceAPI
final int amount = satoshiAmountToSend - feeForOneOutput;
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: [amount],
@ -2364,19 +2366,17 @@ class LitecoinWallet extends CoinServiceAPI
"recipientAmt": amount,
"fee": feeForOneOutput,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1],
))["vSize"] as int;
final int vSizeForTwoOutPuts = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [
recipientAddress,
@ -2442,7 +2442,6 @@ class LitecoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2470,7 +2469,6 @@ class LitecoinWallet extends CoinServiceAPI
Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs',
level: LogLevel.Info);
txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2483,7 +2481,7 @@ class LitecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": feeBeingPaid,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
} else {
@ -2500,7 +2498,6 @@ class LitecoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2511,7 +2508,7 @@ class LitecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
@ -2530,7 +2527,6 @@ class LitecoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2541,7 +2537,7 @@ class LitecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
@ -2560,7 +2556,6 @@ class LitecoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2571,7 +2566,7 @@ class LitecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": feeForOneOutput,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
} else {
@ -2597,245 +2592,146 @@ class LitecoinWallet extends CoinServiceAPI
}
}
Future<Map<String, dynamic>> fetchBuildTxData(
Future<List<SigningData>> fetchBuildTxData(
List<isar_models.UTXO> utxosToUse,
) async {
// return data
Map<String, dynamic> results = {};
Map<String, List<String>> addressTxid = {};
// addresses to check
List<String> addressesP2PKH = [];
List<String> addressesP2SH = [];
List<String> addressesP2WPKH = [];
List<SigningData> signingData = [];
try {
// Populating the addresses to check
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
final tx = await _cachedElectrumXClient.getTransaction(
txHash: txid,
coin: coin,
);
for (final output in tx["vout"] as List) {
final n = output["n"];
if (n != null && n == utxosToUse[i].vout) {
final address = output["scriptPubKey"]["addresses"][0] 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");
if (utxosToUse[i].address == null) {
final txid = utxosToUse[i].txid;
final tx = await _cachedElectrumXClient.getTransaction(
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,
);
}
}
}
final derivePathType = addressType(address: utxosToUse[i].address!);
signingData.add(
SigningData(
derivePathType: derivePathType,
utxo: utxosToUse[i],
),
);
}
// p2pkh / bip44
final p2pkhLength = addressesP2PKH.length;
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;
Map<DerivePathType, Map<String, dynamic>> receiveDerivations = {};
Map<DerivePathType, Map<String, dynamic>> changeDerivations = {};
for (String tx in addressTxid[addressesP2PKH[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[addressesP2PKH[i]];
// if a match exists it will not be null
if (changeDerivation != null) {
final data = P2PKH(
for (final sd in signingData) {
String? pubKey;
String? wif;
// fetch receiving derivations if null
receiveDerivations[sd.derivePathType] ??= await _fetchDerivations(
chain: 0,
derivePathType: sd.derivePathType,
);
final receiveDerivation =
receiveDerivations[sd.derivePathType]![sd.utxo.address!];
if (receiveDerivation != null) {
pubKey = receiveDerivation["pubKey"] as String;
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(
pubkey: Format.stringToUint8List(
changeDerivation["pubKey"] as String)),
pubkey: Format.stringToUint8List(pubKey),
),
network: _network,
).data;
redeemScript = null;
break;
for (String tx in addressTxid[addressesP2PKH[i]]!) {
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) {
case DerivePathType.bip49:
final p2wpkh = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
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,
data: PaymentData(
pubkey: Format.stringToUint8List(pubKey),
),
};
}
} 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(
pubkey: Format.stringToUint8List(
changeDerivation["pubKey"] as String)),
network: _network,
overridePrefix: _network.bech32!)
.data;
network: _network,
overridePrefix: _network.bech32!,
).data;
redeemScript = p2wpkh.output;
data = P2SH(
data: PaymentData(redeem: p2wpkh),
network: _network,
).data;
break;
for (String tx in addressTxid[addressesP2WPKH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
),
};
}
}
case DerivePathType.bip84:
data = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(pubKey),
),
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) {
Logging.instance
.log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error);
@ -2845,8 +2741,7 @@ class LitecoinWallet extends CoinServiceAPI
/// Builds and signs a transaction
Future<Map<String, dynamic>> buildTransaction({
required List<isar_models.UTXO> utxosToUse,
required Map<String, dynamic> utxoSigningData,
required List<SigningData> utxoSigningData,
required List<String> recipients,
required List<int> satoshiAmounts,
}) async {
@ -2857,10 +2752,15 @@ class LitecoinWallet extends CoinServiceAPI
txb.setVersion(1);
// Add transaction inputs
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
txb.addInput(txid, utxosToUse[i].vout, null,
utxoSigningData[txid]["output"] as Uint8List, _network.bech32!);
for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxoSigningData[i].utxo.txid;
txb.addInput(
txid,
utxoSigningData[i].utxo.vout,
null,
utxoSigningData[i].output!,
_network.bech32!,
);
}
// Add transaction output
@ -2870,14 +2770,14 @@ class LitecoinWallet extends CoinServiceAPI
try {
// Sign the transaction accordingly
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
for (var i = 0; i < utxoSigningData.length; i++) {
txb.sign(
vin: i,
keyPair: utxoSigningData[txid]["keyPair"] as ECPair,
witnessValue: utxosToUse[i].value,
redeemScript: utxoSigningData[txid]["redeemScript"] as Uint8List?,
overridePrefix: _network.bech32!);
vin: i,
keyPair: utxoSigningData[i].keyPair!,
witnessValue: utxoSigningData[i].utxo.value,
redeemScript: utxoSigningData[i].redeemScript,
overridePrefix: _network.bech32!,
);
}
} catch (e, 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/isar/models/isar_models.dart' as isar_models;
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/event_bus/events/global/node_connection_status_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;
}
final bool coinControl = utxos != null;
final txData = await coinSelection(
satoshiAmountToSend: satoshiAmount,
selectedTxFeeRate: rate,
recipientAddress: address,
isSendAll: isSendAll,
utxos: utxos?.toList(),
coinControl: utxos is List<isar_models.UTXO>,
coinControl: coinControl,
);
Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
@ -2307,6 +2310,7 @@ class NamecoinWallet extends CoinServiceAPI
} else {
satoshisBeingUsed = spendableSatoshiValue;
utxoObjectsToUse = spendableOutputs;
inputsBeingConsumed = spendableOutputs.length;
}
Logging.instance
@ -2328,7 +2332,6 @@ class NamecoinWallet extends CoinServiceAPI
.log("Attempting to send all $coin", level: LogLevel.Info);
final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1],
@ -2346,7 +2349,6 @@ class NamecoinWallet extends CoinServiceAPI
final int amount = satoshiAmountToSend - feeForOneOutput;
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: [amount],
@ -2357,19 +2359,17 @@ class NamecoinWallet extends CoinServiceAPI
"recipientAmt": amount,
"fee": feeForOneOutput,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1],
))["vSize"] as int;
final int vSizeForTwoOutPuts = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [
recipientAddress,
@ -2435,7 +2435,6 @@ class NamecoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2463,7 +2462,6 @@ class NamecoinWallet extends CoinServiceAPI
Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs',
level: LogLevel.Info);
txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2476,7 +2474,7 @@ class NamecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": feeBeingPaid,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
} else {
@ -2493,7 +2491,6 @@ class NamecoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2504,7 +2501,7 @@ class NamecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
@ -2523,7 +2520,6 @@ class NamecoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2534,7 +2530,7 @@ class NamecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
@ -2553,7 +2549,6 @@ class NamecoinWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2564,7 +2559,7 @@ class NamecoinWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": feeForOneOutput,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
} else {
@ -2590,248 +2585,144 @@ class NamecoinWallet extends CoinServiceAPI
}
}
Future<Map<String, dynamic>> fetchBuildTxData(
Future<List<SigningData>> fetchBuildTxData(
List<isar_models.UTXO> utxosToUse,
) async {
// return data
Map<String, dynamic> results = {};
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);
List<SigningData> signingData = [];
try {
// Populating the addresses to check
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
final tx = await _cachedElectrumXClient.getTransaction(
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"];
if (n != null && n == utxosToUse[i].vout) {
final address = 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");
if (utxosToUse[i].address == null) {
final txid = utxosToUse[i].txid;
final tx = await _cachedElectrumXClient.getTransaction(
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,
);
}
}
}
final derivePathType = addressType(address: utxosToUse[i].address!);
signingData.add(
SigningData(
derivePathType: derivePathType,
utxo: utxosToUse[i],
),
);
}
// p2pkh / bip44
final p2pkhLength = addressesP2PKH.length;
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;
Map<DerivePathType, Map<String, dynamic>> receiveDerivations = {};
Map<DerivePathType, Map<String, dynamic>> changeDerivations = {};
for (String tx in addressTxid[addressesP2PKH[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[addressesP2PKH[i]];
// if a match exists it will not be null
if (changeDerivation != null) {
final data = P2PKH(
for (final sd in signingData) {
String? pubKey;
String? wif;
// fetch receiving derivations if null
receiveDerivations[sd.derivePathType] ??= await _fetchDerivations(
chain: 0,
derivePathType: sd.derivePathType,
);
final receiveDerivation =
receiveDerivations[sd.derivePathType]![sd.utxo.address!];
if (receiveDerivation != null) {
pubKey = receiveDerivation["pubKey"] as String;
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(
pubkey: Format.stringToUint8List(
changeDerivation["pubKey"] as String)),
pubkey: Format.stringToUint8List(pubKey),
),
network: _network,
).data;
redeemScript = null;
break;
for (String tx in addressTxid[addressesP2PKH[i]]!) {
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) {
case DerivePathType.bip49:
final p2wpkh = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
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,
data: PaymentData(
pubkey: Format.stringToUint8List(pubKey),
),
};
}
} 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(
pubkey: Format.stringToUint8List(
changeDerivation["pubKey"] as String)),
network: _network,
overridePrefix: namecoin.bech32!)
network: _network,
overridePrefix: _network.bech32!,
).data;
redeemScript = p2wpkh.output;
data = P2SH(data: PaymentData(redeem: p2wpkh), network: _network)
.data;
break;
for (String tx in addressTxid[addressesP2WPKH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
),
};
}
}
case DerivePathType.bip84:
data = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(pubKey),
),
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) {
Logging.instance
.log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error);
@ -2841,8 +2732,7 @@ class NamecoinWallet extends CoinServiceAPI
/// Builds and signs a transaction
Future<Map<String, dynamic>> buildTransaction({
required List<isar_models.UTXO> utxosToUse,
required Map<String, dynamic> utxoSigningData,
required List<SigningData> utxoSigningData,
required List<String> recipients,
required List<int> satoshiAmounts,
}) async {
@ -2853,10 +2743,15 @@ class NamecoinWallet extends CoinServiceAPI
txb.setVersion(2);
// Add transaction inputs
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
txb.addInput(txid, utxosToUse[i].vout, null,
utxoSigningData[txid]["output"] as Uint8List, namecoin.bech32!);
for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxoSigningData[i].utxo.txid;
txb.addInput(
txid,
utxoSigningData[i].utxo.vout,
null,
utxoSigningData[i].output!,
_network.bech32!,
);
}
// Add transaction output
@ -2866,14 +2761,15 @@ class NamecoinWallet extends CoinServiceAPI
try {
// Sign the transaction accordingly
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
txb.sign(
vin: i,
keyPair: utxoSigningData[txid]["keyPair"] as ECPair,
witnessValue: utxosToUse[i].value,
redeemScript: utxoSigningData[txid]["redeemScript"] as Uint8List?,
overridePrefix: namecoin.bech32!);
for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxoSigningData[i].utxo.txid;
txb.addInput(
txid,
utxoSigningData[i].utxo.vout,
null,
utxoSigningData[i].output!,
_network.bech32!,
);
}
} catch (e, 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/isar/models/isar_models.dart' as isar_models;
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/event_bus/events/global/node_connection_status_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;
}
final bool coinControl = utxos != null;
final txData = await coinSelection(
satoshiAmountToSend: satoshiAmount,
selectedTxFeeRate: rate,
recipientAddress: address,
isSendAll: isSendAll,
utxos: utxos?.toList(),
coinControl: utxos is List<isar_models.UTXO>,
coinControl: coinControl,
);
Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
@ -2468,6 +2471,7 @@ class ParticlWallet extends CoinServiceAPI
} else {
satoshisBeingUsed = spendableSatoshiValue;
utxoObjectsToUse = spendableOutputs;
inputsBeingConsumed = spendableOutputs.length;
}
Logging.instance
@ -2489,7 +2493,6 @@ class ParticlWallet extends CoinServiceAPI
.log("Attempting to send all $coin", level: LogLevel.Info);
final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1],
@ -2507,7 +2510,6 @@ class ParticlWallet extends CoinServiceAPI
final int amount = satoshiAmountToSend - feeForOneOutput;
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: [amount],
@ -2518,19 +2520,17 @@ class ParticlWallet extends CoinServiceAPI
"recipientAmt": amount,
"fee": feeForOneOutput,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
final int vSizeForOneOutput = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [recipientAddress],
satoshiAmounts: [satoshisBeingUsed - 1],
))["vSize"] as int;
final int vSizeForTwoOutPuts = (await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: [
recipientAddress,
@ -2596,7 +2596,6 @@ class ParticlWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForTwoOutputs', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2624,7 +2623,6 @@ class ParticlWallet extends CoinServiceAPI
Logging.instance.log('Adjusted Estimated fee: $feeForTwoOutputs',
level: LogLevel.Info);
txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2637,7 +2635,7 @@ class ParticlWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": feeBeingPaid,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
} else {
@ -2654,7 +2652,6 @@ class ParticlWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2665,7 +2662,7 @@ class ParticlWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
@ -2684,7 +2681,6 @@ class ParticlWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2695,7 +2691,7 @@ class ParticlWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": satoshisBeingUsed - satoshiAmountToSend,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
@ -2714,7 +2710,6 @@ class ParticlWallet extends CoinServiceAPI
Logging.instance
.log('Estimated fee: $feeForOneOutput', level: LogLevel.Info);
dynamic txn = await buildTransaction(
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
recipients: recipientsArray,
satoshiAmounts: recipientsAmtArray,
@ -2725,7 +2720,7 @@ class ParticlWallet extends CoinServiceAPI
"recipientAmt": recipientsAmtArray[0],
"fee": feeForOneOutput,
"vSize": txn["vSize"],
"usedUTXOs": utxoObjectsToUse,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
} else {
@ -2751,168 +2746,130 @@ class ParticlWallet extends CoinServiceAPI
}
}
Future<Map<String, dynamic>> fetchBuildTxData(
Future<List<SigningData>> fetchBuildTxData(
List<isar_models.UTXO> utxosToUse,
) async {
// return data
Map<String, dynamic> results = {};
Map<String, List<String>> addressTxid = {};
// addresses to check
List<String> addressesP2PKH = [];
List<String> addressesP2WPKH = [];
List<SigningData> signingData = [];
try {
// Populating the addresses to check
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
final tx = await _cachedElectrumXClient.getTransaction(
txHash: txid,
coin: coin,
);
for (final output in tx["vout"] as List) {
final n = output["n"];
if (n != null && n == utxosToUse[i].vout) {
final address = output["scriptPubKey"]["addresses"][0] 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.bip84:
addressesP2WPKH.add(address);
break;
default:
throw Exception(
"DerivePathType ${addressType(address: address)} not supported");
if (utxosToUse[i].address == null) {
final txid = utxosToUse[i].txid;
final tx = await _cachedElectrumXClient.getTransaction(
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,
);
}
}
}
final derivePathType = addressType(address: utxosToUse[i].address!);
signingData.add(
SigningData(
derivePathType: derivePathType,
utxo: utxosToUse[i],
),
);
}
// p2pkh / bip44
final p2pkhLength = addressesP2PKH.length;
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;
Map<DerivePathType, Map<String, dynamic>> receiveDerivations = {};
Map<DerivePathType, Map<String, dynamic>> changeDerivations = {};
for (String tx in addressTxid[addressesP2PKH[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[addressesP2PKH[i]];
// if a match exists it will not be null
if (changeDerivation != null) {
final data = P2PKH(
for (final sd in signingData) {
String? pubKey;
String? wif;
// fetch receiving derivations if null
receiveDerivations[sd.derivePathType] ??= await _fetchDerivations(
chain: 0,
derivePathType: sd.derivePathType,
);
final receiveDerivation =
receiveDerivations[sd.derivePathType]![sd.utxo.address!];
if (receiveDerivation != null) {
pubKey = receiveDerivation["pubKey"] as String;
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(
pubkey: Format.stringToUint8List(
changeDerivation["pubKey"] as String)),
pubkey: Format.stringToUint8List(pubKey),
),
network: _network,
).data;
redeemScript = null;
break;
for (String tx in addressTxid[addressesP2PKH[i]]!) {
results[tx] = {
"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(
case DerivePathType.bip84:
data = P2WPKH(
data: PaymentData(
pubkey: Format.stringToUint8List(
changeDerivation["pubKey"] as String)),
pubkey: Format.stringToUint8List(pubKey),
),
network: _network,
).data;
redeemScript = null;
break;
for (String tx in addressTxid[addressesP2WPKH[i]]!) {
results[tx] = {
"output": data.output,
"keyPair": ECPair.fromWIF(
changeDerivation["wif"] as String,
network: _network,
),
};
}
}
default:
throw Exception("DerivePathType unsupported");
}
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 results;
return signingData;
} catch (e, s) {
Logging.instance
.log("fetchBuildTxData() threw: $e,\n$s", level: LogLevel.Error);
@ -2922,8 +2879,7 @@ class ParticlWallet extends CoinServiceAPI
/// Builds and signs a transaction
Future<Map<String, dynamic>> buildTransaction({
required List<isar_models.UTXO> utxosToUse,
required Map<String, dynamic> utxoSigningData,
required List<SigningData> utxoSigningData,
required List<String> recipients,
required List<int> satoshiAmounts,
}) async {
@ -2937,11 +2893,15 @@ class ParticlWallet extends CoinServiceAPI
txb.setVersion(160);
// Add transaction inputs
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
txb.addInput(txid, utxosToUse[i].vout, null,
utxoSigningData[txid]["output"] as Uint8List, '');
for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxoSigningData[i].utxo.txid;
txb.addInput(
txid,
utxoSigningData[i].utxo.vout,
null,
utxoSigningData[i].output!,
'',
);
}
// Add transaction output
@ -2951,13 +2911,13 @@ class ParticlWallet extends CoinServiceAPI
try {
// Sign the transaction accordingly
for (var i = 0; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
for (var i = 0; i < utxoSigningData.length; i++) {
txb.sign(
vin: i,
keyPair: utxoSigningData[txid]["keyPair"] as ECPair,
witnessValue: utxosToUse[i].value,
redeemScript: utxoSigningData[txid]["redeemScript"] as Uint8List?);
vin: i,
keyPair: utxoSigningData[i].keyPair!,
witnessValue: utxoSigningData[i].utxo.value,
redeemScript: utxoSigningData[i].redeemScript,
);
}
} catch (e, 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/paynym_send_exception.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/bip47_utils.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -57,7 +58,7 @@ mixin PaynymWalletInterface {
late final Future<int> Function({
required String address,
}) _getTxCount;
late final Future<Map<String, dynamic>> Function(
late final Future<List<SigningData>> Function(
List<UTXO> utxosToUse,
) _fetchBuildTxData;
late final Future<void> Function() _refresh;
@ -100,7 +101,7 @@ mixin PaynymWalletInterface {
required String address,
})
getTxCount,
required Future<Map<String, dynamic>> Function(
required Future<List<SigningData>> Function(
List<UTXO> utxosToUse,
)
fetchBuildTxData,
@ -455,7 +456,6 @@ mixin PaynymWalletInterface {
final int vSizeForNoChange = (await _createNotificationTx(
targetPaymentCodeString: targetPaymentCodeString,
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
change: 0,
dustLimit:
@ -465,7 +465,6 @@ mixin PaynymWalletInterface {
final int vSizeForWithChange = (await _createNotificationTx(
targetPaymentCodeString: targetPaymentCodeString,
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
change: satoshisBeingUsed - amountToSend,
))
@ -503,7 +502,6 @@ mixin PaynymWalletInterface {
feeForWithChange) {
var txn = await _createNotificationTx(
targetPaymentCodeString: targetPaymentCodeString,
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
change: changeAmount,
);
@ -516,7 +514,6 @@ mixin PaynymWalletInterface {
feeBeingPaid += 1;
txn = await _createNotificationTx(
targetPaymentCodeString: targetPaymentCodeString,
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
change: changeAmount,
);
@ -528,6 +525,7 @@ mixin PaynymWalletInterface {
"amount": amountToSend,
"fee": feeBeingPaid,
"vSize": txn.item2,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
} else {
@ -535,7 +533,6 @@ mixin PaynymWalletInterface {
// than the dust limit. Try without change
final txn = await _createNotificationTx(
targetPaymentCodeString: targetPaymentCodeString,
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
change: 0,
);
@ -548,6 +545,7 @@ mixin PaynymWalletInterface {
"amount": amountToSend,
"fee": feeBeingPaid,
"vSize": txn.item2,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
}
@ -556,7 +554,6 @@ mixin PaynymWalletInterface {
// build without change here
final txn = await _createNotificationTx(
targetPaymentCodeString: targetPaymentCodeString,
utxosToUse: utxoObjectsToUse,
utxoSigningData: utxoSigningData,
change: 0,
);
@ -569,6 +566,7 @@ mixin PaynymWalletInterface {
"amount": amountToSend,
"fee": feeBeingPaid,
"vSize": txn.item2,
"usedUTXOs": utxoSigningData.map((e) => e.utxo).toList(),
};
return transactionObject;
} else {
@ -594,8 +592,7 @@ mixin PaynymWalletInterface {
// equal to its vSize
Future<Tuple2<String, int>> _createNotificationTx({
required String targetPaymentCodeString,
required List<UTXO> utxosToUse,
required Map<String, dynamic> utxoSigningData,
required List<SigningData> utxoSigningData,
required int change,
int? dustLimit,
}) async {
@ -604,7 +601,7 @@ mixin PaynymWalletInterface {
PaymentCode.fromPaymentCode(targetPaymentCodeString, _network);
final myCode = await getPaymentCode(DerivePathType.bip44);
final utxo = utxosToUse.first;
final utxo = utxoSigningData.first.utxo;
final txPoint = utxo.txid.fromHex.reversed.toList();
final txPointIndex = utxo.vout;
@ -613,8 +610,7 @@ mixin PaynymWalletInterface {
final buffer = rev.buffer.asByteData();
buffer.setUint32(txPoint.length, txPointIndex, Endian.little);
final myKeyPair =
utxoSigningData[utxo.txid]["keyPair"] as btc_dart.ECPair;
final myKeyPair = utxoSigningData.first.keyPair!;
final S = SecretPoint(
myKeyPair.privateKey!,
@ -642,17 +638,17 @@ mixin PaynymWalletInterface {
utxo.txid,
txPointIndex,
null,
utxoSigningData[utxo.txid]["output"] as Uint8List,
utxoSigningData.first.output!,
);
// add rest of possible inputs
for (var i = 1; i < utxosToUse.length; i++) {
final utxo = utxosToUse[i];
for (var i = 1; i < utxoSigningData.length; i++) {
final utxo = utxoSigningData[i].utxo;
txb.addInput(
utxo.txid,
utxo.vout,
null,
utxoSigningData[utxo.txid]["output"] as Uint8List,
utxoSigningData[i].output!,
);
}
@ -675,18 +671,16 @@ mixin PaynymWalletInterface {
vin: 0,
keyPair: myKeyPair,
witnessValue: utxo.value,
witnessScript: utxoSigningData[utxo.txid]["redeemScript"] as Uint8List?,
witnessScript: utxoSigningData.first.redeemScript,
);
// sign rest of possible inputs
for (var i = 1; i < utxosToUse.length; i++) {
final txid = utxosToUse[i].txid;
for (var i = 1; i < utxoSigningData.length; i++) {
txb.sign(
vin: i,
keyPair: utxoSigningData[txid]["keyPair"] as btc_dart.ECPair,
witnessValue: utxosToUse[i].value,
witnessScript:
utxoSigningData[utxo.txid]["redeemScript"] as Uint8List?,
keyPair: utxoSigningData[i].keyPair!,
witnessValue: utxoSigningData[i].utxo.value,
witnessScript: utxoSigningData[i].redeemScript,
);
}

View file

@ -21,20 +21,24 @@ class StackDialogBase extends StatelessWidget {
mainAxisAlignment:
!Util.isDesktop ? MainAxisAlignment.end : MainAxisAlignment.center,
children: [
Material(
borderRadius: BorderRadius.circular(
20,
),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.popupBG,
Flexible(
child: SingleChildScrollView(
child: Material(
borderRadius: BorderRadius.circular(
20,
),
),
child: Padding(
padding: padding,
child: child,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.popupBG,
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
git clone https://github.com/flutter/flutter.git
cd flutter
git checkout 3.3.4
git checkout 3.7.6
export FLUTTER_DIR=$(pwd)/bin
echo 'export PATH="$PATH:'${FLUTTER_DIR}'"' >> ~/.bashrc
source ~/.bashrc

View file

@ -4,7 +4,7 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i22;
import 'dart:typed_data' as _i28;
import 'dart:typed_data' as _i29;
import 'dart:ui' as _i24;
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/node_model.dart' as _i25;
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/coin_service.dart' as _i19;
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/transaction_notification_tracker.dart'
as _i8;
import 'package:stackwallet/services/wallets.dart' as _i20;
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/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'
as _i7;
import 'package:stackwallet/utilities/prefs.dart' as _i23;
@ -1438,7 +1439,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
},
));
@override
_i22.Future<Map<String, dynamic>> fetchBuildTxData(
_i22.Future<List<_i28.SigningData>> fetchBuildTxData(
List<_i16.UTXO>? utxosToUse) =>
(super.noSuchMethod(
Invocation.method(
@ -1446,12 +1447,11 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
[utxosToUse],
),
returnValue:
_i22.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
) as _i22.Future<Map<String, dynamic>>);
_i22.Future<List<_i28.SigningData>>.value(<_i28.SigningData>[]),
) as _i22.Future<List<_i28.SigningData>>);
@override
_i22.Future<Map<String, dynamic>> buildTransaction({
required List<_i16.UTXO>? utxosToUse,
required Map<String, dynamic>? utxoSigningData,
required List<_i28.SigningData>? utxoSigningData,
required List<String>? recipients,
required List<int>? satoshiAmounts,
}) =>
@ -1460,7 +1460,6 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
#buildTransaction,
[],
{
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData,
#recipients: recipients,
#satoshiAmounts: satoshiAmounts,
@ -1714,7 +1713,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
})?
prepareSend,
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,
required _i22.Future<void> Function()? refresh,
required _i22.Future<void> Function()? checkChangeAddressForTransactions,
@ -1866,14 +1865,14 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
)),
) as _i22.Future<_i18.PaymentCode>);
@override
_i22.Future<_i28.Uint8List> signWithNotificationKey(_i28.Uint8List? data) =>
_i22.Future<_i29.Uint8List> signWithNotificationKey(_i29.Uint8List? data) =>
(super.noSuchMethod(
Invocation.method(
#signWithNotificationKey,
[data],
),
returnValue: _i22.Future<_i28.Uint8List>.value(_i28.Uint8List(0)),
) as _i22.Future<_i28.Uint8List>);
returnValue: _i22.Future<_i29.Uint8List>.value(_i29.Uint8List(0)),
) as _i22.Future<_i29.Uint8List>);
@override
_i22.Future<String> signStringWithNotificationKey(String? data) =>
(super.noSuchMethod(
@ -2209,7 +2208,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
/// A class which mocks [LocaleService].
///
/// 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() {
_i1.throwOnMissingStub(this);
}
@ -2327,12 +2326,12 @@ class MockPrefs extends _i1.Mock implements _i23.Prefs {
returnValueForMissingStub: null,
);
@override
_i30.SyncingType get syncType => (super.noSuchMethod(
_i31.SyncingType get syncType => (super.noSuchMethod(
Invocation.getter(#syncType),
returnValue: _i30.SyncingType.currentWalletOnly,
) as _i30.SyncingType);
returnValue: _i31.SyncingType.currentWalletOnly,
) as _i31.SyncingType);
@override
set syncType(_i30.SyncingType? syncType) => super.noSuchMethod(
set syncType(_i31.SyncingType? syncType) => super.noSuchMethod(
Invocation.setter(
#syncType,
syncType,
@ -2465,12 +2464,12 @@ class MockPrefs extends _i1.Mock implements _i23.Prefs {
returnValueForMissingStub: null,
);
@override
_i31.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod(
_i32.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod(
Invocation.getter(#backupFrequencyType),
returnValue: _i31.BackupFrequencyType.everyTenMinutes,
) as _i31.BackupFrequencyType);
returnValue: _i32.BackupFrequencyType.everyTenMinutes,
) as _i32.BackupFrequencyType);
@override
set backupFrequencyType(_i31.BackupFrequencyType? backupFrequencyType) =>
set backupFrequencyType(_i32.BackupFrequencyType? backupFrequencyType) =>
super.noSuchMethod(
Invocation.setter(
#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/models/balance.dart' as _i6;
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/signing_data.dart' as _i13;
import 'package:stackwallet/services/coins/firo/firo_wallet.dart' as _i9;
import 'package:stackwallet/services/transaction_notification_tracker.dart'
as _i2;
@ -485,7 +486,7 @@ class MockFiroWallet extends _i1.Mock implements _i9.FiroWallet {
},
));
@override
_i10.Future<Map<String, dynamic>> fetchBuildTxData(
_i10.Future<List<_i13.SigningData>> fetchBuildTxData(
List<_i12.UTXO>? utxosToUse) =>
(super.noSuchMethod(
Invocation.method(
@ -493,12 +494,11 @@ class MockFiroWallet extends _i1.Mock implements _i9.FiroWallet {
[utxosToUse],
),
returnValue:
_i10.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
) as _i10.Future<Map<String, dynamic>>);
_i10.Future<List<_i13.SigningData>>.value(<_i13.SigningData>[]),
) as _i10.Future<List<_i13.SigningData>>);
@override
_i10.Future<Map<String, dynamic>> buildTransaction({
required List<_i12.UTXO>? utxosToUse,
required Map<String, dynamic>? utxoSigningData,
required List<_i13.SigningData>? utxoSigningData,
required List<String>? recipients,
required List<int>? satoshiAmounts,
}) =>
@ -507,7 +507,6 @@ class MockFiroWallet extends _i1.Mock implements _i9.FiroWallet {
#buildTransaction,
[],
{
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData,
#recipients: recipients,
#satoshiAmounts: satoshiAmounts,
@ -570,14 +569,14 @@ class MockFiroWallet extends _i1.Mock implements _i9.FiroWallet {
returnValueForMissingStub: _i10.Future<void>.value(),
) as _i10.Future<void>);
@override
List<Map<dynamic, _i13.LelantusCoin>> getLelantusCoinMap() =>
List<Map<dynamic, _i14.LelantusCoin>> getLelantusCoinMap() =>
(super.noSuchMethod(
Invocation.method(
#getLelantusCoinMap,
[],
),
returnValue: <Map<dynamic, _i13.LelantusCoin>>[],
) as List<Map<dynamic, _i13.LelantusCoin>>);
returnValue: <Map<dynamic, _i14.LelantusCoin>>[],
) as List<Map<dynamic, _i14.LelantusCoin>>);
@override
_i10.Future<void> anonymizeAllPublicFunds() => (super.noSuchMethod(
Invocation.method(

View file

@ -4,7 +4,7 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i22;
import 'dart:typed_data' as _i27;
import 'dart:typed_data' as _i28;
import 'dart:ui' as _i24;
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/models/balance.dart' as _i11;
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/signing_data.dart' as _i27;
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/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/transaction_notification_tracker.dart'
as _i7;
@ -1230,7 +1231,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
},
));
@override
_i22.Future<Map<String, dynamic>> fetchBuildTxData(
_i22.Future<List<_i27.SigningData>> fetchBuildTxData(
List<_i15.UTXO>? utxosToUse) =>
(super.noSuchMethod(
Invocation.method(
@ -1238,12 +1239,11 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
[utxosToUse],
),
returnValue:
_i22.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
) as _i22.Future<Map<String, dynamic>>);
_i22.Future<List<_i27.SigningData>>.value(<_i27.SigningData>[]),
) as _i22.Future<List<_i27.SigningData>>);
@override
_i22.Future<Map<String, dynamic>> buildTransaction({
required List<_i15.UTXO>? utxosToUse,
required Map<String, dynamic>? utxoSigningData,
required List<_i27.SigningData>? utxoSigningData,
required List<String>? recipients,
required List<int>? satoshiAmounts,
}) =>
@ -1252,7 +1252,6 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
#buildTransaction,
[],
{
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData,
#recipients: recipients,
#satoshiAmounts: satoshiAmounts,
@ -1506,7 +1505,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
})?
prepareSend,
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,
required _i22.Future<void> Function()? refresh,
required _i22.Future<void> Function()? checkChangeAddressForTransactions,
@ -1658,14 +1657,14 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
)),
) as _i22.Future<_i17.PaymentCode>);
@override
_i22.Future<_i27.Uint8List> signWithNotificationKey(_i27.Uint8List? data) =>
_i22.Future<_i28.Uint8List> signWithNotificationKey(_i28.Uint8List? data) =>
(super.noSuchMethod(
Invocation.method(
#signWithNotificationKey,
[data],
),
returnValue: _i22.Future<_i27.Uint8List>.value(_i27.Uint8List(0)),
) as _i22.Future<_i27.Uint8List>);
returnValue: _i22.Future<_i28.Uint8List>.value(_i28.Uint8List(0)),
) as _i22.Future<_i28.Uint8List>);
@override
_i22.Future<String> signStringWithNotificationKey(String? data) =>
(super.noSuchMethod(
@ -2001,7 +2000,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
/// A class which mocks [LocaleService].
///
/// 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() {
_i1.throwOnMissingStub(this);
}
@ -2073,15 +2072,15 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
),
) as _i18.SecureStorageInterface);
@override
List<_i29.NodeModel> get primaryNodes => (super.noSuchMethod(
List<_i30.NodeModel> get primaryNodes => (super.noSuchMethod(
Invocation.getter(#primaryNodes),
returnValue: <_i29.NodeModel>[],
) as List<_i29.NodeModel>);
returnValue: <_i30.NodeModel>[],
) as List<_i30.NodeModel>);
@override
List<_i29.NodeModel> get nodes => (super.noSuchMethod(
List<_i30.NodeModel> get nodes => (super.noSuchMethod(
Invocation.getter(#nodes),
returnValue: <_i29.NodeModel>[],
) as List<_i29.NodeModel>);
returnValue: <_i30.NodeModel>[],
) as List<_i30.NodeModel>);
@override
bool get hasListeners => (super.noSuchMethod(
Invocation.getter(#hasListeners),
@ -2099,7 +2098,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
@override
_i22.Future<void> setPrimaryNodeFor({
required _i21.Coin? coin,
required _i29.NodeModel? node,
required _i30.NodeModel? node,
bool? shouldNotifyListeners = false,
}) =>
(super.noSuchMethod(
@ -2116,40 +2115,40 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
returnValueForMissingStub: _i22.Future<void>.value(),
) as _i22.Future<void>);
@override
_i29.NodeModel? getPrimaryNodeFor({required _i21.Coin? coin}) =>
_i30.NodeModel? getPrimaryNodeFor({required _i21.Coin? coin}) =>
(super.noSuchMethod(Invocation.method(
#getPrimaryNodeFor,
[],
{#coin: coin},
)) as _i29.NodeModel?);
)) as _i30.NodeModel?);
@override
List<_i29.NodeModel> getNodesFor(_i21.Coin? coin) => (super.noSuchMethod(
List<_i30.NodeModel> getNodesFor(_i21.Coin? coin) => (super.noSuchMethod(
Invocation.method(
#getNodesFor,
[coin],
),
returnValue: <_i29.NodeModel>[],
) as List<_i29.NodeModel>);
returnValue: <_i30.NodeModel>[],
) as List<_i30.NodeModel>);
@override
_i29.NodeModel? getNodeById({required String? id}) =>
_i30.NodeModel? getNodeById({required String? id}) =>
(super.noSuchMethod(Invocation.method(
#getNodeById,
[],
{#id: id},
)) as _i29.NodeModel?);
)) as _i30.NodeModel?);
@override
List<_i29.NodeModel> failoverNodesFor({required _i21.Coin? coin}) =>
List<_i30.NodeModel> failoverNodesFor({required _i21.Coin? coin}) =>
(super.noSuchMethod(
Invocation.method(
#failoverNodesFor,
[],
{#coin: coin},
),
returnValue: <_i29.NodeModel>[],
) as List<_i29.NodeModel>);
returnValue: <_i30.NodeModel>[],
) as List<_i30.NodeModel>);
@override
_i22.Future<void> add(
_i29.NodeModel? node,
_i30.NodeModel? node,
String? password,
bool? shouldNotifyListeners,
) =>
@ -2201,7 +2200,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
) as _i22.Future<void>);
@override
_i22.Future<void> edit(
_i29.NodeModel? editedNode,
_i30.NodeModel? editedNode,
String? password,
bool? shouldNotifyListeners,
) =>

View file

@ -4,7 +4,7 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i21;
import 'dart:typed_data' as _i27;
import 'dart:typed_data' as _i28;
import 'dart:ui' as _i23;
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/isar/models/isar_models.dart' as _i15;
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/coin_service.dart' as _i18;
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/derive_path_type_enum.dart' as _i25;
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
as _i26;
as _i27;
import 'package:stackwallet/utilities/prefs.dart' as _i22;
import 'package:tuple/tuple.dart' as _i14;
@ -1217,7 +1218,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
},
));
@override
_i21.Future<Map<String, dynamic>> fetchBuildTxData(
_i21.Future<List<_i26.SigningData>> fetchBuildTxData(
List<_i15.UTXO>? utxosToUse) =>
(super.noSuchMethod(
Invocation.method(
@ -1225,12 +1226,11 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
[utxosToUse],
),
returnValue:
_i21.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
) as _i21.Future<Map<String, dynamic>>);
_i21.Future<List<_i26.SigningData>>.value(<_i26.SigningData>[]),
) as _i21.Future<List<_i26.SigningData>>);
@override
_i21.Future<Map<String, dynamic>> buildTransaction({
required List<_i15.UTXO>? utxosToUse,
required Map<String, dynamic>? utxoSigningData,
required List<_i26.SigningData>? utxoSigningData,
required List<String>? recipients,
required List<int>? satoshiAmounts,
}) =>
@ -1239,7 +1239,6 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
#buildTransaction,
[],
{
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData,
#recipients: recipients,
#satoshiAmounts: satoshiAmounts,
@ -1474,7 +1473,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
required _i20.Coin? coin,
required _i12.MainDB? db,
required _i9.ElectrumX? electrumXClient,
required _i26.SecureStorageInterface? secureStorage,
required _i27.SecureStorageInterface? secureStorage,
required int? dustLimitP2PKH,
required int? minConfirms,
required _i21.Future<String?> Function()? getMnemonicString,
@ -1493,7 +1492,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
})?
prepareSend,
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,
required _i21.Future<void> Function()? refresh,
required _i21.Future<void> Function()? checkChangeAddressForTransactions,
@ -1645,14 +1644,14 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
)),
) as _i21.Future<_i17.PaymentCode>);
@override
_i21.Future<_i27.Uint8List> signWithNotificationKey(_i27.Uint8List? data) =>
_i21.Future<_i28.Uint8List> signWithNotificationKey(_i28.Uint8List? data) =>
(super.noSuchMethod(
Invocation.method(
#signWithNotificationKey,
[data],
),
returnValue: _i21.Future<_i27.Uint8List>.value(_i27.Uint8List(0)),
) as _i21.Future<_i27.Uint8List>);
returnValue: _i21.Future<_i28.Uint8List>.value(_i28.Uint8List(0)),
) as _i21.Future<_i28.Uint8List>);
@override
_i21.Future<String> signStringWithNotificationKey(String? data) =>
(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/isar/models/isar_models.dart' as _i21;
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/firo/firo_wallet.dart' as _i22;
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/notes_service.dart' as _i27;
import 'package:stackwallet/services/price_service.dart' as _i26;
import 'package:stackwallet/services/notes_service.dart' as _i28;
import 'package:stackwallet/services/price_service.dart' as _i27;
import 'package:stackwallet/services/transaction_notification_tracker.dart'
as _i10;
import 'package:stackwallet/services/wallets.dart' as _i16;
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/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:tuple/tuple.dart' as _i15;
@ -1443,7 +1444,7 @@ class MockFiroWallet extends _i1.Mock implements _i22.FiroWallet {
},
));
@override
_i18.Future<Map<String, dynamic>> fetchBuildTxData(
_i18.Future<List<_i23.SigningData>> fetchBuildTxData(
List<_i21.UTXO>? utxosToUse) =>
(super.noSuchMethod(
Invocation.method(
@ -1451,12 +1452,11 @@ class MockFiroWallet extends _i1.Mock implements _i22.FiroWallet {
[utxosToUse],
),
returnValue:
_i18.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
) as _i18.Future<Map<String, dynamic>>);
_i18.Future<List<_i23.SigningData>>.value(<_i23.SigningData>[]),
) as _i18.Future<List<_i23.SigningData>>);
@override
_i18.Future<Map<String, dynamic>> buildTransaction({
required List<_i21.UTXO>? utxosToUse,
required Map<String, dynamic>? utxoSigningData,
required List<_i23.SigningData>? utxoSigningData,
required List<String>? recipients,
required List<int>? satoshiAmounts,
}) =>
@ -1465,7 +1465,6 @@ class MockFiroWallet extends _i1.Mock implements _i22.FiroWallet {
#buildTransaction,
[],
{
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData,
#recipients: recipients,
#satoshiAmounts: satoshiAmounts,
@ -2001,7 +2000,7 @@ class MockFiroWallet extends _i1.Mock implements _i22.FiroWallet {
/// A class which mocks [LocaleService].
///
/// 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() {
_i1.throwOnMissingStub(this);
}
@ -2119,12 +2118,12 @@ class MockPrefs extends _i1.Mock implements _i19.Prefs {
returnValueForMissingStub: null,
);
@override
_i24.SyncingType get syncType => (super.noSuchMethod(
_i25.SyncingType get syncType => (super.noSuchMethod(
Invocation.getter(#syncType),
returnValue: _i24.SyncingType.currentWalletOnly,
) as _i24.SyncingType);
returnValue: _i25.SyncingType.currentWalletOnly,
) as _i25.SyncingType);
@override
set syncType(_i24.SyncingType? syncType) => super.noSuchMethod(
set syncType(_i25.SyncingType? syncType) => super.noSuchMethod(
Invocation.setter(
#syncType,
syncType,
@ -2257,12 +2256,12 @@ class MockPrefs extends _i1.Mock implements _i19.Prefs {
returnValueForMissingStub: null,
);
@override
_i25.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod(
_i26.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod(
Invocation.getter(#backupFrequencyType),
returnValue: _i25.BackupFrequencyType.everyTenMinutes,
) as _i25.BackupFrequencyType);
returnValue: _i26.BackupFrequencyType.everyTenMinutes,
) as _i26.BackupFrequencyType);
@override
set backupFrequencyType(_i25.BackupFrequencyType? backupFrequencyType) =>
set backupFrequencyType(_i26.BackupFrequencyType? backupFrequencyType) =>
super.noSuchMethod(
Invocation.setter(
#backupFrequencyType,
@ -2425,7 +2424,7 @@ class MockPrefs extends _i1.Mock implements _i19.Prefs {
/// A class which mocks [PriceService].
///
/// 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() {
_i1.throwOnMissingStub(this);
}
@ -2533,7 +2532,7 @@ class MockPriceService extends _i1.Mock implements _i26.PriceService {
/// A class which mocks [NotesService].
///
/// 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() {
_i1.throwOnMissingStub(this);
}

View file

@ -4,7 +4,7 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i20;
import 'dart:typed_data' as _i26;
import 'dart:typed_data' as _i27;
import 'dart:ui' as _i22;
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/isar/models/isar_models.dart' as _i15;
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/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/transaction_notification_tracker.dart'
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/derive_path_type_enum.dart' as _i24;
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
as _i25;
as _i26;
import 'package:stackwallet/utilities/prefs.dart' as _i21;
import 'package:tuple/tuple.dart' as _i14;
@ -980,7 +981,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i23.BitcoinWallet {
},
));
@override
_i20.Future<Map<String, dynamic>> fetchBuildTxData(
_i20.Future<List<_i25.SigningData>> fetchBuildTxData(
List<_i15.UTXO>? utxosToUse) =>
(super.noSuchMethod(
Invocation.method(
@ -988,12 +989,11 @@ class MockBitcoinWallet extends _i1.Mock implements _i23.BitcoinWallet {
[utxosToUse],
),
returnValue:
_i20.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
) as _i20.Future<Map<String, dynamic>>);
_i20.Future<List<_i25.SigningData>>.value(<_i25.SigningData>[]),
) as _i20.Future<List<_i25.SigningData>>);
@override
_i20.Future<Map<String, dynamic>> buildTransaction({
required List<_i15.UTXO>? utxosToUse,
required Map<String, dynamic>? utxoSigningData,
required List<_i25.SigningData>? utxoSigningData,
required List<String>? recipients,
required List<int>? satoshiAmounts,
}) =>
@ -1002,7 +1002,6 @@ class MockBitcoinWallet extends _i1.Mock implements _i23.BitcoinWallet {
#buildTransaction,
[],
{
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData,
#recipients: recipients,
#satoshiAmounts: satoshiAmounts,
@ -1237,7 +1236,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i23.BitcoinWallet {
required _i19.Coin? coin,
required _i12.MainDB? db,
required _i9.ElectrumX? electrumXClient,
required _i25.SecureStorageInterface? secureStorage,
required _i26.SecureStorageInterface? secureStorage,
required int? dustLimitP2PKH,
required int? minConfirms,
required _i20.Future<String?> Function()? getMnemonicString,
@ -1256,7 +1255,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i23.BitcoinWallet {
})?
prepareSend,
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,
required _i20.Future<void> Function()? refresh,
required _i20.Future<void> Function()? checkChangeAddressForTransactions,
@ -1408,14 +1407,14 @@ class MockBitcoinWallet extends _i1.Mock implements _i23.BitcoinWallet {
)),
) as _i20.Future<_i17.PaymentCode>);
@override
_i20.Future<_i26.Uint8List> signWithNotificationKey(_i26.Uint8List? data) =>
_i20.Future<_i27.Uint8List> signWithNotificationKey(_i27.Uint8List? data) =>
(super.noSuchMethod(
Invocation.method(
#signWithNotificationKey,
[data],
),
returnValue: _i20.Future<_i26.Uint8List>.value(_i26.Uint8List(0)),
) as _i20.Future<_i26.Uint8List>);
returnValue: _i20.Future<_i27.Uint8List>.value(_i27.Uint8List(0)),
) as _i20.Future<_i27.Uint8List>);
@override
_i20.Future<String> signStringWithNotificationKey(String? data) =>
(super.noSuchMethod(
@ -1751,7 +1750,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i23.BitcoinWallet {
/// A class which mocks [LocaleService].
///
/// 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() {
_i1.throwOnMissingStub(this);
}

View file

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

View file

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