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.staking.repositories.StakingRepository
import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.walletmanager.WalletManagersFacade
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
@ -67,6 +68,7 @@ internal object ManageTokensDomainModule {
networksRepository: NetworksRepository, networksRepository: NetworksRepository,
derivationsRepository: DerivationsRepository, derivationsRepository: DerivationsRepository,
stakingRepository: StakingRepository, stakingRepository: StakingRepository,
quotesRepository: QuotesRepository,
): SaveManagedTokensUseCase { ): SaveManagedTokensUseCase {
return SaveManagedTokensUseCase( return SaveManagedTokensUseCase(
customTokensRepository = customTokensRepository, customTokensRepository = customTokensRepository,
@ -75,6 +77,7 @@ internal object ManageTokensDomainModule {
networksRepository = networksRepository, networksRepository = networksRepository,
derivationsRepository = derivationsRepository, derivationsRepository = derivationsRepository,
stakingRepository = stakingRepository, 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.card.repository.DerivationsRepository
import com.tangem.domain.markets.* import com.tangem.domain.markets.*
import com.tangem.domain.markets.repositories.MarketsTokenRepository 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.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.tokens.repository.QuotesRepository
@ -57,12 +58,16 @@ object MarketsDomainModule {
marketsTokenRepository: MarketsTokenRepository, marketsTokenRepository: MarketsTokenRepository,
currenciesRepository: CurrenciesRepository, currenciesRepository: CurrenciesRepository,
networksRepository: NetworksRepository, networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
quotesRepository: QuotesRepository,
): SaveMarketTokensUseCase { ): SaveMarketTokensUseCase {
return SaveMarketTokensUseCase( return SaveMarketTokensUseCase(
derivationsRepository = derivationsRepository, derivationsRepository = derivationsRepository,
marketsTokenRepository = marketsTokenRepository, marketsTokenRepository = marketsTokenRepository,
currenciesRepository = currenciesRepository, currenciesRepository = currenciesRepository,
networksRepository = networksRepository, networksRepository = networksRepository,
stakingRepository = stakingRepository,
quotesRepository = quotesRepository,
) )
} }

View file

@ -27,10 +27,14 @@ internal object TokensDomainModule {
fun provideAddCryptoCurrenciesUseCase( fun provideAddCryptoCurrenciesUseCase(
currenciesRepository: CurrenciesRepository, currenciesRepository: CurrenciesRepository,
networksRepository: NetworksRepository, networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
quotesRepository: QuotesRepository,
): AddCryptoCurrenciesUseCase { ): AddCryptoCurrenciesUseCase {
return AddCryptoCurrenciesUseCase( return AddCryptoCurrenciesUseCase(
currenciesRepository = currenciesRepository, currenciesRepository = currenciesRepository,
networksRepository = networksRepository, 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.model.Network
import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.UserWalletId
@Suppress("LongParameterList")
class SaveManagedTokensUseCase( class SaveManagedTokensUseCase(
private val customTokensRepository: CustomTokensRepository, private val customTokensRepository: CustomTokensRepository,
private val walletManagersFacade: WalletManagersFacade, private val walletManagersFacade: WalletManagersFacade,
@ -20,6 +22,7 @@ class SaveManagedTokensUseCase(
private val networksRepository: NetworksRepository, private val networksRepository: NetworksRepository,
private val derivationsRepository: DerivationsRepository, private val derivationsRepository: DerivationsRepository,
private val stakingRepository: StakingRepository, private val stakingRepository: StakingRepository,
private val quotesRepository: QuotesRepository,
) { ) {
suspend operator fun invoke( suspend operator fun invoke(
@ -54,6 +57,8 @@ class SaveManagedTokensUseCase(
) )
refreshUpdatedYieldBalances(userWalletId, existingCurrencies) refreshUpdatedYieldBalances(userWalletId, existingCurrencies)
refreshUpdatedQuotes(addingCurrencies)
} }
private suspend fun removeCurrenciesFromWalletManager( 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 * Determines if the [existingCurrencies] list contains a coin that corresponds
* to the given [network]. * to the given [network].

View file

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

View file

@ -3,10 +3,12 @@ package com.tangem.domain.markets
import arrow.core.Either import arrow.core.Either
import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.markets.repositories.MarketsTokenRepository 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.CryptoCurrency
import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.UserWalletId
/** /**
@ -23,6 +25,8 @@ class SaveMarketTokensUseCase(
private val marketsTokenRepository: MarketsTokenRepository, private val marketsTokenRepository: MarketsTokenRepository,
private val currenciesRepository: CurrenciesRepository, private val currenciesRepository: CurrenciesRepository,
private val networksRepository: NetworksRepository, private val networksRepository: NetworksRepository,
private val stakingRepository: StakingRepository,
private val quotesRepository: QuotesRepository,
) { ) {
suspend operator fun invoke( suspend operator fun invoke(
@ -60,11 +64,37 @@ class SaveMarketTokensUseCase(
currenciesRepository.addCurrencies(userWalletId = userWalletId, currencies = addedCurrencies) currenciesRepository.addCurrencies(userWalletId = userWalletId, currencies = addedCurrencies)
networksRepository.getNetworkStatusesSync( refreshUpdatedNetworks(userWalletId, addedCurrencies)
userWalletId = userWalletId,
networks = addedCurrencies.map(CryptoCurrency::network).toSet(), refreshUpdatedYieldBalances(userWalletId, addedCurrencies)
refresh = true,
) 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.catch
import arrow.core.raise.either import arrow.core.raise.either
import arrow.core.toNonEmptyListOrNull import arrow.core.toNonEmptyListOrNull
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.UserWalletId
/** /**
@ -21,6 +23,8 @@ import com.tangem.domain.wallets.models.UserWalletId
class AddCryptoCurrenciesUseCase( class AddCryptoCurrenciesUseCase(
private val currenciesRepository: CurrenciesRepository, private val currenciesRepository: CurrenciesRepository,
private val networksRepository: NetworksRepository, private val networksRepository: NetworksRepository,
private val stakingRepository: StakingRepository,
private val quotesRepository: QuotesRepository,
) { ) {
/** /**
@ -105,6 +109,8 @@ class AddCryptoCurrenciesUseCase(
val tokenToAdd = createTokenCurrency(userWalletId, contractAddress, networkId) val tokenToAdd = createTokenCurrency(userWalletId, contractAddress, networkId)
addCurrencies(userWalletId, listOf(tokenToAdd)) addCurrencies(userWalletId, listOf(tokenToAdd))
refreshUpdatedNetworks(userWalletId, listOf(tokenToAdd), existingCurrencies) refreshUpdatedNetworks(userWalletId, listOf(tokenToAdd), existingCurrencies)
refreshUpdatedYieldBalances(userWalletId, existingCurrencies)
refreshUpdatedQuotes(existingCurrencies)
tokenToAdd 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( private suspend fun Raise<Throwable>.createTokenCurrency(
userWalletId: UserWalletId, userWalletId: UserWalletId,
contractAddress: String, 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.lce
import com.tangem.domain.core.lce.lceFlow import com.tangem.domain.core.lce.lceFlow
import com.tangem.domain.core.utils.EitherFlow 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.core.utils.lceError
import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.staking.model.stakekit.YieldBalanceList import com.tangem.domain.staking.model.stakekit.YieldBalanceList
@ -44,30 +45,49 @@ class CachedCurrenciesStatusesOperations(
userWalletId: UserWalletId, userWalletId: UserWalletId,
currenciesFlow: EitherFlow<TokenListError, List<CryptoCurrency>>, currenciesFlow: EitherFlow<TokenListError, List<CryptoCurrency>>,
): LceFlow<TokenListError, List<CryptoCurrencyStatus>> = lceFlow { ): LceFlow<TokenListError, List<CryptoCurrencyStatus>> = lceFlow {
currenciesFlow.flatMapLatest { maybeCurrencies -> val prevStatuses = MutableStateFlow(value = emptyList<CryptoCurrencyStatus>())
val isUpdating = MutableStateFlow(value = true)
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()) } .getOrElse { return@flatMapLatest flowOf(it.lceError()) }
.toNonEmptyListOrNull() .toNonEmptyListOrNull()
if (nonEmptyCurrencies.isNullOrEmpty()) { if (currencies.isNullOrEmpty()) {
prevStatuses.value = emptyList()
return@flatMapLatest flowOf(TokenListError.EmptyTokens.lceError()) return@flatMapLatest flowOf(TokenListError.EmptyTokens.lceError())
} }
// This is only 'true' when the flow here is empty, such as during initial loading // This is only 'true' when the flow here is empty, such as during initial loading
if (isLoading.get()) { if (isLoading.get()) {
val loadingCurrencies = createCurrenciesStatuses( val loadingCurrencies = createCurrenciesStatuses(
currencies = nonEmptyCurrencies, currencies = currencies,
maybeNetworkStatuses = null, maybeNetworkStatuses = null,
maybeQuotes = null, maybeQuotes = null,
maybeYieldBalances = null, maybeYieldBalances = null,
isUpdating = true, isUpdating = true,
) )
loadingCurrencies.getOrNull()?.let { prevStatuses.value = it }
send(loadingCurrencies) send(loadingCurrencies)
} }
val (networks, currenciesIds) = getIds(nonEmptyCurrencies) val (networks, currenciesIds) = getIds(currencies)
fun createCurrenciesStatuses( fun createCurrenciesStatuses(
maybeQuotes: Either<TokenListError, Set<Quote>>?, maybeQuotes: Either<TokenListError, Set<Quote>>?,
@ -75,26 +95,46 @@ class CachedCurrenciesStatusesOperations(
maybeYieldBalances: Either<TokenListError, YieldBalanceList>?, maybeYieldBalances: Either<TokenListError, YieldBalanceList>?,
isUpdating: Boolean, isUpdating: Boolean,
) = createCurrenciesStatuses( ) = createCurrenciesStatuses(
currencies = nonEmptyCurrencies, currencies = currencies,
maybeQuotes = maybeQuotes, maybeQuotes = maybeQuotes,
maybeNetworkStatuses = maybeNetworkStatuses, maybeNetworkStatuses = maybeNetworkStatuses,
maybeYieldBalances = maybeYieldBalances, maybeYieldBalances = maybeYieldBalances,
isUpdating = isUpdating, isUpdating = isUpdating,
) )
launch { fetchComponents(userWalletId, networks, currenciesIds, nonEmptyCurrencies) } // removing token
.invokeOnCompletion { isUpdating.value = false } 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( combine(
flow = getQuotes(currenciesIds), flow = getQuotes(currenciesIds),
flow2 = getNetworksStatuses(userWalletId, networks), flow2 = getNetworksStatuses(userWalletId, networks),
flow3 = getYieldBalances(userWalletId, nonEmptyCurrencies), flow3 = getYieldBalances(userWalletId, currencies),
flow4 = isUpdating, flow4 = isUpdating,
transform = ::createCurrenciesStatuses, transform = ::createCurrenciesStatuses,
) )
.distinctUntilChanged() .distinctUntilChanged()
} }
.onEach(::send) .onEach { statusesLce ->
statusesLce.getOrNull()?.let { prevStatuses.value = it }
send(statusesLce)
}
.launchIn(scope = this) .launchIn(scope = this)
} }
@ -112,13 +152,7 @@ class CachedCurrenciesStatusesOperations(
val rawCurrenciesIds = currenciesIds.mapNotNullTo(mutableSetOf()) { it.rawCurrencyId } val rawCurrenciesIds = currenciesIds.mapNotNullTo(mutableSetOf()) { it.rawCurrencyId }
quotesRepository.fetchQuotes(rawCurrenciesIds) quotesRepository.fetchQuotes(rawCurrenciesIds)
}, },
async { async { stakingRepository.fetchMultiYieldBalance(userWalletId, currencies) },
if (currencies.size == 1) {
stakingRepository.fetchSingleYieldBalance(userWalletId, currencies.first())
} else {
stakingRepository.fetchMultiYieldBalance(userWalletId, currencies)
}
},
) )
} }
.map { } .map { }