Updated on 2026-08-14
This commit is contained in:
parent
66efd406cc
commit
90fea21f17
14 changed files with 103 additions and 398 deletions
|
|
@ -3,14 +3,19 @@ package com.tangem.data.tokens.di
|
|||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||
import com.tangem.data.tokens.repository.*
|
||||
import com.tangem.data.tokens.repository.DefaultCurrenciesRepository
|
||||
import com.tangem.data.tokens.repository.DefaultCurrencyChecksRepository
|
||||
import com.tangem.data.tokens.repository.DefaultPolkadotAccountHealthCheckRepository
|
||||
import com.tangem.data.tokens.repository.DefaultQuotesRepository
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
||||
import com.tangem.datasource.local.network.NetworksStatusesStore
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.quote.QuotesStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.tokens.repository.*
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
|
||||
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -67,30 +72,6 @@ internal object TokensDataModule {
|
|||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideNetworksRepository(
|
||||
networksStatusesStore: NetworksStatusesStore,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
userWalletsStore: UserWalletsStore,
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
cacheRegistry: CacheRegistry,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||
): NetworksRepository {
|
||||
return DefaultNetworksRepository(
|
||||
networksStatusesStore = networksStatusesStore,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
userWalletsStore = userWalletsStore,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
cacheRegistry = cacheRegistry,
|
||||
dispatchers = dispatchers,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCurrencyChecksRepository(
|
||||
|
|
|
|||
|
|
@ -1,260 +0,0 @@
|
|||
package com.tangem.data.tokens.repository
|
||||
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
||||
import com.tangem.data.networks.utils.NetworkStatusFactory
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.local.network.NetworksStatusesStore
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.model.NetworkStatus
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class DefaultNetworksRepository(
|
||||
private val networksStatusesStore: NetworksStatusesStore,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val cacheRegistry: CacheRegistry,
|
||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
) : NetworksRepository {
|
||||
|
||||
private val responseCurrenciesFactory = ResponseCryptoCurrenciesFactory(excludedBlockchains)
|
||||
|
||||
override fun getNetworkStatusesUpdates(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network>,
|
||||
): Flow<Set<NetworkStatus>> {
|
||||
return networksStatusesStore.get(userWalletId, networks)
|
||||
.distinctUntilChanged()
|
||||
.flowOn(dispatchers.io)
|
||||
}
|
||||
|
||||
override suspend fun fetchNetworkStatuses(userWalletId: UserWalletId, networks: Set<Network>, refresh: Boolean) {
|
||||
withContext(dispatchers.io) {
|
||||
fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun fetchNetworkPendingTransactions(userWalletId: UserWalletId, networks: Set<Network>) {
|
||||
val currencies = getCurrencies(userWalletId, networks)
|
||||
withContext(dispatchers.io) {
|
||||
fetchNetworksPendingTransactions(userWalletId, networks, currencies)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getNetworkStatusesSync(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network>,
|
||||
refresh: Boolean,
|
||||
): Set<NetworkStatus> = withContext(dispatchers.io) {
|
||||
fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh)
|
||||
networksStatusesStore.getSyncOrNull(userWalletId).orEmpty()
|
||||
}
|
||||
|
||||
override suspend fun getNetworkAddresses(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): List<CryptoCurrencyAddress> = withContext(dispatchers.io) {
|
||||
// Get list of currencies matching [network]
|
||||
val currencies = getCurrencies(userWalletId)
|
||||
.filter { currency -> network.id == currency.network.id }
|
||||
|
||||
// There is no currencies matching given [networks] in [userWalletId]
|
||||
if (currencies.toList().isEmpty()) return@withContext emptyList()
|
||||
|
||||
currencies.toList().map { currency ->
|
||||
CryptoCurrencyAddress(
|
||||
cryptoCurrency = currency,
|
||||
address = walletManagersFacade.getAddresses(userWalletId, currency.network)
|
||||
.firstOrNull { it.type == AddressType.Default }
|
||||
?.value.orEmpty(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchNetworksStatusesIfCacheExpired(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network>,
|
||||
refresh: Boolean,
|
||||
) = coroutineScope {
|
||||
if (refresh) {
|
||||
networksStatusesStore.refresh(key = userWalletId, networks = networks)
|
||||
}
|
||||
|
||||
val currencies = getCurrencies(userWalletId, networks)
|
||||
val networksDeferred = networks.mapNotNull { network ->
|
||||
coroutineScope {
|
||||
val key = getNetworksStatusesCacheKey(userWalletId, network)
|
||||
|
||||
if (refresh || cacheRegistry.isExpired(key)) {
|
||||
async {
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = key,
|
||||
skipCache = refresh,
|
||||
block = { fetchNetworkStatus(userWalletId, network, currencies) },
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
networksDeferred.awaitAll()
|
||||
}
|
||||
|
||||
private suspend fun fetchNetworksPendingTransactions(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network>,
|
||||
currencies: Sequence<CryptoCurrency>,
|
||||
) {
|
||||
coroutineScope {
|
||||
networks
|
||||
.map { network ->
|
||||
async {
|
||||
fetchNetworkPendingTransactions(userWalletId, network, currencies)
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchNetworkStatus(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
currencies: Sequence<CryptoCurrency>,
|
||||
) {
|
||||
val networkCurrencies = currencies.filter { it.network == network }
|
||||
|
||||
val result = walletManagersFacade.update(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
extraTokens = networkCurrencies
|
||||
.filterIsInstance<CryptoCurrency.Token>()
|
||||
.toSet(),
|
||||
)
|
||||
|
||||
withContext(NonCancellable) {
|
||||
invalidateCacheKeyIfNeeded(userWalletId, network, result)
|
||||
}
|
||||
|
||||
val networkStatus = NetworkStatusFactory.create(
|
||||
network = network,
|
||||
updatingResult = result,
|
||||
addedCurrencies = networkCurrencies.toSet(),
|
||||
)
|
||||
|
||||
networksStatusesStore.store(userWalletId, networkStatus)
|
||||
}
|
||||
|
||||
private suspend fun fetchNetworkPendingTransactions(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
currencies: Sequence<CryptoCurrency>,
|
||||
) {
|
||||
val result = walletManagersFacade.updatePendingTransactions(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
)
|
||||
|
||||
withContext(NonCancellable) {
|
||||
invalidateCacheKeyIfNeeded(userWalletId, network, result)
|
||||
}
|
||||
|
||||
val networkStatus = NetworkStatusFactory.create(
|
||||
network = network,
|
||||
updatingResult = result,
|
||||
addedCurrencies = currencies.toSet(),
|
||||
)
|
||||
|
||||
networksStatusesStore.store(userWalletId, networkStatus)
|
||||
}
|
||||
|
||||
private suspend fun getCurrencies(userWalletId: UserWalletId, networks: Set<Network>): Sequence<CryptoCurrency> {
|
||||
val currencies = getCurrencies(userWalletId)
|
||||
return currencies.filter { it.network in networks }
|
||||
}
|
||||
|
||||
private suspend fun getCurrencies(userWalletId: UserWalletId): Sequence<CryptoCurrency> {
|
||||
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||
"Unable to find user wallet with provided ID: $userWalletId"
|
||||
}
|
||||
|
||||
return if (userWallet.isMultiCurrency) {
|
||||
val response = requireNotNull(
|
||||
value = appPreferencesStore.getObjectSyncOrNull<UserTokensResponse>(
|
||||
key = PreferencesKeys.getUserTokensKey(userWalletId.stringValue),
|
||||
),
|
||||
lazyMessage = {
|
||||
"Unable to find tokens response for user wallet with provided ID: $userWalletId"
|
||||
},
|
||||
)
|
||||
|
||||
responseCurrenciesFactory.createCurrencies(response, userWallet.scanResponse).asSequence()
|
||||
} else {
|
||||
if (userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) {
|
||||
cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
.asSequence()
|
||||
} else {
|
||||
val currency = cardCryptoCurrencyFactory.createPrimaryCurrencyForSingleCurrencyCard(
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
|
||||
sequenceOf(currency)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun invalidateCacheKeyIfNeeded(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
result: UpdateWalletManagerResult,
|
||||
) {
|
||||
when (result) {
|
||||
is UpdateWalletManagerResult.Verified,
|
||||
is UpdateWalletManagerResult.NoAccount,
|
||||
-> Unit
|
||||
is UpdateWalletManagerResult.Unreachable,
|
||||
is UpdateWalletManagerResult.MissedDerivation,
|
||||
-> {
|
||||
Timber.w(
|
||||
"""
|
||||
Invalidate network cache key
|
||||
|- User wallet ID: $userWalletId
|
||||
|- Network: ${network.id}
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
cacheRegistry.invalidate(getNetworksStatusesCacheKey(userWalletId, network))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNetworksStatusesCacheKey(userWalletId: UserWalletId, network: Network): String {
|
||||
return "network_status_${userWalletId}_${network.id.value}_${network.derivationPath.value}"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue