Updated on 2026-08-14
This commit is contained in:
commit
20279dcc7a
602 changed files with 18887 additions and 6569 deletions
|
|
@ -21,4 +21,10 @@ abstract class SingleAccountListSupplier(
|
|||
params = SingleAccountListProducer.Params(userWalletId = userWalletId),
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun getSyncOrNull(userWalletId: UserWalletId): AccountList? {
|
||||
return getSyncOrNull(
|
||||
params = SingleAccountListProducer.Params(userWalletId = userWalletId),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ dependencies {
|
|||
api(projects.domain.referral)
|
||||
api(projects.domain.staking)
|
||||
api(projects.domain.tokens)
|
||||
api(projects.domain.tokens.models)
|
||||
api(projects.domain.walletManager)
|
||||
api(projects.domain.wallets)
|
||||
|
||||
|
|
@ -36,6 +37,7 @@ dependencies {
|
|||
implementation(deps.kotlin.datetime)
|
||||
implementation(deps.kotlin.serialization)
|
||||
implementation(deps.timber)
|
||||
implementation(deps.kermit)
|
||||
|
||||
implementation(tangemDeps.blockchain)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.domain.account.status.di
|
||||
|
||||
import com.tangem.domain.account.status.producer.*
|
||||
import com.tangem.domain.core.flow.FlowProducerScope
|
||||
import com.tangem.domain.core.flow.FlowProducerTools
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
|
@ -28,8 +27,4 @@ internal interface AccountStatusListProducerFactoryModule {
|
|||
@Binds
|
||||
@Singleton
|
||||
fun bindDefaultFlowProducerTools(impl: DefaultFlowProducerTools): FlowProducerTools
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindFlowProducerScope(impl: DefaultFlowProducerAppScope): FlowProducerScope
|
||||
}
|
||||
|
|
@ -21,13 +21,12 @@ 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.AppCoroutineScope
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
|
|
@ -76,10 +75,10 @@ internal object AccountStatusUseCaseModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetWalletTotalBalanceUseCaseV2(
|
||||
fun provideGetWalletTotalBalanceUseCase(
|
||||
multiAccountStatusListSupplier: MultiAccountStatusListSupplier,
|
||||
): GetWalletTotalBalanceUseCaseV2 {
|
||||
return GetWalletTotalBalanceUseCaseV2(
|
||||
): GetWalletTotalBalanceUseCase {
|
||||
return GetWalletTotalBalanceUseCase(
|
||||
multiAccountStatusListSupplier = multiAccountStatusListSupplier,
|
||||
)
|
||||
}
|
||||
|
|
@ -104,6 +103,7 @@ internal object AccountStatusUseCaseModule {
|
|||
cryptoCurrencyMetadataCleaner: CryptoCurrencyMetadataCleaner,
|
||||
expressServiceFetcher: ExpressServiceFetcher,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
appScope: AppCoroutineScope,
|
||||
): ManageCryptoCurrenciesUseCase {
|
||||
return ManageCryptoCurrenciesUseCase(
|
||||
singleAccountStatusListSupplier = singleAccountStatusListSupplier,
|
||||
|
|
@ -114,7 +114,7 @@ internal object AccountStatusUseCaseModule {
|
|||
cryptoCurrencyBalanceFetcher = cryptoCurrencyBalanceFetcher,
|
||||
cryptoCurrencyMetadataCleaner = cryptoCurrencyMetadataCleaner,
|
||||
expressServiceFetcher = expressServiceFetcher,
|
||||
parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.default),
|
||||
parallelUpdatingScope = appScope,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
|
@ -126,14 +126,14 @@ internal object AccountStatusUseCaseModule {
|
|||
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
|
||||
multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
|
||||
stakingIdFactory: StakingIdFactory,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
appScope: AppCoroutineScope,
|
||||
): CryptoCurrencyBalanceFetcher {
|
||||
return CryptoCurrencyBalanceFetcher(
|
||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
||||
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
|
||||
multiStakingBalanceFetcher = multiStakingBalanceFetcher,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.default),
|
||||
parallelUpdatingScope = appScope,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -157,6 +157,18 @@ internal object AccountStatusUseCaseModule {
|
|||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetFeePaidCryptoCurrencyStatusSyncUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
): GetFeePaidCryptoCurrencyStatusSyncUseCase {
|
||||
return GetFeePaidCryptoCurrencyStatusSyncUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
singleAccountStatusListSupplier = singleAccountStatusListSupplier,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCryptoCurrencyMetadataCleaner(
|
||||
|
|
|
|||
|
|
@ -1,54 +1,19 @@
|
|||
package com.tangem.domain.account.status.producer
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.domain.core.flow.FlowProducer
|
||||
import com.tangem.domain.core.flow.FlowProducerScope
|
||||
import com.tangem.domain.core.flow.FlowProducerTools
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class DefaultFlowProducerAppScope @Inject constructor(
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
) : FlowProducerScope {
|
||||
|
||||
private val tag = "FlowProducerScope"
|
||||
|
||||
override val coroutineContext: CoroutineContext = SupervisorJob() +
|
||||
dispatchers.default +
|
||||
CoroutineName(tag) +
|
||||
CoroutineExceptionHandler { context, throwable ->
|
||||
@Suppress("NullableToStringCall")
|
||||
val coroutineName = context[CoroutineName]?.name.toString()
|
||||
logError(throwable, coroutineName)
|
||||
}
|
||||
|
||||
private fun logError(throwable: Throwable, coroutineName: String) {
|
||||
Timber.tag("FlowProducerExceptionHandler").e(
|
||||
throwable,
|
||||
"CoroutineName $coroutineName",
|
||||
)
|
||||
val event = ExceptionAnalyticsEvent(
|
||||
exception = throwable,
|
||||
params = mapOf(
|
||||
"source" to tag,
|
||||
"coroutineName" to coroutineName,
|
||||
),
|
||||
)
|
||||
analyticsExceptionHandler.sendException(event)
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultFlowProducerTools @Inject constructor(
|
||||
private val scope: FlowProducerScope,
|
||||
private val scope: AppCoroutineScope,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
) : FlowProducerTools {
|
||||
|
||||
|
|
@ -72,6 +37,7 @@ class DefaultFlowProducerTools @Inject constructor(
|
|||
}
|
||||
|
||||
return upstream
|
||||
.flowOn(dispatchers.default)
|
||||
.distinctUntilChanged()
|
||||
.shareIn(
|
||||
scope = scope,
|
||||
|
|
@ -89,8 +55,8 @@ class DefaultFlowProducerTools @Inject constructor(
|
|||
|
||||
private fun logError(cause: Throwable, flowProducerName: String, attempt: Long) {
|
||||
val tag = "FlowProducerRetryWhen"
|
||||
Timber.tag(tag)
|
||||
.e(cause, "flowProducerName $flowProducerName attempt $attempt")
|
||||
Logger.withTag(tag)
|
||||
.e("flowProducerName $flowProducerName attempt $attempt", cause)
|
||||
|
||||
val event = ExceptionAnalyticsEvent(
|
||||
exception = cause,
|
||||
|
|
|
|||
|
|
@ -20,4 +20,9 @@ abstract class SingleAccountStatusListSupplier(
|
|||
val params = SingleAccountStatusListProducer.Params(userWalletId)
|
||||
return this.invoke(params)
|
||||
}
|
||||
|
||||
suspend fun getSyncOrNull(userWalletId: UserWalletId): AccountStatusList? {
|
||||
val params = SingleAccountStatusListProducer.Params(userWalletId)
|
||||
return this.getSyncOrNull(params)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.tangem.domain.account.status.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCoinStatus
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.FeePaidCurrency
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
|
||||
class GetFeePaidCryptoCurrencyStatusSyncUseCase(
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
): Either<TokenListError, CryptoCurrencyStatus?> {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val network = cryptoCurrency.network
|
||||
val feePaidCurrency = currenciesRepository.getFeePaidCurrency(userWalletId, network)
|
||||
|
||||
return either {
|
||||
when (feePaidCurrency) {
|
||||
is FeePaidCurrency.Coin -> {
|
||||
val accountStatusList = singleAccountStatusListSupplier.getSyncOrNull(userWalletId)
|
||||
|
||||
accountStatusList?.getCoinStatus(network)?.getOrNull()
|
||||
}
|
||||
is FeePaidCurrency.Token -> {
|
||||
val accountStatusList = singleAccountStatusListSupplier.getSyncOrNull(userWalletId)
|
||||
|
||||
accountStatusList.getCryptoCurrencyStatus(
|
||||
currencyId = feePaidCurrency.tokenId,
|
||||
network = network,
|
||||
).getOrNull()
|
||||
}
|
||||
is FeePaidCurrency.SameCurrency,
|
||||
is FeePaidCurrency.FeeResource,
|
||||
-> cryptoCurrencyStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ import kotlinx.coroutines.flow.map
|
|||
*
|
||||
* @param multiAccountStatusListSupplier Supplier that provides the status list for multiple accounts.
|
||||
*/
|
||||
class GetWalletTotalBalanceUseCaseV2(
|
||||
class GetWalletTotalBalanceUseCase(
|
||||
private val multiAccountStatusListSupplier: MultiAccountStatusListSupplier,
|
||||
) {
|
||||
|
||||
|
|
@ -8,6 +8,7 @@ import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
|||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyMetadataCleaner
|
||||
import com.tangem.utils.coroutines.AppCoroutineScope
|
||||
import com.tangem.domain.core.utils.eitherOn
|
||||
import com.tangem.domain.express.ExpressServiceFetcher
|
||||
import com.tangem.domain.express.models.ExpressAsset
|
||||
|
|
@ -51,7 +52,7 @@ class ManageCryptoCurrenciesUseCase(
|
|||
private val cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher,
|
||||
private val cryptoCurrencyMetadataCleaner: CryptoCurrencyMetadataCleaner,
|
||||
private val expressServiceFetcher: ExpressServiceFetcher,
|
||||
private val parallelUpdatingScope: CoroutineScope,
|
||||
private val parallelUpdatingScope: AppCoroutineScope,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
package com.tangem.domain.account.status.utils
|
||||
|
||||
import arrow.core.Option
|
||||
import arrow.core.raise.option
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.status.model.AccountCryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
||||
/**
|
||||
* Extension functions for retrieving [AccountCryptoCurrency] from an [AccountList].
|
||||
*
|
||||
* This object provides convenient extension functions to search and retrieve [AccountCryptoCurrency] instances
|
||||
* from an [AccountList] using various parameters such as cryptocurrency ID and network.
|
||||
*
|
||||
* Usage example:
|
||||
* ```
|
||||
* val accountList: AccountList? = ...
|
||||
* val result: Option<AccountCryptoCurrency> = accountList.getAccountCryptoCurrency(currency)
|
||||
* result.fold(
|
||||
* ifEmpty = { /* handle not found */ },
|
||||
* ifSome = { accountCryptoCurrency -> /* use the found result */ }
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* @see AccountCryptoCurrency
|
||||
* @see AccountList
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object AccountCryptoCurrencyOperations {
|
||||
|
||||
// region AccountList
|
||||
|
||||
/**
|
||||
* Retrieves the [AccountCryptoCurrency] for the specified [currency] from this [AccountList].
|
||||
*
|
||||
* @receiver the [AccountList] to search within, can be null
|
||||
* @param currency the cryptocurrency whose account association is to be retrieved
|
||||
* @return [Option] containing the [AccountCryptoCurrency] if found, or [Option.None] otherwise
|
||||
*/
|
||||
fun AccountList?.getAccountCryptoCurrency(currency: CryptoCurrency): Option<AccountCryptoCurrency> {
|
||||
return getAccountCryptoCurrency(currencyId = currency.id, network = currency.network)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the [AccountCryptoCurrency] for the specified [currencyId] and [network] from this [AccountList].
|
||||
*
|
||||
* @receiver the [AccountList] to search within, can be null
|
||||
* @param currencyId the ID of the cryptocurrency whose account association is to be retrieved
|
||||
* @param network the network associated with the cryptocurrency, can be null
|
||||
* @return [Option] containing the [AccountCryptoCurrency] if found, or [Option.None] otherwise
|
||||
*/
|
||||
fun AccountList?.getAccountCryptoCurrency(
|
||||
currencyId: CryptoCurrency.ID,
|
||||
network: Network?,
|
||||
): Option<AccountCryptoCurrency> = option {
|
||||
val accountList = this@getAccountCryptoCurrency
|
||||
|
||||
ensureNotNull(accountList)
|
||||
|
||||
val accountCryptoCurrency = AccountCryptoCurrencyStatusFinder(
|
||||
accountList = accountList,
|
||||
currencyId = currencyId,
|
||||
network = network,
|
||||
)
|
||||
|
||||
ensureNotNull(accountCryptoCurrency)
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.domain.account.status.utils
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.model.AccountCryptoCurrency
|
||||
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
|
||||
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatuses
|
||||
import com.tangem.domain.models.account.Account
|
||||
|
|
@ -9,16 +11,23 @@ import com.tangem.domain.models.account.AccountStatus
|
|||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.account.filterCryptoPortfolio
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
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.
|
||||
* Finds a specific cryptocurrency or its status associated with an account from the provided account list.
|
||||
*
|
||||
* The base implementation uses [AccountList] for searching. For [AccountStatusList], it first converts
|
||||
* to [AccountList], finds the [AccountCryptoCurrency], then retrieves the status using accountId and currencyId.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Suppress("MethodOverloading")
|
||||
internal object AccountCryptoCurrencyStatusFinder {
|
||||
|
||||
// region AccountCryptoCurrencyStatus methods
|
||||
|
||||
/**
|
||||
* Retrieves the [AccountCryptoCurrencyStatus] for the specified [currency] from the given [accountStatusList].
|
||||
*
|
||||
|
|
@ -48,25 +57,19 @@ internal object AccountCryptoCurrencyStatusFinder {
|
|||
currencyId: CryptoCurrency.ID,
|
||||
network: Network?,
|
||||
): AccountCryptoCurrencyStatus? {
|
||||
return accountStatusList.getExpectedAccountStatuses(network)
|
||||
.asSequence()
|
||||
.filterCryptoPortfolio()
|
||||
.mapNotNull { accountStatus ->
|
||||
val status = accountStatus.flattenCurrencies().firstOrNull { it.currency.id == currencyId }
|
||||
?: return@mapNotNull null
|
||||
val accountList = accountStatusList.toAccountList().getOrNull() ?: return null
|
||||
val accountCurrency = invoke(accountList, currencyId, network) ?: return null
|
||||
|
||||
AccountCryptoCurrencyStatus(account = accountStatus.account, status = status)
|
||||
}
|
||||
.firstOrNull()
|
||||
return accountStatusList.findStatus(accountCurrency)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a map of accounts to their corresponding list of [AccountCryptoCurrencyStatus] for the specified
|
||||
* Retrieves a map of accounts to their corresponding list of [CryptoCurrencyStatus] for the specified
|
||||
* list of [currencies] from the given [accountStatusList].
|
||||
*
|
||||
* @param accountStatusList the list of account statuses to search within.
|
||||
* @param currencies the list of cryptocurrencies whose statuses are to be retrieved.
|
||||
* @return a map where the key is the account and the value is a list of [AccountCryptoCurrencyStatus].
|
||||
* @return a map where the key is the account and the value is a list of [CryptoCurrencyStatus].
|
||||
*/
|
||||
operator fun invoke(
|
||||
accountStatusList: AccountStatusList,
|
||||
|
|
@ -104,15 +107,86 @@ internal object AccountCryptoCurrencyStatusFinder {
|
|||
derivationPath: Network.DerivationPath,
|
||||
contractAddress: String?,
|
||||
): AccountCryptoCurrencyStatus? {
|
||||
return accountStatusList.getExpectedAccountStatuses(
|
||||
val accountList = accountStatusList.toAccountList().getOrNull() ?: return null
|
||||
val accountCurrency = invoke(
|
||||
accountList = accountList,
|
||||
networkId = networkId,
|
||||
derivationPath = derivationPath,
|
||||
contractAddress = contractAddress,
|
||||
) ?: return null
|
||||
|
||||
return accountStatusList.findStatus(accountCurrency)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region AccountCryptoCurrency methods
|
||||
|
||||
/**
|
||||
* Retrieves the [AccountCryptoCurrency] for the specified [currency] from the given [accountList].
|
||||
*
|
||||
* @param accountList the list of accounts to search within.
|
||||
* @param currency the cryptocurrency to be retrieved.
|
||||
* @return the [AccountCryptoCurrency] if found, otherwise null.
|
||||
*/
|
||||
operator fun invoke(accountList: AccountList, currency: CryptoCurrency): AccountCryptoCurrency? {
|
||||
return invoke(
|
||||
accountList = accountList,
|
||||
currencyId = currency.id,
|
||||
network = currency.network,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the [AccountCryptoCurrency] for the specified [currencyId] and [network]
|
||||
* from the given [accountList].
|
||||
*
|
||||
* @param accountList the list of accounts to search within.
|
||||
* @param currencyId the ID of the cryptocurrency to be retrieved.
|
||||
* @param network the network associated with the cryptocurrency.
|
||||
* @return the [AccountCryptoCurrency] if found, otherwise null.
|
||||
*/
|
||||
operator fun invoke(
|
||||
accountList: AccountList,
|
||||
currencyId: CryptoCurrency.ID,
|
||||
network: Network?,
|
||||
): AccountCryptoCurrency? {
|
||||
return accountList.getExpectedAccounts(network)
|
||||
.asSequence()
|
||||
.filterIsInstance<Account.CryptoPortfolio>()
|
||||
.mapNotNull { account ->
|
||||
val currency = account.cryptoCurrencies.firstOrNull { it.id == currencyId }
|
||||
?: return@mapNotNull null
|
||||
|
||||
AccountCryptoCurrency(account = account, cryptoCurrency = currency)
|
||||
}
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the [AccountCryptoCurrency] for the specified [networkId], [derivationPath],
|
||||
* and optional [contractAddress] from the given [accountList].
|
||||
*
|
||||
* @param accountList the list of accounts to search within.
|
||||
* @param networkId the ID of the network associated with the cryptocurrency.
|
||||
* @param derivationPath the derivation path of the account.
|
||||
* @param contractAddress the optional contract address of the token (if applicable).
|
||||
* @return the [AccountCryptoCurrency] if found, otherwise null.
|
||||
*/
|
||||
operator fun invoke(
|
||||
accountList: AccountList,
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
contractAddress: String?,
|
||||
): AccountCryptoCurrency? {
|
||||
return accountList.getExpectedAccounts(
|
||||
rawNetworkId = networkId.rawId.value,
|
||||
derivationPath = derivationPath,
|
||||
)
|
||||
.asSequence()
|
||||
.filterCryptoPortfolio()
|
||||
.mapNotNull { accountStatus ->
|
||||
val status = accountStatus.flattenCurrencies().firstOrNull {
|
||||
val currency = it.currency
|
||||
.filterIsInstance<Account.CryptoPortfolio>()
|
||||
.mapNotNull { account ->
|
||||
val currency = account.cryptoCurrencies.firstOrNull { currency ->
|
||||
val isContractAddressMatch = contractAddress == null ||
|
||||
currency.id.contractAddress.equals(contractAddress, ignoreCase = true)
|
||||
|
||||
|
|
@ -122,74 +196,101 @@ internal object AccountCryptoCurrencyStatusFinder {
|
|||
}
|
||||
?: return@mapNotNull null
|
||||
|
||||
AccountCryptoCurrencyStatus(account = accountStatus.account, status = status)
|
||||
AccountCryptoCurrency(account = account, cryptoCurrency = currency)
|
||||
}
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
private fun AccountStatusList.getExpectedAccountStatuses(network: Network?): List<AccountStatus> {
|
||||
return getExpectedAccountStatuses(rawNetworkId = network?.rawId, derivationPath = network?.derivationPath)
|
||||
// endregion
|
||||
|
||||
// region AccountStatusList helpers
|
||||
|
||||
private fun AccountStatusList.findStatus(accountCurrency: AccountCryptoCurrency): AccountCryptoCurrencyStatus? {
|
||||
val accountStatus = accountStatuses
|
||||
.filterCryptoPortfolio()
|
||||
.firstOrNull { it.account.accountId == accountCurrency.account.accountId }
|
||||
?: return null
|
||||
|
||||
val currencyStatus = accountStatus.flattenCurrencies()
|
||||
.firstOrNull { it.currency.id == accountCurrency.cryptoCurrency.id }
|
||||
?: return null
|
||||
|
||||
return AccountCryptoCurrencyStatus(account = accountCurrency.account, status = currencyStatus)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the expected account statuses based on the provided [rawNetworkId] and [derivationPath].
|
||||
* If either parameter is null, all account statuses are returned.
|
||||
* If both parameters are provided, it filters the accounts based on the derivation index.
|
||||
*
|
||||
* @param rawNetworkId the raw ID of the network to filter accounts by, can be null.
|
||||
* @param derivationPath the derivation path of the network to filter accounts by, can be null.
|
||||
* @return a list of [AccountStatus] that match the expected criteria.
|
||||
*/
|
||||
private fun AccountStatusList.getExpectedAccountStatuses(
|
||||
rawNetworkId: String?,
|
||||
derivationPath: Network.DerivationPath?,
|
||||
): List<AccountStatus> {
|
||||
val possibleAccountIndex = if (rawNetworkId != null && derivationPath != null) {
|
||||
getAccountIndexOrNull(rawNetworkId, derivationPath)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
internal fun AccountStatusList.getExpectedAccountStatuses(networkId: Network.ID): List<AccountStatus> {
|
||||
val possibleAccountIndex = getAccountIndexOrNull(
|
||||
rawNetworkId = networkId.rawId.value,
|
||||
derivationPath = networkId.derivationPath,
|
||||
)
|
||||
|
||||
return when (possibleAccountIndex) {
|
||||
// currency can be in any account
|
||||
null -> accountStatuses
|
||||
// currency only in the main account
|
||||
DerivationIndex.Main.value -> listOf(mainAccount)
|
||||
// currency only in the account with specific derivation index or in the main account
|
||||
else -> {
|
||||
val accountStatus = accountStatuses.firstOrNull { accountStatus ->
|
||||
val cryptoPortfolio = accountStatus.account as? Account.CryptoPortfolio ?: return@firstOrNull false
|
||||
|
||||
cryptoPortfolio.derivationIndex.value == possibleAccountIndex
|
||||
val account = accountStatuses.firstOrNull { account ->
|
||||
val cryptoPortfolio = account as? AccountStatus.CryptoPortfolio ?: return@firstOrNull false
|
||||
cryptoPortfolio.account.derivationIndex.value == possibleAccountIndex
|
||||
}
|
||||
|
||||
listOfNotNull(accountStatus, mainAccount)
|
||||
listOfNotNull(account, mainAccount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun AccountStatusList.getExpectedAccountStatuses(networks: List<Network>): List<AccountStatus> {
|
||||
val possibleAccountIndexes = networks.mapNotNull { it.getAccountIndexOrNull() }
|
||||
internal fun AccountStatusList.getExpectedAccountStatuses(networks: List<Network>): List<AccountStatus> {
|
||||
val possibleAccountIndexes = networks.mapNotNull { getAccountIndexOrNull(it.rawId, it.derivationPath) }
|
||||
|
||||
if (possibleAccountIndexes.isEmpty()) return this@getExpectedAccountStatuses.accountStatuses
|
||||
if (possibleAccountIndexes.isEmpty()) return accountStatuses
|
||||
|
||||
val accountStatuses = this@getExpectedAccountStatuses.accountStatuses.filter { accountStatus ->
|
||||
val filteredStatuses = accountStatuses.filter { accountStatus ->
|
||||
val cryptoPortfolio = accountStatus.account as? Account.CryptoPortfolio ?: return@filter false
|
||||
|
||||
cryptoPortfolio.derivationIndex.value in possibleAccountIndexes
|
||||
}
|
||||
|
||||
return accountStatuses + listOf(mainAccount)
|
||||
return filteredStatuses + listOf(mainAccount)
|
||||
}
|
||||
|
||||
private fun Network.getAccountIndexOrNull(): Int? {
|
||||
return getAccountIndexOrNull(rawNetworkId = rawId, derivationPath = derivationPath)
|
||||
// endregion
|
||||
|
||||
// region AccountList helpers
|
||||
|
||||
internal fun AccountList.getExpectedAccounts(network: Network?): List<Account> {
|
||||
return getExpectedAccounts(rawNetworkId = network?.rawId, derivationPath = network?.derivationPath)
|
||||
}
|
||||
|
||||
private fun getAccountIndexOrNull(rawNetworkId: String, derivationPath: Network.DerivationPath): Int? {
|
||||
private fun AccountList.getExpectedAccounts(
|
||||
rawNetworkId: String?,
|
||||
derivationPath: Network.DerivationPath?,
|
||||
): List<Account> {
|
||||
val possibleAccountIndex = getAccountIndexOrNull(rawNetworkId, derivationPath)
|
||||
|
||||
return when (possibleAccountIndex) {
|
||||
null -> accounts
|
||||
DerivationIndex.Main.value -> listOf(mainAccount)
|
||||
// currency only in the account with specific derivation index or in the main account
|
||||
else -> {
|
||||
val account = accounts.firstOrNull { account ->
|
||||
val cryptoPortfolio = account as? Account.CryptoPortfolio ?: return@firstOrNull false
|
||||
cryptoPortfolio.derivationIndex.value == possibleAccountIndex
|
||||
}
|
||||
listOfNotNull(account, mainAccount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Common helpers
|
||||
|
||||
private fun getAccountIndexOrNull(rawNetworkId: String?, derivationPath: Network.DerivationPath?): Int? {
|
||||
if (rawNetworkId == null || derivationPath == null) return null
|
||||
|
||||
val blockchain = Blockchain.fromId(id = rawNetworkId)
|
||||
val recognizer = AccountNodeRecognizer(blockchain)
|
||||
|
||||
return recognizer.recognize(derivationPath)?.toInt()
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package com.tangem.domain.account.status.utils
|
||||
|
||||
import arrow.core.Option
|
||||
import arrow.core.raise.option
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
||||
/**
|
||||
* Extension functions for retrieving [AccountCryptoCurrencyStatus] from an [AccountStatusList].
|
||||
*
|
||||
* This object provides convenient extension functions to search and retrieve [AccountCryptoCurrencyStatus]
|
||||
* instances from an [AccountStatusList] using various parameters such as cryptocurrency ID and network.
|
||||
*
|
||||
* The primary difference from [AccountCryptoCurrencyOperations] is that this object works with
|
||||
* [AccountStatusList] and returns status information along with the account-currency association.
|
||||
*
|
||||
* Usage example:
|
||||
* ```
|
||||
* val accountStatusList: AccountStatusList? = ...
|
||||
* val result: Option<AccountCryptoCurrencyStatus> = accountStatusList.getAccountCryptoCurrencyStatus(currency)
|
||||
* result.fold(
|
||||
* ifEmpty = { /* handle not found */ },
|
||||
* ifSome = { accountCurrencyStatus -> /* use the found result */ }
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* @see AccountCryptoCurrencyStatus
|
||||
* @see AccountStatusList
|
||||
* @see AccountCryptoCurrencyOperations
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object AccountCryptoCurrencyStatusOperations {
|
||||
|
||||
// region AccountStatusList
|
||||
|
||||
/**
|
||||
* Retrieves the [AccountCryptoCurrencyStatus] for the specified [currency] from this [AccountStatusList].
|
||||
*
|
||||
* @receiver the [AccountStatusList] to search within, can be null
|
||||
* @param currency the cryptocurrency whose account status association is to be retrieved
|
||||
* @return [Option] containing the [AccountCryptoCurrencyStatus] if found, or [Option.None] otherwise
|
||||
*/
|
||||
fun AccountStatusList?.getAccountCryptoCurrencyStatus(
|
||||
currency: CryptoCurrency,
|
||||
): Option<AccountCryptoCurrencyStatus> {
|
||||
return getAccountCryptoCurrencyStatus(currencyId = currency.id, network = currency.network)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the [AccountCryptoCurrencyStatus] for the specified [currencyId] and [network]
|
||||
* from this [AccountStatusList].
|
||||
*
|
||||
* @receiver the [AccountStatusList] to search within, can be null
|
||||
* @param currencyId the ID of the cryptocurrency whose account status association is to be retrieved
|
||||
* @param network the network associated with the cryptocurrency, can be null
|
||||
* @return [Option] containing the [AccountCryptoCurrencyStatus] if found, or [Option.None] otherwise
|
||||
*/
|
||||
fun AccountStatusList?.getAccountCryptoCurrencyStatus(
|
||||
currencyId: CryptoCurrency.ID,
|
||||
network: Network?,
|
||||
): Option<AccountCryptoCurrencyStatus> = option {
|
||||
val accountStatusList = this@getAccountCryptoCurrencyStatus
|
||||
|
||||
ensureNotNull(accountStatusList)
|
||||
|
||||
val accountCryptoCurrencyStatus = AccountCryptoCurrencyStatusFinder(
|
||||
accountStatusList = accountStatusList,
|
||||
currencyId = currencyId,
|
||||
network = network,
|
||||
)
|
||||
|
||||
ensureNotNull(accountCryptoCurrencyStatus)
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -35,6 +35,10 @@ class CryptoCurrencyBalanceFetcher(
|
|||
|
||||
private val mutex = Mutex()
|
||||
|
||||
operator fun invoke(userWalletId: UserWalletId, currency: CryptoCurrency) {
|
||||
invoke(userWalletId = userWalletId, currencies = listOf(currency))
|
||||
}
|
||||
|
||||
operator fun invoke(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||
if (currencies.isEmpty()) return
|
||||
|
||||
|
|
@ -45,6 +49,18 @@ class CryptoCurrencyBalanceFetcher(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun invokeAndAwait(userWalletId: UserWalletId, currency: CryptoCurrency) {
|
||||
invokeAndAwait(userWalletId = userWalletId, currencies = listOf(currency))
|
||||
}
|
||||
|
||||
suspend fun invokeAndAwait(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||
if (currencies.isEmpty()) return
|
||||
|
||||
mutex.withLock {
|
||||
refreshBalances(userWalletId, currencies)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun refreshBalances(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||
coroutineScope {
|
||||
val results = listOf(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
package com.tangem.domain.account.status.utils
|
||||
|
||||
import arrow.core.None
|
||||
import arrow.core.Option
|
||||
import arrow.core.raise.catch
|
||||
import arrow.core.raise.option
|
||||
import arrow.core.toOption
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.status.utils.AccountCryptoCurrencyOperations.getAccountCryptoCurrency
|
||||
import com.tangem.domain.account.status.utils.AccountCryptoCurrencyStatusFinder.getExpectedAccounts
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Extension functions for retrieving [CryptoCurrency] from an [AccountList] or [Account.CryptoPortfolio].
|
||||
*
|
||||
* This object provides convenient extension functions to search and retrieve [CryptoCurrency] instances
|
||||
* from account-related data structures using various parameters such as cryptocurrency ID and network.
|
||||
*
|
||||
* Unlike [AccountCryptoCurrencyOperations], this object returns only the [CryptoCurrency] without
|
||||
* the associated account information.
|
||||
*
|
||||
* Usage example:
|
||||
* ```
|
||||
* val accountList: AccountList? = ...
|
||||
* val result: Option<CryptoCurrency> = accountList.getCryptoCurrency(currencyId, network)
|
||||
* result.fold(
|
||||
* ifEmpty = { /* handle not found */ },
|
||||
* ifSome = { currency -> /* use the found currency */ }
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* @see CryptoCurrency
|
||||
* @see AccountList
|
||||
* @see Account.CryptoPortfolio
|
||||
* @see AccountCryptoCurrencyOperations
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object CryptoCurrencyOperations {
|
||||
|
||||
// region AccountList
|
||||
|
||||
/**
|
||||
* Retrieves the [CryptoCurrency] matching the specified [cryptoCurrency] from this [AccountList].
|
||||
*
|
||||
* @receiver the [AccountList] to search within, can be null
|
||||
* @param cryptoCurrency the cryptocurrency to match
|
||||
* @return [Option] containing the [CryptoCurrency] if found, or [Option.None] otherwise
|
||||
*/
|
||||
fun AccountList?.getCryptoCurrency(cryptoCurrency: CryptoCurrency): Option<CryptoCurrency> {
|
||||
return getCryptoCurrency(currencyId = cryptoCurrency.id, network = cryptoCurrency.network)
|
||||
}
|
||||
|
||||
fun AccountList?.getCryptoCurrency(currencyIdValue: String): Option<CryptoCurrency> = option {
|
||||
val currencyId = catch(
|
||||
block = { CryptoCurrency.ID.fromValue(currencyIdValue) },
|
||||
catch = { throwable ->
|
||||
Timber.e("Error on converting currencyId: $throwable")
|
||||
raise(None)
|
||||
},
|
||||
)
|
||||
|
||||
return getCryptoCurrency(currencyId = currencyId, network = null)
|
||||
}
|
||||
|
||||
fun AccountList.getCoin(currency: CryptoCurrency): Option<CryptoCurrency.Coin> {
|
||||
return getCoin(network = currency.network)
|
||||
}
|
||||
|
||||
fun AccountList.getCoin(network: Network): Option<CryptoCurrency.Coin> {
|
||||
return getExpectedAccounts(network = network)
|
||||
.flatMap { account ->
|
||||
(account as? Account.CryptoPortfolio)
|
||||
?.cryptoCurrencies.orEmpty()
|
||||
.filterIsInstance<CryptoCurrency.Coin>()
|
||||
}
|
||||
.firstOrNull { currency -> currency.network.id == network.id }
|
||||
.toOption()
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the [CryptoCurrency] for the specified [currencyId] and [network] from this [AccountList].
|
||||
*
|
||||
* @receiver the [AccountList] to search within, can be null
|
||||
* @param currencyId the ID of the cryptocurrency to be retrieved
|
||||
* @param network the network associated with the cryptocurrency, can be null
|
||||
* @return [Option] containing the [CryptoCurrency] if found, or [Option.None] otherwise
|
||||
*/
|
||||
fun AccountList?.getCryptoCurrency(currencyId: CryptoCurrency.ID, network: Network?): Option<CryptoCurrency> {
|
||||
return getAccountCryptoCurrency(currencyId, network)
|
||||
.map { it.cryptoCurrency }
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Account.CryptoPortfolio
|
||||
|
||||
/**
|
||||
* Retrieves the [CryptoCurrency] matching the specified [currencyId] from this [Account.CryptoPortfolio].
|
||||
*
|
||||
* @receiver the [Account.CryptoPortfolio] to search within
|
||||
* @param currencyId the ID of the cryptocurrency to be retrieved
|
||||
* @return [Option] containing the [CryptoCurrency] if found, or [Option.None] otherwise
|
||||
*/
|
||||
fun Account.CryptoPortfolio.getCryptoCurrency(currencyId: CryptoCurrency.ID): Option<CryptoCurrency> {
|
||||
return cryptoCurrencies.firstOrNull { it.id == currencyId }.toOption()
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
package com.tangem.domain.account.status.utils
|
||||
|
||||
import arrow.core.Option
|
||||
import arrow.core.toOption
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
|
||||
import com.tangem.domain.account.status.utils.AccountCryptoCurrencyStatusFinder.getExpectedAccountStatuses
|
||||
import com.tangem.domain.account.status.utils.AccountCryptoCurrencyStatusOperations.getAccountCryptoCurrencyStatus
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
||||
/**
|
||||
* Extension functions for retrieving [CryptoCurrencyStatus] from an [AccountStatusList] or
|
||||
* [AccountStatus.CryptoPortfolio].
|
||||
*
|
||||
* This object provides convenient extension functions to search and retrieve [CryptoCurrencyStatus]
|
||||
* instances from account status-related data structures using various parameters such as cryptocurrency ID
|
||||
* and network.
|
||||
*
|
||||
* Unlike [AccountCryptoCurrencyStatusOperations], this object returns only the [CryptoCurrencyStatus] without
|
||||
* the associated account information.
|
||||
*
|
||||
* Usage example:
|
||||
* ```
|
||||
* val accountStatusList: AccountStatusList? = ...
|
||||
* val result: Option<CryptoCurrencyStatus> = accountStatusList.getCryptoCurrencyStatus(currency)
|
||||
* result.fold(
|
||||
* ifEmpty = { /* handle not found */ },
|
||||
* ifSome = { status -> /* use the found status */ }
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* @see CryptoCurrencyStatus
|
||||
* @see AccountStatusList
|
||||
* @see AccountStatus.CryptoPortfolio
|
||||
* @see AccountCryptoCurrencyStatusOperations
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object CryptoCurrencyStatusOperations {
|
||||
|
||||
// region AccountStatusList
|
||||
|
||||
/**
|
||||
* Retrieves the [CryptoCurrencyStatus] for the specified [currency] from this [AccountStatusList].
|
||||
*
|
||||
* @receiver the [AccountStatusList] to search within, can be null
|
||||
* @param currency the cryptocurrency whose status is to be retrieved
|
||||
* @return [Option] containing the [CryptoCurrencyStatus] if found, or [Option.None] otherwise
|
||||
*/
|
||||
fun AccountStatusList?.getCryptoCurrencyStatus(currency: CryptoCurrency): Option<CryptoCurrencyStatus> {
|
||||
return getCryptoCurrencyStatus(currencyId = currency.id, network = currency.network)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the [CryptoCurrencyStatus] for the specified [currencyId] and [network]
|
||||
* from this [AccountStatusList].
|
||||
*
|
||||
* @receiver the [AccountStatusList] to search within, can be null
|
||||
* @param currencyId the ID of the cryptocurrency whose status is to be retrieved
|
||||
* @param network the network associated with the cryptocurrency, can be null
|
||||
* @return [Option] containing the [CryptoCurrencyStatus] if found, or [Option.None] otherwise
|
||||
*/
|
||||
fun AccountStatusList?.getCryptoCurrencyStatus(
|
||||
currencyId: CryptoCurrency.ID,
|
||||
network: Network?,
|
||||
): Option<CryptoCurrencyStatus> {
|
||||
return getAccountCryptoCurrencyStatus(currencyId = currencyId, network = network)
|
||||
.map(AccountCryptoCurrencyStatus::status)
|
||||
}
|
||||
|
||||
fun AccountStatusList.getCoinStatus(currency: CryptoCurrency): Option<CryptoCurrencyStatus> {
|
||||
return getCoinStatus(network = currency.network)
|
||||
}
|
||||
|
||||
fun AccountStatusList.getCoinStatus(network: Network): Option<CryptoCurrencyStatus> {
|
||||
return getCoinStatus(networkId = network.id)
|
||||
}
|
||||
|
||||
fun AccountStatusList.getCoinStatus(networkId: Network.ID): Option<CryptoCurrencyStatus> {
|
||||
return getExpectedAccountStatuses(networkId)
|
||||
.flatMap { accountStatus ->
|
||||
(accountStatus as? AccountStatus.CryptoPortfolio)
|
||||
?.flattenCurrencies().orEmpty()
|
||||
.filter { it.currency is CryptoCurrency.Coin }
|
||||
}
|
||||
.firstOrNull { status -> status.currency.network.id == networkId }
|
||||
.toOption()
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region AccountStatus.CryptoPortfolio
|
||||
|
||||
/**
|
||||
* Retrieves the [CryptoCurrencyStatus] for the specified [currencyId]
|
||||
* from this [AccountStatus.CryptoPortfolio].
|
||||
*
|
||||
* @receiver the [AccountStatus.CryptoPortfolio] to search within
|
||||
* @param currencyId the ID of the cryptocurrency whose status is to be retrieved
|
||||
* @return [Option] containing the [CryptoCurrencyStatus] if found, or [Option.None] otherwise
|
||||
*/
|
||||
fun AccountStatus.CryptoPortfolio.getCryptoCurrencyStatus(
|
||||
currencyId: CryptoCurrency.ID,
|
||||
): Option<CryptoCurrencyStatus> {
|
||||
return flattenCurrencies().firstOrNull { it.currency.id == currencyId }.toOption()
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -7,11 +7,10 @@ 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.core.utils.lceLoading
|
||||
import com.tangem.domain.models.TokensGroupType
|
||||
import com.tangem.domain.models.TokensSortType
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
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.account.*
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
|
|
@ -19,7 +18,10 @@ import com.tangem.domain.models.wallet.UserWalletId
|
|||
import com.tangem.test.core.assertNone
|
||||
import com.tangem.test.core.assertSome
|
||||
import com.tangem.test.core.getEmittedValues
|
||||
import io.mockk.*
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerifyOrder
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
|
@ -78,9 +80,15 @@ class GetAccountCurrencyStatusUseCaseTest {
|
|||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns listOf(accountStatus)
|
||||
}
|
||||
val accountStatusList = AccountStatusList(
|
||||
userWalletId = userWalletId,
|
||||
accountStatuses = listOf(accountStatus),
|
||||
totalAccounts = 1,
|
||||
totalArchivedAccounts = 0,
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
)
|
||||
|
||||
coEvery { supplier.getSyncOrNull(supplierParams) } returns accountStatusList
|
||||
|
||||
|
|
@ -101,10 +109,14 @@ class GetAccountCurrencyStatusUseCaseTest {
|
|||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
val account = mockk<Account.CryptoPortfolio>(relaxed = true) {
|
||||
every { this@mockk.derivationIndex } returns DerivationIndex(1).getOrNull()!!
|
||||
every { this@mockk.cryptoCurrencies } returns listOf(currency)
|
||||
}
|
||||
val derivationIndex = DerivationIndex(1).getOrNull()!!
|
||||
val account = Account.CryptoPortfolio(
|
||||
accountId = AccountId.forCryptoPortfolio(userWalletId, derivationIndex),
|
||||
accountName = AccountName("Test Account").getOrNull()!!,
|
||||
icon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex = derivationIndex,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
val currencyStatus = CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading)
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
|
|
@ -116,9 +128,15 @@ class GetAccountCurrencyStatusUseCaseTest {
|
|||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns listOf(mainAccountStatus, accountStatus, mockk())
|
||||
}
|
||||
val accountStatusList = AccountStatusList(
|
||||
userWalletId = userWalletId,
|
||||
accountStatuses = listOf(mainAccountStatus, accountStatus),
|
||||
totalAccounts = 2,
|
||||
totalArchivedAccounts = 0,
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
)
|
||||
|
||||
coEvery { supplier.getSyncOrNull(supplierParams) } returns accountStatusList
|
||||
|
||||
|
|
@ -139,9 +157,10 @@ class GetAccountCurrencyStatusUseCaseTest {
|
|||
@Test
|
||||
fun `invokeSync returns Some if network is null`() = runTest {
|
||||
// Arrange
|
||||
val account = mockk<Account.CryptoPortfolio>(relaxed = true) {
|
||||
every { this@mockk.cryptoCurrencies } returns listOf(currency)
|
||||
}
|
||||
val account = Account.CryptoPortfolio.createMainAccount(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
val currencyStatus = CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading)
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
|
|
@ -153,9 +172,15 @@ class GetAccountCurrencyStatusUseCaseTest {
|
|||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns listOf(accountStatus)
|
||||
}
|
||||
val accountStatusList = AccountStatusList(
|
||||
userWalletId = userWalletId,
|
||||
accountStatuses = listOf(accountStatus),
|
||||
totalAccounts = 1,
|
||||
totalArchivedAccounts = 0,
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
)
|
||||
|
||||
coEvery { supplier.getSyncOrNull(supplierParams) } returns accountStatusList
|
||||
|
||||
|
|
@ -197,9 +222,15 @@ class GetAccountCurrencyStatusUseCaseTest {
|
|||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns listOf(accountStatus)
|
||||
}
|
||||
val accountStatusList = AccountStatusList(
|
||||
userWalletId = userWalletId,
|
||||
accountStatuses = listOf(accountStatus),
|
||||
totalAccounts = 1,
|
||||
totalArchivedAccounts = 0,
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
)
|
||||
|
||||
coEvery { supplier(supplierParams) } returns flowOf(accountStatusList)
|
||||
|
||||
|
|
@ -221,10 +252,14 @@ class GetAccountCurrencyStatusUseCaseTest {
|
|||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
val account = mockk<Account.CryptoPortfolio>(relaxed = true) {
|
||||
every { this@mockk.derivationIndex } returns DerivationIndex(1).getOrNull()!!
|
||||
every { this@mockk.cryptoCurrencies } returns listOf(currency)
|
||||
}
|
||||
val derivationIndex = DerivationIndex(1).getOrNull()!!
|
||||
val account = Account.CryptoPortfolio(
|
||||
accountId = AccountId.forCryptoPortfolio(userWalletId, derivationIndex),
|
||||
accountName = AccountName("Test Account").getOrNull()!!,
|
||||
icon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex = derivationIndex,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
val currencyStatus = CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading)
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
|
|
@ -236,9 +271,15 @@ class GetAccountCurrencyStatusUseCaseTest {
|
|||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns listOf(mainAccountStatus, accountStatus, mockk())
|
||||
}
|
||||
val accountStatusList = AccountStatusList(
|
||||
userWalletId = userWalletId,
|
||||
accountStatuses = listOf(mainAccountStatus, accountStatus),
|
||||
totalAccounts = 2,
|
||||
totalArchivedAccounts = 0,
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
)
|
||||
|
||||
coEvery { supplier(supplierParams) } returns flowOf(accountStatusList)
|
||||
|
||||
|
|
@ -256,9 +297,10 @@ class GetAccountCurrencyStatusUseCaseTest {
|
|||
@Test
|
||||
fun `invoke returns data if network is null`() = runTest {
|
||||
// Arrange
|
||||
val account = mockk<Account.CryptoPortfolio>(relaxed = true) {
|
||||
every { this@mockk.cryptoCurrencies } returns listOf(currency)
|
||||
}
|
||||
val account = Account.CryptoPortfolio.createMainAccount(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
val currencyStatus = CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading)
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
|
|
@ -270,9 +312,15 @@ class GetAccountCurrencyStatusUseCaseTest {
|
|||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
|
||||
val accountStatusList = mockk<AccountStatusList>(relaxed = true) {
|
||||
every { this@mockk.accountStatuses } returns listOf(accountStatus)
|
||||
}
|
||||
val accountStatusList = AccountStatusList(
|
||||
userWalletId = userWalletId,
|
||||
accountStatuses = listOf(accountStatus),
|
||||
totalAccounts = 1,
|
||||
totalArchivedAccounts = 0,
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
)
|
||||
|
||||
coEvery { supplier(supplierParams) } returns flowOf(accountStatusList)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,169 @@
|
|||
package com.tangem.domain.account.status.utils
|
||||
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.status.model.AccountCryptoCurrency
|
||||
import com.tangem.domain.account.status.utils.AccountCryptoCurrencyOperations.getAccountCryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.test.core.assertNone
|
||||
import com.tangem.test.core.assertSome
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
/**
|
||||
* Tests for [AccountCryptoCurrencyOperations].
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class AccountCryptoCurrencyOperationsTest {
|
||||
|
||||
private val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
|
||||
private val userWalletId = UserWalletId("011")
|
||||
private val currency = cryptoCurrencyFactory.ethereum
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetAccountCryptoCurrencyByCurrency {
|
||||
|
||||
@Test
|
||||
fun `returns None when AccountList is null`() {
|
||||
// Arrange
|
||||
val accountList: AccountList? = null
|
||||
// Act
|
||||
val result = accountList.getAccountCryptoCurrency(currency)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns None when currency is not found in AccountList`() {
|
||||
// Arrange
|
||||
val accountList = AccountList.empty(userWalletId)
|
||||
// Act
|
||||
val result = accountList.getAccountCryptoCurrency(currency)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when currency is found in AccountList`() {
|
||||
// Arrange
|
||||
val accountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
val expected = AccountCryptoCurrency(
|
||||
account = accountList.mainAccount,
|
||||
cryptoCurrency = currency,
|
||||
)
|
||||
// Act
|
||||
val result = accountList.getAccountCryptoCurrency(currency)
|
||||
// Assert
|
||||
assertSome(result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetAccountCryptoCurrencyByCurrencyIdAndNetwork {
|
||||
|
||||
@Test
|
||||
fun `returns None when AccountList is null`() {
|
||||
// Arrange
|
||||
val accountList: AccountList? = null
|
||||
// Act
|
||||
val result = accountList.getAccountCryptoCurrency(
|
||||
currencyId = currency.id,
|
||||
network = currency.network,
|
||||
)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns None when currency id is not found`() {
|
||||
// Arrange
|
||||
val accountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
val otherCurrencyId = CryptoCurrency.ID(
|
||||
prefix = CryptoCurrency.ID.Prefix.COIN_PREFIX,
|
||||
body = CryptoCurrency.ID.Body.NetworkId("bitcoin"),
|
||||
suffix = CryptoCurrency.ID.Suffix.RawID("bitcoin"),
|
||||
)
|
||||
// Act
|
||||
val result = accountList.getAccountCryptoCurrency(
|
||||
currencyId = otherCurrencyId,
|
||||
network = currency.network,
|
||||
)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when currency id is found with null network`() {
|
||||
// Arrange
|
||||
val accountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
val expected = AccountCryptoCurrency(
|
||||
account = accountList.mainAccount,
|
||||
cryptoCurrency = currency,
|
||||
)
|
||||
// Act
|
||||
val result = accountList.getAccountCryptoCurrency(
|
||||
currencyId = currency.id,
|
||||
network = null,
|
||||
)
|
||||
// Assert
|
||||
assertSome(result, expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when currency id and network match`() {
|
||||
// Arrange
|
||||
val accountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
val expected = AccountCryptoCurrency(
|
||||
account = accountList.mainAccount,
|
||||
cryptoCurrency = currency,
|
||||
)
|
||||
// Act
|
||||
val result = accountList.getAccountCryptoCurrency(
|
||||
currencyId = currency.id,
|
||||
network = currency.network,
|
||||
)
|
||||
// Assert
|
||||
assertSome(result, expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some with first matching currency when multiple currencies exist`() {
|
||||
// Arrange
|
||||
val currencies = cryptoCurrencyFactory.ethereumAndStellar
|
||||
val accountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = currencies,
|
||||
)
|
||||
val targetCurrency = currencies.first()
|
||||
val expected = AccountCryptoCurrency(
|
||||
account = accountList.mainAccount,
|
||||
cryptoCurrency = targetCurrency,
|
||||
)
|
||||
// Act
|
||||
val result = accountList.getAccountCryptoCurrency(
|
||||
currencyId = targetCurrency.id,
|
||||
network = targetCurrency.network,
|
||||
)
|
||||
// Assert
|
||||
assertSome(result, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
package com.tangem.domain.account.status.utils
|
||||
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
|
||||
import com.tangem.domain.account.status.utils.AccountCryptoCurrencyStatusOperations.getAccountCryptoCurrencyStatus
|
||||
import com.tangem.domain.core.utils.lceLoading
|
||||
import com.tangem.domain.models.TokensGroupType
|
||||
import com.tangem.domain.models.TokensSortType
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.test.core.assertNone
|
||||
import com.tangem.test.core.assertSome
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
/**
|
||||
* Tests for [AccountCryptoCurrencyStatusOperations].
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class AccountCryptoCurrencyStatusOperationsTest {
|
||||
|
||||
private val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
|
||||
private val userWalletId = UserWalletId("011")
|
||||
private val currency = cryptoCurrencyFactory.ethereum
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetAccountCryptoCurrencyStatusByCurrency {
|
||||
|
||||
@Test
|
||||
fun `returns None when AccountStatusList is null`() {
|
||||
val accountStatusList: AccountStatusList? = null
|
||||
val result = accountStatusList.getAccountCryptoCurrencyStatus(currency)
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns None when currency is not found in AccountStatusList`() {
|
||||
val accountStatusList = createAccountStatusList(currencies = emptyList())
|
||||
val result = accountStatusList.getAccountCryptoCurrencyStatus(currency)
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when currency is found in AccountStatusList`() {
|
||||
val currencyStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(currency),
|
||||
currencyStatuses = listOf(currencyStatus),
|
||||
)
|
||||
val expected = AccountCryptoCurrencyStatus(
|
||||
account = accountStatusList.mainAccount.account,
|
||||
status = currencyStatus,
|
||||
)
|
||||
val result = accountStatusList.getAccountCryptoCurrencyStatus(currency)
|
||||
assertSome(result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetAccountCryptoCurrencyStatusByCurrencyIdAndNetwork {
|
||||
|
||||
@Test
|
||||
fun `returns None when AccountStatusList is null`() {
|
||||
val accountStatusList: AccountStatusList? = null
|
||||
val result = accountStatusList.getAccountCryptoCurrencyStatus(
|
||||
currencyId = currency.id,
|
||||
network = currency.network,
|
||||
)
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns None when currency id is not found`() {
|
||||
val currencyStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(currency),
|
||||
currencyStatuses = listOf(currencyStatus),
|
||||
)
|
||||
val otherCurrencyId = CryptoCurrency.ID(
|
||||
prefix = CryptoCurrency.ID.Prefix.COIN_PREFIX,
|
||||
body = CryptoCurrency.ID.Body.NetworkId("bitcoin"),
|
||||
suffix = CryptoCurrency.ID.Suffix.RawID("bitcoin"),
|
||||
)
|
||||
val result = accountStatusList.getAccountCryptoCurrencyStatus(
|
||||
currencyId = otherCurrencyId,
|
||||
network = currency.network,
|
||||
)
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when currency id is found with null network`() {
|
||||
val currencyStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(currency),
|
||||
currencyStatuses = listOf(currencyStatus),
|
||||
)
|
||||
val expected = AccountCryptoCurrencyStatus(
|
||||
account = accountStatusList.mainAccount.account,
|
||||
status = currencyStatus,
|
||||
)
|
||||
val result = accountStatusList.getAccountCryptoCurrencyStatus(
|
||||
currencyId = currency.id,
|
||||
network = null,
|
||||
)
|
||||
assertSome(result, expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when currency id and network match`() {
|
||||
val currencyStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(currency),
|
||||
currencyStatuses = listOf(currencyStatus),
|
||||
)
|
||||
val expected = AccountCryptoCurrencyStatus(
|
||||
account = accountStatusList.mainAccount.account,
|
||||
status = currencyStatus,
|
||||
)
|
||||
val result = accountStatusList.getAccountCryptoCurrencyStatus(
|
||||
currencyId = currency.id,
|
||||
network = currency.network,
|
||||
)
|
||||
assertSome(result, expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some with first matching currency when multiple currencies exist`() {
|
||||
val currencies = cryptoCurrencyFactory.ethereumAndStellar
|
||||
val currencyStatuses = currencies.map {
|
||||
CryptoCurrencyStatus(currency = it, value = CryptoCurrencyStatus.Loading)
|
||||
}
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = currencies,
|
||||
currencyStatuses = currencyStatuses,
|
||||
)
|
||||
val targetCurrency = currencies.first()
|
||||
val expected = AccountCryptoCurrencyStatus(
|
||||
account = accountStatusList.mainAccount.account,
|
||||
status = currencyStatuses.first(),
|
||||
)
|
||||
val result = accountStatusList.getAccountCryptoCurrencyStatus(
|
||||
currencyId = targetCurrency.id,
|
||||
network = targetCurrency.network,
|
||||
)
|
||||
assertSome(result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAccountStatusList(
|
||||
currencies: List<CryptoCurrency>,
|
||||
currencyStatuses: List<CryptoCurrencyStatus> = emptyList(),
|
||||
): AccountStatusList {
|
||||
val account = Account.CryptoPortfolio.createMainAccount(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = currencies,
|
||||
)
|
||||
val tokenList = if (currencyStatuses.isEmpty()) {
|
||||
TokenList.Empty
|
||||
} else {
|
||||
TokenList.Ungrouped(
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortedBy = TokensSortType.NONE,
|
||||
currencies = currencyStatuses,
|
||||
)
|
||||
}
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
tokenList = tokenList,
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
return AccountStatusList(
|
||||
userWalletId = userWalletId,
|
||||
accountStatuses = listOf(accountStatus),
|
||||
totalAccounts = 1,
|
||||
totalArchivedAccounts = 0,
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
package com.tangem.domain.account.status.utils
|
||||
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyOperations.getCryptoCurrency
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.test.core.assertNone
|
||||
import com.tangem.test.core.assertSome
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
/**
|
||||
* Tests for [CryptoCurrencyOperations].
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class CryptoCurrencyOperationsTest {
|
||||
|
||||
private val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
|
||||
private val userWalletId = UserWalletId("011")
|
||||
private val currency = cryptoCurrencyFactory.ethereum
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetCryptoCurrencyFromAccountListByCurrency {
|
||||
|
||||
@Test
|
||||
fun `returns None when currency is not found in AccountList`() {
|
||||
// Arrange
|
||||
val accountList = AccountList.empty(userWalletId)
|
||||
// Act
|
||||
val result = accountList.getCryptoCurrency(currency)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when currency is found in AccountList`() {
|
||||
// Arrange
|
||||
val accountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
// Act
|
||||
val result = accountList.getCryptoCurrency(currency)
|
||||
// Assert
|
||||
assertSome(result, currency)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetCryptoCurrencyFromAccountListByCurrencyIdAndNetwork {
|
||||
|
||||
@Test
|
||||
fun `returns None when AccountList is null`() {
|
||||
// Arrange
|
||||
val accountList: AccountList? = null
|
||||
// Act
|
||||
val result = accountList.getCryptoCurrency(
|
||||
currencyId = currency.id,
|
||||
network = currency.network,
|
||||
)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns None when currency id is not found`() {
|
||||
// Arrange
|
||||
val accountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
val otherCurrencyId = CryptoCurrency.ID(
|
||||
prefix = CryptoCurrency.ID.Prefix.COIN_PREFIX,
|
||||
body = CryptoCurrency.ID.Body.NetworkId("bitcoin"),
|
||||
suffix = CryptoCurrency.ID.Suffix.RawID("bitcoin"),
|
||||
)
|
||||
// Act
|
||||
val result = accountList.getCryptoCurrency(
|
||||
currencyId = otherCurrencyId,
|
||||
network = currency.network,
|
||||
)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when currency id is found with null network`() {
|
||||
// Arrange
|
||||
val accountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
// Act
|
||||
val result = accountList.getCryptoCurrency(
|
||||
currencyId = currency.id,
|
||||
network = null,
|
||||
)
|
||||
// Assert
|
||||
assertSome(result, currency)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when currency id and network match`() {
|
||||
// Arrange
|
||||
val accountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
// Act
|
||||
val result = accountList.getCryptoCurrency(
|
||||
currencyId = currency.id,
|
||||
network = currency.network,
|
||||
)
|
||||
// Assert
|
||||
assertSome(result, currency)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some with first matching currency when multiple currencies exist`() {
|
||||
// Arrange
|
||||
val currencies = cryptoCurrencyFactory.ethereumAndStellar
|
||||
val accountList = AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = currencies,
|
||||
)
|
||||
val targetCurrency = currencies.first()
|
||||
// Act
|
||||
val result = accountList.getCryptoCurrency(
|
||||
currencyId = targetCurrency.id,
|
||||
network = targetCurrency.network,
|
||||
)
|
||||
// Assert
|
||||
assertSome(result, targetCurrency)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetCryptoCurrencyFromCryptoPortfolio {
|
||||
|
||||
@Test
|
||||
fun `returns None when currency id is not found in CryptoPortfolio`() {
|
||||
// Arrange
|
||||
val account = Account.CryptoPortfolio.createMainAccount(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
val otherCurrencyId = CryptoCurrency.ID(
|
||||
prefix = CryptoCurrency.ID.Prefix.COIN_PREFIX,
|
||||
body = CryptoCurrency.ID.Body.NetworkId("bitcoin"),
|
||||
suffix = CryptoCurrency.ID.Suffix.RawID("bitcoin"),
|
||||
)
|
||||
// Act
|
||||
val result = account.getCryptoCurrency(otherCurrencyId)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when currency id is found in CryptoPortfolio`() {
|
||||
// Arrange
|
||||
val account = Account.CryptoPortfolio.createMainAccount(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
// Act
|
||||
val result = account.getCryptoCurrency(currency.id)
|
||||
// Assert
|
||||
assertSome(result, currency)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns None when CryptoPortfolio has no currencies`() {
|
||||
// Arrange
|
||||
val account = Account.CryptoPortfolio.createMainAccount(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = emptyList(),
|
||||
)
|
||||
// Act
|
||||
val result = account.getCryptoCurrency(currency.id)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some with matching currency when multiple currencies exist`() {
|
||||
// Arrange
|
||||
val currencies = cryptoCurrencyFactory.ethereumAndStellar
|
||||
val account = Account.CryptoPortfolio.createMainAccount(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = currencies,
|
||||
)
|
||||
val targetCurrency = currencies.last()
|
||||
// Act
|
||||
val result = account.getCryptoCurrency(targetCurrency.id)
|
||||
// Assert
|
||||
assertSome(result, targetCurrency)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,545 @@
|
|||
package com.tangem.domain.account.status.utils
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCoinStatus
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
|
||||
import com.tangem.domain.core.utils.lceLoading
|
||||
import com.tangem.domain.models.TokensGroupType
|
||||
import com.tangem.domain.models.TokensSortType
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.test.core.assertNone
|
||||
import com.tangem.test.core.assertSome
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
/**
|
||||
* Tests for [CryptoCurrencyStatusOperations].
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class CryptoCurrencyStatusOperationsTest {
|
||||
|
||||
private val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
|
||||
private val userWalletId = UserWalletId("011")
|
||||
private val currency = cryptoCurrencyFactory.ethereum
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetCryptoCurrencyStatusFromAccountStatusListByCurrency {
|
||||
|
||||
@Test
|
||||
fun `returns None when AccountStatusList is null`() {
|
||||
// Arrange
|
||||
val accountStatusList: AccountStatusList? = null
|
||||
// Act
|
||||
val result = accountStatusList.getCryptoCurrencyStatus(currency)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns None when currency is not found in AccountStatusList`() {
|
||||
// Arrange
|
||||
val accountStatusList = createAccountStatusList(currencies = emptyList())
|
||||
// Act
|
||||
val result = accountStatusList.getCryptoCurrencyStatus(currency)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when currency is found in AccountStatusList`() {
|
||||
// Arrange
|
||||
val currencyStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(currency),
|
||||
currencyStatuses = listOf(currencyStatus),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatusList.getCryptoCurrencyStatus(currency)
|
||||
// Assert
|
||||
assertSome(result, currencyStatus)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetCryptoCurrencyStatusFromAccountStatusListByCurrencyIdAndNetwork {
|
||||
|
||||
@Test
|
||||
fun `returns None when AccountStatusList is null`() {
|
||||
// Arrange
|
||||
val accountStatusList: AccountStatusList? = null
|
||||
// Act
|
||||
val result = accountStatusList.getCryptoCurrencyStatus(
|
||||
currencyId = currency.id,
|
||||
network = currency.network,
|
||||
)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns None when currency id is not found`() {
|
||||
// Arrange
|
||||
val currencyStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(currency),
|
||||
currencyStatuses = listOf(currencyStatus),
|
||||
)
|
||||
val otherCurrencyId = CryptoCurrency.ID(
|
||||
prefix = CryptoCurrency.ID.Prefix.COIN_PREFIX,
|
||||
body = CryptoCurrency.ID.Body.NetworkId("bitcoin"),
|
||||
suffix = CryptoCurrency.ID.Suffix.RawID("bitcoin"),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatusList.getCryptoCurrencyStatus(
|
||||
currencyId = otherCurrencyId,
|
||||
network = currency.network,
|
||||
)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when currency id is found with null network`() {
|
||||
// Arrange
|
||||
val currencyStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(currency),
|
||||
currencyStatuses = listOf(currencyStatus),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatusList.getCryptoCurrencyStatus(
|
||||
currencyId = currency.id,
|
||||
network = null,
|
||||
)
|
||||
// Assert
|
||||
assertSome(result, currencyStatus)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when currency id and network match`() {
|
||||
// Arrange
|
||||
val currencyStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(currency),
|
||||
currencyStatuses = listOf(currencyStatus),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatusList.getCryptoCurrencyStatus(
|
||||
currencyId = currency.id,
|
||||
network = currency.network,
|
||||
)
|
||||
// Assert
|
||||
assertSome(result, currencyStatus)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some with first matching currency status when multiple currencies exist`() {
|
||||
// Arrange
|
||||
val currencies = cryptoCurrencyFactory.ethereumAndStellar
|
||||
val currencyStatuses = currencies.map {
|
||||
CryptoCurrencyStatus(currency = it, value = CryptoCurrencyStatus.Loading)
|
||||
}
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = currencies,
|
||||
currencyStatuses = currencyStatuses,
|
||||
)
|
||||
val targetCurrency = currencies.first()
|
||||
val expectedStatus = currencyStatuses.first()
|
||||
// Act
|
||||
val result = accountStatusList.getCryptoCurrencyStatus(
|
||||
currencyId = targetCurrency.id,
|
||||
network = targetCurrency.network,
|
||||
)
|
||||
// Assert
|
||||
assertSome(result, expectedStatus)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetCryptoCurrencyStatusFromCryptoPortfolio {
|
||||
|
||||
@Test
|
||||
fun `returns None when currency id is not found in CryptoPortfolio`() {
|
||||
// Arrange
|
||||
val currencyStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val account = Account.CryptoPortfolio.createMainAccount(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
tokenList = TokenList.Ungrouped(
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortedBy = TokensSortType.NONE,
|
||||
currencies = listOf(currencyStatus),
|
||||
),
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
val otherCurrencyId = CryptoCurrency.ID(
|
||||
prefix = CryptoCurrency.ID.Prefix.COIN_PREFIX,
|
||||
body = CryptoCurrency.ID.Body.NetworkId("bitcoin"),
|
||||
suffix = CryptoCurrency.ID.Suffix.RawID("bitcoin"),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatus.getCryptoCurrencyStatus(otherCurrencyId)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when currency id is found in CryptoPortfolio`() {
|
||||
// Arrange
|
||||
val currencyStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val account = Account.CryptoPortfolio.createMainAccount(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(currency),
|
||||
)
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
tokenList = TokenList.Ungrouped(
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortedBy = TokensSortType.NONE,
|
||||
currencies = listOf(currencyStatus),
|
||||
),
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatus.getCryptoCurrencyStatus(currency.id)
|
||||
// Assert
|
||||
assertSome(result, currencyStatus)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns None when CryptoPortfolio has empty token list`() {
|
||||
// Arrange
|
||||
val account = Account.CryptoPortfolio.createMainAccount(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = emptyList(),
|
||||
)
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
tokenList = TokenList.Empty,
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatus.getCryptoCurrencyStatus(currency.id)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some with matching currency status when multiple currencies exist`() {
|
||||
// Arrange
|
||||
val currencies = cryptoCurrencyFactory.ethereumAndStellar
|
||||
val currencyStatuses = currencies.map {
|
||||
CryptoCurrencyStatus(currency = it, value = CryptoCurrencyStatus.Loading)
|
||||
}
|
||||
val account = Account.CryptoPortfolio.createMainAccount(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = currencies,
|
||||
)
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
tokenList = TokenList.Ungrouped(
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortedBy = TokensSortType.NONE,
|
||||
currencies = currencyStatuses,
|
||||
),
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
val targetCurrency = currencies.last()
|
||||
val expectedStatus = currencyStatuses.last()
|
||||
// Act
|
||||
val result = accountStatus.getCryptoCurrencyStatus(targetCurrency.id)
|
||||
// Assert
|
||||
assertSome(result, expectedStatus)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetCoinStatusByCurrency {
|
||||
|
||||
@Test
|
||||
fun `returns None when no coin exists for the currency network`() {
|
||||
// Arrange
|
||||
val token = cryptoCurrencyFactory.createToken(Blockchain.Ethereum)
|
||||
val tokenStatus = CryptoCurrencyStatus(
|
||||
currency = token,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(token),
|
||||
currencyStatuses = listOf(tokenStatus),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatusList.getCoinStatus(token)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when coin exists for the currency network`() {
|
||||
// Arrange
|
||||
val coinStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(currency),
|
||||
currencyStatuses = listOf(coinStatus),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatusList.getCoinStatus(currency)
|
||||
// Assert
|
||||
assertSome(result, coinStatus)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetCoinStatusByNetwork {
|
||||
|
||||
@Test
|
||||
fun `returns None when AccountStatusList has empty token list`() {
|
||||
// Arrange
|
||||
val accountStatusList = createAccountStatusList(currencies = emptyList())
|
||||
// Act
|
||||
val result = accountStatusList.getCoinStatus(currency.network)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns None when only tokens exist for network`() {
|
||||
// Arrange
|
||||
val token = cryptoCurrencyFactory.createToken(Blockchain.Ethereum)
|
||||
val tokenStatus = CryptoCurrencyStatus(
|
||||
currency = token,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(token),
|
||||
currencyStatuses = listOf(tokenStatus),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatusList.getCoinStatus(currency.network)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when coin exists for network`() {
|
||||
// Arrange
|
||||
val coinStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(currency),
|
||||
currencyStatuses = listOf(coinStatus),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatusList.getCoinStatus(currency.network)
|
||||
// Assert
|
||||
assertSome(result, coinStatus)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns coin status when both coin and token exist for same network`() {
|
||||
// Arrange
|
||||
val token = cryptoCurrencyFactory.createToken(Blockchain.Ethereum)
|
||||
val coinStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val tokenStatus = CryptoCurrencyStatus(
|
||||
currency = token,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(currency, token),
|
||||
currencyStatuses = listOf(coinStatus, tokenStatus),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatusList.getCoinStatus(currency.network)
|
||||
// Assert
|
||||
assertSome(result, coinStatus)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class GetCoinStatusByNetworkId {
|
||||
|
||||
@Test
|
||||
fun `returns None when AccountStatusList has empty token list`() {
|
||||
// Arrange
|
||||
val accountStatusList = createAccountStatusList(currencies = emptyList())
|
||||
// Act
|
||||
val result = accountStatusList.getCoinStatus(currency.network.id)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns None when network id does not match any currency`() {
|
||||
// Arrange
|
||||
val coinStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(currency),
|
||||
currencyStatuses = listOf(coinStatus),
|
||||
)
|
||||
val otherNetworkId = Network.ID(value = "bitcoin", derivationPath = Network.DerivationPath.None)
|
||||
// Act
|
||||
val result = accountStatusList.getCoinStatus(otherNetworkId)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns None when only tokens exist for network id`() {
|
||||
// Arrange
|
||||
val token = cryptoCurrencyFactory.createToken(Blockchain.Ethereum)
|
||||
val tokenStatus = CryptoCurrencyStatus(
|
||||
currency = token,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(token),
|
||||
currencyStatuses = listOf(tokenStatus),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatusList.getCoinStatus(token.network.id)
|
||||
// Assert
|
||||
assertNone(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns Some when coin exists for network id`() {
|
||||
// Arrange
|
||||
val coinStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(currency),
|
||||
currencyStatuses = listOf(coinStatus),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatusList.getCoinStatus(currency.network.id)
|
||||
// Assert
|
||||
assertSome(result, coinStatus)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns coin status when both coin and token exist for same network id`() {
|
||||
// Arrange
|
||||
val token = cryptoCurrencyFactory.createToken(Blockchain.Ethereum)
|
||||
val coinStatus = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val tokenStatus = CryptoCurrencyStatus(
|
||||
currency = token,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = listOf(currency, token),
|
||||
currencyStatuses = listOf(coinStatus, tokenStatus),
|
||||
)
|
||||
// Act
|
||||
val result = accountStatusList.getCoinStatus(currency.network.id)
|
||||
// Assert
|
||||
assertSome(result, coinStatus)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns correct coin when multiple networks exist`() {
|
||||
// Arrange
|
||||
val currencies = cryptoCurrencyFactory.ethereumAndStellar
|
||||
val currencyStatuses = currencies.map {
|
||||
CryptoCurrencyStatus(currency = it, value = CryptoCurrencyStatus.Loading)
|
||||
}
|
||||
val accountStatusList = createAccountStatusList(
|
||||
currencies = currencies,
|
||||
currencyStatuses = currencyStatuses,
|
||||
)
|
||||
val targetCurrency = currencies.last()
|
||||
val expectedStatus = currencyStatuses.last()
|
||||
// Act
|
||||
val result = accountStatusList.getCoinStatus(targetCurrency.network.id)
|
||||
// Assert
|
||||
assertSome(result, expectedStatus)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAccountStatusList(
|
||||
currencies: List<CryptoCurrency>,
|
||||
currencyStatuses: List<CryptoCurrencyStatus> = emptyList(),
|
||||
): AccountStatusList {
|
||||
val account = Account.CryptoPortfolio.createMainAccount(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = currencies,
|
||||
)
|
||||
val tokenList = if (currencyStatuses.isEmpty()) {
|
||||
TokenList.Empty
|
||||
} else {
|
||||
TokenList.Ungrouped(
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortedBy = TokensSortType.NONE,
|
||||
currencies = currencyStatuses,
|
||||
)
|
||||
}
|
||||
val accountStatus = AccountStatus.CryptoPortfolio(
|
||||
account = account,
|
||||
tokenList = tokenList,
|
||||
priceChangeLce = lceLoading(),
|
||||
)
|
||||
return AccountStatusList(
|
||||
userWalletId = userWalletId,
|
||||
accountStatuses = listOf(accountStatus),
|
||||
totalAccounts = 1,
|
||||
totalArchivedAccounts = 0,
|
||||
totalFiatBalance = TotalFiatBalance.Loading,
|
||||
sortType = TokensSortType.NONE,
|
||||
groupType = TokensGroupType.NONE,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue