diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt index 858fdccb46..83636c2a98 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt @@ -9,6 +9,7 @@ import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.format.bigdecimal.shorted +import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import java.math.BigDecimal sealed class NotificationUM(val config: NotificationConfig) { @@ -307,4 +308,17 @@ sealed class NotificationUM(val config: NotificationConfig) { ), ) } + + sealed interface Solana { + + data class RentInfo( + private val rentInfo: CryptoCurrencyWarning.Rent, + ) : Info( + title = TextReference.Res(R.string.warning_rent_fee_title), + subtitle = TextReference.Res( + id = R.string.warning_solana_rent_fee_message, + formatArgs = wrappedList(rentInfo.rent, rentInfo.exemptionAmount), + ), + ) + } } \ No newline at end of file diff --git a/data/tokens/build.gradle.kts b/data/tokens/build.gradle.kts index 7585dedc01..bf90a79e76 100644 --- a/data/tokens/build.gradle.kts +++ b/data/tokens/build.gradle.kts @@ -19,6 +19,7 @@ dependencies { implementation(projects.domain.tokens.models) implementation(projects.domain.txhistory.models) implementation(projects.domain.wallets.models) + implementation(projects.domain.staking.models) /** Project - Data */ implementation(projects.core.datasource) diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt index a62d70a3f8..afa7be79cc 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt @@ -6,13 +6,17 @@ import com.tangem.blockchain.common.MinimumSendAmountProvider import com.tangem.blockchain.common.ReserveAmountProvider import com.tangem.blockchain.common.UtxoAmountLimitProvider import com.tangem.data.tokens.converters.UtxoConverter +import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.CurrencyAmount import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.blockchains.UtxoAmountLimit +import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.extensions.isZero import kotlinx.coroutines.withContext import java.math.BigDecimal @@ -118,4 +122,23 @@ internal class DefaultCurrencyChecksRepository( } return utxoAmount?.let(UtxoConverter()::convert) } + + override suspend fun getRentInfoWarning( + userWalletId: UserWalletId, + currencyStatus: CryptoCurrencyStatus, + balanceAfterTransaction: BigDecimal?, + ): CryptoCurrencyWarning.Rent? { + val rentData = walletManagersFacade.getRentInfo(userWalletId, currencyStatus.currency.network) ?: return null + val balanceValue = currencyStatus.value as? CryptoCurrencyStatus.Loaded ?: return null + val stakingTotalBalance = + (balanceValue.yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance() ?: BigDecimal.ZERO + val amount = balanceAfterTransaction ?: balanceValue.amount + return when { + amount.isZero() && stakingTotalBalance.isZero() -> null + amount < rentData.exemptionAmount && stakingTotalBalance.isZero() -> { + CryptoCurrencyWarning.Rent(rentData.rent, rentData.exemptionAmount) + } + else -> null + } + } } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index 5a531976c7..a684f579c9 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -25,11 +25,11 @@ import com.tangem.domain.common.util.hasDerivation import com.tangem.domain.demo.DemoConfig import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network -import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.transaction.models.AssetRequirementsCondition import com.tangem.domain.txhistory.models.PaginationWrapper import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryState +import com.tangem.domain.walletmanager.model.RentData import com.tangem.domain.walletmanager.model.SmartContractMethod import com.tangem.domain.walletmanager.model.TokenInfo import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult @@ -413,7 +413,7 @@ class DefaultWalletManagersFacade( return manager?.wallet?.addresses.orEmpty() } - override suspend fun getRentInfo(userWalletId: UserWalletId, network: Network): CryptoCurrencyWarning.Rent? { + override suspend fun getRentInfo(userWalletId: UserWalletId, network: Network): RentData? { val manager = getOrCreateWalletManager( userWalletId = userWalletId, network = network, @@ -422,24 +422,7 @@ class DefaultWalletManagersFacade( return when (val result = manager.minimalBalanceForRentExemption()) { is Result.Success -> { - val balance = manager.wallet.fundsAvailable(AmountType.Coin) - val outgoingTxs = manager.wallet.recentTransactions - .filter { - it.sourceAddress == manager.wallet.address && - it.amount.type == AmountType.Coin && - it.status != TransactionStatus.Confirmed - } - - val rentExempt = result.data - val setRent = if (outgoingTxs.isEmpty()) { - balance < rentExempt - } else { - val outgoingAmount = outgoingTxs.sumOf { it.amount.value ?: BigDecimal.ZERO } - val rest = balance.minus(outgoingAmount) - balance < rest - } - - if (setRent) CryptoCurrencyWarning.Rent(manager.rentAmount(), rentExempt) else null + RentData(manager.rentAmount(), result.data) } is Result.Failure -> null } diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt index 2f6317a316..6031b2300b 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt @@ -12,11 +12,11 @@ import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.SimpleResult import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network -import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.transaction.models.AssetRequirementsCondition import com.tangem.domain.txhistory.models.PaginationWrapper import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryState +import com.tangem.domain.walletmanager.model.RentData import com.tangem.domain.walletmanager.model.TokenInfo import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult import com.tangem.domain.wallets.models.UserWalletId @@ -140,7 +140,7 @@ interface WalletManagersFacade { * @param userWalletId selected wallet id * @param network network of currency */ - suspend fun getRentInfo(userWalletId: UserWalletId, network: Network): CryptoCurrencyWarning.Rent? + suspend fun getRentInfo(userWalletId: UserWalletId, network: Network): RentData? @Deprecated("Will be removed in future") fun getAll(userWalletId: UserWalletId): Flow> diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/RentData.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/RentData.kt new file mode 100644 index 0000000000..05973c0c00 --- /dev/null +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/RentData.kt @@ -0,0 +1,11 @@ +package com.tangem.domain.walletmanager.model + +import java.math.BigDecimal + +/** + * Represents wallet blockchain rent + * @param rent Amount that will be charged in overtime if the blockchain does not have an amount greater than + * the [exemptionAmount] + * @param exemptionAmount Amount that should be on the blockchain balance not to pay rent + */ +data class RentData(val rent: BigDecimal, val exemptionAmount: BigDecimal) \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt index dae8238b02..906c1ff0a4 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt @@ -10,4 +10,5 @@ data class CryptoCurrencyCheck( val existentialDeposit: BigDecimal?, val utxoAmountLimit: UtxoAmountLimit?, val isAccountFunded: Boolean, + val rentWarning: CryptoCurrencyWarning.Rent?, ) \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt index ba9e837248..8de4b3ea7e 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt @@ -18,6 +18,7 @@ class GetCurrencyCheckUseCase( currencyStatus: CryptoCurrencyStatus, amount: BigDecimal?, fee: BigDecimal?, + balanceAfterTransaction: BigDecimal?, recipientAddress: String? = null, ): CryptoCurrencyCheck { return withContext(dispatchers.io) { @@ -26,6 +27,8 @@ class GetCurrencyCheckUseCase( val reserveAmount = currencyChecksRepository.getReserveAmount(userWalletId, network) val minimumSendAmount = currencyChecksRepository.getMinimumSendAmount(userWalletId, network) val existentialDeposit = currencyChecksRepository.getExistentialDeposit(userWalletId, network) + val rentWarning = + currencyChecksRepository.getRentInfoWarning(userWalletId, currencyStatus, balanceAfterTransaction) val isAccountFunded = recipientAddress?.let { currencyChecksRepository.checkIfAccountFunded( userWalletId, @@ -51,6 +54,7 @@ class GetCurrencyCheckUseCase( existentialDeposit = existentialDeposit, utxoAmountLimit = utxoAmountLimit, isAccountFunded = isAccountFunded, + rentWarning = rentWarning, ) } } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt index 9118e3098d..f52bcea470 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt @@ -58,7 +58,7 @@ class GetCurrencyWarningsUseCase( derivationPath = derivationPath, isSingleWalletWithTokens = isSingleWalletWithTokens, ), - flowOf(walletManagersFacade.getRentInfo(userWalletId, currency.network)), + flowOf(currencyChecksRepository.getRentInfoWarning(userWalletId, currencyStatus, null)), flowOf(currencyChecksRepository.getExistentialDeposit(userWalletId, currency.network)), flowOf(currencyChecksRepository.getFeeResourceAmount(userWalletId, currency.network)), getSwapPromoNotificationWarning( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt index 4ff37c6420..726e942b37 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt @@ -1,8 +1,10 @@ package com.tangem.domain.tokens.repository +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.CurrencyAmount import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.blockchains.UtxoAmountLimit +import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.wallets.models.UserWalletId import java.math.BigDecimal @@ -39,4 +41,14 @@ interface CurrencyChecksRepository { amount: BigDecimal, fee: BigDecimal, ): UtxoAmountLimit? + + /** + * Checks if need warning for Solana rent + * @param balanceAfterTransaction if non null, checks with balance remains after transaction + */ + suspend fun getRentInfoWarning( + userWalletId: UserWalletId, + currencyStatus: CryptoCurrencyStatus, + balanceAfterTransaction: BigDecimal?, + ): CryptoCurrencyWarning.Rent? } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt index 9975712433..21604ac0b0 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt @@ -22,6 +22,7 @@ import com.tangem.domain.tokens.GetBalanceNotEnoughForFeeWarningUseCase import com.tangem.domain.tokens.GetCurrencyCheckUseCase import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck +import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.ValidateTransactionUseCase import com.tangem.domain.utils.convertToSdkAmount @@ -93,12 +94,15 @@ internal class SendNotificationFactory( val feeError = (feeState.feeSelectorState as? FeeSelectorState.Error)?.error val recipientAddress = state.recipientState?.addressTextField?.value + val statusValue = cryptoCurrencyStatus.value as? CryptoCurrencyStatus.Loaded + val balanceAfterTransaction = statusValue?.let { it.amount - sendingAmount - feeValue } val currencyCheck = getCurrencyCheckUseCase( userWalletId = userWalletId, currencyStatus = cryptoCurrencyStatus, amount = sendingAmount, fee = feeValue, recipientAddress = recipientAddress, + balanceAfterTransaction = balanceAfterTransaction, ) buildList { addErrorNotifications( @@ -221,6 +225,11 @@ internal class SendNotificationFactory( network = cryptoCurrencyStatus.currency.network, ).leftOrNull() } + + addRentExemptionNotification( + rentWarning = currencyCheck.rentWarning, + ) + addExistentialWarningNotification( existentialDeposit = currencyCheck.existentialDeposit, feeAmount = feeState.fee?.amount?.value.orZero(), @@ -303,4 +312,9 @@ internal class SendNotificationFactory( add(NotificationUM.Warning.TooHigh(diff)) } } + + private fun MutableList.addRentExemptionNotification(rentWarning: CryptoCurrencyWarning.Rent?) { + if (rentWarning == null) return + add(NotificationUM.Solana.RentInfo(rentWarning)) + } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/common/Notifications.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/common/Notifications.kt index b6d93d00c8..501c61649f 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/common/Notifications.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/common/Notifications.kt @@ -44,7 +44,7 @@ internal fun LazyListScope.notifications( is NotificationUM.Warning, -> null is NotificationUM.Error -> TangemTheme.colors.icon.warning - is NotificationUM.Info -> null + is NotificationUM.Info -> TangemTheme.colors.icon.accent }, isEnabled = !isClickDisabled, ) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt index b58a5d7dfb..b28cfd94ad 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt @@ -638,6 +638,7 @@ internal class StakingViewModel @Inject constructor( currencyStatus = cryptoCurrencyStatus, amount = amount, fee = fee, + balanceAfterTransaction = null, // ignore this in staking ) stateController.update( AddStakingNotificationsTransformer(