Updated on 2026-08-14

This commit is contained in:
Tangem 2024-06-13 19:57:22 +01:00
parent e8c5c35e56
commit 8cb94b6b7c
5 changed files with 72 additions and 27 deletions

View file

@ -4,7 +4,10 @@ import java.math.BigDecimal
sealed class Warning {
data class ExistentialDepositWarning(val existentialDeposit: BigDecimal) : Warning()
data class ExistentialDepositWarning(
val existentialDeposit: BigDecimal,
val minAvailableAmount: BigDecimal,
) : Warning()
data class MinAmountWarning(val dustValue: BigDecimal) : Warning()

View file

@ -386,7 +386,7 @@ internal class SwapInteractorImpl @Inject constructor(
val fromToken = fromTokenStatus.currency
val userWalletId = getSelectedWallet()?.walletId ?: return emptyList()
val warnings = mutableListOf<Warning>()
manageExistentialDepositWarning(warnings, userWalletId, amount, fromToken)
manageExistentialDepositWarning(warnings, userWalletId, amount, fromToken, feeState)
manageDustWarning(warnings, feeState, userWalletId, fromTokenStatus, amount)
manageReduceAmountWarning(warnings, fromTokenStatus, amount)
manageTransactionValidationWarnings(
@ -404,6 +404,7 @@ internal class SwapInteractorImpl @Inject constructor(
userWalletId: UserWalletId,
amount: SwapAmount,
fromToken: CryptoCurrency,
txFee: TxFeeState,
) {
val existentialDeposit = currencyChecksRepository.getExistentialDeposit(userWalletId, fromToken.network)
if (existentialDeposit != null) {
@ -411,8 +412,14 @@ internal class SwapInteractorImpl @Inject constructor(
fromToken.network.backendId,
fromToken.network.derivationPath.value,
) ?: ProxyAmount.empty()
val fee = when (txFee) {
TxFeeState.Empty -> BigDecimal.ZERO
is TxFeeState.MultipleFeeState -> txFee.priorityFee.feeValue
is TxFeeState.SingleFeeState -> txFee.fee.feeValue
}
val minAvailableAmount = nativeBalance.value - existentialDeposit - fee
if (nativeBalance.value.minus(amount.value) < existentialDeposit) {
warnings.add(Warning.ExistentialDepositWarning(existentialDeposit))
warnings.add(Warning.ExistentialDepositWarning(existentialDeposit, minAvailableAmount))
}
}
}

View file

@ -15,6 +15,7 @@ data class UiActions(
val onBackClicked: () -> Unit,
val onMaxAmountSelected: () -> Unit,
val onReduceAmount: (SwapAmount) -> Unit,
val onLeaveExistentialDeposit: (SwapAmount) -> Unit,
val openPermissionBottomSheet: () -> Unit,
val onChangeApproveType: (ApproveType) -> Unit,
// region new actions

View file

@ -358,21 +358,7 @@ internal class StateBuilder(
quoteModel.warnings.forEach {
when (it) {
is Warning.ExistentialDepositWarning -> {
warnings.add(
SwapWarning.GeneralInformational(
NotificationConfig(
title = resourceReference(R.string.warning_existential_deposit_title),
subtitle = resourceReference(
R.string.warning_existential_deposit_message,
wrappedList(
quoteModel.fromTokenInfo.cryptoCurrencyStatus.currency.name,
it.existentialDeposit.toPlainString(),
),
),
iconResId = R.drawable.ic_alert_circle_24,
),
),
)
warnings.add(createLeaveExistentialDepositWarning(quoteModel, it))
}
is Warning.MinAmountWarning -> {
warnings.add(
@ -443,6 +429,41 @@ internal class StateBuilder(
}
}
private fun createLeaveExistentialDepositWarning(
quoteModel: SwapState.QuotesLoadedState,
domainWarning: Warning.ExistentialDepositWarning,
): SwapWarning {
val fromCurrency = quoteModel.fromTokenInfo.cryptoCurrencyStatus.currency
val deposit = BigDecimalFormatter.formatCryptoAmountUncapped(
cryptoAmount = domainWarning.existentialDeposit,
cryptoCurrency = fromCurrency,
)
return SwapWarning.GeneralError(
NotificationConfig(
title = resourceReference(R.string.send_notification_existential_deposit_title),
subtitle = resourceReference(
R.string.send_notification_existential_deposit_text,
wrappedList(deposit),
),
iconResId = R.drawable.ic_alert_circle_24,
buttonsState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
text = resourceReference(
R.string.send_notification_leave_button,
wrappedList(deposit),
),
onClick = {
actions.onLeaveExistentialDeposit(
SwapAmount(
domainWarning.minAvailableAmount,
fromCurrency.decimals,
),
)
},
),
),
)
}
private fun maybeAddPermissionNeededWarning(
quoteModel: SwapState.QuotesLoadedState,
warnings: MutableList<SwapWarning>,
@ -468,19 +489,25 @@ internal class StateBuilder(
when (quoteModel.preparedSwapConfigState.includeFeeInAmount) {
is IncludeFeeInAmount.Included -> {
val fee = selectFeeByType(selectedFeeType, quoteModel.txFee) ?: return
warnings.add(
SwapWarning.GeneralWarning(
createNetworkFeeCoverageNotificationConfig(
fee.feeCryptoFormatted,
fee.feeFiatFormatted,
if (needShowNetworkFeeCoverageWarningShow(quoteModel)) {
warnings.add(
SwapWarning.GeneralWarning(
createNetworkFeeCoverageNotificationConfig(
fee.feeCryptoFormatted,
fee.feeFiatFormatted,
),
),
),
)
)
}
}
else -> Unit
}
}
private fun needShowNetworkFeeCoverageWarningShow(quoteModel: SwapState.QuotesLoadedState): Boolean {
return quoteModel.warnings.none { it is Warning.ExistentialDepositWarning }
}
private fun selectFeeByType(feeType: FeeType, txFeeState: TxFeeState): TxFee? {
return when (txFeeState) {
TxFeeState.Empty -> null
@ -544,8 +571,10 @@ internal class StateBuilder(
// check has MinAmountWarning warning
val hasCriticalWarning = quoteModel.warnings.any {
it is Warning.MinAmountWarning || it is Warning.Cardano.InsufficientBalanceToTransferCoin ||
it is Warning.Cardano.InsufficientBalanceToTransferToken
it is Warning.MinAmountWarning ||
it is Warning.Cardano.InsufficientBalanceToTransferCoin ||
it is Warning.Cardano.InsufficientBalanceToTransferToken ||
it is Warning.ExistentialDepositWarning
}
if (hasCriticalWarning) return false

View file

@ -823,6 +823,10 @@ internal class SwapViewModel @Inject constructor(
onAmountChanged(newAmount.formatToUIRepresentation())
}
private fun onLeaveExistentialDepositClicked(newAmount: SwapAmount) {
onAmountChanged(newAmount.formatToUIRepresentation())
}
@Suppress("UnusedPrivateMember")
private fun onAmountSelected(selected: Boolean) {
if (selected) {
@ -887,6 +891,7 @@ internal class SwapViewModel @Inject constructor(
},
onMaxAmountSelected = ::onMaxAmountClicked,
onReduceAmount = ::onReduceAmountClicked,
onLeaveExistentialDeposit = ::onLeaveExistentialDepositClicked,
openPermissionBottomSheet = {
singleTaskScheduler.cancelTask()
analyticsEventHandler.send(SwapEvents.ButtonGivePermissionClicked)