diff --git a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/Warning.kt b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/Warning.kt index a7e59700b2..4d0d6904c4 100644 --- a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/Warning.kt +++ b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/domain/Warning.kt @@ -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() diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index c4c6346a13..e0fdbe57f1 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -386,7 +386,7 @@ internal class SwapInteractorImpl @Inject constructor( val fromToken = fromTokenStatus.currency val userWalletId = getSelectedWallet()?.walletId ?: return emptyList() val warnings = mutableListOf() - 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)) } } } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt index 2a873d453c..6bbee12717 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt @@ -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 diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index a49c381eff..c902406819 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -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, @@ -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 diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index 2adc1f3505..ee09df73c4 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -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)