From 9d5ba7e3eeaf54b97509f19d0b75acc112a9fe7f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 13 Jun 2024 19:57:22 +0100 Subject: [PATCH 1/2] Updated on 2026-08-14 --- .../swap/domain/models/domain/Warning.kt | 5 +- .../feature/swap/domain/SwapInteractorImpl.kt | 17 +++- .../tangem/feature/swap/models/UiActions.kt | 1 + .../tangem/feature/swap/ui/StateBuilder.kt | 78 +++++++++++++------ .../tangem/feature/swap/ui/TransactionCard.kt | 9 ++- .../feature/swap/viewmodels/SwapViewModel.kt | 5 ++ 6 files changed, 84 insertions(+), 31 deletions(-) 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 318b5635de..cc91593e45 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 31834e873f..62a0ba68e6 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 @@ -376,7 +376,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) 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)) } } } 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 bd84a05cac..87e4d70044 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 onReduceAmountIgnoreClick: () -> Unit, val openPermissionBottomSheet: () -> Unit, val onChangeApproveType: (ApproveType) -> Unit, 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 bfb07ed4a0..188ec65b0b 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 @@ -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, @@ -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().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 -> diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt index bd77a3f66e..e54128fe4d 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt @@ -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 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 d9598de96f..d85a174ead 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 @@ -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, From 6852c58265b828f241687680943790ad8978ea0f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 25 Jun 2024 11:15:12 +0100 Subject: [PATCH 2/2] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 7d9390b41a..6d75483fe7 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -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 ^