Updated on 2026-08-14
This commit is contained in:
parent
57d3a102ab
commit
c501b56063
23 changed files with 296 additions and 256 deletions
|
|
@ -26,6 +26,7 @@ dependencies {
|
|||
api(projects.domain.referral)
|
||||
api(projects.domain.staking)
|
||||
api(projects.domain.tokens)
|
||||
api(projects.domain.walletManager)
|
||||
api(projects.domain.wallets)
|
||||
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@
|
|||
<ID>MultilineLambdaItParameter:AccountCryptoCurrencyStatusFinder.kt$AccountCryptoCurrencyStatusFinder${ val currency = it.currency val isContractAddressMatch = contractAddress == null || currency.id.contractAddress.equals(contractAddress, ignoreCase = true) currency.network.rawId == networkId.rawId.value && currency.network.derivationPath.value == derivationPath.value && isContractAddressMatch }</ID>
|
||||
<ID>MultilineLambdaItParameter:ApplyTokenListSortingUseCaseV2.kt$ApplyTokenListSortingUseCaseV2${ errors[account.accountId] = it return@map account }</ID>
|
||||
<ID>MultilineLambdaItParameter:DefaultMultiAccountStatusListProducer.kt$DefaultMultiAccountStatusListProducer${ singleAccountStatusListSupplier( params = SingleAccountStatusListProducer.Params(it.walletId), ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:ManageCryptoCurrenciesUseCase.kt$ManageCryptoCurrenciesUseCase${ ExpressAsset.ID( networkId = it.network.backendId, contractAddress = (it as? CryptoCurrency.Token)?.contractAddress, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:ManageCryptoCurrenciesUseCase.kt$ManageCryptoCurrenciesUseCase${ it.network.backendId == networkId && !it.isCustom && it.contractAddress.equals(contractAddress, true) }</ID>
|
||||
<ID>UnnecessaryAbstractClass:MultiAccountStatusListSupplier.kt$MultiAccountStatusListSupplier$MultiAccountStatusListSupplier</ID>
|
||||
<ID>UnnecessaryAbstractClass:SingleAccountStatusListSupplier.kt$SingleAccountStatusListSupplier$SingleAccountStatusListSupplier</ID>
|
||||
<ID>UnnecessaryAbstractClass:SingleAccountStatusSupplier.kt$SingleAccountStatusSupplier$SingleAccountStatusSupplier</ID>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
|
|||
import com.tangem.domain.staking.utils.StakingCleaner
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.derivations.DerivationsRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -95,6 +96,7 @@ internal object AccountStatusUseCaseModule {
|
|||
accountsCRUDRepository: AccountsCRUDRepository,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
derivationsRepository: DerivationsRepository,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher,
|
||||
stakingIdFactory: StakingIdFactory,
|
||||
networksCleaner: NetworksCleaner,
|
||||
|
|
@ -107,6 +109,7 @@ internal object AccountStatusUseCaseModule {
|
|||
accountsCRUDRepository = accountsCRUDRepository,
|
||||
currenciesRepository = currenciesRepository,
|
||||
derivationsRepository = derivationsRepository,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
cryptoCurrencyBalanceFetcher = cryptoCurrencyBalanceFetcher,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
networksCleaner = networksCleaner,
|
||||
|
|
@ -120,7 +123,6 @@ internal object AccountStatusUseCaseModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideCryptoCurrencyBalanceFetcher(
|
||||
accountsCRUDRepository: AccountsCRUDRepository,
|
||||
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
|
||||
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
|
|
@ -128,7 +130,6 @@ internal object AccountStatusUseCaseModule {
|
|||
dispatchers: CoroutineDispatcherProvider,
|
||||
): CryptoCurrencyBalanceFetcher {
|
||||
return CryptoCurrencyBalanceFetcher(
|
||||
accountsCRUDRepository = accountsCRUDRepository,
|
||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
||||
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
|
||||
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
|
||||
|
|
|
|||
|
|
@ -21,8 +21,10 @@ import com.tangem.domain.networks.utils.NetworksCleaner
|
|||
import com.tangem.domain.staking.StakingIdFactory
|
||||
import com.tangem.domain.staking.utils.StakingCleaner
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.derivations.DerivationsRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.runSuspendCatching
|
||||
import kotlinx.coroutines.*
|
||||
import timber.log.Timber
|
||||
|
||||
|
|
@ -49,6 +51,7 @@ class ManageCryptoCurrenciesUseCase(
|
|||
private val accountsCRUDRepository: AccountsCRUDRepository,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val derivationsRepository: DerivationsRepository,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher,
|
||||
private val stakingIdFactory: StakingIdFactory,
|
||||
private val networksCleaner: NetworksCleaner,
|
||||
|
|
@ -83,6 +86,11 @@ class ManageCryptoCurrenciesUseCase(
|
|||
val modifiedCurrencyList = accountStatus.tokenList.flattenCurrencies()
|
||||
.modify(add = add, remove = remove)
|
||||
|
||||
if (!modifiedCurrencyList.hasChanges) {
|
||||
Timber.d("No changes in currencies, skipping")
|
||||
return@withContext
|
||||
}
|
||||
|
||||
saveAccount(
|
||||
account = accountStatus.account.copy(cryptoCurrencies = modifiedCurrencyList.total.toSet()),
|
||||
)
|
||||
|
|
@ -90,17 +98,7 @@ class ManageCryptoCurrenciesUseCase(
|
|||
derivePublicKeys(userWalletId = userWalletId, currencies = modifiedCurrencyList.added)
|
||||
|
||||
parallelUpdatingScope.launch {
|
||||
/*
|
||||
* If only removal of currencies happened, we need to sync tokens. Otherwise, tokens will be synced
|
||||
* when balances are refreshed for added currencies.
|
||||
*/
|
||||
val isOnlyRemoval = modifiedCurrencyList.added.isEmpty() && modifiedCurrencyList.removed.isNotEmpty()
|
||||
|
||||
if (isOnlyRemoval) {
|
||||
launch { accountsCRUDRepository.syncTokens(userWalletId) }
|
||||
clearMetadata(userWalletId = userWalletId, currencies = modifiedCurrencyList.removed)
|
||||
return@launch
|
||||
}
|
||||
syncTokens(userWalletId, modifiedCurrencyList)
|
||||
|
||||
cryptoCurrencyBalanceFetcher(userWalletId = userWalletId, currencies = modifiedCurrencyList.added)
|
||||
refreshExpress(userWalletId = userWalletId, currencies = modifiedCurrencyList.total)
|
||||
|
|
@ -121,10 +119,10 @@ class ManageCryptoCurrenciesUseCase(
|
|||
|
||||
val foundToken = accountStatus.tokenList.flattenCurrencies()
|
||||
.mapNotNull { it.currency as? CryptoCurrency.Token }
|
||||
.firstOrNull {
|
||||
it.network.backendId == networkId &&
|
||||
!it.isCustom &&
|
||||
it.contractAddress.equals(contractAddress, true)
|
||||
.firstOrNull { token ->
|
||||
token.network.backendId == networkId &&
|
||||
!token.isCustom &&
|
||||
token.contractAddress.equals(contractAddress, true)
|
||||
}
|
||||
|
||||
if (foundToken != null) return@withContext foundToken
|
||||
|
|
@ -137,6 +135,8 @@ class ManageCryptoCurrenciesUseCase(
|
|||
saveAccount(account = accountStatus.account.copy(cryptoCurrencies = modifiedCurrencyList.total.toSet()))
|
||||
|
||||
parallelUpdatingScope.launch {
|
||||
syncTokens(userWalletId, modifiedCurrencyList)
|
||||
|
||||
cryptoCurrencyBalanceFetcher(userWalletId = userWalletId, currencies = listOf(tokenToAdd))
|
||||
refreshExpress(userWalletId = userWalletId, currencies = modifiedCurrencyList.total)
|
||||
}
|
||||
|
|
@ -260,15 +260,40 @@ class ManageCryptoCurrenciesUseCase(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun syncTokens(userWalletId: UserWalletId, modifiedCurrencyList: ModifiedCurrencyList) {
|
||||
createWalletManagers(userWalletId = userWalletId, currencies = modifiedCurrencyList.added)
|
||||
|
||||
runSuspendCatching { accountsCRUDRepository.syncTokens(userWalletId) }
|
||||
.onFailure { Timber.e(it, "Failed to sync tokens for wallet $userWalletId") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates wallet managers for the given [currencies] if they do not already exist.
|
||||
* The method will generate addresses for new networks to ensure the stability of the "Push notifications" feature.
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param currencies The list of cryptocurrencies for which to create wallet managers.
|
||||
*/
|
||||
private suspend fun createWalletManagers(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||
val networks = currencies.mapTo(hashSetOf(), CryptoCurrency::network)
|
||||
|
||||
for (network in networks) {
|
||||
runSuspendCatching {
|
||||
walletManagersFacade.getOrCreateWalletManager(userWalletId = userWalletId, network = network)
|
||||
}
|
||||
.onFailure { Timber.e(it, "Failed to create wallet manager for network ${network.id}") }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun refreshExpress(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||
if (currencies.isEmpty()) return
|
||||
|
||||
coroutineScope {
|
||||
launch {
|
||||
val assetIds = currencies.mapTo(hashSetOf()) {
|
||||
val assetIds = currencies.mapTo(hashSetOf()) { currency ->
|
||||
ExpressAsset.ID(
|
||||
networkId = it.network.backendId,
|
||||
contractAddress = (it as? CryptoCurrency.Token)?.contractAddress,
|
||||
networkId = currency.network.backendId,
|
||||
contractAddress = (currency as? CryptoCurrency.Token)?.contractAddress,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -325,5 +350,8 @@ class ManageCryptoCurrenciesUseCase(
|
|||
val added: List<CryptoCurrency>,
|
||||
val removed: List<CryptoCurrency>,
|
||||
val total: List<CryptoCurrency>,
|
||||
)
|
||||
) {
|
||||
|
||||
val hasChanges get() = added.isNotEmpty() || removed.isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package com.tangem.domain.account.status.utils
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
||||
|
|
@ -19,7 +17,6 @@ import timber.log.Timber
|
|||
* Utility class responsible for fetching and refreshing the balances of various crypto currencies
|
||||
* associated with a user's wallet.
|
||||
*
|
||||
* @property accountsCRUDRepository Repository for managing account data.
|
||||
* @property multiNetworkStatusFetcher Fetcher for updating network statuses.
|
||||
* @property multiQuoteStatusFetcher Fetcher for updating quote statuses.
|
||||
* @property multiYieldBalanceFetcher Fetcher for updating yield balances.
|
||||
|
|
@ -29,7 +26,6 @@ import timber.log.Timber
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class CryptoCurrencyBalanceFetcher(
|
||||
private val accountsCRUDRepository: AccountsCRUDRepository,
|
||||
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
|
||||
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
|
|
@ -80,22 +76,13 @@ class CryptoCurrencyBalanceFetcher(
|
|||
private suspend fun refreshNetworks(
|
||||
userWalletId: UserWalletId,
|
||||
currencies: List<CryptoCurrency>,
|
||||
): Either<Throwable, Unit> = either {
|
||||
val either = multiNetworkStatusFetcher(
|
||||
): Either<Throwable, Unit> {
|
||||
return multiNetworkStatusFetcher(
|
||||
params = MultiNetworkStatusFetcher.Params(
|
||||
userWalletId = userWalletId,
|
||||
networks = currencies.mapTo(hashSetOf(), CryptoCurrency::network),
|
||||
),
|
||||
)
|
||||
|
||||
arrow.core.raise.catch(
|
||||
block = { accountsCRUDRepository.syncTokens(userWalletId) },
|
||||
catch = {
|
||||
Timber.e(it, "Failed to sync tokens for wallet: $userWalletId")
|
||||
},
|
||||
)
|
||||
|
||||
return either
|
||||
}
|
||||
|
||||
private suspend fun refreshYieldBalances(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue