mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 03:49:22 +00:00
Merge branch 'cypherstack:staging' into staging
This commit is contained in:
commit
56dcc0ba4a
6 changed files with 155 additions and 109 deletions
|
@ -1 +1 @@
|
|||
Subproject commit db7585d8cd493b143e0a0652c618904d1f636d1d
|
||||
Subproject commit c0b8994009d29bc7fd2f0f2edbb004c39bf93951
|
|
@ -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(
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue