Merge branch 'cypherstack:staging' into staging

This commit is contained in:
dethe 2024-09-01 14:49:46 +03:00 committed by GitHub
commit 56dcc0ba4a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 155 additions and 109 deletions

@ -1 +1 @@
Subproject commit db7585d8cd493b143e0a0652c618904d1f636d1d
Subproject commit c0b8994009d29bc7fd2f0f2edbb004c39bf93951

View file

@ -522,9 +522,11 @@ class _ConfirmTransactionViewState
SelectableText(
ref.watch(pAmountFormatter(coin)).format(
amountWithoutChange,
ethContract: ref
.watch(pCurrentTokenWallet)
?.tokenContract,
ethContract: widget.isTokenTx
? ref
.watch(pCurrentTokenWallet)!
.tokenContract
: null,
),
style: STextStyles.itemSubtitle12(context),
textAlign: TextAlign.right,
@ -745,9 +747,11 @@ class _ConfirmTransactionViewState
SelectableText(
ref.watch(pAmountFormatter(coin)).format(
amountWithoutChange,
ethContract: ref
.read(pCurrentTokenWallet)
?.tokenContract,
ethContract: widget.isTokenTx
? ref
.watch(pCurrentTokenWallet)!
.tokenContract
: null,
),
style: STextStyles
.desktopTextExtraExtraSmall(

View file

@ -881,6 +881,48 @@ class _SendViewState extends ConsumerState<SendView> {
}
}
String _getSendAllTitle(bool showCoinControl, Set<UTXO> selectedUTXOs) {
if (showCoinControl && selectedUTXOs.isNotEmpty) {
return "Send all selected";
}
return "Send all ${coin.ticker}";
}
Amount _selectedUtxosAmount(Set<UTXO> utxos) => Amount(
rawValue:
utxos.map((e) => BigInt.from(e.value)).reduce((v, e) => v += e),
fractionDigits: ref.read(pWalletCoin(walletId)).fractionDigits,
);
Future<void> _sendAllTapped(bool showCoinControl) async {
final Amount amount;
if (showCoinControl && selectedUTXOs.isNotEmpty) {
amount = _selectedUtxosAmount(selectedUTXOs);
} else if (isFiro) {
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
case FiroType.public:
amount = ref.read(pWalletBalance(walletId)).spendable;
break;
case FiroType.lelantus:
amount = ref.read(pWalletBalanceSecondary(walletId)).spendable;
break;
case FiroType.spark:
amount = ref.read(pWalletBalanceTertiary(walletId)).spendable;
break;
}
} else {
amount = ref.read(pWalletBalance(walletId)).spendable;
}
cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format(
amount,
withUnitName: false,
);
_cryptoAmountChanged();
}
bool get isPaynymSend => widget.accountLite != null;
bool isCustomFee = false;
@ -1772,59 +1814,9 @@ class _SendViewState extends ConsumerState<SendView> {
),
if (coin is! Ethereum && coin is! Tezos)
CustomTextButton(
text: "Send all ${coin.ticker}",
onTap: () async {
if (isFiro) {
final Amount amount;
switch (ref
.read(
publicPrivateBalanceStateProvider
.state,
)
.state) {
case FiroType.public:
amount = ref
.read(pWalletBalance(walletId))
.spendable;
break;
case FiroType.lelantus:
amount = ref
.read(
pWalletBalanceSecondary(
walletId,
),
)
.spendable;
break;
case FiroType.spark:
amount = ref
.read(
pWalletBalanceTertiary(
walletId,
),
)
.spendable;
break;
}
cryptoAmountController.text = ref
.read(pAmountFormatter(coin))
.format(
amount,
withUnitName: false,
);
} else {
cryptoAmountController.text = ref
.read(pAmountFormatter(coin))
.format(
ref
.read(pWalletBalance(walletId))
.spendable,
withUnitName: false,
);
}
_cryptoAmountChanged();
},
text: _getSendAllTitle(
showCoinControl, selectedUTXOs),
onTap: () => _sendAllTapped(showCoinControl),
),
],
),

View file

@ -18,6 +18,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../../../../models/isar/models/blockchain_data/utxo.dart';
import '../../../../models/isar/models/contact_entry.dart';
import '../../../../models/paynym/paynym_account_lite.dart';
import '../../../../models/send_view_auto_fill_data.dart';
@ -932,30 +933,45 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
ref.read(pSendAmount.notifier).state = amount;
}
Future<void> sendAllTapped() async {
final info = ref.read(pWalletInfo(walletId));
String _getSendAllTitle(bool showCoinControl, Set<UTXO> selectedUTXOs) {
if (showCoinControl && selectedUTXOs.isNotEmpty) {
return "Send all selected";
}
if (coin is Firo) {
return "Send all ${coin.ticker}";
}
Amount _selectedUtxosAmount(Set<UTXO> utxos) => Amount(
rawValue:
utxos.map((e) => BigInt.from(e.value)).reduce((v, e) => v += e),
fractionDigits: ref.read(pWalletCoin(walletId)).fractionDigits,
);
Future<void> _sendAllTapped(bool showCoinControl) async {
final Amount amount;
if (showCoinControl && ref.read(desktopUseUTXOs).isNotEmpty) {
amount = _selectedUtxosAmount(ref.read(desktopUseUTXOs));
} else if (coin is Firo) {
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
case FiroType.public:
cryptoAmountController.text = info.cachedBalance.spendable.decimal
.toStringAsFixed(coin.fractionDigits);
amount = ref.read(pWalletBalance(walletId)).spendable;
break;
case FiroType.lelantus:
cryptoAmountController.text = info
.cachedBalanceSecondary.spendable.decimal
.toStringAsFixed(coin.fractionDigits);
amount = ref.read(pWalletBalanceSecondary(walletId)).spendable;
break;
case FiroType.spark:
cryptoAmountController.text = info
.cachedBalanceTertiary.spendable.decimal
.toStringAsFixed(coin.fractionDigits);
amount = ref.read(pWalletBalanceTertiary(walletId)).spendable;
break;
}
} else {
cryptoAmountController.text = info.cachedBalance.spendable.decimal
.toStringAsFixed(coin.fractionDigits);
amount = ref.read(pWalletBalance(walletId)).spendable;
}
cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format(
amount,
withUnitName: false,
);
}
void _showDesktopCoinControl() async {
@ -1280,8 +1296,11 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
),
if (coin is! Ethereum && coin is! Tezos)
CustomTextButton(
text: "Send all ${coin.ticker}",
onTap: sendAllTapped,
text: _getSendAllTitle(
showCoinControl,
ref.watch(desktopUseUTXOs),
),
onTap: () => _sendAllTapped(showCoinControl),
),
],
),

View file

@ -105,6 +105,7 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
required TxData txData,
required bool coinControl,
required bool isSendAll,
required bool isSendAllCoinControlUtxos,
int additionalOutputs = 0,
List<UTXO>? utxos,
}) async {
@ -144,7 +145,9 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
if (spendableSatoshiValue < satoshiAmountToSend) {
throw Exception("Insufficient balance");
} else if (spendableSatoshiValue == satoshiAmountToSend && !isSendAll) {
} else if (spendableSatoshiValue == satoshiAmountToSend &&
!isSendAll &&
!isSendAllCoinControlUtxos) {
throw Exception("Insufficient balance to pay transaction fee");
}
@ -220,7 +223,13 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
// gather required signing data
final utxoSigningData = await fetchBuildTxData(utxoObjectsToUse);
if (isSendAll) {
if (isSendAll || isSendAllCoinControlUtxos) {
if (satoshiAmountToSend != satoshisBeingUsed) {
throw Exception(
"Something happened that should never actually happen. "
"Please report this error to the developers.",
);
}
return await _sendAllBuilder(
txData: txData,
recipientAddress: recipientAddress,
@ -357,6 +366,7 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
additionalOutputs: additionalOutputs + 1,
utxos: utxos,
coinControl: coinControl,
isSendAllCoinControlUtxos: isSendAllCoinControlUtxos,
);
}
throw Exception("Insufficient balance to pay transaction fee");
@ -1681,11 +1691,23 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
@override
Future<TxData> prepareSend({required TxData txData}) async {
try {
if (txData.amount == null) {
throw Exception("No recipients in attempted transaction!");
}
final feeRateType = txData.feeRateType;
final customSatsPerVByte = txData.satsPerVByte;
final feeRateAmount = txData.feeRateAmount;
final utxos = txData.utxos;
final bool coinControl = utxos != null;
final isSendAllCoinControlUtxos = coinControl &&
txData.amount!.raw ==
utxos
.map((e) => e.value)
.fold(BigInt.zero, (p, e) => p + BigInt.from(e));
if (customSatsPerVByte != null) {
// check for send all
bool isSendAll = false;
@ -1694,8 +1716,6 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
isSendAll = true;
}
final bool coinControl = utxos != null;
if (coinControl &&
this is CpfpInterface &&
txData.amount ==
@ -1709,6 +1729,7 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
isSendAll: isSendAll,
utxos: utxos?.toList(),
coinControl: coinControl,
isSendAllCoinControlUtxos: isSendAllCoinControlUtxos,
);
Logging.instance
@ -1750,8 +1771,6 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
isSendAll = true;
}
final bool coinControl = utxos != null;
final result = await coinSelection(
txData: txData.copyWith(
feeRateAmount: rate,
@ -1759,6 +1778,7 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
isSendAll: isSendAll,
utxos: utxos?.toList(),
coinControl: coinControl,
isSendAllCoinControlUtxos: isSendAllCoinControlUtxos,
);
Logging.instance.log("prepare send: $result", level: LogLevel.Info);

View file

@ -23,6 +23,7 @@ import '../../isar/models/spark_coin.dart';
import '../../isar/models/wallet_info.dart';
import '../../models/tx_data.dart';
import '../intermediate/bip39_hd_wallet.dart';
import 'cpfp_interface.dart';
import 'electrumx_interface.dart';
const kDefaultSparkIndex = 1;
@ -1809,20 +1810,25 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
throw Exception("Attempted send of zero amount");
}
final utxos = txData.utxos;
final bool coinControl = utxos != null;
final utxosTotal = coinControl
? utxos
.map((e) => e.value)
.fold(BigInt.zero, (p, e) => p + BigInt.from(e))
: null;
if (coinControl && utxosTotal! < total) {
throw Exception("Insufficient selected UTXOs!");
}
final isSendAllCoinControlUtxos = coinControl && total == utxosTotal;
final currentHeight = await chainHeight;
// coin control not enabled for firo currently so we can ignore this
// final utxosToUse = txData.utxos?.toList() ?? await mainDB.isar.utxos
// .where()
// .walletIdEqualTo(walletId)
// .filter()
// .isBlockedEqualTo(false)
// .and()
// .group((q) => q.usedEqualTo(false).or().usedIsNull())
// .and()
// .valueGreaterThan(0)
// .findAll();
final spendableUtxos = await mainDB.isar.utxos
final availableOutputs = utxos?.toList() ??
await mainDB.isar.utxos
.where()
.walletIdEqualTo(walletId)
.filter()
@ -1833,12 +1839,15 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
.valueGreaterThan(0)
.findAll();
spendableUtxos.removeWhere(
(e) => !e.isConfirmed(
currentHeight,
cryptoCurrency.minConfirms,
),
);
final canCPFP = this is CpfpInterface && coinControl;
final spendableUtxos = availableOutputs
.where(
(e) =>
canCPFP ||
e.isConfirmed(currentHeight, cryptoCurrency.minConfirms),
)
.toList();
if (spendableUtxos.isEmpty) {
throw Exception("No available UTXOs found to anonymize");
@ -1849,7 +1858,9 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
.reduce((value, element) => value += element);
final bool subtractFeeFromAmount;
if (available < total) {
if (isSendAllCoinControlUtxos) {
subtractFeeFromAmount = true;
} else if (available < total) {
throw Exception("Insufficient balance");
} else if (available == total) {
subtractFeeFromAmount = true;