Updated on 2026-08-14

This commit is contained in:
Tangem 2025-02-19 16:23:03 +03:00
parent 6f47e7d742
commit 6a5a8300cf
10 changed files with 116 additions and 18 deletions

View file

@ -1,11 +1,13 @@
package com.tangem.tap.di.domain
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.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -64,6 +66,18 @@ object MarketsDomainModule {
)
}
@Provides
@Singleton
fun provideFilterNetworksUseCase(
userWalletsListManager: UserWalletsListManager,
excludedBlockchains: ExcludedBlockchains,
): FilterAvailableNetworksForWalletUseCase {
return FilterAvailableNetworksForWalletUseCase(
userWalletsListManager = userWalletsListManager,
excludedBlockchains = excludedBlockchains,
)
}
@Provides
@Singleton
fun provideGetTokenExchangesUseCase(marketsTokenRepository: MarketsTokenRepository): GetTokenExchangesUseCase {

View file

@ -64,4 +64,12 @@ inline fun <T> MutableList<T>.addOrReplace(item: T, predicate: (T) -> Boolean) {
if (!isReplaced) {
add(item)
}
}
fun <T> List<T>.filterIf(condition: Boolean, predicate: (T) -> Boolean): List<T> {
return if (condition) {
this.filter(predicate)
} else {
this
}
}

View file

@ -22,6 +22,8 @@ import com.tangem.datasource.local.preferences.utils.getObject
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
import com.tangem.datasource.local.preferences.utils.storeObject
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.common.extensions.canHandleBlockchain
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.core.error.DataError
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.tokens.model.CryptoCurrency
@ -33,6 +35,7 @@ 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 com.tangem.utils.extensions.filterIf
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import timber.log.Timber
@ -47,7 +50,7 @@ internal class DefaultCurrenciesRepository(
private val appPreferencesStore: AppPreferencesStore,
private val expressServiceLoader: ExpressServiceLoader,
private val dispatchers: CoroutineDispatcherProvider,
excludedBlockchains: ExcludedBlockchains,
private val excludedBlockchains: ExcludedBlockchains,
) : CurrenciesRepository {
private val demoConfig = DemoConfig()
@ -486,12 +489,14 @@ internal class DefaultCurrenciesRepository(
@OptIn(ExperimentalCoroutinesApi::class)
override fun getAllWalletsCryptoCurrencies(
currencyRawId: CryptoCurrency.RawID,
needFilterByAvailable: Boolean,
): Flow<Map<UserWallet, List<CryptoCurrency>>> {
return userWalletsStore.userWallets.flatMapLatest { userWallets ->
userWallets.forEach { fetchTokensIfCacheExpired(userWallet = it, refresh = false) }
val userWalletsWithCurrencies = userWallets
.filterNot(UserWallet::isLocked)
.filterIf(needFilterByAvailable) { it.filterWalletByAvailableBlockchain(currencyRawId) }
.map { userWallet ->
if (userWallet.isMultiCurrency) {
getSavedUserTokensResponse(userWallet.walletId).map { storedTokens ->
@ -525,6 +530,15 @@ internal class DefaultCurrenciesRepository(
}
}
private fun UserWallet.filterWalletByAvailableBlockchain(currencyRawId: CryptoCurrency.RawID): Boolean {
val blockchain = Blockchain.fromNetworkId(currencyRawId.value) ?: return true
return this.scanResponse.card.canHandleBlockchain(
blockchain = blockchain,
cardTypesResolver = this.cardTypesResolver,
excludedBlockchains = excludedBlockchains,
)
}
override fun isNetworkFeeZero(userWalletId: UserWalletId, network: Network): Boolean {
val blockchain = Blockchain.fromNetworkId(network.backendId)
return blockchain?.isNetworkFeeZero() ?: false

View file

@ -17,12 +17,21 @@ dependencies {
api(projects.domain.core)
api(projects.domain.markets.models)
api(projects.domain.wallets.models)
api(projects.domain.models)
api(projects.domain.legacy)
api(projects.domain.wallets)
implementation(projects.domain.tokens.models)
implementation(projects.domain.tokens)
api(projects.core.pagination)
/* Libs */
api(projects.libs.blockchainSdk)
/* SDK */
implementation(tangemDeps.blockchain)
/* Utils */
implementation(deps.kotlin.serialization)
implementation(projects.core.utils)

View file

@ -0,0 +1,38 @@
package com.tangem.domain.markets
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.domain.common.extensions.supportedBlockchains
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UserWalletId
class FilterAvailableNetworksForWalletUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val excludedBlockchains: ExcludedBlockchains,
) {
/**
* Filters [networks] list according supported blockchains for this card
* If [userWalletId] not found then returns the same list
*/
operator fun invoke(
userWalletId: UserWalletId,
networks: Set<TokenMarketInfo.Network>,
): Set<TokenMarketInfo.Network> {
val userWallet = userWalletsListManager.userWalletsSync.firstOrNull {
it.walletId == userWalletId
} ?: return networks.toSet()
val supportedBlockchains = userWallet.scanResponse.card.supportedBlockchains(
cardTypesResolver = userWallet.scanResponse.cardTypesResolver,
excludedBlockchains = excludedBlockchains,
)
return networks.filter {
val blockchain = Blockchain.fromNetworkId(it.networkId)
supportedBlockchains.contains(blockchain)
}.toSet()
}
}

View file

@ -42,8 +42,9 @@ class GetAllWalletsCryptoCurrencyStatusesUseCase(
@OptIn(ExperimentalCoroutinesApi::class)
operator fun invoke(
currencyRawId: CryptoCurrency.RawID,
needFilterByAvailable: Boolean = false,
): Flow<Map<UserWallet, List<Either<CurrencyStatusError, CryptoCurrencyStatus>>>> {
return currenciesRepository.getAllWalletsCryptoCurrencies(currencyRawId)
return currenciesRepository.getAllWalletsCryptoCurrencies(currencyRawId, needFilterByAvailable)
.flatMapLatest { userWalletsWithCurrencies: Map<UserWallet, List<CryptoCurrency>> ->
val walletStatusFlows = userWalletsWithCurrencies.map { (userWallet, cryptoCurrencies) ->
val operations = CurrenciesStatusesOperations(
@ -64,6 +65,7 @@ class GetAllWalletsCryptoCurrencyStatusesUseCase(
}
combine(walletStatusFlows) { it.toMap() }
.onEmpty { emit(emptyMap()) }
}
.flowOn(dispatchers.io)
}

View file

@ -248,7 +248,10 @@ interface CurrenciesRepository {
): CryptoCurrency.Token
/** Get crypto currencies by [currencyRawId] from all user wallets */
fun getAllWalletsCryptoCurrencies(currencyRawId: CryptoCurrency.RawID): Flow<Map<UserWallet, List<CryptoCurrency>>>
fun getAllWalletsCryptoCurrencies(
currencyRawId: CryptoCurrency.RawID,
needFilterByAvailable: Boolean,
): Flow<Map<UserWallet, List<CryptoCurrency>>>
fun isNetworkFeeZero(userWalletId: UserWalletId, network: Network): Boolean
}

View file

@ -155,6 +155,7 @@ internal class MockCurrenciesRepository(
override fun getAllWalletsCryptoCurrencies(
currencyRawId: CryptoCurrency.RawID,
needFilterByAvailable: Boolean,
): Flow<Map<UserWallet, List<CryptoCurrency>>> {
return emptyFlow()
}

View file

@ -65,7 +65,7 @@ internal class PortfolioDataLoader @Inject constructor(
private fun getAllWalletsCryptoCurrenciesData(
currencyRawId: CryptoCurrency.RawID,
): Flow<Map<UserWallet, List<PortfolioData.CryptoCurrencyData>>> {
return getAllWalletsCryptoCurrencyStatusesUseCase(currencyRawId)
return getAllWalletsCryptoCurrencyStatusesUseCase(currencyRawId, true)
.distinctUntilChanged()
.map { walletsWithMaybeStatuses ->
walletsWithMaybeStatuses.mapValues { entry ->
@ -94,20 +94,21 @@ internal class PortfolioDataLoader @Inject constructor(
}
}
}
}.onEmpty {
emit(
walletsWithStatuses.mapValues { (wallet, statuses) ->
statuses.map {
PortfolioData.CryptoCurrencyData(
userWallet = wallet,
status = it,
actions = emptyList(),
)
}
},
)
}
.onEmpty {
emit(
walletsWithStatuses.mapValues { (wallet, statuses) ->
statuses.map {
PortfolioData.CryptoCurrencyData(
userWallet = wallet,
status = it,
actions = emptyList(),
)
}
},
)
}
}.onEmpty {
emit(emptyMap())
}
.distinctUntilChanged()
}

View file

@ -18,6 +18,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.card.HasMissedDerivationsUseCase
import com.tangem.domain.managetokens.CheckCurrencyUnsupportedUseCase
import com.tangem.domain.managetokens.model.CurrencyUnsupportedState
import com.tangem.domain.markets.FilterAvailableNetworksForWalletUseCase
import com.tangem.domain.markets.SaveMarketTokensUseCase
import com.tangem.domain.markets.TokenMarketInfo
import com.tangem.domain.wallets.models.UserWalletId
@ -49,6 +50,7 @@ internal class MarketsPortfolioModel @Inject constructor(
private val portfolioDataLoader: PortfolioDataLoader,
private val hasMissedDerivationsUseCase: HasMissedDerivationsUseCase,
private val saveMarketTokensUseCase: SaveMarketTokensUseCase,
private val filterAvailableNetworks: FilterAvailableNetworksForWalletUseCase,
private val addToPortfolioManager: AddToPortfolioManager,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() {
@ -175,10 +177,16 @@ internal class MarketsPortfolioModel @Inject constructor(
flow2 = selectedMultiWalletIdFlow,
flow3 = addToPortfolioManager.getAddToPortfolioData(),
transform = { portfolioBSVisibilityModel, selectedWalletId, addToPortfolioData ->
val filteredNetworks = selectedWalletId?.let {
filterAvailableNetworks(selectedWalletId, addToPortfolioData.availableNetworks ?: emptySet())
} ?: emptySet()
PortfolioUIData(
portfolioBSVisibilityModel = portfolioBSVisibilityModel,
selectedWalletId = selectedWalletId,
addToPortfolioData = addToPortfolioData,
addToPortfolioData = addToPortfolioData.copy(
availableNetworks = filteredNetworks,
),
hasMissedDerivations = hasMissedDerivations(selectedWalletId, addToPortfolioData),
)
},