diff --git a/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt index 02bb0f9562..611bce95ac 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt @@ -4,6 +4,9 @@ import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.managetokens.* import com.tangem.domain.managetokens.repository.CustomTokensRepository import com.tangem.domain.managetokens.repository.ManageTokensRepository +import com.tangem.domain.tokens.repository.CurrenciesRepository +import com.tangem.domain.tokens.repository.NetworksRepository +import com.tangem.domain.walletmanager.WalletManagersFacade import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -57,11 +60,17 @@ internal object ManageTokensDomainModule { @Provides @Singleton fun provideSaveManagedTokensUseCase( - manageTokensRepository: ManageTokensRepository, + customTokensRepository: CustomTokensRepository, + walletManagersFacade: WalletManagersFacade, + currenciesRepository: CurrenciesRepository, + networksRepository: NetworksRepository, derivationsRepository: DerivationsRepository, ): SaveManagedTokensUseCase { return SaveManagedTokensUseCase( - manageTokensRepository = manageTokensRepository, + customTokensRepository = customTokensRepository, + walletManagersFacade = walletManagersFacade, + currenciesRepository = currenciesRepository, + networksRepository = networksRepository, derivationsRepository = derivationsRepository, ) } @@ -81,4 +90,10 @@ internal object ManageTokensDomainModule { ): ValidateDerivationPathUseCase { return ValidateDerivationPathUseCase(customTokensRepository) } + + @Provides + @Singleton + fun provideCheckHasLinkedTokensUseCase(repository: ManageTokensRepository): CheckHasLinkedTokensUseCase { + return CheckHasLinkedTokensUseCase(repository) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index 5169a4b83a..8ec7199796 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -431,12 +431,6 @@ internal object TokensDomainModule { ) } - @Provides - @Singleton - fun provideCheckHasLinkedTokensUseCase(currenciesRepository: CurrenciesRepository): CheckHasLinkedTokensUseCase { - return CheckHasLinkedTokensUseCase(currenciesRepository) - } - @Provides @Singleton fun provideGetCurrencyCheckUseCase(currencyChecksRepository: CurrencyChecksRepository): GetCurrencyCheckUseCase { diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt index 752cef4833..3f637d2d33 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt @@ -130,6 +130,21 @@ internal class DefaultCustomTokensRepository( return cryptoCurrencyFactory.createCoin(network) } + override fun createToken( + managedCryptoCurrency: ManagedCryptoCurrency.Token, + sourceNetwork: ManagedCryptoCurrency.SourceNetwork.Default, + rawId: String?, + ): CryptoCurrency.Token { + return cryptoCurrencyFactory.createToken( + network = sourceNetwork.network, + rawId = rawId, + name = managedCryptoCurrency.name, + symbol = managedCryptoCurrency.symbol, + decimals = sourceNetwork.decimals, + contractAddress = sourceNetwork.contractAddress, + ) + } + override suspend fun createCustomToken( networkId: Network.ID, derivationPath: Network.DerivationPath, diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt index caea014d2c..90409a1f9d 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt @@ -3,27 +3,16 @@ package com.tangem.data.managetokens import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.utils.isSupportedInApp import com.tangem.blockchainsdk.utils.toNetworkId -import com.tangem.data.common.api.safeApiCall -import com.tangem.data.common.currency.CryptoCurrencyFactory -import com.tangem.data.common.currency.UserTokensResponseFactory import com.tangem.data.common.currency.getBlockchain -import com.tangem.data.common.currency.hasCoinForToken import com.tangem.data.common.utils.retryOnError import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher import com.tangem.data.managetokens.utils.ManagedCryptoCurrencyFactory -import com.tangem.data.tokens.utils.UserTokensBackwardCompatibility import com.tangem.datasource.api.common.response.getOrThrow -import com.tangem.datasource.api.express.TangemExpressApi -import com.tangem.datasource.api.express.models.TangemExpressValues -import com.tangem.datasource.api.express.models.request.AssetsRequestBody -import com.tangem.datasource.api.express.models.request.LeastTokenInfo import com.tangem.datasource.api.tangemTech.TangemTechApi 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.getObjectSyncOrNull -import com.tangem.datasource.local.preferences.utils.storeObject -import com.tangem.datasource.local.token.ExpressAssetsStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.common.extensions.supportedBlockchains import com.tangem.domain.common.util.cardTypesResolver @@ -35,8 +24,6 @@ import com.tangem.domain.managetokens.model.ManagedCryptoCurrency import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork import com.tangem.domain.managetokens.repository.ManageTokensRepository import com.tangem.domain.tokens.model.Network -import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.domain.walletmanager.model.TokenInfo import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.pagination.BatchFetchResult @@ -44,8 +31,6 @@ import com.tangem.pagination.BatchListSource import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher import com.tangem.pagination.toBatchFlow import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.withContext -import timber.log.Timber @Suppress("LongParameterList") internal class DefaultManageTokensRepository( @@ -54,15 +39,9 @@ internal class DefaultManageTokensRepository( private val manageTokensUpdateFetcher: ManageTokensUpdateFetcher, private val appPreferencesStore: AppPreferencesStore, private val dispatchers: CoroutineDispatcherProvider, - private val walletManagerFacade: WalletManagersFacade, - private val tangemExpressApi: TangemExpressApi, - private val expressAssetsStore: ExpressAssetsStore, ) : ManageTokensRepository { private val managedCryptoCurrencyFactory = ManagedCryptoCurrencyFactory() - private val cryptoCurrencyFactory = CryptoCurrencyFactory() - private val userTokensResponseFactory = UserTokensResponseFactory() - private val userTokensBackwardCompatibility = UserTokensBackwardCompatibility() // region getTokenListBatchFlow override fun getTokenListBatchFlow( @@ -108,7 +87,7 @@ internal class DefaultManageTokensRepository( retryOnError(call = call) } - val tokensResponse = getStoredUserTokens(request.params.userWalletId) + val tokensResponse = request.params.userWalletId?.let { getSavedUserTokensResponseSync(it) } val items = if (isFirstBatchFetching && tokensResponse != null && userWallet != null && @@ -135,16 +114,6 @@ internal class DefaultManageTokensRepository( }, ) - private suspend fun getStoredUserTokens(userWalletId: UserWalletId?): UserTokensResponse? { - return if (userWalletId != null) { - appPreferencesStore.getObjectSyncOrNull( - key = PreferencesKeys.getUserTokensKey(userWalletId.stringValue), - ) - } else { - null - } - } - private suspend fun getUserWallet(userWalletId: UserWalletId): UserWallet { return requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { "Unable to find a user wallet with provided ID: $userWalletId" @@ -160,158 +129,44 @@ internal class DefaultManageTokensRepository( } // endregion - // region addManagedCurrencies - override suspend fun saveManagedCurrencies( + override suspend fun hasLinkedTokens( userWalletId: UserWalletId, - currenciesToAdd: Map>, - currenciesToRemove: Map>, - ) { - return withContext(dispatchers.io) { - val savedCurrencies = requireNotNull( - value = getSavedUserTokensResponseSync(key = userWalletId), - lazyMessage = { "Saved tokens empty. Can not perform add currencies action" }, - ) + network: Network, + tempAddedTokens: Map>, + tempRemovedTokens: Map>, + ): Boolean { + val addedTokens = tempAddedTokens.mapToResponseTokens() + val removedTokens = tempRemovedTokens.mapToResponseTokens() - val filteredTokens = currenciesToAdd.mapToTokenDataList() - .filterNot { tokenData -> - val blockchain = getBlockchain(networkId = tokenData.sourceNetwork.network.id) - val networkId = blockchain.toNetworkId() - val contractAddress = (tokenData.sourceNetwork as? SourceNetwork.Default)?.contractAddress - savedCurrencies.tokens.firstOrNull { token -> - token.contractAddress == contractAddress && - token.networkId == networkId && - token.derivationPath == tokenData.sourceNetwork.network.derivationPath.value - } != null - } - val newCoins = createCoinsForNewTokens( - userWalletId = userWalletId, - newTokensNetworks = filteredTokens.mapNotNull { tokenData -> - (tokenData.sourceNetwork as? SourceNetwork.Default)?.network - }, - savedCurrencies = savedCurrencies.tokens, - ) - val tokensToSave = filteredTokens.mapToUserResponseToken() - - val tokensToRemove = currenciesToRemove.mapToTokenDataList() - removeCurrenciesFromWalletManager(userWalletId = userWalletId, tokensDataList = tokensToRemove) - - val newCurrencies = (newCoins + tokensToSave).distinct() - val updatedResponse = savedCurrencies.copy( - tokens = savedCurrencies.tokens + newCurrencies - tokensToRemove.mapToUserResponseToken().toSet(), - ) - storeAndPushTokens(userWalletId = userWalletId, response = updatedResponse) - fetchExchangeableUserMarketCoinsByIds(userWalletId = userWalletId, userTokens = updatedResponse) - } - } - - private suspend fun storeAndPushTokens(userWalletId: UserWalletId, response: UserTokensResponse) { - val compatibleUserTokensResponse = userTokensBackwardCompatibility.applyCompatibilityAndGetUpdated(response) - appPreferencesStore.storeObject( - key = PreferencesKeys.getUserTokensKey(userWalletId = userWalletId.stringValue), - value = compatibleUserTokensResponse, + val storedTokens = requireNotNull( + value = getSavedUserTokensResponseSync(userWalletId), + lazyMessage = { "Unable to find tokens response for user wallet with provided ID: $userWalletId" }, ) + val newTokensList = storedTokens.tokens + addedTokens - removedTokens.toSet() - pushTokens(userWalletId, response) - } - - private suspend fun pushTokens(userWalletId: UserWalletId, response: UserTokensResponse) { - safeApiCall({ tangemTechApi.saveUserTokens(userWalletId.stringValue, response).bind() }) { - Timber.e(it, "Unable to save user tokens for: ${userWalletId.stringValue}") + return newTokensList.any { + it.contractAddress != null && + it.networkId == network.backendId && + it.derivationPath == network.derivationPath.value } } - private suspend fun fetchExchangeableUserMarketCoinsByIds( - userWalletId: UserWalletId, - userTokens: UserTokensResponse, - ) { - try { - val tokensList = userTokens.tokens - .map { - LeastTokenInfo( - contractAddress = it.contractAddress ?: TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE, - network = it.networkId, - ) - } - - if (tokensList.isNotEmpty()) { - val response = tangemExpressApi.getAssets( - AssetsRequestBody( - tokensList = tokensList, - ), - ) - - expressAssetsStore.store(userWalletId, response.getOrThrow()) - } - } catch (e: Throwable) { - Timber.e(e, "Unable to fetch assets for: ${userWalletId.stringValue}") - } - } - - private suspend fun removeCurrenciesFromWalletManager( - userWalletId: UserWalletId, - tokensDataList: List, - ) { - walletManagerFacade.removeTokensByTokenInfo( - userWalletId = userWalletId, - tokenInfos = tokensDataList.mapNotNullTo(hashSetOf()) { tokenData -> - val contractAddress = - (tokenData.sourceNetwork as? SourceNetwork.Default)?.contractAddress ?: return@mapNotNullTo null - TokenInfo( - network = tokenData.sourceNetwork.network, - name = tokenData.token.name, - symbol = tokenData.token.symbol, - contractAddress = contractAddress, - decimals = tokenData.sourceNetwork.decimals, - id = tokenData.token.id.value, - ) - }, - ) - walletManagerFacade.remove( - userWalletId = userWalletId, - networks = tokensDataList.mapNotNullTo(hashSetOf()) { tokenData -> - (tokenData.sourceNetwork as? SourceNetwork.Main)?.network - }, - ) - } - - private suspend fun createCoinsForNewTokens( - userWalletId: UserWalletId, - newTokensNetworks: List, - savedCurrencies: List, - ): List { - return newTokensNetworks - .filterNot { savedCurrencies.hasCoinForToken(it) } // tokens without coins - .mapNotNull { - cryptoCurrencyFactory.createCoin( - blockchain = getBlockchain(networkId = it.id), - extraDerivationPath = it.derivationPath.value, - derivationStyleProvider = getUserWallet(userWalletId).scanResponse.derivationStyleProvider, - ) - } - .distinct() - .map(userTokensResponseFactory::createResponseToken) - } - - private fun List.mapToUserResponseToken(): List { - return map { tokenData -> - val blockchain = getBlockchain(tokenData.sourceNetwork.network.id) - UserTokensResponse.Token( - id = tokenData.token.id.value, - networkId = blockchain.toNetworkId(), - derivationPath = tokenData.sourceNetwork.network.derivationPath.value, - name = tokenData.token.name, - symbol = tokenData.token.symbol, - decimals = tokenData.sourceNetwork.decimals, - contractAddress = (tokenData.sourceNetwork as? SourceNetwork.Default)?.contractAddress, - ) - } - } - - private fun Map>.mapToTokenDataList(): List { + private fun Map>.mapToResponseTokens(): List { return flatMap { (token, networks) -> token.availableNetworks .filter { sourceNetwork -> networks.contains(sourceNetwork.network) } - .map { sourceNetwork -> TokenWithSourceNetwork(token = token, sourceNetwork = sourceNetwork) } + .map { sourceNetwork -> + val blockchain = getBlockchain(sourceNetwork.network.id) + UserTokensResponse.Token( + id = token.id.value, + networkId = blockchain.toNetworkId(), + derivationPath = sourceNetwork.network.derivationPath.value, + name = token.name, + symbol = token.symbol, + decimals = sourceNetwork.decimals, + contractAddress = (sourceNetwork as? SourceNetwork.Default)?.contractAddress, + ) + } } } diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt index 64317a96e8..d5d9009ec3 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt @@ -3,10 +3,8 @@ package com.tangem.data.managetokens.di import com.tangem.data.managetokens.DefaultCustomTokensRepository import com.tangem.data.managetokens.DefaultManageTokensRepository import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher -import com.tangem.datasource.api.express.TangemExpressApi import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.local.preferences.AppPreferencesStore -import com.tangem.datasource.local.token.ExpressAssetsStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.managetokens.repository.CustomTokensRepository import com.tangem.domain.managetokens.repository.ManageTokensRepository @@ -30,9 +28,6 @@ internal object ManageTokensDataModule { manageTokensUpdateFetcher: ManageTokensUpdateFetcher, appPreferencesStore: AppPreferencesStore, dispatchers: CoroutineDispatcherProvider, - walletManagersFacade: WalletManagersFacade, - tangemExpressApi: TangemExpressApi, - expressAssetsStore: ExpressAssetsStore, ): ManageTokensRepository { return DefaultManageTokensRepository( tangemTechApi, @@ -40,9 +35,6 @@ internal object ManageTokensDataModule { manageTokensUpdateFetcher, appPreferencesStore, dispatchers, - walletManagersFacade, - tangemExpressApi, - expressAssetsStore, ) } diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt index 8fda73fff1..d66f067313 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt @@ -478,26 +478,6 @@ internal class DefaultCurrenciesRepository( ) ?: error("Unable to create token") } - override suspend fun hasTokens(userWalletId: UserWalletId, network: Network): Boolean { - val userWallet = getUserWallet(userWalletId) - fetchTokensIfCacheExpired(userWallet, refresh = false) - - val storedTokens = requireNotNull( - value = appPreferencesStore.getObjectSyncOrNull( - key = PreferencesKeys.getUserTokensKey(userWallet.walletId.stringValue), - ), - lazyMessage = { - "Unable to find tokens response for user wallet with provided ID: $userWalletId" - }, - ) - - return storedTokens.tokens.any { - it.contractAddress != null && - it.networkId == network.backendId && - it.derivationPath == network.derivationPath.value - } - } - @OptIn(ExperimentalCoroutinesApi::class) override fun getAllWalletsCryptoCurrencies(currencyRawId: String): Flow>> { return userWalletsStore.userWallets.flatMapLatest { userWallets -> diff --git a/domain/manage-tokens/build.gradle.kts b/domain/manage-tokens/build.gradle.kts index 51a0e5f6ab..5ed6875afa 100644 --- a/domain/manage-tokens/build.gradle.kts +++ b/domain/manage-tokens/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { api(projects.domain.core) implementation(projects.domain.wallets.models) implementation(projects.domain.tokens.models) + implementation(projects.domain.tokens) implementation(projects.domain.card) implementation(projects.domain.legacy) diff --git a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/CheckHasLinkedTokensUseCase.kt b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/CheckHasLinkedTokensUseCase.kt new file mode 100644 index 0000000000..c0acb6bffd --- /dev/null +++ b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/CheckHasLinkedTokensUseCase.kt @@ -0,0 +1,28 @@ +package com.tangem.domain.managetokens + +import arrow.core.Either +import com.tangem.domain.managetokens.model.ManagedCryptoCurrency +import com.tangem.domain.managetokens.repository.ManageTokensRepository +import com.tangem.domain.tokens.model.Network +import com.tangem.domain.wallets.models.UserWalletId + +class CheckHasLinkedTokensUseCase( + private val repository: ManageTokensRepository, +) { + + suspend operator fun invoke( + userWalletId: UserWalletId, + network: Network, + tempAddedTokens: Map>, + tempRemovedTokens: Map>, + ): Either { + return Either.catch { + repository.hasLinkedTokens( + userWalletId = userWalletId, + network = network, + tempAddedTokens = tempAddedTokens, + tempRemovedTokens = tempRemovedTokens, + ) + } + } +} \ No newline at end of file diff --git a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt index 5a6ad0a7ce..2db0a45d91 100644 --- a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt +++ b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt @@ -4,12 +4,19 @@ import arrow.core.Either import arrow.core.flatten import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.managetokens.model.ManagedCryptoCurrency -import com.tangem.domain.managetokens.repository.ManageTokensRepository +import com.tangem.domain.managetokens.repository.CustomTokensRepository +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network +import com.tangem.domain.tokens.repository.CurrenciesRepository +import com.tangem.domain.tokens.repository.NetworksRepository +import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWalletId class SaveManagedTokensUseCase( - private val manageTokensRepository: ManageTokensRepository, + private val customTokensRepository: CustomTokensRepository, + private val walletManagersFacade: WalletManagersFacade, + private val currenciesRepository: CurrenciesRepository, + private val networksRepository: NetworksRepository, private val derivationsRepository: DerivationsRepository, ) { @@ -19,15 +26,102 @@ class SaveManagedTokensUseCase( currenciesToRemove: Map>, ): Either { return Either.catch { - derivationsRepository.derivePublicKeysByNetworks( - userWalletId = userWalletId, - networks = currenciesToAdd.values.flatten(), - ) - manageTokensRepository.saveManagedCurrencies( - userWalletId = userWalletId, - currenciesToAdd = currenciesToAdd, - currenciesToRemove = currenciesToRemove, - ) + // TODO: Currently order is matter. [REDACTED_JIRA] + removeCurrencies(userWalletId, currenciesToRemove) + addCurrencies(userWalletId, currenciesToAdd) + } + } + + private suspend fun removeCurrencies( + userWalletId: UserWalletId, + currenciesToRemove: Map>, + ) { + if (currenciesToRemove.isEmpty()) return + + val currencies = currenciesToRemove.mapToCryptoCurrencies() + currenciesRepository.removeCurrencies(userWalletId = userWalletId, currencies = currencies) + + walletManagersFacade.remove( + userWalletId = userWalletId, + networks = currencies + .filterIsInstance() + .mapTo(hashSetOf(), CryptoCurrency::network), + ) + + walletManagersFacade.removeTokens( + userWalletId = userWalletId, + tokens = currencies.filterIsInstance().toSet(), + ) + } + + private suspend fun addCurrencies( + userWalletId: UserWalletId, + currenciesToAdd: Map>, + ) { + if (currenciesToAdd.isEmpty()) return + + val existingCurrencies = currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId) + val currencies = currenciesToAdd.mapToCryptoCurrencies() + derivationsRepository.derivePublicKeysByNetworks( + userWalletId = userWalletId, + networks = currenciesToAdd.values.flatten(), + ) + currenciesRepository.addCurrencies(userWalletId, currencies) + refreshUpdatedNetworks( + userWalletId = userWalletId, + existingCurrencies = existingCurrencies, + currenciesToAdd = currencies, + ) + } + + private suspend fun refreshUpdatedNetworks( + userWalletId: UserWalletId, + currenciesToAdd: List, + existingCurrencies: List, + ) { + val networksToUpdate = currenciesToAdd + .asSequence() + .filterIsInstance() + .map(CryptoCurrency.Token::network) + .filterTo(hashSetOf()) { hasCoinForNetwork(existingCurrencies, it) } + + val networkToUpdate = currenciesToAdd.map { it.network } + .subtract(existingCurrencies.map { it.network }.toSet()) + + networksRepository.getNetworkStatusesSync( + userWalletId = userWalletId, + networks = networksToUpdate + networkToUpdate, + refresh = true, + ) + } + + /** + * Determines if the [existingCurrencies] list contains a coin that corresponds + * to the given [network]. + */ + private fun hasCoinForNetwork(existingCurrencies: List, network: Network): Boolean { + return existingCurrencies.any { currency -> + currency is CryptoCurrency.Coin && currency.network == network + } + } + + private fun Map>.mapToCryptoCurrencies(): List { + return flatMap { (token, networks) -> + token.availableNetworks + .filter { sourceNetwork -> networks.contains(sourceNetwork.network) } + .map { sourceNetwork -> + when (sourceNetwork) { + is ManagedCryptoCurrency.SourceNetwork.Default -> customTokensRepository.createToken( + managedCryptoCurrency = token, + sourceNetwork = sourceNetwork, + rawId = token.id.value, + ) + is ManagedCryptoCurrency.SourceNetwork.Main -> customTokensRepository.createCoin( + networkId = sourceNetwork.id, + derivationPath = sourceNetwork.network.derivationPath, + ) + } + } } } } \ No newline at end of file diff --git a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/repository/CustomTokensRepository.kt b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/repository/CustomTokensRepository.kt index c0f898207e..eb45ba52ad 100644 --- a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/repository/CustomTokensRepository.kt +++ b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/repository/CustomTokensRepository.kt @@ -26,6 +26,12 @@ interface CustomTokensRepository { fun createCoin(networkId: Network.ID, derivationPath: Network.DerivationPath): CryptoCurrency.Coin + fun createToken( + managedCryptoCurrency: ManagedCryptoCurrency.Token, + sourceNetwork: ManagedCryptoCurrency.SourceNetwork.Default, + rawId: String?, + ): CryptoCurrency.Token + suspend fun createCustomToken( networkId: Network.ID, derivationPath: Network.DerivationPath, diff --git a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/repository/ManageTokensRepository.kt b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/repository/ManageTokensRepository.kt index fab91ff3bb..c977402d75 100644 --- a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/repository/ManageTokensRepository.kt +++ b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/repository/ManageTokensRepository.kt @@ -10,9 +10,10 @@ interface ManageTokensRepository { fun getTokenListBatchFlow(context: ManageTokensListBatchingContext, batchSize: Int): ManageTokensListBatchFlow - suspend fun saveManagedCurrencies( + suspend fun hasLinkedTokens( userWalletId: UserWalletId, - currenciesToAdd: Map>, - currenciesToRemove: Map>, - ) + network: Network, + tempAddedTokens: Map>, + tempRemovedTokens: Map>, + ): Boolean } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/CheckHasLinkedTokensUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/CheckHasLinkedTokensUseCase.kt deleted file mode 100644 index 503731cabc..0000000000 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/CheckHasLinkedTokensUseCase.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tangem.domain.tokens - -import arrow.core.Either -import com.tangem.domain.tokens.model.Network -import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.wallets.models.UserWalletId - -class CheckHasLinkedTokensUseCase( - private val currenciesRepository: CurrenciesRepository, -) { - - suspend operator fun invoke(userWalletId: UserWalletId, network: Network): Either { - return Either.catch { - currenciesRepository.hasTokens(userWalletId, network) - } - } -} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt index dcf638ef97..f2578bd4c4 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt @@ -234,8 +234,6 @@ interface CurrenciesRepository { networkId: String, ): CryptoCurrency.Token - suspend fun hasTokens(userWalletId: UserWalletId, network: Network): Boolean - /** Get crypto currencies by [currencyRawId] from all user wallets */ fun getAllWalletsCryptoCurrencies(currencyRawId: String): Flow>> } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt index 317a1a3dbe..68d9c77d7f 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt @@ -150,10 +150,6 @@ internal class MockCurrenciesRepository( error("not implemented") } - override suspend fun hasTokens(userWalletId: UserWalletId, network: Network): Boolean { - return false - } - override fun getAllWalletsCryptoCurrencies(currencyRawId: String): Flow>> { return emptyFlow() } diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListManager.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListManager.kt index 565bb8de58..3282da26fe 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListManager.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListManager.kt @@ -12,7 +12,7 @@ import com.tangem.domain.managetokens.model.ManageTokensListBatchingContext import com.tangem.domain.managetokens.model.ManageTokensListConfig import com.tangem.domain.managetokens.model.ManageTokensUpdateAction import com.tangem.domain.managetokens.model.ManagedCryptoCurrency -import com.tangem.domain.tokens.CheckHasLinkedTokensUseCase +import com.tangem.domain.managetokens.CheckHasLinkedTokensUseCase import com.tangem.domain.tokens.model.Network import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.managetokens.entity.item.CurrencyItemUM @@ -198,7 +198,12 @@ internal class ManageTokensListManager @Inject constructor( } override suspend fun checkHasLinkedTokens(userWalletId: UserWalletId, network: Network): Boolean { - return checkHasLinkedTokensUseCase(userWalletId, network).getOrElse { + return checkHasLinkedTokensUseCase( + userWalletId = userWalletId, + network = network, + tempAddedTokens = changedCurrenciesManager.currenciesToAdd.value, + tempRemovedTokens = changedCurrenciesManager.currenciesToRemove.value, + ).getOrElse { Timber.e( it, """