Updated on 2026-08-14

This commit is contained in:
Tangem 2025-05-29 18:56:04 +04:00
parent b98b0f659c
commit 8b0a7f01cf
18 changed files with 83 additions and 281 deletions

View file

@ -14,7 +14,6 @@ import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWalletId
@ -25,7 +24,6 @@ class SaveManagedTokensUseCase(
private val currenciesRepository: CurrenciesRepository,
private val derivationsRepository: DerivationsRepository,
private val stakingRepository: StakingRepository,
private val quotesRepository: QuotesRepository,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
private val multiQuoteFetcher: MultiQuoteFetcher,
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
@ -129,19 +127,12 @@ class SaveManagedTokensUseCase(
}
private suspend fun refreshUpdatedQuotes(addedCurrencies: List<CryptoCurrency>) {
if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(
currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId },
appCurrencyId = null,
),
)
} else {
quotesRepository.fetchQuotes(
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(
currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId },
refresh = true,
)
}
appCurrencyId = null,
),
)
}
/**

View file

@ -6,18 +6,14 @@ import arrow.core.toOption
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.quotes.single.SingleQuoteProducer
import com.tangem.domain.quotes.single.SingleQuoteSupplier
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.repository.QuotesRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
class GetCurrencyQuotesUseCase(
private val quotesRepository: QuotesRepository,
private val singleQuoteSupplier: SingleQuoteSupplier,
private val tokensFeatureToggles: TokensFeatureToggles,
) {
// TODO apply interval parameter [REDACTED_TASK_KEY]
operator fun invoke(
@ -27,18 +23,10 @@ class GetCurrencyQuotesUseCase(
): Flow<Option<Quote.Value>> {
val rawId = currencyID.rawCurrencyId ?: return flowOf(None)
return if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
singleQuoteSupplier(
params = SingleQuoteProducer.Params(rawCurrencyId = rawId),
)
.map { (it as? Quote.Value).toOption() }
} else {
quotesRepository.getQuotesUpdatesLegacy(
currenciesIds = setOf(rawId),
refresh = refresh,
)
.map { it.filterIsInstance<Quote.Value>().firstOrNull().toOption() }
}
return singleQuoteSupplier(
params = SingleQuoteProducer.Params(rawCurrencyId = rawId),
)
.map { (it as? Quote.Value).toOption() }
.catch { emit(None) }
}
}

View file

@ -12,7 +12,6 @@ import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
/**
@ -30,7 +29,6 @@ class SaveMarketTokensUseCase(
private val marketsTokenRepository: MarketsTokenRepository,
private val currenciesRepository: CurrenciesRepository,
private val stakingRepository: StakingRepository,
private val quotesRepository: QuotesRepository,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
private val multiQuoteFetcher: MultiQuoteFetcher,
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
@ -110,18 +108,11 @@ class SaveMarketTokensUseCase(
}
private suspend fun refreshUpdatedQuotes(addedCurrencies: List<CryptoCurrency>) {
if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(
currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId },
appCurrencyId = null,
),
)
} else {
quotesRepository.fetchQuotes(
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(
currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId },
refresh = true,
)
}
appCurrencyId = null,
),
)
}
}

View file

@ -12,7 +12,6 @@ import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -29,7 +28,6 @@ import kotlinx.coroutines.coroutineScope
class AddCryptoCurrenciesUseCase(
private val currenciesRepository: CurrenciesRepository,
private val stakingRepository: StakingRepository,
private val quotesRepository: QuotesRepository,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
private val multiQuoteFetcher: MultiQuoteFetcher,
private val singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
@ -165,19 +163,12 @@ class AddCryptoCurrenciesUseCase(
}
private suspend fun refreshUpdatedQuotes(currencyToAdd: CryptoCurrency) {
if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(
currenciesIds = setOfNotNull(currencyToAdd.id.rawCurrencyId),
appCurrencyId = null,
),
)
} else {
quotesRepository.fetchQuotes(
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(
currenciesIds = setOfNotNull(currencyToAdd.id.rawCurrencyId),
refresh = true,
)
}
appCurrencyId = null,
),
)
}
private suspend fun Raise<Throwable>.createTokenCurrency(

View file

@ -13,7 +13,6 @@ import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -22,7 +21,6 @@ import kotlinx.coroutines.coroutineScope
@Suppress("LongParameterList")
class FetchCardTokenListUseCase(
private val currenciesRepository: CurrenciesRepository,
private val quotesRepository: QuotesRepository,
private val stakingRepository: StakingRepository,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
private val multiQuoteFetcher: MultiQuoteFetcher,
@ -44,7 +42,6 @@ class FetchCardTokenListUseCase(
val fetchQuotes = async {
fetchQuotes(
currenciesIds = currencies.mapNotNullTo(destination = hashSetOf()) { it.id.rawCurrencyId },
refresh = refresh,
)
}
val yieldBalances = async {
@ -85,17 +82,10 @@ class FetchCardTokenListUseCase(
.bind()
}
private suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.RawID>, refresh: Boolean) {
if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(currenciesIds = currenciesIds, appCurrencyId = null),
)
} else {
catch(
block = { quotesRepository.getQuotesSync(currenciesIds, refresh) },
catch = { /* Ignore error */ },
)
}
private suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.RawID>) {
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(currenciesIds = currenciesIds, appCurrencyId = null),
)
}
private suspend fun fetchYieldBalances(

View file

@ -13,7 +13,6 @@ import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -25,13 +24,11 @@ import kotlinx.coroutines.coroutineScope
* by providing a specific currency ID or fetching the status of the primary currency.
*
* @param currenciesRepository The repository for retrieving currency-related data.
* @param quotesRepository The repository for retrieving cryptocurrency quotes.
*/
// TODO: Add tests
@Suppress("LongParameterList")
class FetchCurrencyStatusUseCase(
private val currenciesRepository: CurrenciesRepository,
private val quotesRepository: QuotesRepository,
private val stakingRepository: StakingRepository,
private val singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
private val multiQuoteFetcher: MultiQuoteFetcher,
@ -86,7 +83,7 @@ class FetchCurrencyStatusUseCase(
fetchNetworkStatus(userWalletId, currency.network)
}
val fetchQuote = async {
fetchQuote(currency.id, refresh)
fetchQuote(currency.id)
}
val fetchStakingBalance = async {
fetchStakingBalance(userWalletId, currency, refresh)
@ -125,21 +122,15 @@ class FetchCurrencyStatusUseCase(
.bind()
}
private suspend fun Raise<CurrencyStatusError>.fetchQuote(currencyId: CryptoCurrency.ID, refresh: Boolean) {
if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(
currenciesIds = setOfNotNull(currencyId.rawCurrencyId),
appCurrencyId = null,
),
)
} else {
catch(
block = { quotesRepository.getQuotesSync(setOfNotNull(currencyId.rawCurrencyId), refresh) },
) {
raise(CurrencyStatusError.DataError(it))
}
}
private suspend fun Raise<CurrencyStatusError>.fetchQuote(currencyId: CryptoCurrency.ID) {
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(
currenciesIds = setOfNotNull(currencyId.rawCurrencyId),
appCurrencyId = null,
),
)
.mapLeft { CurrencyStatusError.DataError(it) }
.bind()
}
private suspend fun Raise<CurrencyStatusError>.fetchStakingBalance(

View file

@ -15,7 +15,6 @@ import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -26,14 +25,12 @@ import kotlinx.coroutines.coroutineScope
* network statuses, and quotes for tokens associated with a user's wallet.
*
* @param currenciesRepository The repository for retrieving currency-related data.
* @param quotesRepository The repository for retrieving cryptocurrency quotes.
* @param stakingRepository The repository for retrieving staking-related data.
*/
// TODO: Add tests
@Suppress("LongParameterList")
class FetchTokenListUseCase(
private val currenciesRepository: CurrenciesRepository,
private val quotesRepository: QuotesRepository,
private val stakingRepository: StakingRepository,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
private val multiQuoteFetcher: MultiQuoteFetcher,
@ -73,7 +70,6 @@ class FetchTokenListUseCase(
val fetchQuotes = async {
fetchQuotes(
currenciesIds = currencies.mapTo(hashSetOf()) { it.id },
refresh = mode.refreshQuotes,
)
}
@ -115,24 +111,15 @@ class FetchTokenListUseCase(
.bind()
}
private suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean) {
if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(
currenciesIds = currenciesIds.mapNotNull { it.rawCurrencyId }.toSet(),
appCurrencyId = null,
),
)
} else {
catch(
block = {
val rawIds = currenciesIds.mapNotNull { it.rawCurrencyId }.toSet()
quotesRepository.getQuotesSync(rawIds, refresh)
},
) {
/* Ignore error */
}
}
private suspend fun Raise<TokenListError>.fetchQuotes(currenciesIds: Set<CryptoCurrency.ID>) {
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(
currenciesIds = currenciesIds.mapNotNull { it.rawCurrencyId }.toSet(),
appCurrencyId = null,
),
)
.mapLeft(TokenListError::DataError)
.bind()
}
private suspend fun fetchYieldBalances(

View file

@ -8,17 +8,14 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.quotes.multi.MultiQuoteFetcher
import com.tangem.domain.tokens.error.QuotesError
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
class RefreshMultiCurrencyWalletQuotesUseCase(
private val quotesRepository: QuotesRepository,
private val currenciesRepository: CurrenciesRepository,
private val multiQuoteFetcher: MultiQuoteFetcher,
private val tokensFeatureToggles: TokensFeatureToggles,
) {
suspend operator fun invoke(userWalletId: UserWalletId): Either<QuotesError, Unit> {
@ -48,23 +45,11 @@ class RefreshMultiCurrencyWalletQuotesUseCase(
}
private suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.ID>) {
if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(
currenciesIds = currenciesIds.mapNotNullTo(hashSetOf(), CryptoCurrency.ID::rawCurrencyId),
appCurrencyId = null,
),
)
} else {
catch(
block = {
quotesRepository.fetchQuotes(
currenciesIds = currenciesIds.mapNotNullTo(hashSetOf(), CryptoCurrency.ID::rawCurrencyId),
refresh = true,
)
},
catch = { /* Ignore error */ },
)
}
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(
currenciesIds = currenciesIds.mapNotNullTo(hashSetOf(), CryptoCurrency.ID::rawCurrencyId),
appCurrencyId = null,
),
)
}
}

View file

@ -7,7 +7,5 @@ package com.tangem.domain.tokens
*/
interface TokensFeatureToggles {
val isQuotesLoadingRefactoringEnabled: Boolean
val isStakingLoadingRefactoringEnabled: Boolean
}

View file

@ -26,7 +26,6 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.tokens.utils.CurrencyStatusProxyCreator
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.*
@ -35,7 +34,6 @@ import kotlinx.coroutines.flow.*
* Base operations for working with currency status
*
* @property currenciesRepository repository for currencies
* @property quotesRepository repository for quotes
* @property stakingRepository repository for staking
*
[REDACTED_AUTHOR]
@ -43,7 +41,6 @@ import kotlinx.coroutines.flow.*
@Suppress("LargeClass", "LongParameterList")
abstract class BaseCurrencyStatusOperations(
private val currenciesRepository: CurrenciesRepository,
private val quotesRepository: QuotesRepository,
private val quotesRepositoryV2: QuotesRepositoryV2,
private val stakingRepository: StakingRepository,
private val multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
@ -194,12 +191,8 @@ abstract class BaseCurrencyStatusOperations(
}
val quote = cryptoCurrencyId.rawCurrencyId?.let { rawId ->
if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
singleQuoteSupplier(params = SingleQuoteProducer.Params(rawCurrencyId = rawId))
.firstOrNull()
} else {
quotesRepository.getQuoteSync(rawId)
}
singleQuoteSupplier(params = SingleQuoteProducer.Params(rawCurrencyId = rawId))
.firstOrNull()
}
?.right()
?: Error.EmptyQuotes.left()
@ -264,11 +257,7 @@ abstract class BaseCurrencyStatusOperations(
val (_, currenciesIds) = getIds(nonEmptyCurrencies)
val rawIds = currenciesIds.mapNotNull { it.rawCurrencyId }.toSet()
val quotes = if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
quotesRepositoryV2.getMultiQuoteSyncOrNull(currenciesIds = rawIds)?.right()
} else {
quotesRepository.getQuotesSync(rawIds, false).right()
}
val quotes = quotesRepositoryV2.getMultiQuoteSyncOrNull(currenciesIds = rawIds)?.right()
val networkStatuses = multiNetworkStatusSupplier(
params = MultiNetworkStatusProducer.Params(userWalletId = userWalletId),
@ -297,22 +286,12 @@ abstract class BaseCurrencyStatusOperations(
catch = { raise(Error.DataError(it)) },
)
val quotes = if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
currency.id.rawCurrencyId?.let {
singleQuoteSupplier(params = SingleQuoteProducer.Params(rawCurrencyId = it))
.firstOrNull()
}
?.right()
?: Error.EmptyQuotes.left()
} else {
catch(
block = {
currency.id.rawCurrencyId?.let { quotesRepository.getQuoteSync(it) }
?.right() ?: Error.EmptyQuotes.left()
},
catch = { Error.DataError(it).left() },
)
val quotes = currency.id.rawCurrencyId?.let {
singleQuoteSupplier(params = SingleQuoteProducer.Params(rawCurrencyId = it))
.firstOrNull()
}
?.right()
?: Error.EmptyQuotes.left()
val networkStatus = singleNetworkStatusSupplier(
params = SingleNetworkStatusProducer.Params(userWalletId = userWalletId, network = currency.network),

View file

@ -34,7 +34,6 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.tokens.utils.extractAddress
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.extensions.addOrReplace
@ -44,7 +43,6 @@ import kotlinx.coroutines.flow.*
@Suppress("LongParameterList", "LargeClass")
class CachedCurrenciesStatusesOperations(
private val currenciesRepository: CurrenciesRepository,
private val quotesRepository: QuotesRepository,
quotesRepositoryV2: QuotesRepositoryV2,
private val stakingRepository: StakingRepository,
private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
@ -58,7 +56,6 @@ class CachedCurrenciesStatusesOperations(
) : BaseCurrenciesStatusesOperations,
BaseCurrencyStatusOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
quotesRepositoryV2 = quotesRepositoryV2,
stakingRepository = stakingRepository,
multiNetworkStatusSupplier = multiNetworkStatusSupplier,
@ -192,13 +189,9 @@ class CachedCurrenciesStatusesOperations(
async {
val rawCurrenciesIds = currenciesIds.mapNotNullTo(mutableSetOf()) { it.rawCurrencyId }
if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(currenciesIds = rawCurrenciesIds, appCurrencyId = null),
)
} else {
quotesRepository.fetchQuotes(rawCurrenciesIds)
}
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(currenciesIds = rawCurrenciesIds, appCurrencyId = null),
)
},
async {
if (tokensFeatureToggles.isStakingLoadingRefactoringEnabled) {
@ -280,44 +273,20 @@ class CachedCurrenciesStatusesOperations(
}
private fun getQuotes(tokensIds: NonEmptySet<CryptoCurrency.ID>): Flow<Either<TokenListError, Set<Quote>>> {
return if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
getQuotesUpdates(
rawCurrencyIds = tokensIds.mapNotNullTo(
destination = hashSetOf(),
transform = CryptoCurrency.ID::rawCurrencyId,
),
)
} else {
quotesRepository.getQuotesUpdates(tokensIds.mapNotNull { it.rawCurrencyId }.toSet())
.map<Set<Quote>, Either<TokenListError, Set<Quote>>> { it.right() }
.retryWhen { cause, _ ->
emit(TokenListError.DataError(cause).left())
// adding delay before retry to avoid spam when flow restarted
delay(RETRY_DELAY)
true
}
.distinctUntilChanged()
}
return getQuotesUpdates(
rawCurrencyIds = tokensIds.mapNotNullTo(
destination = hashSetOf(),
transform = CryptoCurrency.ID::rawCurrencyId,
),
)
}
override fun getQuotes(id: CryptoCurrency.RawID): Flow<Either<Error, Set<Quote>>> {
return if (tokensFeatureToggles.isQuotesLoadingRefactoringEnabled) {
singleQuoteSupplier(
params = SingleQuoteProducer.Params(rawCurrencyId = id),
)
.map<Quote, Either<Error, Set<Quote>>> { setOf(it).right() }
.distinctUntilChanged()
} else {
quotesRepository.getQuotesUpdates(setOf(id))
.map<Set<Quote>, Either<Error, Set<Quote>>> { it.right() }
.retryWhen { cause, _ ->
emit(Error.DataError(cause).left())
// adding delay before retry to avoid spam when flow restarted
delay(RETRY_DELAY)
true
}
.distinctUntilChanged()
}
return singleQuoteSupplier(
params = SingleQuoteProducer.Params(rawCurrencyId = id),
)
.map<Quote, Either<Error, Set<Quote>>> { setOf(it).right() }
.distinctUntilChanged()
}
override fun getNetworksStatuses(