Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-20 13:11:49 +04:00
parent 78fafc0ee2
commit 74fa237c0d
8 changed files with 136 additions and 23 deletions

View file

@ -7,6 +7,7 @@ import com.tangem.domain.managetokens.repository.ManageTokensRepository
import com.tangem.domain.staking.repositories.StakingRepository
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 dagger.Module
import dagger.Provides
@ -67,6 +68,7 @@ internal object ManageTokensDomainModule {
networksRepository: NetworksRepository,
derivationsRepository: DerivationsRepository,
stakingRepository: StakingRepository,
quotesRepository: QuotesRepository,
): SaveManagedTokensUseCase {
return SaveManagedTokensUseCase(
customTokensRepository = customTokensRepository,
@ -75,6 +77,7 @@ internal object ManageTokensDomainModule {
networksRepository = networksRepository,
derivationsRepository = derivationsRepository,
stakingRepository = stakingRepository,
quotesRepository = quotesRepository,
)
}

View file

@ -4,6 +4,7 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.markets.*
import com.tangem.domain.markets.repositories.MarketsTokenRepository
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
@ -57,12 +58,16 @@ object MarketsDomainModule {
marketsTokenRepository: MarketsTokenRepository,
currenciesRepository: CurrenciesRepository,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
quotesRepository: QuotesRepository,
): SaveMarketTokensUseCase {
return SaveMarketTokensUseCase(
derivationsRepository = derivationsRepository,
marketsTokenRepository = marketsTokenRepository,
currenciesRepository = currenciesRepository,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
quotesRepository = quotesRepository,
)
}

View file

@ -27,10 +27,14 @@ internal object TokensDomainModule {
fun provideAddCryptoCurrenciesUseCase(
currenciesRepository: CurrenciesRepository,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
quotesRepository: QuotesRepository,
): AddCryptoCurrenciesUseCase {
return AddCryptoCurrenciesUseCase(
currenciesRepository = currenciesRepository,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
quotesRepository = quotesRepository,
)
}

View file

@ -10,9 +10,11 @@ import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
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.UserWalletId
@Suppress("LongParameterList")
class SaveManagedTokensUseCase(
private val customTokensRepository: CustomTokensRepository,
private val walletManagersFacade: WalletManagersFacade,
@ -20,6 +22,7 @@ class SaveManagedTokensUseCase(
private val networksRepository: NetworksRepository,
private val derivationsRepository: DerivationsRepository,
private val stakingRepository: StakingRepository,
private val quotesRepository: QuotesRepository,
) {
suspend operator fun invoke(
@ -54,6 +57,8 @@ class SaveManagedTokensUseCase(
)
refreshUpdatedYieldBalances(userWalletId, existingCurrencies)
refreshUpdatedQuotes(addingCurrencies)
}
private suspend fun removeCurrenciesFromWalletManager(
@ -105,6 +110,13 @@ class SaveManagedTokensUseCase(
)
}
private suspend fun refreshUpdatedQuotes(addedCurrencies: List<CryptoCurrency>) {
quotesRepository.fetchQuotes(
currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId },
refresh = true,
)
}
/**
* Determines if the [existingCurrencies] list contains a coin that corresponds
* to the given [network].

View file

@ -20,6 +20,7 @@ dependencies {
api(projects.domain.models)
api(projects.domain.legacy)
api(projects.domain.wallets)
api(projects.domain.staking)
implementation(projects.domain.tokens.models)
implementation(projects.domain.tokens)

View file

@ -3,10 +3,12 @@ package com.tangem.domain.markets
import arrow.core.Either
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.markets.repositories.MarketsTokenRepository
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
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.wallets.models.UserWalletId
/**
@ -23,6 +25,8 @@ class SaveMarketTokensUseCase(
private val marketsTokenRepository: MarketsTokenRepository,
private val currenciesRepository: CurrenciesRepository,
private val networksRepository: NetworksRepository,
private val stakingRepository: StakingRepository,
private val quotesRepository: QuotesRepository,
) {
suspend operator fun invoke(
@ -60,11 +64,37 @@ class SaveMarketTokensUseCase(
currenciesRepository.addCurrencies(userWalletId = userWalletId, currencies = addedCurrencies)
networksRepository.getNetworkStatusesSync(
userWalletId = userWalletId,
networks = addedCurrencies.map(CryptoCurrency::network).toSet(),
refresh = true,
)
refreshUpdatedNetworks(userWalletId, addedCurrencies)
refreshUpdatedYieldBalances(userWalletId, addedCurrencies)
refreshUpdatedQuotes(addedCurrencies)
}
}
private suspend fun refreshUpdatedNetworks(userWalletId: UserWalletId, addedCurrencies: List<CryptoCurrency>) {
networksRepository.getNetworkStatusesSync(
userWalletId = userWalletId,
networks = addedCurrencies.map(CryptoCurrency::network).toSet(),
refresh = true,
)
}
private suspend fun refreshUpdatedYieldBalances(
userWalletId: UserWalletId,
existingCurrencies: List<CryptoCurrency>,
) {
stakingRepository.fetchMultiYieldBalance(
userWalletId = userWalletId,
cryptoCurrencies = existingCurrencies,
refresh = true,
)
}
private suspend fun refreshUpdatedQuotes(addedCurrencies: List<CryptoCurrency>) {
quotesRepository.fetchQuotes(
currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId },
refresh = true,
)
}
}

View file

@ -5,10 +5,12 @@ import arrow.core.raise.Raise
import arrow.core.raise.catch
import arrow.core.raise.either
import arrow.core.toNonEmptyListOrNull
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
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.wallets.models.UserWalletId
/**
@ -21,6 +23,8 @@ import com.tangem.domain.wallets.models.UserWalletId
class AddCryptoCurrenciesUseCase(
private val currenciesRepository: CurrenciesRepository,
private val networksRepository: NetworksRepository,
private val stakingRepository: StakingRepository,
private val quotesRepository: QuotesRepository,
) {
/**
@ -105,6 +109,8 @@ class AddCryptoCurrenciesUseCase(
val tokenToAdd = createTokenCurrency(userWalletId, contractAddress, networkId)
addCurrencies(userWalletId, listOf(tokenToAdd))
refreshUpdatedNetworks(userWalletId, listOf(tokenToAdd), existingCurrencies)
refreshUpdatedYieldBalances(userWalletId, existingCurrencies)
refreshUpdatedQuotes(existingCurrencies)
tokenToAdd
}
@ -139,6 +145,24 @@ class AddCryptoCurrenciesUseCase(
}
}
private suspend fun refreshUpdatedYieldBalances(
userWalletId: UserWalletId,
existingCurrencies: List<CryptoCurrency>,
) {
stakingRepository.fetchMultiYieldBalance(
userWalletId = userWalletId,
cryptoCurrencies = existingCurrencies,
refresh = true,
)
}
private suspend fun refreshUpdatedQuotes(addedCurrencies: List<CryptoCurrency>) {
quotesRepository.fetchQuotes(
currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId },
refresh = true,
)
}
private suspend fun Raise<Throwable>.createTokenCurrency(
userWalletId: UserWalletId,
contractAddress: String,

View file

@ -7,6 +7,7 @@ import com.tangem.domain.core.lce.LceFlow
import com.tangem.domain.core.lce.lce
import com.tangem.domain.core.lce.lceFlow
import com.tangem.domain.core.utils.EitherFlow
import com.tangem.domain.core.utils.lceContent
import com.tangem.domain.core.utils.lceError
import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.staking.model.stakekit.YieldBalanceList
@ -44,30 +45,49 @@ class CachedCurrenciesStatusesOperations(
userWalletId: UserWalletId,
currenciesFlow: EitherFlow<TokenListError, List<CryptoCurrency>>,
): LceFlow<TokenListError, List<CryptoCurrencyStatus>> = lceFlow {
currenciesFlow.flatMapLatest { maybeCurrencies ->
val isUpdating = MutableStateFlow(value = true)
val prevStatuses = MutableStateFlow(value = emptyList<CryptoCurrencyStatus>())
val nonEmptyCurrencies = maybeCurrencies
val isUpdating = MutableStateFlow(value = true)
val isFetchingStarted = MutableStateFlow(value = false)
val nonEmptyCurrencies = currenciesFlow.mapNotNull { it.getOrNull() }.firstOrNull()?.toNonEmptyListOrNull()
if (nonEmptyCurrencies != null) {
launch {
isFetchingStarted.value = true
val (networks, currenciesIds) = getIds(nonEmptyCurrencies)
fetchComponents(userWalletId, networks, currenciesIds, nonEmptyCurrencies)
}
.invokeOnCompletion { isUpdating.value = false }
}
currenciesFlow.flatMapLatest { maybeCurrencies ->
val currencies = maybeCurrencies
.getOrElse { return@flatMapLatest flowOf(it.lceError()) }
.toNonEmptyListOrNull()
if (nonEmptyCurrencies.isNullOrEmpty()) {
if (currencies.isNullOrEmpty()) {
prevStatuses.value = emptyList()
return@flatMapLatest flowOf(TokenListError.EmptyTokens.lceError())
}
// This is only 'true' when the flow here is empty, such as during initial loading
if (isLoading.get()) {
val loadingCurrencies = createCurrenciesStatuses(
currencies = nonEmptyCurrencies,
currencies = currencies,
maybeNetworkStatuses = null,
maybeQuotes = null,
maybeYieldBalances = null,
isUpdating = true,
)
loadingCurrencies.getOrNull()?.let { prevStatuses.value = it }
send(loadingCurrencies)
}
val (networks, currenciesIds) = getIds(nonEmptyCurrencies)
val (networks, currenciesIds) = getIds(currencies)
fun createCurrenciesStatuses(
maybeQuotes: Either<TokenListError, Set<Quote>>?,
@ -75,26 +95,46 @@ class CachedCurrenciesStatusesOperations(
maybeYieldBalances: Either<TokenListError, YieldBalanceList>?,
isUpdating: Boolean,
) = createCurrenciesStatuses(
currencies = nonEmptyCurrencies,
currencies = currencies,
maybeQuotes = maybeQuotes,
maybeNetworkStatuses = maybeNetworkStatuses,
maybeYieldBalances = maybeYieldBalances,
isUpdating = isUpdating,
)
launch { fetchComponents(userWalletId, networks, currenciesIds, nonEmptyCurrencies) }
.invokeOnCompletion { isUpdating.value = false }
// removing token
val prevStatusesValue = prevStatuses.value
if (prevStatusesValue.size - currencies.size == 1) {
val removed = prevStatusesValue.map { it.currency } - currencies
return@flatMapLatest flowOf(
prevStatusesValue.filter { it.currency !in removed }.lceContent(),
)
}
if (!isFetchingStarted.value) {
launch {
isFetchingStarted.value = true
fetchComponents(userWalletId, networks, currenciesIds, currencies)
}
.invokeOnCompletion { isUpdating.value = false }
}
combine(
flow = getQuotes(currenciesIds),
flow2 = getNetworksStatuses(userWalletId, networks),
flow3 = getYieldBalances(userWalletId, nonEmptyCurrencies),
flow3 = getYieldBalances(userWalletId, currencies),
flow4 = isUpdating,
transform = ::createCurrenciesStatuses,
)
.distinctUntilChanged()
}
.onEach(::send)
.onEach { statusesLce ->
statusesLce.getOrNull()?.let { prevStatuses.value = it }
send(statusesLce)
}
.launchIn(scope = this)
}
@ -112,13 +152,7 @@ class CachedCurrenciesStatusesOperations(
val rawCurrenciesIds = currenciesIds.mapNotNullTo(mutableSetOf()) { it.rawCurrencyId }
quotesRepository.fetchQuotes(rawCurrenciesIds)
},
async {
if (currencies.size == 1) {
stakingRepository.fetchSingleYieldBalance(userWalletId, currencies.first())
} else {
stakingRepository.fetchMultiYieldBalance(userWalletId, currencies)
}
},
async { stakingRepository.fetchMultiYieldBalance(userWalletId, currencies) },
)
}
.map { }