Updated on 2026-08-14

This commit is contained in:
Tangem 2023-09-19 17:58:14 +05:00
parent b90a305734
commit 190e5ee2c6
5 changed files with 197 additions and 3 deletions

View file

@ -6,6 +6,7 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.MarketCryptoCurrencyRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
@ -58,6 +59,24 @@ internal object TokensDomainModule {
return GetCurrencyStatusUpdatesUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers)
}
@Provides
@ViewModelScoped
fun provideGetCurrencyWarningsUseCase(
walletManagersFacade: WalletManagersFacade,
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
dispatchers: CoroutineDispatcherProvider,
): GetCurrencyWarningsUseCase {
return GetCurrencyWarningsUseCase(
walletManagersFacade = walletManagersFacade,
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
networksRepository = networksRepository,
dispatchers = dispatchers,
)
}
@Provides
@ViewModelScoped
fun provideGetPrimaryCurrencyUseCase(

View file

@ -1,8 +1,8 @@
package com.tangem.domain.walletmanager
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
import com.tangem.blockchain.blockchains.solana.RentProvider
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.address.Address
import com.tangem.blockchain.extensions.Result
import com.tangem.crypto.hdWallet.DerivationPath
@ -13,14 +13,17 @@ import com.tangem.domain.common.util.hasDerivation
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.tokens.models.CryptoCurrency
import com.tangem.domain.tokens.models.Network
import com.tangem.domain.tokens.models.warnings.CryptoCurrencyWarning
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.UpdateWalletManagerResult
import com.tangem.domain.walletmanager.utils.*
import com.tangem.domain.walletmanager.utils.WalletManagerFactory
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import timber.log.Timber
import java.math.BigDecimal
// FIXME: Move to its own module and make internal
@Deprecated("Inject the WalletManagerFacade interface using DI instead")
@ -218,6 +221,49 @@ class DefaultWalletManagersFacade(
.orEmpty()
}
override suspend fun getRentInfo(userWalletId: UserWalletId, network: Network): CryptoCurrencyWarning.Rent? {
val userWallet = getUserWallet(userWalletId)
val blockchain = Blockchain.fromId(network.id.value)
val manager = getOrCreateWalletManager(
userWallet = userWallet,
blockchain = blockchain,
derivationPath = network.derivationPath.value,
)
if (manager !is RentProvider) return null
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 }
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
}
is Result.Failure -> null
}
}
override suspend fun getExistentialDeposit(userWalletId: UserWalletId, network: Network): BigDecimal? {
val userWallet = getUserWallet(userWalletId)
val blockchain = Blockchain.fromId(network.id.value)
val manager = getOrCreateWalletManager(
userWallet = userWallet,
blockchain = blockchain,
derivationPath = network.derivationPath.value,
)
return if (manager is ExistentialDepositProvider) manager.getExistentialDeposit() else null
}
private fun updateWalletManagerTokensIfNeeded(walletManager: WalletManager, tokens: Set<CryptoCurrency.Token>) {
if (tokens.isEmpty()) return

View file

@ -3,14 +3,17 @@ package com.tangem.domain.walletmanager
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.address.Address
import com.tangem.blockchain.blockchains.solana.RentProvider
import com.tangem.domain.tokens.models.CryptoCurrency
import com.tangem.domain.tokens.models.Network
import com.tangem.domain.tokens.models.warnings.CryptoCurrencyWarning
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.UpdateWalletManagerResult
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import java.math.BigDecimal
// TODO: Move to its own module
/**
@ -79,4 +82,18 @@ interface WalletManagersFacade {
* @param network network of currency
*/
suspend fun getAddress(userWalletId: UserWalletId, network: Network): List<Address>
/**
* Returns info about rent if wallet manager implemented [RentProvider], otherwise null
*
* @param userWalletId selected wallet id
* @param network network of currency
*/
suspend fun getRentInfo(userWalletId: UserWalletId, network: Network): CryptoCurrencyWarning.Rent?
/**
* Returns value which indicates if the account balance drops below the existential deposit value, it will be
* deactivated and any remaining funds will be destroyed.
*/
suspend fun getExistentialDeposit(userWalletId: UserWalletId, network: Network): BigDecimal?
}

View file

@ -0,0 +1,28 @@
package com.tangem.domain.tokens.models.warnings
import com.tangem.domain.tokens.models.CryptoCurrency
import java.math.BigDecimal
sealed class CryptoCurrencyWarning {
data class ExistentialDeposit(
val currencyName: String,
val edStringValueWithSymbol: String,
) : CryptoCurrencyWarning()
data class BalanceNotEnoughForFee(
val currency: CryptoCurrency,
val blockchainFullName: String,
val blockchainSymbol: String,
) : CryptoCurrencyWarning()
object SomeNetworksUnreachable : CryptoCurrencyWarning()
/**
* 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 Rent(val rent: BigDecimal, val exemptionAmount: BigDecimal) : CryptoCurrencyWarning()
}

View file

@ -0,0 +1,84 @@
package com.tangem.domain.tokens
import com.tangem.domain.tokens.models.CryptoCurrency
import com.tangem.domain.tokens.models.Network
import com.tangem.domain.tokens.models.warnings.CryptoCurrencyWarning
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
import java.math.BigDecimal
class GetCurrencyWarningsUseCase(
private val walletManagersFacade: WalletManagersFacade,
private val currenciesRepository: CurrenciesRepository,
private val quotesRepository: QuotesRepository,
private val networksRepository: NetworksRepository,
private val dispatchers: CoroutineDispatcherProvider,
) {
suspend operator fun invoke(userWallet: UserWallet, currency: CryptoCurrency): Flow<Set<CryptoCurrencyWarning>> {
return combine(
getFeeWarningFlow(
userWalletId = userWallet.walletId,
networkId = currency.network.id,
currencyId = currency.id,
),
flowOf(walletManagersFacade.getRentInfo(userWallet.walletId, currency.network)),
flowOf(walletManagersFacade.getExistentialDeposit(userWallet.walletId, currency.network)),
) { maybeFeeWarning, maybeRentWarning, maybeEdWarning ->
setOfNotNull(
maybeRentWarning,
maybeEdWarning?.let {
CryptoCurrencyWarning.ExistentialDeposit(
currencyName = currency.name,
edStringValueWithSymbol = "${it.toPlainString()} ${currency.symbol}",
)
},
maybeFeeWarning,
)
}.flowOn(dispatchers.io)
}
private suspend fun getFeeWarningFlow(
userWalletId: UserWalletId,
networkId: Network.ID,
currencyId: CryptoCurrency.ID,
): Flow<CryptoCurrencyWarning?> {
val operations = CurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
networksRepository = networksRepository,
userWalletId = userWalletId,
)
return combine(
operations.getCurrencyStatusFlow(currencyId).map { it.getOrNull() },
operations.getNetworkCoinFlow(networkId).map { it.getOrNull() },
) { tokenStatus, coinStatus ->
when {
tokenStatus != null && coinStatus != null -> {
if (!tokenStatus.value.amount.isZero() && coinStatus.value.amount.isZero()) {
CryptoCurrencyWarning.BalanceNotEnoughForFee(
currency = tokenStatus.currency,
blockchainFullName = coinStatus.currency.name,
blockchainSymbol = coinStatus.currency.symbol,
)
} else {
null
}
}
else -> CryptoCurrencyWarning.SomeNetworksUnreachable
}
}
}
private fun BigDecimal?.isZero(): Boolean {
return this?.compareTo(BigDecimal.ZERO) == 0
}
}