Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-13 14:22:04 +04:00
parent 5b832e2591
commit 69deadf5fe
3 changed files with 147 additions and 52 deletions

View file

@ -3,19 +3,14 @@ package com.tangem.domain.account.status.usecase
import arrow.core.Option
import arrow.core.none
import arrow.core.toOption
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.account.status.utils.AccountCryptoCurrencyStatusFinder
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.lib.crypto.derivation.AccountNodeRecognizer
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.mapNotNull
@ -100,51 +95,10 @@ class GetAccountCurrencyStatusUseCase(
currencyId: CryptoCurrency.ID,
network: Network?,
): AccountCryptoCurrencyStatus? {
return getExpectedAccountStatuses(network)
.asSequence()
.filterIsInstance<AccountStatus.CryptoPortfolio>()
.mapNotNull { accountStatus ->
val status = accountStatus.flattenCurrencies().firstOrNull { it.currency.id == currencyId }
?: return@mapNotNull null
AccountCryptoCurrencyStatus(account = accountStatus.account, status = status)
}
.firstOrNull()
}
/**
* Retrieves the expected account statuses based on the provided [network].
* If the [network] is null, all account statuses are returned.
* If the network has a specific derivation index, it filters the accounts accordingly.
*
* @param network the network to filter accounts by, can be null.
* @return a set of [AccountStatus] that match the expected criteria.
*/
private fun AccountStatusList.getExpectedAccountStatuses(network: Network?): Set<AccountStatus> {
val possibleAccountIndex = network?.getAccountIndexOrNull()
return when (possibleAccountIndex) {
// currency can be in any account
null -> accountStatuses
// currency only in the main account
DerivationIndex.Main.value -> setOf(mainAccount)
// currency only in the account with specific derivation index or in the main account
else -> {
val accountStatus = accountStatuses.firstOrNull {
val cryptoPortfolio = it.account as? Account.CryptoPortfolio ?: return@firstOrNull false
cryptoPortfolio.derivationIndex.value == possibleAccountIndex
}
setOfNotNull(accountStatus, mainAccount)
}
}
}
private fun Network.getAccountIndexOrNull(): Int? {
val blockchain = Blockchain.fromNetworkId(networkId = rawId) ?: return null
val recognizer = AccountNodeRecognizer(blockchain)
return recognizer.recognize(derivationPath)?.toInt()
return AccountCryptoCurrencyStatusFinder(
accountStatusList = this,
currencyId = currencyId,
network = network,
)
}
}

View file

@ -0,0 +1,53 @@
package com.tangem.domain.account.status.usecase
import com.tangem.domain.account.repository.AccountsCRUDRepository
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.utils.AccountCryptoCurrencyStatusFinder
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.model.TokenActionsState
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.transformLatest
/**
* Use case to retrieve the available actions for a specific cryptocurrency associated with an account.
*
* @property accountsCRUDRepository repository for account CRUD operations.
* @property singleAccountStatusListSupplier supplier to get the list of account statuses.
* @property getCryptoCurrencyActionsUseCase use case to get the actions for a specific cryptocurrency status.
*
[REDACTED_AUTHOR]
*/
class GetCryptoCurrencyActionsUseCaseV2(
private val accountsCRUDRepository: AccountsCRUDRepository,
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
) {
@OptIn(ExperimentalCoroutinesApi::class)
operator fun invoke(accountId: AccountId, currency: CryptoCurrency): Flow<TokenActionsState> {
return singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId = accountId.userWalletId),
)
.transformLatest { accountStatusList ->
val accountCurrencyStatus = AccountCryptoCurrencyStatusFinder(
accountStatusList = accountStatusList,
currency = currency,
)
if (accountCurrencyStatus != null) {
val userWallet = accountsCRUDRepository.getUserWallet(userWalletId = accountId.userWalletId)
val actionsFlow = getCryptoCurrencyActionsUseCase(
userWallet = userWallet,
cryptoCurrencyStatus = accountCurrencyStatus.status,
)
emitAll(actionsFlow)
}
}
}
}

View file

@ -0,0 +1,88 @@
package com.tangem.domain.account.status.utils
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.lib.crypto.derivation.AccountNodeRecognizer
/**
* Finds the status of a specific cryptocurrency associated with an account from the provided account status list.
*
[REDACTED_AUTHOR]
*/
internal object AccountCryptoCurrencyStatusFinder {
/**
* Retrieves the [AccountCryptoCurrencyStatus] for the specified [currency] from the given [accountStatusList].
*
* @param accountStatusList the list of account statuses to search within.
* @param currency the cryptocurrency whose status is to be retrieved.
* @return the [AccountCryptoCurrencyStatus] if found, otherwise null.
*/
operator fun invoke(accountStatusList: AccountStatusList, currency: CryptoCurrency): AccountCryptoCurrencyStatus? {
return invoke(
accountStatusList = accountStatusList,
currencyId = currency.id,
network = currency.network,
)
}
operator fun invoke(
accountStatusList: AccountStatusList,
currencyId: CryptoCurrency.ID,
network: Network?,
): AccountCryptoCurrencyStatus? {
return accountStatusList.getExpectedAccountStatuses(network)
.asSequence()
.filterIsInstance<AccountStatus.CryptoPortfolio>()
.mapNotNull { accountStatus ->
val status = accountStatus.flattenCurrencies().firstOrNull { it.currency.id == currencyId }
?: return@mapNotNull null
AccountCryptoCurrencyStatus(account = accountStatus.account, status = status)
}
.firstOrNull()
}
/**
* Retrieves the expected account statuses based on the provided [network].
* If the [network] is null, all account statuses are returned.
* If the network has a specific derivation index, it filters the accounts accordingly.
*
* @param network the network to filter accounts by, can be null.
* @return a set of [AccountStatus] that match the expected criteria.
*/
private fun AccountStatusList.getExpectedAccountStatuses(network: Network?): Set<AccountStatus> {
val possibleAccountIndex = network?.getAccountIndexOrNull()
return when (possibleAccountIndex) {
// currency can be in any account
null -> accountStatuses
// currency only in the main account
DerivationIndex.Main.value -> setOf(mainAccount)
// currency only in the account with specific derivation index or in the main account
else -> {
val accountStatus = accountStatuses.firstOrNull {
val cryptoPortfolio = it.account as? Account.CryptoPortfolio ?: return@firstOrNull false
cryptoPortfolio.derivationIndex.value == possibleAccountIndex
}
setOfNotNull(accountStatus, mainAccount)
}
}
}
private fun Network.getAccountIndexOrNull(): Int? {
val blockchain = Blockchain.fromNetworkId(networkId = rawId) ?: return null
val recognizer = AccountNodeRecognizer(blockchain)
return recognizer.recognize(derivationPath)?.toInt()
}
}