Updated on 2026-08-14
This commit is contained in:
commit
c5cab520f7
7 changed files with 85 additions and 32 deletions
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -376,7 +376,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)
|
||||
return warnings
|
||||
|
|
@ -387,6 +387,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
userWalletId: UserWalletId,
|
||||
amount: SwapAmount,
|
||||
fromToken: CryptoCurrency,
|
||||
txFee: TxFeeState,
|
||||
) {
|
||||
val existentialDeposit = currencyChecksRepository.getExistentialDeposit(userWalletId, fromToken.network)
|
||||
if (existentialDeposit != null) {
|
||||
|
|
@ -394,8 +395,18 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
fromToken.network.backendId,
|
||||
fromToken.network.derivationPath.value,
|
||||
) ?: ProxyAmount.empty()
|
||||
if (nativeBalance.value.minus(amount.value) < existentialDeposit) {
|
||||
warnings.add(Warning.ExistentialDepositWarning(existentialDeposit))
|
||||
// ignore if amount is bigger than balance
|
||||
if (amount.value > nativeBalance.value) {
|
||||
return
|
||||
}
|
||||
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 + fee) < existentialDeposit) {
|
||||
warnings.add(Warning.ExistentialDepositWarning(existentialDeposit, minAvailableAmount))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ data class UiActions(
|
|||
val onBackClicked: () -> Unit,
|
||||
val onMaxAmountSelected: () -> Unit,
|
||||
val onReduceAmount: (SwapAmount) -> Unit,
|
||||
val onLeaveExistentialDeposit: (SwapAmount) -> Unit,
|
||||
val onReduceAmountIgnoreClick: () -> Unit,
|
||||
val openPermissionBottomSheet: () -> Unit,
|
||||
val onChangeApproveType: (ApproveType) -> Unit,
|
||||
|
|
|
|||
|
|
@ -365,21 +365,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(
|
||||
|
|
@ -444,6 +430,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>,
|
||||
|
|
@ -469,19 +490,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
|
||||
|
|
@ -543,7 +570,12 @@ internal class StateBuilder(
|
|||
// check has has outgoing transaction
|
||||
if (preparedSwapConfigState.hasOutgoingTransaction) return false
|
||||
// check has MinAmountWarning warning
|
||||
if (quoteModel.warnings.filterIsInstance<Warning.MinAmountWarning>().isNotEmpty()) return false
|
||||
val hasCriticalWarning = quoteModel.warnings.any {
|
||||
it is Warning.MinAmountWarning || it is Warning.ExistentialDepositWarning
|
||||
}
|
||||
|
||||
if (hasCriticalWarning) return false
|
||||
|
||||
return when (preparedSwapConfigState.includeFeeInAmount) {
|
||||
IncludeFeeInAmount.BalanceNotEnough -> false
|
||||
IncludeFeeInAmount.Excluded ->
|
||||
|
|
|
|||
|
|
@ -283,7 +283,10 @@ private fun Content(
|
|||
|
||||
if (amountEquivalent != null) {
|
||||
if (type is TransactionCardType.ReadOnly) {
|
||||
Row(modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size20)) {
|
||||
Row(
|
||||
modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size20),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (priceImpact is PriceImpact.Value) {
|
||||
Text(
|
||||
text = makePriceImpactBalanceWarning(
|
||||
|
|
@ -292,8 +295,6 @@ private fun Content(
|
|||
),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterVertically),
|
||||
)
|
||||
} else {
|
||||
AnimatedContent(targetState = amountEquivalent, label = "") {
|
||||
|
|
@ -313,7 +314,7 @@ private fun Content(
|
|||
modifier = Modifier.size(size = TangemTheme.dimens.size20),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_alert_24),
|
||||
painter = painterResource(id = R.drawable.ic_information_24),
|
||||
contentDescription = null,
|
||||
tint = if (priceImpact is PriceImpact.Value) {
|
||||
TangemTheme.colors.text.attention
|
||||
|
|
|
|||
|
|
@ -817,6 +817,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) {
|
||||
|
|
@ -881,6 +885,7 @@ internal class SwapViewModel @Inject constructor(
|
|||
},
|
||||
onMaxAmountSelected = ::onMaxAmountClicked,
|
||||
onReduceAmount = ::onReduceAmountClicked,
|
||||
onLeaveExistentialDeposit = ::onLeaveExistentialDepositClicked,
|
||||
onReduceAmountIgnoreClick = {
|
||||
uiState = uiState.copy(
|
||||
reduceAmountIgnore = true,
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ markdown = "0.7.2"
|
|||
# endregion Other libraries
|
||||
|
||||
# region Tangem
|
||||
tangemBlockchainSdk = "release-app_5.11-676"
|
||||
tangemBlockchainSdk = "release-app_5.11-686"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "release-app_5.11-365"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue