Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-10 15:21:55 +05:00
parent 4570bc334c
commit 602e20a1d2
53 changed files with 489 additions and 368 deletions

View file

@ -161,7 +161,7 @@ dependencies {
implementation(projects.domain.hotWallet)
implementation(projects.domain.news)
implementation(projects.domain.earn)
implementation(projects.domain.tokensync)
implementation(projects.domain.assetsdiscovery)
implementation(projects.domain.search)
implementation(projects.common)
@ -192,7 +192,7 @@ dependencies {
implementation(projects.data.common)
implementation(projects.data.settings)
implementation(projects.data.tokens)
implementation(projects.data.tokensync)
implementation(projects.data.assetsdiscovery)
implementation(projects.data.txhistory)
implementation(projects.data.wallets)
implementation(projects.data.analytics)

View file

@ -0,0 +1,52 @@
package com.tangem.tap.di.domain
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
import com.tangem.domain.assetsdiscovery.repository.AssetsDiscoveryRepository
import com.tangem.domain.assetsdiscovery.usecase.AcknowledgeAssetsDiscoveryCompletionUseCase
import com.tangem.domain.assetsdiscovery.usecase.ObserveAssetsDiscoveryUseCase
import com.tangem.domain.assetsdiscovery.usecase.StartAssetsDiscoveryUseCase
import com.tangem.utils.coroutines.AppCoroutineScope
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object AssetsDiscoveryDomainModule {
@Provides
@Singleton
fun provideObserveAssetsDiscoveryUseCase(
assetsDiscoveryRepository: AssetsDiscoveryRepository,
): ObserveAssetsDiscoveryUseCase {
return ObserveAssetsDiscoveryUseCase(
assetsDiscoveryRepository = assetsDiscoveryRepository,
)
}
@Provides
@Singleton
fun provideAcknowledgeAssetsDiscoveryCompletionUseCase(
assetsDiscoveryRepository: AssetsDiscoveryRepository,
): AcknowledgeAssetsDiscoveryCompletionUseCase {
return AcknowledgeAssetsDiscoveryCompletionUseCase(
assetsDiscoveryRepository = assetsDiscoveryRepository,
)
}
@Provides
@Singleton
fun provideStartAssetsDiscoveryUseCase(
assetsDiscoveryRepository: AssetsDiscoveryRepository,
manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
appCoroutineScope: AppCoroutineScope,
): StartAssetsDiscoveryUseCase {
return StartAssetsDiscoveryUseCase(
assetsDiscoveryRepository = assetsDiscoveryRepository,
manageCryptoCurrenciesUseCase = manageCryptoCurrenciesUseCase,
appCoroutineScope = appCoroutineScope,
)
}
}

View file

@ -1,50 +0,0 @@
package com.tangem.tap.di.domain
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
import com.tangem.domain.tokensync.repository.TokenSyncRepository
import com.tangem.domain.tokensync.usecase.AcknowledgeTokenSyncCompletionUseCase
import com.tangem.domain.tokensync.usecase.ObserveTokenSyncUseCase
import com.tangem.domain.tokensync.usecase.StartTokenSyncUseCase
import com.tangem.utils.coroutines.AppCoroutineScope
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object TokenSyncDomainModule {
@Provides
@Singleton
fun provideObserveTokenSyncUseCase(tokenSyncRepository: TokenSyncRepository): ObserveTokenSyncUseCase {
return ObserveTokenSyncUseCase(
tokenSyncRepository = tokenSyncRepository,
)
}
@Provides
@Singleton
fun provideAcknowledgeTokenSyncCompletionUseCase(
tokenSyncRepository: TokenSyncRepository,
): AcknowledgeTokenSyncCompletionUseCase {
return AcknowledgeTokenSyncCompletionUseCase(
tokenSyncRepository = tokenSyncRepository,
)
}
@Provides
@Singleton
fun provideStartTokenSyncUseCase(
tokenSyncRepository: TokenSyncRepository,
manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
appCoroutineScope: AppCoroutineScope,
): StartTokenSyncUseCase {
return StartTokenSyncUseCase(
tokenSyncRepository = tokenSyncRepository,
manageCryptoCurrenciesUseCase = manageCryptoCurrenciesUseCase,
appCoroutineScope = appCoroutineScope,
)
}
}

View file

@ -73,7 +73,7 @@
"version": "undefined"
},
{
"name": "TOKEN_SYNC_ENABLED",
"name": "ASSETS_DISCOVERY_ENABLED",
"version": "undefined"
},
{

View file

@ -136,7 +136,7 @@ object PreferencesKeys {
val HAS_HAD_FIRST_TOP_UP_KEY by lazy { stringPreferencesKey(name = "hasHadFirstTopUp") }
val PENDING_DISCOVERY_SYNC_KEY by lazy { stringPreferencesKey(name = "pendingDiscoverySync") }
val PENDING_ASSETS_DISCOVERY_KEY by lazy { stringPreferencesKey(name = "pendingAssetsDiscovery") }
// region Notifications
val NOTIFICATIONS_APPLICATION_ID_KEY by lazy { stringPreferencesKey(name = "notificationsApplicationId") }

View file

@ -7,22 +7,25 @@ plugins {
}
android {
namespace = "com.tangem.data.tokensync"
namespace = "com.tangem.data.assetsdiscovery"
}
dependencies {
api(projects.domain.tokensync)
api(projects.domain.assetsdiscovery)
implementation(projects.domain.tokens)
implementation(projects.domain.tokens.models)
implementation(projects.domain.models)
implementation(projects.domain.walletManager)
implementation(projects.domain.wallets)
implementation(projects.data.common)
implementation(projects.data.walletManager)
implementation(projects.libs.blockchainSdk)
implementation(projects.libs.tangemSdkApi)
implementation(projects.core.datasource)
implementation(projects.core.utils)
implementation(tangemDeps.blockchain)
implementation(tangemDeps.card.core)
implementation(deps.androidx.datastore)

View file

@ -0,0 +1,86 @@
package com.tangem.data.assetsdiscovery
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.address.AddressType
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.data.walletmanager.extensions.makePublicKey
import com.tangem.domain.assetsdiscovery.AssetsDiscoveryFacade
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.common.wallets.getSyncStrict
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.config.curvesConfig
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.withContext
import javax.inject.Inject
internal class DefaultAssetsDiscoveryFacade @Inject constructor(
private val blockchainSDKFactory: BlockchainSDKFactory,
private val userWalletsListRepository: UserWalletsListRepository,
private val dispatchers: CoroutineDispatcherProvider,
) : AssetsDiscoveryFacade {
override suspend fun getAssetsDiscoveryService(
userWalletId: UserWalletId,
network: Network,
): AssetsDiscoveryFacade.AssetsDiscoveryServiceInfo? = withContext(dispatchers.io) {
val userWallet = userWalletsListRepository.getSyncStrict(userWalletId)
if (userWallet !is UserWallet.Hot) return@withContext null
val assetsDiscoveryServiceFactory = blockchainSDKFactory.getAssetsDiscoveryServiceFactorySync()
?: return@withContext null
val blockchain = network.toBlockchain()
val address = makeAddress(userWallet, blockchain, network.derivationPath.value)
?: return@withContext null
AssetsDiscoveryFacade.AssetsDiscoveryServiceInfo(
address = address,
service = assetsDiscoveryServiceFactory.create(blockchain),
)
}
private fun makeAddress(hotWallet: UserWallet.Hot, blockchain: Blockchain, derivationPath: String?): String? {
val curve = hotWallet.curvesConfig.primaryCurve(blockchain)
val selectedWallet = hotWallet.wallets.orEmpty().firstOrNull { it.curve == curve }
?: return null
val path = derivationPath?.let { DerivationPath(rawPath = it) }
val publicKey = if (path != null) {
makePublicKey(
seedKey = selectedWallet.publicKey,
blockchain = blockchain,
derivationPath = path,
derivedWalletKeys = selectedWallet.derivedKeys,
isWallet2 = true,
) ?: return null
} else {
null
}
return try {
val addresses = if (publicKey != null) {
blockchain.makeAddresses(
walletPublicKey = publicKey.blockchainKey,
pairPublicKey = null,
curve = selectedWallet.curve,
)
} else {
blockchain.makeAddresses(
walletPublicKey = selectedWallet.publicKey,
pairPublicKey = null,
curve = selectedWallet.curve,
)
}
addresses.find { it.type == AddressType.Default }?.value
} catch (e: Throwable) {
TangemLogger.w("Failed to derive address for $blockchain", e)
null
}
}
}

View file

@ -0,0 +1,61 @@
package com.tangem.data.assetsdiscovery.di
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.data.assetsdiscovery.DefaultAssetsDiscoveryFacade
import com.tangem.data.assetsdiscovery.repository.DefaultAssetsDiscoveryRepository
import com.tangem.data.assetsdiscovery.store.AssetsDiscoveryStoreFactory
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.data.common.network.NetworkFactory
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.assetsdiscovery.AssetsDiscoveryFacade
import com.tangem.domain.assetsdiscovery.repository.AssetsDiscoveryRepository
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object AssetsDiscoveryDataModule {
@Provides
@Singleton
fun provideAssetsDiscoveryFacade(
blockchainSDKFactory: BlockchainSDKFactory,
userWalletsListRepository: UserWalletsListRepository,
dispatchers: CoroutineDispatcherProvider,
): AssetsDiscoveryFacade = DefaultAssetsDiscoveryFacade(
blockchainSDKFactory = blockchainSDKFactory,
userWalletsListRepository = userWalletsListRepository,
dispatchers = dispatchers,
)
@Provides
@Singleton
fun provideAssetsDiscoveryRepository(
assetsDiscoveryFacade: AssetsDiscoveryFacade,
tangemTechApi: TangemTechApi,
userWalletsListRepository: UserWalletsListRepository,
networkFactory: NetworkFactory,
appPreferencesStore: AppPreferencesStore,
assetsDiscoveryStoreFactory: AssetsDiscoveryStoreFactory,
responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
dispatchers: CoroutineDispatcherProvider,
excludedBlockchains: ExcludedBlockchains,
): AssetsDiscoveryRepository = DefaultAssetsDiscoveryRepository(
assetsDiscoveryFacade = assetsDiscoveryFacade,
tangemTechApi = tangemTechApi,
userWalletsListRepository = userWalletsListRepository,
networkFactory = networkFactory,
appPreferencesStore = appPreferencesStore,
assetsDiscoveryStoreFactory = assetsDiscoveryStoreFactory,
responseCryptoCurrenciesFactory = responseCryptoCurrenciesFactory,
dispatchers = dispatchers,
excludedBlockchains = excludedBlockchains,
)
}

View file

@ -1,12 +1,12 @@
package com.tangem.data.tokensync.repository
package com.tangem.data.assetsdiscovery.repository
import com.tangem.blockchain.assetsdiscovery.models.DiscoveredAsset
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.tokenbalance.models.TokenBalance
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.data.assetsdiscovery.store.AssetsDiscoveryStore
import com.tangem.data.assetsdiscovery.store.AssetsDiscoveryStoreFactory
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.data.common.network.NetworkFactory
import com.tangem.data.tokensync.store.TokenSyncStore
import com.tangem.data.tokensync.store.TokenSyncStoreFactory
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
@ -14,15 +14,15 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectMapSync
import com.tangem.domain.assetsdiscovery.AssetsDiscoveryFacade
import com.tangem.domain.assetsdiscovery.model.AssetsDiscoveryProgress
import com.tangem.domain.assetsdiscovery.repository.AssetsDiscoveryRepository
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.common.wallets.getSyncStrict
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokensync.model.TokenSyncProgress
import com.tangem.domain.tokensync.repository.TokenSyncRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.async
@ -30,42 +30,39 @@ import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext
import java.math.BigDecimal
import java.util.concurrent.ConcurrentHashMap
@Suppress("LongParameterList")
internal class DefaultTokenSyncRepository(
private val walletManagersFacade: WalletManagersFacade,
internal class DefaultAssetsDiscoveryRepository(
private val assetsDiscoveryFacade: AssetsDiscoveryFacade,
private val tangemTechApi: TangemTechApi,
private val userWalletsListRepository: UserWalletsListRepository,
private val networkFactory: NetworkFactory,
private val appPreferencesStore: AppPreferencesStore,
private val tokenSyncStoreFactory: TokenSyncStoreFactory,
private val assetsDiscoveryStoreFactory: AssetsDiscoveryStoreFactory,
private val responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
private val dispatchers: CoroutineDispatcherProvider,
private val excludedBlockchains: ExcludedBlockchains,
) : TokenSyncRepository {
) : AssetsDiscoveryRepository {
private val semaphore = Semaphore(MAX_CONCURRENT_REQUESTS)
private val progressStates = ConcurrentHashMap<String, MutableStateFlow<TokenSyncProgress>>()
private val progressStates = ConcurrentHashMap<String, MutableStateFlow<AssetsDiscoveryProgress>>()
override fun observeSyncProgress(userWalletId: UserWalletId): Flow<TokenSyncProgress> {
override fun observeDiscoveryProgress(userWalletId: UserWalletId): Flow<AssetsDiscoveryProgress> {
return getProgressFlow(userWalletId)
}
override fun acknowledgeCompletion(userWalletId: UserWalletId) {
val key = userWalletId.stringValue
val stateFlow = progressStates[key] ?: return
stateFlow.value = TokenSyncProgress.Idle
stateFlow.value = AssetsDiscoveryProgress.Idle
progressStates.remove(key, stateFlow)
}
override suspend fun getDiscoveredCurrencies(userWalletId: UserWalletId): List<CryptoCurrency> {
val tokenSyncStore = tokenSyncStoreFactory.provide(userWalletId)
val storedTokens = tokenSyncStore.get()
val assetsDiscoveryStore = assetsDiscoveryStoreFactory.provide(userWalletId)
val storedTokens = assetsDiscoveryStore.get()
if (storedTokens.isEmpty()) return emptyList()
val userWallet = userWalletsListRepository.getSyncStrict(userWalletId)
@ -77,34 +74,34 @@ internal class DefaultTokenSyncRepository(
}
override suspend fun clearDiscoveredTokens(userWalletId: UserWalletId) {
val tokenSyncStore = tokenSyncStoreFactory.provide(userWalletId)
tokenSyncStore.clear()
val assetsDiscoveryStore = assetsDiscoveryStoreFactory.provide(userWalletId)
assetsDiscoveryStore.clear()
}
override suspend fun clearPendingFlag(userWalletId: UserWalletId) {
setPendingFlag(userWalletId, value = false)
}
override suspend fun getPendingSyncWalletIds(): List<UserWalletId> {
override suspend fun getPendingDiscoveryWalletIds(): List<UserWalletId> {
val pendingMap = appPreferencesStore
.getObjectMapSync<Boolean>(PreferencesKeys.PENDING_DISCOVERY_SYNC_KEY)
.getObjectMapSync<Boolean>(PreferencesKeys.PENDING_ASSETS_DISCOVERY_KEY)
return pendingMap
.filter { it.value }
.map { UserWalletId(it.key) }
}
override suspend fun runSync(userWalletId: UserWalletId) {
override suspend fun runDiscovery(userWalletId: UserWalletId) {
val networks = getSupportedNetworks(userWalletId)
if (networks.isEmpty()) return
setPendingFlag(userWalletId, value = true)
val tokenSyncStore = tokenSyncStoreFactory.provide(userWalletId)
tokenSyncStore.clear()
val assetsDiscoveryStore = assetsDiscoveryStoreFactory.provide(userWalletId)
assetsDiscoveryStore.clear()
val batches = networks.chunked(MAX_CONCURRENT_REQUESTS)
var completedNetworks = 0
getProgressFlow(userWalletId).value = TokenSyncProgress.InProgress(
getProgressFlow(userWalletId).value = AssetsDiscoveryProgress.InProgress(
completedNetworks = 0,
totalNetworks = networks.size,
)
@ -114,26 +111,24 @@ internal class DefaultTokenSyncRepository(
completedNetworks = handleBatchResults(
userWalletId = userWalletId,
results = batchResults,
tokenSyncStore = tokenSyncStore,
assetsDiscoveryStore = assetsDiscoveryStore,
completedNetworks = completedNetworks,
totalNetworks = networks.size,
)
}
}
override suspend fun completeSync(userWalletId: UserWalletId) {
override suspend fun completeDiscovery(userWalletId: UserWalletId) {
setPendingFlag(userWalletId, value = false)
getProgressFlow(userWalletId).value = TokenSyncProgress.Completed
getProgressFlow(userWalletId).value = AssetsDiscoveryProgress.Completed
}
private suspend fun processBatch(userWalletId: UserWalletId, batch: List<Network>): List<NetworkResult> {
return coroutineScope {
batch.map { network ->
async(dispatchers.io) {
semaphore.withPermit {
processNetwork(userWalletId, network)
}
}
}.awaitAll()
}
}
@ -141,7 +136,7 @@ internal class DefaultTokenSyncRepository(
private suspend fun handleBatchResults(
userWalletId: UserWalletId,
results: List<NetworkResult>,
tokenSyncStore: TokenSyncStore,
assetsDiscoveryStore: AssetsDiscoveryStore,
completedNetworks: Int,
totalNetworks: Int,
): Int {
@ -150,8 +145,8 @@ internal class DefaultTokenSyncRepository(
for (result in results) {
completed++
handleNetworkResult(result, tokenSyncStore)
progressFlow.value = TokenSyncProgress.InProgress(
handleNetworkResult(result, assetsDiscoveryStore)
progressFlow.value = AssetsDiscoveryProgress.InProgress(
completedNetworks = completed,
totalNetworks = totalNetworks,
)
@ -160,35 +155,35 @@ internal class DefaultTokenSyncRepository(
return completed
}
private suspend fun handleNetworkResult(result: NetworkResult, tokenSyncStore: TokenSyncStore) {
private suspend fun handleNetworkResult(result: NetworkResult, assetsDiscoveryStore: AssetsDiscoveryStore) {
when (result) {
is NetworkResult.Success -> {
if (result.responseTokens.isNotEmpty()) {
try {
tokenSyncStore.append(result.responseTokens)
assetsDiscoveryStore.append(result.responseTokens)
} catch (e: Exception) {
TangemLogger.e("Failed to store discovered tokens for network: ${result.networkId}", e)
}
}
}
is NetworkResult.Error -> {
TangemLogger.e("Token sync failed for network: ${result.networkId}", result.cause)
TangemLogger.e("Assets discovery failed for network: ${result.networkId}", result.cause)
}
}
}
private suspend fun processNetwork(userWalletId: UserWalletId, network: Network): NetworkResult {
return try {
val tokenBalances = fetchAndFilterTokenBalances(userWalletId, network)
val discoveredAssets = discoverAndFilterAssets(userWalletId, network)
if (tokenBalances.isEmpty()) {
if (discoveredAssets.isEmpty()) {
return NetworkResult.Success(
networkId = network.rawId,
responseTokens = emptyList(),
)
}
val enrichedTokens = enrichTokensWithCatalog(tokenBalances, network)
val enrichedTokens = enrichTokensWithCatalog(discoveredAssets, network)
val responseTokens = enrichedTokens
.filter { it.contractAddress != null }
@ -203,36 +198,35 @@ internal class DefaultTokenSyncRepository(
}
}
private suspend fun fetchAndFilterTokenBalances(userWalletId: UserWalletId, network: Network): List<TokenBalance> {
return withContext(dispatchers.io) {
walletManagersFacade.getTokenBalances(userWalletId, network)
private suspend fun discoverAndFilterAssets(userWalletId: UserWalletId, network: Network): List<DiscoveredAsset> =
withContext(dispatchers.io) {
val providerInfo = assetsDiscoveryFacade.getAssetsDiscoveryService(userWalletId, network)
?: return@withContext emptyList()
providerInfo.service.discoverAssets(providerInfo.address)
.filter { it.amount > BigDecimal.ZERO }
}
}
private suspend fun enrichTokensWithCatalog(
tokenBalances: List<TokenBalance>,
assets: List<DiscoveredAsset>,
network: Network,
): List<DiscoveredToken> = withContext(dispatchers.io) {
val tokensToEnrich = tokenBalances.filter { !it.isNativeToken }
): List<EnrichedDiscoveredAsset> = withContext(dispatchers.io) {
val tokensToEnrich = assets.filterIsInstance<DiscoveredAsset.Token>()
val catalogMap = fetchCatalogInfo(
networkId = network.rawId,
contractAddresses = tokensToEnrich.mapNotNull(TokenBalance::contractAddress),
contractAddresses = tokensToEnrich.map { it.contractAddress },
)
tokenBalances.mapNotNull { balance ->
if (balance.isNativeToken) return@mapNotNull null
val contractAddressLower = balance.contractAddress?.lowercase()
val coin = contractAddressLower?.let { catalogMap[it] } ?: return@mapNotNull null
tokensToEnrich.mapNotNull { balance ->
val contractAddressLower = balance.contractAddress.lowercase()
val coin = catalogMap[contractAddressLower] ?: return@mapNotNull null
val decimals = coin.networks
.find { it.contractAddress?.lowercase() == contractAddressLower }
?.decimalCount
?.toInt()
?: 0
DiscoveredToken(
EnrichedDiscoveredAsset(
contractAddress = balance.contractAddress,
symbol = coin.symbol,
name = coin.name,
@ -290,23 +284,23 @@ internal class DefaultTokenSyncRepository(
}
}
private fun getProgressFlow(userWalletId: UserWalletId): MutableStateFlow<TokenSyncProgress> {
private fun getProgressFlow(userWalletId: UserWalletId): MutableStateFlow<AssetsDiscoveryProgress> {
return progressStates.getOrPut(userWalletId.stringValue) {
MutableStateFlow(TokenSyncProgress.Idle)
MutableStateFlow(AssetsDiscoveryProgress.Idle)
}
}
private suspend fun setPendingFlag(userWalletId: UserWalletId, value: Boolean) {
appPreferencesStore.editData { prefs ->
prefs.setObjectMap(
key = PreferencesKeys.PENDING_DISCOVERY_SYNC_KEY,
value = prefs.getObjectMap<Boolean>(PreferencesKeys.PENDING_DISCOVERY_SYNC_KEY)
key = PreferencesKeys.PENDING_ASSETS_DISCOVERY_KEY,
value = prefs.getObjectMap<Boolean>(PreferencesKeys.PENDING_ASSETS_DISCOVERY_KEY)
.plus(userWalletId.stringValue to value),
)
}
}
private fun DiscoveredToken.toResponseToken(): UserTokensResponse.Token {
private fun EnrichedDiscoveredAsset.toResponseToken(): UserTokensResponse.Token {
return UserTokensResponse.Token(
id = currencyId,
networkId = networkId,
@ -317,7 +311,7 @@ internal class DefaultTokenSyncRepository(
)
}
private data class DiscoveredToken(
private data class EnrichedDiscoveredAsset(
val contractAddress: String?,
val symbol: String,
val name: String,

View file

@ -1,8 +1,8 @@
package com.tangem.data.tokensync.store
package com.tangem.data.assetsdiscovery.store
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
interface TokenSyncStore {
interface AssetsDiscoveryStore {
suspend fun get(): List<UserTokensResponse.Token>

View file

@ -1,4 +1,4 @@
package com.tangem.data.tokensync.store
package com.tangem.data.assetsdiscovery.store
import android.content.Context
import androidx.datastore.core.DataStore
@ -17,18 +17,18 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class TokenSyncStoreFactory @Inject constructor(
class AssetsDiscoveryStoreFactory @Inject constructor(
@NetworkMoshi private val moshi: Moshi,
@ApplicationContext private val context: Context,
private val appScope: AppCoroutineScope,
) {
private val stores = ConcurrentHashMap<String, TokenSyncStore>()
private val stores = ConcurrentHashMap<String, AssetsDiscoveryStore>()
fun provide(userWalletId: UserWalletId): TokenSyncStore {
fun provide(userWalletId: UserWalletId): AssetsDiscoveryStore {
val userWalletStringId = userWalletId.formatted()
return stores.computeIfAbsent(userWalletStringId) {
DefaultTokenSyncStore(
DefaultAssetsDiscoveryStore(
persistenceStore = createPersistenceStore(
fileName = "token_sync_$userWalletStringId",
types = listTypes<UserTokensResponse.Token>(),

View file

@ -1,12 +1,12 @@
package com.tangem.data.tokensync.store
package com.tangem.data.assetsdiscovery.store
import androidx.datastore.core.DataStore
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import kotlinx.coroutines.flow.firstOrNull
internal class DefaultTokenSyncStore(
internal class DefaultAssetsDiscoveryStore(
private val persistenceStore: DataStore<List<UserTokensResponse.Token>>,
) : TokenSyncStore {
) : AssetsDiscoveryStore {
override suspend fun get(): List<UserTokensResponse.Token> {
return persistenceStore.data.firstOrNull().orEmpty()

View file

@ -1,49 +0,0 @@
package com.tangem.data.tokensync.di
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.data.common.network.NetworkFactory
import com.tangem.data.tokensync.repository.DefaultTokenSyncRepository
import com.tangem.data.tokensync.store.TokenSyncStoreFactory
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.tokensync.repository.TokenSyncRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object TokenSyncDataModule {
@Provides
@Singleton
fun provideTokenSyncRepository(
walletManagersFacade: WalletManagersFacade,
tangemTechApi: TangemTechApi,
userWalletsListRepository: UserWalletsListRepository,
networkFactory: NetworkFactory,
appPreferencesStore: AppPreferencesStore,
tokenSyncStoreFactory: TokenSyncStoreFactory,
responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
dispatchers: CoroutineDispatcherProvider,
excludedBlockchains: ExcludedBlockchains,
): TokenSyncRepository {
return DefaultTokenSyncRepository(
walletManagersFacade = walletManagersFacade,
tangemTechApi = tangemTechApi,
userWalletsListRepository = userWalletsListRepository,
networkFactory = networkFactory,
appPreferencesStore = appPreferencesStore,
tokenSyncStoreFactory = tokenSyncStoreFactory,
responseCryptoCurrenciesFactory = responseCryptoCurrenciesFactory,
dispatchers = dispatchers,
excludedBlockchains = excludedBlockchains,
)
}
}

View file

@ -19,7 +19,6 @@ import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.blockchain.nft.models.NFTAsset
import com.tangem.blockchain.nft.models.NFTCollection
import com.tangem.blockchain.tokenbalance.models.TokenBalance
import com.tangem.blockchain.transactionhistory.models.TransactionHistoryRequest
import com.tangem.blockchain.yieldsupply.YieldSupplyContractCallDataProviderFactory
import com.tangem.blockchainsdk.BlockchainSDKFactory
@ -850,17 +849,6 @@ internal class DefaultWalletManagersFacade @Inject constructor(
return blockchain.getNFTExploreUrl(assetIdentifier)
}
override suspend fun getTokenBalances(userWalletId: UserWalletId, network: Network): List<TokenBalance> {
val blockchain = network.toBlockchain()
val walletManager = getOrCreateWalletManager(
userWalletId = userWalletId,
blockchain = blockchain,
derivationPath = network.derivationPath.value,
) ?: return emptyList()
val address = walletManager.wallet.address
return walletManager.getTokenBalances(address)
}
override suspend fun isAccountInitialized(userWalletId: UserWalletId, network: Network): Boolean {
val walletManager = getOrCreateWalletManager(userWalletId = userWalletId, network = network)
val initializableAccountWalletManger = walletManager as? InitializableAccount ?: return true

View file

@ -5,7 +5,7 @@ plugins {
}
android {
namespace = "com.tangem.domain.tokensync"
namespace = "com.tangem.domain.assetsdiscovery"
}
dependencies {
@ -14,6 +14,9 @@ dependencies {
implementation(projects.domain.account.status)
implementation(projects.core.utils)
implementation(projects.libs.blockchainSdk)
implementation(tangemDeps.blockchain)
implementation(deps.kotlin.coroutines)
implementation(deps.arrow.core)
}

View file

@ -0,0 +1,15 @@
package com.tangem.domain.assetsdiscovery
import com.tangem.blockchain.assetsdiscovery.AssetsDiscoveryService
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
interface AssetsDiscoveryFacade {
suspend fun getAssetsDiscoveryService(userWalletId: UserWalletId, network: Network): AssetsDiscoveryServiceInfo?
data class AssetsDiscoveryServiceInfo(
val address: String,
val service: AssetsDiscoveryService,
)
}

View file

@ -1,13 +1,13 @@
package com.tangem.domain.tokensync.model
package com.tangem.domain.assetsdiscovery.model
sealed class TokenSyncProgress {
sealed class AssetsDiscoveryProgress {
data object Idle : TokenSyncProgress()
data object Idle : AssetsDiscoveryProgress()
data class InProgress(
val completedNetworks: Int,
val totalNetworks: Int,
) : TokenSyncProgress() {
) : AssetsDiscoveryProgress() {
val progressPercent: Int
get() = if (totalNetworks > 0) {
completedNetworks * 100 / totalNetworks
@ -16,5 +16,5 @@ sealed class TokenSyncProgress {
}
}
data object Completed : TokenSyncProgress()
data object Completed : AssetsDiscoveryProgress()
}

View file

@ -0,0 +1,25 @@
package com.tangem.domain.assetsdiscovery.repository
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.assetsdiscovery.model.AssetsDiscoveryProgress
import kotlinx.coroutines.flow.Flow
interface AssetsDiscoveryRepository {
suspend fun runDiscovery(userWalletId: UserWalletId)
suspend fun completeDiscovery(userWalletId: UserWalletId)
suspend fun getPendingDiscoveryWalletIds(): List<UserWalletId>
fun observeDiscoveryProgress(userWalletId: UserWalletId): Flow<AssetsDiscoveryProgress>
fun acknowledgeCompletion(userWalletId: UserWalletId)
suspend fun clearPendingFlag(userWalletId: UserWalletId)
suspend fun getDiscoveredCurrencies(userWalletId: UserWalletId): List<CryptoCurrency>
suspend fun clearDiscoveredTokens(userWalletId: UserWalletId)
}

View file

@ -0,0 +1,13 @@
package com.tangem.domain.assetsdiscovery.usecase
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.assetsdiscovery.repository.AssetsDiscoveryRepository
class AcknowledgeAssetsDiscoveryCompletionUseCase(
private val assetsDiscoveryRepository: AssetsDiscoveryRepository,
) {
operator fun invoke(userWalletId: UserWalletId) {
assetsDiscoveryRepository.acknowledgeCompletion(userWalletId)
}
}

View file

@ -0,0 +1,15 @@
package com.tangem.domain.assetsdiscovery.usecase
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.assetsdiscovery.model.AssetsDiscoveryProgress
import com.tangem.domain.assetsdiscovery.repository.AssetsDiscoveryRepository
import kotlinx.coroutines.flow.Flow
class ObserveAssetsDiscoveryUseCase(
private val assetsDiscoveryRepository: AssetsDiscoveryRepository,
) {
operator fun invoke(userWalletId: UserWalletId): Flow<AssetsDiscoveryProgress> {
return assetsDiscoveryRepository.observeDiscoveryProgress(userWalletId)
}
}

View file

@ -1,18 +1,18 @@
package com.tangem.domain.tokensync.usecase
package com.tangem.domain.assetsdiscovery.usecase
import arrow.core.Either
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokensync.repository.TokenSyncRepository
import com.tangem.domain.assetsdiscovery.repository.AssetsDiscoveryRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import java.util.concurrent.ConcurrentHashMap
class StartTokenSyncUseCase(
private val tokenSyncRepository: TokenSyncRepository,
class StartAssetsDiscoveryUseCase(
private val assetsDiscoveryRepository: AssetsDiscoveryRepository,
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
private val appCoroutineScope: AppCoroutineScope,
) {
@ -23,9 +23,9 @@ class StartTokenSyncUseCase(
activeSyncJobs[userWalletId]?.cancel()
activeSyncJobs[userWalletId] = appCoroutineScope.launch {
try {
tokenSyncRepository.runSync(userWalletId)
assetsDiscoveryRepository.runDiscovery(userWalletId)
applyDiscoveredTokens(userWalletId)
tokenSyncRepository.completeSync(userWalletId)
assetsDiscoveryRepository.completeDiscovery(userWalletId)
} catch (e: Exception) {
TangemLogger.e("Token sync failed for wallet: $userWalletId", e)
} finally {
@ -36,18 +36,18 @@ class StartTokenSyncUseCase(
suspend fun cancel(userWalletId: UserWalletId): Either<Throwable, Unit> = Either.catch {
activeSyncJobs.remove(userWalletId)?.cancel()
tokenSyncRepository.clearPendingFlag(userWalletId)
tokenSyncRepository.clearDiscoveredTokens(userWalletId)
assetsDiscoveryRepository.clearPendingFlag(userWalletId)
assetsDiscoveryRepository.clearDiscoveredTokens(userWalletId)
}
fun applyPendingSyncs() {
fun applyPendingAssetsDiscovery() {
appCoroutineScope.launch {
try {
val pendingIds = tokenSyncRepository.getPendingSyncWalletIds()
val pendingIds = assetsDiscoveryRepository.getPendingDiscoveryWalletIds()
for (walletId in pendingIds) {
val isApplied = applyDiscoveredTokens(walletId)
if (isApplied) {
tokenSyncRepository.clearPendingFlag(walletId)
assetsDiscoveryRepository.clearPendingFlag(walletId)
}
}
} catch (e: Exception) {
@ -57,7 +57,7 @@ class StartTokenSyncUseCase(
}
private suspend fun applyDiscoveredTokens(userWalletId: UserWalletId): Boolean {
val currencies = tokenSyncRepository.getDiscoveredCurrencies(userWalletId)
val currencies = assetsDiscoveryRepository.getDiscoveredCurrencies(userWalletId)
if (currencies.isEmpty()) return true
@ -67,7 +67,7 @@ class StartTokenSyncUseCase(
add = currencies,
).fold(
ifRight = {
tokenSyncRepository.clearDiscoveredTokens(userWalletId)
assetsDiscoveryRepository.clearDiscoveredTokens(userWalletId)
true
},
ifLeft = { error ->

View file

@ -1,25 +0,0 @@
package com.tangem.domain.tokensync.repository
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokensync.model.TokenSyncProgress
import kotlinx.coroutines.flow.Flow
interface TokenSyncRepository {
suspend fun runSync(userWalletId: UserWalletId)
suspend fun completeSync(userWalletId: UserWalletId)
suspend fun getPendingSyncWalletIds(): List<UserWalletId>
fun observeSyncProgress(userWalletId: UserWalletId): Flow<TokenSyncProgress>
fun acknowledgeCompletion(userWalletId: UserWalletId)
suspend fun clearPendingFlag(userWalletId: UserWalletId)
suspend fun getDiscoveredCurrencies(userWalletId: UserWalletId): List<CryptoCurrency>
suspend fun clearDiscoveredTokens(userWalletId: UserWalletId)
}

View file

@ -1,13 +0,0 @@
package com.tangem.domain.tokensync.usecase
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokensync.repository.TokenSyncRepository
class AcknowledgeTokenSyncCompletionUseCase(
private val tokenSyncRepository: TokenSyncRepository,
) {
operator fun invoke(userWalletId: UserWalletId) {
tokenSyncRepository.acknowledgeCompletion(userWalletId)
}
}

View file

@ -1,15 +0,0 @@
package com.tangem.domain.tokensync.usecase
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokensync.model.TokenSyncProgress
import com.tangem.domain.tokensync.repository.TokenSyncRepository
import kotlinx.coroutines.flow.Flow
class ObserveTokenSyncUseCase(
private val tokenSyncRepository: TokenSyncRepository,
) {
operator fun invoke(userWalletId: UserWalletId): Flow<TokenSyncProgress> {
return tokenSyncRepository.observeSyncProgress(userWalletId)
}
}

View file

@ -12,7 +12,6 @@ import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.blockchain.nft.models.NFTAsset
import com.tangem.blockchain.nft.models.NFTCollection
import com.tangem.blockchain.tokenbalance.models.TokenBalance
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
@ -282,8 +281,6 @@ interface WalletManagersFacade {
suspend fun getNFTExploreUrl(network: Network, assetIdentifier: NFTAsset.Identifier): String?
suspend fun getTokenBalances(userWalletId: UserWalletId, network: Network): List<TokenBalance>
/**
* If wallet manager implements [InitializableAccount] then returns [InitializableAccount.isAccountInitialized]
* value. Otherwise always return true

View file

@ -22,7 +22,7 @@ internal class PreviewDetailsComponent : DetailsComponent {
router = DummyRouter(),
hotWalletFeatureToggles = object : HotWalletFeatureToggles {
override val isWalletCreationRestrictionEnabled: Boolean = true
override val isTokenSyncEnabled: Boolean = true
override val isAssetsDiscoveryEnabled: Boolean = true
},
).buildAll(
isWalletConnectAvailable = true,

View file

@ -2,5 +2,5 @@ package com.tangem.features.hotwallet
interface HotWalletFeatureToggles {
val isWalletCreationRestrictionEnabled: Boolean
val isTokenSyncEnabled: Boolean
val isAssetsDiscoveryEnabled: Boolean
}

View file

@ -38,7 +38,7 @@ dependencies {
implementation(projects.domain.feedback)
implementation(projects.domain.feedback.models)
implementation(projects.domain.hotWallet)
implementation(projects.domain.tokensync)
implementation(projects.domain.assetsdiscovery)
/** Common */
implementation(projects.common.ui)

View file

@ -10,6 +10,6 @@ internal class DefaultHotWalletFeatureToggles(
override val isWalletCreationRestrictionEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.HOT_WALLET_CREATION_RESTRICTION_ENABLED)
override val isTokenSyncEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.TOKEN_SYNC_ENABLED)
override val isAssetsDiscoveryEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.ASSETS_DISCOVERY_ENABLED)
}

View file

@ -11,7 +11,7 @@ import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.settings.CanUseBiometryUseCase
import com.tangem.domain.tokensync.usecase.StartTokenSyncUseCase
import com.tangem.domain.assetsdiscovery.usecase.StartAssetsDiscoveryUseCase
import com.tangem.domain.wallets.hot.HotWalletAccessCodeAttemptsRepository
import com.tangem.domain.wallets.hot.HotWalletAccessCodeAttemptsRepository.Attempts
import com.tangem.domain.wallets.hot.HotWalletAccessCodeAttemptsRepository.Companion.MAX_FAST_FORWARD_ATTEMPTS
@ -39,7 +39,7 @@ internal class HotAccessCodeRequestModel @Inject constructor(
private val userWalletsListRepository: UserWalletsListRepository,
private val canUseBiometryUseCase: CanUseBiometryUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val startTokenSyncUseCase: StartTokenSyncUseCase,
private val startAssetsDiscoveryUseCase: StartAssetsDiscoveryUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) : Model() {
@ -220,8 +220,8 @@ internal class HotAccessCodeRequestModel @Inject constructor(
val userWallet = userWalletsListRepository.userWalletsSync()
.firstOrNull { it is UserWallet.Hot && it.hotWalletId == currentRequest.hotWalletId } ?: return
if (hotWalletFeatureToggles.isTokenSyncEnabled) {
startTokenSyncUseCase.cancel(userWallet.walletId)
if (hotWalletFeatureToggles.isAssetsDiscoveryEnabled) {
startAssetsDiscoveryUseCase.cancel(userWallet.walletId)
}
userWalletsListRepository.delete(listOf(userWallet.walletId))

View file

@ -17,7 +17,7 @@ import com.tangem.core.ui.message.bottomSheetMessage
import com.tangem.crypto.bip39.Mnemonic
import com.tangem.datasource.local.appsflyer.AppsFlyerStore
import com.tangem.domain.common.wallets.error.SaveWalletError
import com.tangem.domain.tokensync.usecase.StartTokenSyncUseCase
import com.tangem.domain.assetsdiscovery.usecase.StartAssetsDiscoveryUseCase
import com.tangem.domain.wallets.builder.HotUserWalletBuilder
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import com.tangem.features.hotwallet.HotWalletFeatureToggles
@ -42,7 +42,7 @@ internal class AddExistingWalletImportModel @Inject constructor(
private val tangemHotSdk: TangemHotSdk,
private val hotUserWalletBuilderFactory: HotUserWalletBuilder.Factory,
private val saveUserWalletUseCase: SaveWalletUseCase,
private val startTokenSyncUseCase: StartTokenSyncUseCase,
private val startAssetsDiscoveryUseCase: StartAssetsDiscoveryUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
private val analyticsEventHandler: AnalyticsEventHandler,
@ -114,8 +114,8 @@ internal class AddExistingWalletImportModel @Inject constructor(
.onRight {
setImportProgress(false)
if (hotWalletFeatureToggles.isTokenSyncEnabled) {
startTokenSyncUseCase(userWallet.walletId)
if (hotWalletFeatureToggles.isAssetsDiscoveryEnabled) {
startAssetsDiscoveryUseCase(userWallet.walletId)
}
analyticsEventHandler.send(

View file

@ -12,7 +12,7 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.tokensync.usecase.StartTokenSyncUseCase
import com.tangem.domain.assetsdiscovery.usecase.StartAssetsDiscoveryUseCase
import com.tangem.domain.wallets.usecase.DeleteWalletUseCase
import com.tangem.features.hotwallet.ForgetWalletComponent
import com.tangem.features.hotwallet.HotWalletFeatureToggles
@ -33,7 +33,7 @@ internal class ForgetWalletModel @Inject constructor(
private val router: Router,
private val deleteWalletUseCase: DeleteWalletUseCase,
private val uiMessageSender: UiMessageSender,
private val startTokenSyncUseCase: StartTokenSyncUseCase,
private val startAssetsDiscoveryUseCase: StartAssetsDiscoveryUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) : Model() {
@ -84,8 +84,8 @@ internal class ForgetWalletModel @Inject constructor(
private fun forgetWallet() {
modelScope.launch {
if (hotWalletFeatureToggles.isTokenSyncEnabled) {
startTokenSyncUseCase.cancel(params.userWalletId)
if (hotWalletFeatureToggles.isAssetsDiscoveryEnabled) {
startAssetsDiscoveryUseCase.cancel(params.userWalletId)
}
val hasUserWallets = deleteWalletUseCase(params.userWalletId)

View file

@ -50,7 +50,7 @@ dependencies {
implementation(projects.domain.settings)
implementation(projects.domain.notifications.models)
implementation(projects.domain.notifications)
implementation(projects.domain.tokensync)
implementation(projects.domain.assetsdiscovery)
/* AndroidX */
implementation(deps.androidx.fragment.ktx)

View file

@ -37,7 +37,7 @@ import com.tangem.domain.nft.EnableWalletNFTUseCase
import com.tangem.domain.nft.GetWalletNFTEnabledUseCase
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.settings.repositories.PermissionRepository
import com.tangem.domain.tokensync.usecase.StartTokenSyncUseCase
import com.tangem.domain.assetsdiscovery.usecase.StartAssetsDiscoveryUseCase
import com.tangem.domain.wallets.analytics.Settings
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents.RecoveryPhraseScreenAction
@ -90,7 +90,7 @@ internal class WalletSettingsModel @Inject constructor(
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
private val singleAccountListSupplier: SingleAccountListSupplier,
private val accountListSortingSaver: AccountListSortingSaver,
private val startTokenSyncUseCase: StartTokenSyncUseCase,
private val startAssetsDiscoveryUseCase: StartAssetsDiscoveryUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) : Model() {
@ -256,8 +256,8 @@ internal class WalletSettingsModel @Inject constructor(
val userWallet = getUserWalletUseCase(params.userWalletId)
.getOrNull()
if (userWallet is UserWallet.Hot && hotWalletFeatureToggles.isTokenSyncEnabled) {
startTokenSyncUseCase.cancel(params.userWalletId)
if (userWallet is UserWallet.Hot && hotWalletFeatureToggles.isAssetsDiscoveryEnabled) {
startAssetsDiscoveryUseCase.cancel(params.userWalletId)
}
val hasUserWallets = deleteWalletUseCase(params.userWalletId).getOrElse { error ->

View file

@ -125,7 +125,7 @@ dependencies {
implementation(projects.domain.yieldSupply.models)
implementation(projects.domain.appTheme)
implementation(projects.domain.appTheme.models)
implementation(projects.domain.tokensync)
implementation(projects.domain.assetsdiscovery)
/** Feature Apis */
implementation(projects.features.details.api)

View file

@ -38,7 +38,7 @@ import com.tangem.domain.settings.*
import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase
import com.tangem.domain.walletconnect.WcPairService
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.tokensync.usecase.StartTokenSyncUseCase
import com.tangem.domain.assetsdiscovery.usecase.StartAssetsDiscoveryUseCase
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.domain.wallets.usecase.*
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyUpdateUseCase
@ -126,7 +126,7 @@ internal class WalletModel @Inject constructor(
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val uiMessageSender: UiMessageSender,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val startTokenSyncUseCase: StartTokenSyncUseCase,
private val startAssetsDiscoveryUseCase: StartAssetsDiscoveryUseCase,
val screenLifecycleProvider: ScreenLifecycleProvider,
val innerWalletRouter: InnerWalletRouter,
) : Model() {
@ -159,7 +159,7 @@ internal class WalletModel @Inject constructor(
subscribeTangemPayOnWalletState()
subscribeToMainScreenQrScanning()
enableNotificationsIfNeeded()
applyPendingTokenSyncs()
applyPendingAssetsDiscovery()
clickIntents.initialize(innerWalletRouter, modelScope)
@ -840,9 +840,9 @@ internal class WalletModel @Inject constructor(
}
}
private fun applyPendingTokenSyncs() {
if (hotWalletFeatureToggles.isTokenSyncEnabled) {
startTokenSyncUseCase.applyPendingSyncs()
private fun applyPendingAssetsDiscovery() {
if (hotWalletFeatureToggles.isAssetsDiscoveryEnabled) {
startAssetsDiscoveryUseCase.applyPendingAssetsDiscovery()
}
}

View file

@ -40,7 +40,7 @@ import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.Program
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.PromotionBannerClicked
import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.tokensync.usecase.AcknowledgeTokenSyncCompletionUseCase
import com.tangem.domain.assetsdiscovery.usecase.AcknowledgeAssetsDiscoveryCompletionUseCase
import com.tangem.domain.wallets.usecase.*
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic
@ -97,9 +97,9 @@ internal interface WalletWarningsClickIntents {
fun onCloseUpgradeBannerClick(userWalletId: UserWalletId)
fun onDismissTokenSyncNotification(userWalletId: UserWalletId)
fun onDismissAssetsDiscoveryNotification(userWalletId: UserWalletId)
fun onTokenSyncManageClick(userWalletId: UserWalletId)
fun onAssetsDiscoveryManageClick(userWalletId: UserWalletId)
}
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
@ -132,7 +132,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val uiMessageSender: UiMessageSender,
private val reviewManager: ReviewManager,
private val closeHotWalletUpgradeBannerUseCase: CloseHotWalletUpgradeBannerUseCase,
private val acknowledgeTokenSyncCompletionUseCase: AcknowledgeTokenSyncCompletionUseCase,
private val acknowledgeAssetsDiscoveryCompletionUseCase: AcknowledgeAssetsDiscoveryCompletionUseCase,
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
override fun onAddBackupCardClick() {
@ -508,12 +508,12 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
}
}
override fun onDismissTokenSyncNotification(userWalletId: UserWalletId) {
acknowledgeTokenSyncCompletionUseCase(userWalletId)
override fun onDismissAssetsDiscoveryNotification(userWalletId: UserWalletId) {
acknowledgeAssetsDiscoveryCompletionUseCase(userWalletId)
}
override fun onTokenSyncManageClick(userWalletId: UserWalletId) {
acknowledgeTokenSyncCompletionUseCase(userWalletId)
override fun onAssetsDiscoveryManageClick(userWalletId: UserWalletId) {
acknowledgeAssetsDiscoveryCompletionUseCase(userWalletId)
router.openManageTokensScreen(
AccountId.forMainCryptoPortfolio(userWalletId),
)

View file

@ -120,7 +120,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
is WalletNotification.Warning.TangemPayRefreshNeeded -> null
is WalletNotification.Warning.TangemPayUnreachable -> null
is WalletNotification.UpgradeHotWalletPromo -> null
is WalletNotification.TokenSyncCompleted -> null
is WalletNotification.AssetsDiscoveryCompleted -> null
is WalletNotification.CreateTangemPayAccount -> null
}
}

View file

@ -27,8 +27,8 @@ import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
import com.tangem.domain.tokensync.model.TokenSyncProgress
import com.tangem.domain.tokensync.usecase.ObserveTokenSyncUseCase
import com.tangem.domain.assetsdiscovery.model.AssetsDiscoveryProgress
import com.tangem.domain.assetsdiscovery.usecase.ObserveAssetsDiscoveryUseCase
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
@ -65,7 +65,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private val shouldShowUpgradeHotWalletBannerUseCase: ShouldShowUpgradeHotWalletBannerUseCase,
private val getUpgradeBannerClosureTimestampUseCase: GetUpgradeBannerClosureTimestampUseCase,
private val checkHotWalletUpgradeBannerUseCase: CheckHotWalletUpgradeBannerUseCase,
private val observeTokenSyncUseCase: ObserveTokenSyncUseCase,
private val observeAssetsDiscoveryUseCase: ObserveAssetsDiscoveryUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) {
@ -75,10 +75,11 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
val params = SingleAccountStatusListProducer.Params(userWallet.walletId)
val accountStatusListFlow = accountDependencies.singleAccountStatusListSupplier(params)
val tokenSyncProgressFlow = if (hotWalletFeatureToggles.isTokenSyncEnabled && userWallet is UserWallet.Hot) {
observeTokenSyncUseCase(userWallet.walletId).distinctUntilChanged()
val assetsDiscoveryProgressFlow =
if (hotWalletFeatureToggles.isAssetsDiscoveryEnabled && userWallet is UserWallet.Hot) {
observeAssetsDiscoveryUseCase(userWallet.walletId).distinctUntilChanged()
} else {
flowOf(TokenSyncProgress.Idle)
flowOf(AssetsDiscoveryProgress.Idle)
}
return combine(
@ -96,7 +97,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
.distinctUntilChanged(),
getUpgradeBannerClosureTimestampUseCase(userWallet.walletId)
.distinctUntilChanged(),
tokenSyncProgressFlow,
assetsDiscoveryProgressFlow,
) { array -> array }
.map { array ->
val accountStatusList = array[0] as AccountStatusList
@ -108,7 +109,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
val shouldShowYieldPromo = array[6] as Boolean
val shouldShowUpgradeBanner = array[7] as Boolean
val closureTimestamp = array[8] as? Long
val tokenSyncProgress = array[9] as TokenSyncProgress
val assetsDiscoveryProgress = array[9] as AssetsDiscoveryProgress
val flattenCurrencies = accountStatusList.flattenCurrencies()
val paymentAccountStatus = accountStatusList.accountStatuses
@ -153,9 +154,9 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
clickIntents = clickIntents,
)
addTokenSyncCompletedNotification(
addAssetsDiscoveryCompletedNotification(
userWallet = userWallet,
tokenSyncProgress = tokenSyncProgress,
assetsDiscoveryProgress = assetsDiscoveryProgress,
clickIntents = clickIntents,
)
@ -404,17 +405,17 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
// }
// }
private fun MutableList<WalletNotification>.addTokenSyncCompletedNotification(
private fun MutableList<WalletNotification>.addAssetsDiscoveryCompletedNotification(
userWallet: UserWallet,
tokenSyncProgress: TokenSyncProgress,
assetsDiscoveryProgress: AssetsDiscoveryProgress,
clickIntents: WalletClickIntents,
) {
addIf(
element = WalletNotification.TokenSyncCompleted(
onCloseClick = { clickIntents.onDismissTokenSyncNotification(userWallet.walletId) },
onManageTokensClick = { clickIntents.onTokenSyncManageClick(userWallet.walletId) },
element = WalletNotification.AssetsDiscoveryCompleted(
onCloseClick = { clickIntents.onDismissAssetsDiscoveryNotification(userWallet.walletId) },
onManageTokensClick = { clickIntents.onAssetsDiscoveryManageClick(userWallet.walletId) },
),
condition = tokenSyncProgress is TokenSyncProgress.Completed,
condition = assetsDiscoveryProgress is AssetsDiscoveryProgress.Completed,
)
}

View file

@ -10,7 +10,7 @@ import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.card.common.util.getCardsCount
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenSyncProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.model.AssetsDiscoveryProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo
import java.math.BigDecimal
@ -32,7 +32,7 @@ internal object WalletAdditionalInfoFactory {
fun resolve(
wallet: UserWallet,
currencyAmount: BigDecimal? = null,
syncProgress: TokenSyncProgressUM = TokenSyncProgressUM.Idle,
syncProgress: AssetsDiscoveryProgressUM = AssetsDiscoveryProgressUM.Idle,
): WalletAdditionalInfo {
return when (wallet) {
is UserWallet.Cold -> {
@ -46,8 +46,8 @@ internal object WalletAdditionalInfoFactory {
}
}
private fun UserWallet.Hot.resolveAdditionalInfo(syncProgress: TokenSyncProgressUM): WalletAdditionalInfo {
val content = if (syncProgress is TokenSyncProgressUM.InProgress) {
private fun UserWallet.Hot.resolveAdditionalInfo(syncProgress: AssetsDiscoveryProgressUM): WalletAdditionalInfo {
val content = if (syncProgress is AssetsDiscoveryProgressUM.InProgress) {
WalletAdditionalInfo.Content.SyncProgress(syncProgress.progressPercent)
} else {
WalletAdditionalInfo.Content.Text(

View file

@ -18,7 +18,7 @@ internal class MultiWalletContentLoader @AssistedInject constructor(
private val walletNotificationsSubscriberFactory: WalletNotificationsSubscriber.Factory,
private val multiWalletActionButtonsSubscriberFactory: MultiWalletActionButtonsSubscriber.Factory,
private val tangemPayMainSubscriberFactory: TangemPayMainSubscriber.Factory,
private val tokenSyncSubscriberFactory: TokenSyncSubscriber.Factory,
private val assetsDiscoverySubscriberFactory: AssetsDiscoverySubscriber.Factory,
private val tokenListAnalyticsSubscriberFactory: TokenListAnalyticsSubscriber.Factory,
private val designFeatureToggles: DesignFeatureToggles,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
@ -36,8 +36,8 @@ internal class MultiWalletContentLoader @AssistedInject constructor(
multiWalletActionButtonsSubscriberFactory.create(userWallet),
tangemPayMainSubscriberFactory.create(userWallet),
tokenListAnalyticsSubscriberFactory.create(userWallet),
if (hotWalletFeatureToggles.isTokenSyncEnabled && userWallet is UserWallet.Hot) {
tokenSyncSubscriberFactory.create(userWallet)
if (hotWalletFeatureToggles.isAssetsDiscoveryEnabled && userWallet is UserWallet.Hot) {
assetsDiscoverySubscriberFactory.create(userWallet)
} else {
null
},

View file

@ -0,0 +1,13 @@
package com.tangem.feature.wallet.presentation.wallet.state.model
import androidx.compose.runtime.Immutable
@Immutable
internal sealed class AssetsDiscoveryProgressUM {
data object Idle : AssetsDiscoveryProgressUM()
data class InProgress(val progressPercent: Int) : AssetsDiscoveryProgressUM()
data object Completed : AssetsDiscoveryProgressUM()
}

View file

@ -1,13 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.state.model
import androidx.compose.runtime.Immutable
@Immutable
internal sealed class TokenSyncProgressUM {
data object Idle : TokenSyncProgressUM()
data class InProgress(val progressPercent: Int) : TokenSyncProgressUM()
data object Completed : TokenSyncProgressUM()
}

View file

@ -435,7 +435,7 @@ sealed class WalletNotification(val config: NotificationConfig) {
),
)
data class TokenSyncCompleted(
data class AssetsDiscoveryCompleted(
val onCloseClick: () -> Unit,
val onManageTokensClick: () -> Unit,
) : WalletNotification(

View file

@ -27,7 +27,7 @@ internal sealed interface WalletState : WalletStateHolder {
abstract val tangemPayState: TangemPayState
abstract val tangemPayMainUM: TangemPayMainUM
abstract val isTangemPayRefactorEnabled: Boolean // TANGEM_PAY_ACCOUNTS_REFACTOR_ENABLED
abstract val tokenSyncProgressUM: TokenSyncProgressUM
abstract val assetsDiscoveryProgressUM: AssetsDiscoveryProgressUM
data class Content(
override val pullToRefreshConfig: PullToRefreshConfig,
@ -41,7 +41,7 @@ internal sealed interface WalletState : WalletStateHolder {
override val tangemPayState: TangemPayState,
override val tangemPayMainUM: TangemPayMainUM,
override val isTangemPayRefactorEnabled: Boolean,
override val tokenSyncProgressUM: TokenSyncProgressUM = TokenSyncProgressUM.Idle,
override val assetsDiscoveryProgressUM: AssetsDiscoveryProgressUM = AssetsDiscoveryProgressUM.Idle,
) : MultiCurrency()
data class Locked(
@ -63,7 +63,7 @@ internal sealed interface WalletState : WalletStateHolder {
override val tangemPayState: TangemPayState = TangemPayState.Empty
override val tangemPayMainUM: TangemPayMainUM = TangemPayMainUM.Empty
override val isTangemPayRefactorEnabled: Boolean = false
override val tokenSyncProgressUM: TokenSyncProgressUM = TokenSyncProgressUM.Idle
override val assetsDiscoveryProgressUM: AssetsDiscoveryProgressUM = AssetsDiscoveryProgressUM.Idle
}
}

View file

@ -2,21 +2,21 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenSyncProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.model.AssetsDiscoveryProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
internal class SetTokenSyncProgressTransformer(
internal class SetAssetsDiscoveryProgressTransformer(
private val userWallet: UserWallet,
private val progress: TokenSyncProgressUM,
private val progress: AssetsDiscoveryProgressUM,
) : WalletStateTransformer(userWallet.walletId) {
override fun transform(prevState: WalletState): WalletState {
return when (prevState) {
is WalletState.MultiCurrency.Content -> prevState.copy(
walletCardState = updateCardState(prevState.walletCardState),
tokenSyncProgressUM = progress,
assetsDiscoveryProgressUM = progress,
)
else -> prevState
}

View file

@ -61,7 +61,7 @@ internal class SetTokenListErrorTransformer(
walletsBalanceUM = walletUM.walletsBalanceUM.toLoadedState(),
tokensListUM = WalletTokensListUM.Empty(
onEmptyClick = {
clickIntents.onTokenSyncManageClick(walletUM.walletsBalanceUM.id)
clickIntents.onAssetsDiscoveryManageClick(walletUM.walletsBalanceUM.id)
},
),
buttons = walletUM.disableButtons(),

View file

@ -123,7 +123,7 @@ internal class SetTokenListTransformer(
if (params !is TokenConverterParams.Account) {
return WalletTokensListUM.Empty(
onEmptyClick = {
clickIntents.onTokenSyncManageClick(userWallet.walletId)
clickIntents.onAssetsDiscoveryManageClick(userWallet.walletId)
},
)
}

View file

@ -4,7 +4,7 @@ import com.tangem.domain.card.common.util.getCardsCount
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenSyncProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.model.AssetsDiscoveryProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
@ -19,7 +19,7 @@ internal class UpdateWalletCardsCountTransformer(
return when (prevState) {
is WalletState.MultiCurrency.Content -> {
prevState.copy(
walletCardState = prevState.walletCardState.toUpdatedState(prevState.tokenSyncProgressUM),
walletCardState = prevState.walletCardState.toUpdatedState(prevState.assetsDiscoveryProgressUM),
)
}
is WalletState.SingleCurrency.Content -> {
@ -39,7 +39,7 @@ internal class UpdateWalletCardsCountTransformer(
}
private fun WalletCardState.toUpdatedState(
syncProgress: TokenSyncProgressUM = TokenSyncProgressUM.Idle,
syncProgress: AssetsDiscoveryProgressUM = AssetsDiscoveryProgressUM.Idle,
): WalletCardState {
return when (this) {
is WalletCardState.Content -> copy(

View file

@ -1,11 +1,11 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.tokensync.model.TokenSyncProgress
import com.tangem.domain.tokensync.usecase.ObserveTokenSyncUseCase
import com.tangem.domain.assetsdiscovery.model.AssetsDiscoveryProgress
import com.tangem.domain.assetsdiscovery.usecase.ObserveAssetsDiscoveryUseCase
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenSyncProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenSyncProgressTransformer
import com.tangem.feature.wallet.presentation.wallet.state.model.AssetsDiscoveryProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetAssetsDiscoveryProgressTransformer
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@ -13,25 +13,25 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.onEach
internal class TokenSyncSubscriber @AssistedInject constructor(
internal class AssetsDiscoverySubscriber @AssistedInject constructor(
@Assisted private val userWallet: UserWallet,
private val stateController: WalletStateController,
private val observeTokenSyncUseCase: ObserveTokenSyncUseCase,
private val observeAssetsDiscoveryUseCase: ObserveAssetsDiscoveryUseCase,
) : WalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<*> {
return observeTokenSyncUseCase(userWallet.walletId)
return observeAssetsDiscoveryUseCase(userWallet.walletId)
.onEach { current -> handleProgress(userWallet, current) }
}
private fun handleProgress(userWallet: UserWallet, current: TokenSyncProgress) {
private fun handleProgress(userWallet: UserWallet, current: AssetsDiscoveryProgress) {
val progressUM = when (current) {
is TokenSyncProgress.InProgress -> TokenSyncProgressUM.InProgress(current.progressPercent)
is TokenSyncProgress.Completed -> TokenSyncProgressUM.Completed
is TokenSyncProgress.Idle -> TokenSyncProgressUM.Idle
is AssetsDiscoveryProgress.InProgress -> AssetsDiscoveryProgressUM.InProgress(current.progressPercent)
is AssetsDiscoveryProgress.Completed -> AssetsDiscoveryProgressUM.Completed
is AssetsDiscoveryProgress.Idle -> AssetsDiscoveryProgressUM.Idle
}
stateController.update(
SetTokenSyncProgressTransformer(
SetAssetsDiscoveryProgressTransformer(
userWallet = userWallet,
progress = progressUM,
),
@ -40,6 +40,6 @@ internal class TokenSyncSubscriber @AssistedInject constructor(
@AssistedFactory
interface Factory {
fun create(userWallet: UserWallet): TokenSyncSubscriber
fun create(userWallet: UserWallet): AssetsDiscoverySubscriber
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.blockchainsdk
import com.tangem.blockchain.assetsdiscovery.AssetsDiscoveryServiceFactory
import com.tangem.blockchain.common.WalletManagerFactory
import com.tangem.blockchain.common.memo.MemoValidatorFactory
@ -18,4 +19,7 @@ interface BlockchainSDKFactory {
/** Get [MemoValidatorFactory] synchronously */
suspend fun getMemoValidatorFactorySync(): MemoValidatorFactory?
/** Get [AssetsDiscoveryServiceFactory] synchronously */
suspend fun getAssetsDiscoveryServiceFactorySync(): AssetsDiscoveryServiceFactory?
}

View file

@ -1,5 +1,6 @@
package com.tangem.blockchainsdk
import com.tangem.blockchain.assetsdiscovery.AssetsDiscoveryServiceFactory
import com.tangem.blockchain.common.BlockchainSdkConfig
import com.tangem.blockchain.common.WalletManagerFactory
import com.tangem.blockchain.common.memo.MemoValidatorFactory
@ -34,6 +35,8 @@ internal class DefaultBlockchainSDKFactory(
private val walletManagerFactory: Flow<WalletManagerFactory?> = createWalletManagerFactory()
private val memoValidatorFactory: Flow<MemoValidatorFactory?> = createMemoValidatorFactory()
private val assetsDiscoveryServiceFactory: Flow<AssetsDiscoveryServiceFactory?> =
createAssetsDiscoveryServiceFactory()
override suspend fun init() {
coroutineScope {
@ -45,6 +48,9 @@ internal class DefaultBlockchainSDKFactory(
override suspend fun getMemoValidatorFactorySync(): MemoValidatorFactory? = memoValidatorFactory.firstOrNull()
override suspend fun getAssetsDiscoveryServiceFactorySync(): AssetsDiscoveryServiceFactory? =
assetsDiscoveryServiceFactory.firstOrNull()
private fun createWalletManagerFactory(): Flow<WalletManagerFactory?> {
return combine(
flow = flowOf(blockchainSdkConfig),
@ -56,6 +62,16 @@ internal class DefaultBlockchainSDKFactory(
.stateIn(scope = mainScope, started = SharingStarted.Eagerly, initialValue = null)
}
private fun createAssetsDiscoveryServiceFactory(): Flow<AssetsDiscoveryServiceFactory?> {
return combine(
flow = flowOf(blockchainSdkConfig),
flow2 = blockchainProvidersTypesManager.get(),
) { config, providerTypes ->
AssetsDiscoveryServiceFactory(config = config, providerTypes = providerTypes)
}
.stateIn(scope = mainScope, started = SharingStarted.Eagerly, initialValue = null)
}
private fun createMemoValidatorFactory(): Flow<MemoValidatorFactory?> {
return combine(
flow = flowOf(blockchainSdkConfig),

View file

@ -340,7 +340,7 @@ include(":domain:dynamic-addresses:models")
include(":domain:settings")
include(":domain:tokens")
include(":domain:tokens:models")
include(":domain:tokensync")
include(":domain:assetsdiscovery")
include(":domain:wallets")
include(":domain:wallets:models")
include(":domain:txhistory")
@ -409,7 +409,7 @@ include(":data:balance-hiding")
include(":data:common")
include(":data:card")
include(":data:tokens")
include(":data:tokensync")
include(":data:assetsdiscovery")
include(":data:settings")
include(":data:txhistory")
include(":data:wallets")