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(
|
SelectableText(
|
||||||
ref.watch(pAmountFormatter(coin)).format(
|
ref.watch(pAmountFormatter(coin)).format(
|
||||||
amountWithoutChange,
|
amountWithoutChange,
|
||||||
ethContract: ref
|
ethContract: widget.isTokenTx
|
||||||
.watch(pCurrentTokenWallet)
|
? ref
|
||||||
?.tokenContract,
|
.watch(pCurrentTokenWallet)!
|
||||||
|
.tokenContract
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
style: STextStyles.itemSubtitle12(context),
|
style: STextStyles.itemSubtitle12(context),
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
|
@ -745,9 +747,11 @@ class _ConfirmTransactionViewState
|
||||||
SelectableText(
|
SelectableText(
|
||||||
ref.watch(pAmountFormatter(coin)).format(
|
ref.watch(pAmountFormatter(coin)).format(
|
||||||
amountWithoutChange,
|
amountWithoutChange,
|
||||||
ethContract: ref
|
ethContract: widget.isTokenTx
|
||||||
.read(pCurrentTokenWallet)
|
? ref
|
||||||
?.tokenContract,
|
.watch(pCurrentTokenWallet)!
|
||||||
|
.tokenContract
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
style: STextStyles
|
style: STextStyles
|
||||||
.desktopTextExtraExtraSmall(
|
.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 get isPaynymSend => widget.accountLite != null;
|
||||||
|
|
||||||
bool isCustomFee = false;
|
bool isCustomFee = false;
|
||||||
|
@ -1772,59 +1814,9 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
),
|
),
|
||||||
if (coin is! Ethereum && coin is! Tezos)
|
if (coin is! Ethereum && coin is! Tezos)
|
||||||
CustomTextButton(
|
CustomTextButton(
|
||||||
text: "Send all ${coin.ticker}",
|
text: _getSendAllTitle(
|
||||||
onTap: () async {
|
showCoinControl, selectedUTXOs),
|
||||||
if (isFiro) {
|
onTap: () => _sendAllTapped(showCoinControl),
|
||||||
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();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -18,6 +18,7 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.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/isar/models/contact_entry.dart';
|
||||||
import '../../../../models/paynym/paynym_account_lite.dart';
|
import '../../../../models/paynym/paynym_account_lite.dart';
|
||||||
import '../../../../models/send_view_auto_fill_data.dart';
|
import '../../../../models/send_view_auto_fill_data.dart';
|
||||||
|
@ -932,30 +933,45 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
ref.read(pSendAmount.notifier).state = amount;
|
ref.read(pSendAmount.notifier).state = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sendAllTapped() async {
|
String _getSendAllTitle(bool showCoinControl, Set<UTXO> selectedUTXOs) {
|
||||||
final info = ref.read(pWalletInfo(walletId));
|
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) {
|
switch (ref.read(publicPrivateBalanceStateProvider.state).state) {
|
||||||
case FiroType.public:
|
case FiroType.public:
|
||||||
cryptoAmountController.text = info.cachedBalance.spendable.decimal
|
amount = ref.read(pWalletBalance(walletId)).spendable;
|
||||||
.toStringAsFixed(coin.fractionDigits);
|
|
||||||
break;
|
break;
|
||||||
case FiroType.lelantus:
|
case FiroType.lelantus:
|
||||||
cryptoAmountController.text = info
|
amount = ref.read(pWalletBalanceSecondary(walletId)).spendable;
|
||||||
.cachedBalanceSecondary.spendable.decimal
|
|
||||||
.toStringAsFixed(coin.fractionDigits);
|
|
||||||
break;
|
break;
|
||||||
case FiroType.spark:
|
case FiroType.spark:
|
||||||
cryptoAmountController.text = info
|
amount = ref.read(pWalletBalanceTertiary(walletId)).spendable;
|
||||||
.cachedBalanceTertiary.spendable.decimal
|
|
||||||
.toStringAsFixed(coin.fractionDigits);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cryptoAmountController.text = info.cachedBalance.spendable.decimal
|
amount = ref.read(pWalletBalance(walletId)).spendable;
|
||||||
.toStringAsFixed(coin.fractionDigits);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format(
|
||||||
|
amount,
|
||||||
|
withUnitName: false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showDesktopCoinControl() async {
|
void _showDesktopCoinControl() async {
|
||||||
|
@ -1280,8 +1296,11 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
||||||
),
|
),
|
||||||
if (coin is! Ethereum && coin is! Tezos)
|
if (coin is! Ethereum && coin is! Tezos)
|
||||||
CustomTextButton(
|
CustomTextButton(
|
||||||
text: "Send all ${coin.ticker}",
|
text: _getSendAllTitle(
|
||||||
onTap: sendAllTapped,
|
showCoinControl,
|
||||||
|
ref.watch(desktopUseUTXOs),
|
||||||
|
),
|
||||||
|
onTap: () => _sendAllTapped(showCoinControl),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -105,6 +105,7 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
||||||
required TxData txData,
|
required TxData txData,
|
||||||
required bool coinControl,
|
required bool coinControl,
|
||||||
required bool isSendAll,
|
required bool isSendAll,
|
||||||
|
required bool isSendAllCoinControlUtxos,
|
||||||
int additionalOutputs = 0,
|
int additionalOutputs = 0,
|
||||||
List<UTXO>? utxos,
|
List<UTXO>? utxos,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -144,7 +145,9 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
||||||
|
|
||||||
if (spendableSatoshiValue < satoshiAmountToSend) {
|
if (spendableSatoshiValue < satoshiAmountToSend) {
|
||||||
throw Exception("Insufficient balance");
|
throw Exception("Insufficient balance");
|
||||||
} else if (spendableSatoshiValue == satoshiAmountToSend && !isSendAll) {
|
} else if (spendableSatoshiValue == satoshiAmountToSend &&
|
||||||
|
!isSendAll &&
|
||||||
|
!isSendAllCoinControlUtxos) {
|
||||||
throw Exception("Insufficient balance to pay transaction fee");
|
throw Exception("Insufficient balance to pay transaction fee");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +223,13 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
||||||
// gather required signing data
|
// gather required signing data
|
||||||
final utxoSigningData = await fetchBuildTxData(utxoObjectsToUse);
|
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(
|
return await _sendAllBuilder(
|
||||||
txData: txData,
|
txData: txData,
|
||||||
recipientAddress: recipientAddress,
|
recipientAddress: recipientAddress,
|
||||||
|
@ -357,6 +366,7 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
||||||
additionalOutputs: additionalOutputs + 1,
|
additionalOutputs: additionalOutputs + 1,
|
||||||
utxos: utxos,
|
utxos: utxos,
|
||||||
coinControl: coinControl,
|
coinControl: coinControl,
|
||||||
|
isSendAllCoinControlUtxos: isSendAllCoinControlUtxos,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw Exception("Insufficient balance to pay transaction fee");
|
throw Exception("Insufficient balance to pay transaction fee");
|
||||||
|
@ -1681,11 +1691,23 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
||||||
@override
|
@override
|
||||||
Future<TxData> prepareSend({required TxData txData}) async {
|
Future<TxData> prepareSend({required TxData txData}) async {
|
||||||
try {
|
try {
|
||||||
|
if (txData.amount == null) {
|
||||||
|
throw Exception("No recipients in attempted transaction!");
|
||||||
|
}
|
||||||
|
|
||||||
final feeRateType = txData.feeRateType;
|
final feeRateType = txData.feeRateType;
|
||||||
final customSatsPerVByte = txData.satsPerVByte;
|
final customSatsPerVByte = txData.satsPerVByte;
|
||||||
final feeRateAmount = txData.feeRateAmount;
|
final feeRateAmount = txData.feeRateAmount;
|
||||||
final utxos = txData.utxos;
|
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) {
|
if (customSatsPerVByte != null) {
|
||||||
// check for send all
|
// check for send all
|
||||||
bool isSendAll = false;
|
bool isSendAll = false;
|
||||||
|
@ -1694,8 +1716,6 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
||||||
isSendAll = true;
|
isSendAll = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
final bool coinControl = utxos != null;
|
|
||||||
|
|
||||||
if (coinControl &&
|
if (coinControl &&
|
||||||
this is CpfpInterface &&
|
this is CpfpInterface &&
|
||||||
txData.amount ==
|
txData.amount ==
|
||||||
|
@ -1709,6 +1729,7 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
||||||
isSendAll: isSendAll,
|
isSendAll: isSendAll,
|
||||||
utxos: utxos?.toList(),
|
utxos: utxos?.toList(),
|
||||||
coinControl: coinControl,
|
coinControl: coinControl,
|
||||||
|
isSendAllCoinControlUtxos: isSendAllCoinControlUtxos,
|
||||||
);
|
);
|
||||||
|
|
||||||
Logging.instance
|
Logging.instance
|
||||||
|
@ -1750,8 +1771,6 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
||||||
isSendAll = true;
|
isSendAll = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
final bool coinControl = utxos != null;
|
|
||||||
|
|
||||||
final result = await coinSelection(
|
final result = await coinSelection(
|
||||||
txData: txData.copyWith(
|
txData: txData.copyWith(
|
||||||
feeRateAmount: rate,
|
feeRateAmount: rate,
|
||||||
|
@ -1759,6 +1778,7 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
||||||
isSendAll: isSendAll,
|
isSendAll: isSendAll,
|
||||||
utxos: utxos?.toList(),
|
utxos: utxos?.toList(),
|
||||||
coinControl: coinControl,
|
coinControl: coinControl,
|
||||||
|
isSendAllCoinControlUtxos: isSendAllCoinControlUtxos,
|
||||||
);
|
);
|
||||||
|
|
||||||
Logging.instance.log("prepare send: $result", level: LogLevel.Info);
|
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 '../../isar/models/wallet_info.dart';
|
||||||
import '../../models/tx_data.dart';
|
import '../../models/tx_data.dart';
|
||||||
import '../intermediate/bip39_hd_wallet.dart';
|
import '../intermediate/bip39_hd_wallet.dart';
|
||||||
|
import 'cpfp_interface.dart';
|
||||||
import 'electrumx_interface.dart';
|
import 'electrumx_interface.dart';
|
||||||
|
|
||||||
const kDefaultSparkIndex = 1;
|
const kDefaultSparkIndex = 1;
|
||||||
|
@ -1809,36 +1810,44 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
||||||
throw Exception("Attempted send of zero amount");
|
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;
|
final currentHeight = await chainHeight;
|
||||||
|
|
||||||
// coin control not enabled for firo currently so we can ignore this
|
final availableOutputs = utxos?.toList() ??
|
||||||
// final utxosToUse = txData.utxos?.toList() ?? await mainDB.isar.utxos
|
await mainDB.isar.utxos
|
||||||
// .where()
|
.where()
|
||||||
// .walletIdEqualTo(walletId)
|
.walletIdEqualTo(walletId)
|
||||||
// .filter()
|
.filter()
|
||||||
// .isBlockedEqualTo(false)
|
.isBlockedEqualTo(false)
|
||||||
// .and()
|
.and()
|
||||||
// .group((q) => q.usedEqualTo(false).or().usedIsNull())
|
.group((q) => q.usedEqualTo(false).or().usedIsNull())
|
||||||
// .and()
|
.and()
|
||||||
// .valueGreaterThan(0)
|
.valueGreaterThan(0)
|
||||||
// .findAll();
|
.findAll();
|
||||||
final spendableUtxos = await mainDB.isar.utxos
|
|
||||||
.where()
|
|
||||||
.walletIdEqualTo(walletId)
|
|
||||||
.filter()
|
|
||||||
.isBlockedEqualTo(false)
|
|
||||||
.and()
|
|
||||||
.group((q) => q.usedEqualTo(false).or().usedIsNull())
|
|
||||||
.and()
|
|
||||||
.valueGreaterThan(0)
|
|
||||||
.findAll();
|
|
||||||
|
|
||||||
spendableUtxos.removeWhere(
|
final canCPFP = this is CpfpInterface && coinControl;
|
||||||
(e) => !e.isConfirmed(
|
|
||||||
currentHeight,
|
final spendableUtxos = availableOutputs
|
||||||
cryptoCurrency.minConfirms,
|
.where(
|
||||||
),
|
(e) =>
|
||||||
);
|
canCPFP ||
|
||||||
|
e.isConfirmed(currentHeight, cryptoCurrency.minConfirms),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
|
||||||
if (spendableUtxos.isEmpty) {
|
if (spendableUtxos.isEmpty) {
|
||||||
throw Exception("No available UTXOs found to anonymize");
|
throw Exception("No available UTXOs found to anonymize");
|
||||||
|
@ -1849,7 +1858,9 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
|
||||||
.reduce((value, element) => value += element);
|
.reduce((value, element) => value += element);
|
||||||
|
|
||||||
final bool subtractFeeFromAmount;
|
final bool subtractFeeFromAmount;
|
||||||
if (available < total) {
|
if (isSendAllCoinControlUtxos) {
|
||||||
|
subtractFeeFromAmount = true;
|
||||||
|
} else if (available < total) {
|
||||||
throw Exception("Insufficient balance");
|
throw Exception("Insufficient balance");
|
||||||
} else if (available == total) {
|
} else if (available == total) {
|
||||||
subtractFeeFromAmount = true;
|
subtractFeeFromAmount = true;
|
||||||
|
|
Loading…
Reference in a new issue