Updated on 2026-08-14
This commit is contained in:
parent
f55f6f8461
commit
0fdcdda8d5
15 changed files with 213 additions and 250 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -431,12 +431,6 @@ internal object TokensDomainModule {
|
|||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCheckHasLinkedTokensUseCase(currenciesRepository: CurrenciesRepository): CheckHasLinkedTokensUseCase {
|
||||
return CheckHasLinkedTokensUseCase(currenciesRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetCurrencyCheckUseCase(currencyChecksRepository: CurrencyChecksRepository): GetCurrencyCheckUseCase {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<ManagedCryptoCurrency.Token, Set<Network>>,
|
||||
currenciesToRemove: Map<ManagedCryptoCurrency.Token, Set<Network>>,
|
||||
) {
|
||||
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<ManagedCryptoCurrency.Token, Set<Network>>,
|
||||
tempRemovedTokens: Map<ManagedCryptoCurrency.Token, Set<Network>>,
|
||||
): 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<TokenWithSourceNetwork>,
|
||||
) {
|
||||
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<Network>,
|
||||
savedCurrencies: List<UserTokensResponse.Token>,
|
||||
): List<UserTokensResponse.Token> {
|
||||
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<TokenWithSourceNetwork>.mapToUserResponseToken(): List<UserTokensResponse.Token> {
|
||||
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<ManagedCryptoCurrency.Token, Set<Network>>.mapToTokenDataList(): List<TokenWithSourceNetwork> {
|
||||
private fun Map<ManagedCryptoCurrency.Token, Set<Network>>.mapToResponseTokens(): List<UserTokensResponse.Token> {
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<UserTokensResponse>(
|
||||
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<Map<UserWallet, List<CryptoCurrency>>> {
|
||||
return userWalletsStore.userWallets.flatMapLatest { userWallets ->
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ManagedCryptoCurrency.Token, Set<Network>>,
|
||||
tempRemovedTokens: Map<ManagedCryptoCurrency.Token, Set<Network>>,
|
||||
): Either<Throwable, Boolean> {
|
||||
return Either.catch {
|
||||
repository.hasLinkedTokens(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
tempAddedTokens = tempAddedTokens,
|
||||
tempRemovedTokens = tempRemovedTokens,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ManagedCryptoCurrency.Token, Set<Network>>,
|
||||
): Either<Throwable, Unit> {
|
||||
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<ManagedCryptoCurrency.Token, Set<Network>>,
|
||||
) {
|
||||
if (currenciesToRemove.isEmpty()) return
|
||||
|
||||
val currencies = currenciesToRemove.mapToCryptoCurrencies()
|
||||
currenciesRepository.removeCurrencies(userWalletId = userWalletId, currencies = currencies)
|
||||
|
||||
walletManagersFacade.remove(
|
||||
userWalletId = userWalletId,
|
||||
networks = currencies
|
||||
.filterIsInstance<CryptoCurrency.Coin>()
|
||||
.mapTo(hashSetOf(), CryptoCurrency::network),
|
||||
)
|
||||
|
||||
walletManagersFacade.removeTokens(
|
||||
userWalletId = userWalletId,
|
||||
tokens = currencies.filterIsInstance<CryptoCurrency.Token>().toSet(),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun addCurrencies(
|
||||
userWalletId: UserWalletId,
|
||||
currenciesToAdd: Map<ManagedCryptoCurrency.Token, Set<Network>>,
|
||||
) {
|
||||
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<CryptoCurrency>,
|
||||
existingCurrencies: List<CryptoCurrency>,
|
||||
) {
|
||||
val networksToUpdate = currenciesToAdd
|
||||
.asSequence()
|
||||
.filterIsInstance<CryptoCurrency.Token>()
|
||||
.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<CryptoCurrency>, network: Network): Boolean {
|
||||
return existingCurrencies.any { currency ->
|
||||
currency is CryptoCurrency.Coin && currency.network == network
|
||||
}
|
||||
}
|
||||
|
||||
private fun Map<ManagedCryptoCurrency.Token, Set<Network>>.mapToCryptoCurrencies(): List<CryptoCurrency> {
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ interface ManageTokensRepository {
|
|||
|
||||
fun getTokenListBatchFlow(context: ManageTokensListBatchingContext, batchSize: Int): ManageTokensListBatchFlow
|
||||
|
||||
suspend fun saveManagedCurrencies(
|
||||
suspend fun hasLinkedTokens(
|
||||
userWalletId: UserWalletId,
|
||||
currenciesToAdd: Map<ManagedCryptoCurrency.Token, Set<Network>>,
|
||||
currenciesToRemove: Map<ManagedCryptoCurrency.Token, Set<Network>>,
|
||||
)
|
||||
network: Network,
|
||||
tempAddedTokens: Map<ManagedCryptoCurrency.Token, Set<Network>>,
|
||||
tempRemovedTokens: Map<ManagedCryptoCurrency.Token, Set<Network>>,
|
||||
): Boolean
|
||||
}
|
||||
|
|
@ -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<Throwable, Boolean> {
|
||||
return Either.catch {
|
||||
currenciesRepository.hasTokens(userWalletId, network)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Map<UserWallet, List<CryptoCurrency>>>
|
||||
}
|
||||
|
|
@ -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<Map<UserWallet, List<CryptoCurrency>>> {
|
||||
return emptyFlow()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue