Updated on 2026-08-14
This commit is contained in:
parent
cab885e39f
commit
043602ebd6
9 changed files with 987 additions and 221 deletions
|
|
@ -8,12 +8,12 @@ import com.tangem.blockchainsdk.utils.toNetworkId
|
|||
import com.tangem.common.test.domain.card.MockScanResponseFactory
|
||||
import com.tangem.common.test.domain.wallet.MockUserWalletFactory
|
||||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
|
||||
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
||||
import com.tangem.domain.card.configs.GenericCardConfig
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
|
||||
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -110,7 +110,7 @@ class MockCryptoCurrencyFactory(private val userWallet: UserWallet.Cold = defaul
|
|||
)
|
||||
}
|
||||
|
||||
fun createToken(blockchain: Blockchain): CryptoCurrency {
|
||||
fun createToken(blockchain: Blockchain): CryptoCurrency.Token {
|
||||
return factory.createToken(
|
||||
sdkToken = Token(
|
||||
name = "NEVER-MIND",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.data.swap
|
||||
|
||||
import arrow.core.right
|
||||
import arrow.core.none
|
||||
import arrow.core.some
|
||||
import arrow.core.toOption
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.data.common.api.safeApiCall
|
||||
import com.tangem.data.swap.converter.SwapDataConverter
|
||||
|
|
@ -28,7 +30,7 @@ import com.tangem.domain.quotes.single.SingleQuoteStatusProducer
|
|||
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
|
||||
import com.tangem.domain.swap.SwapRepositoryV2
|
||||
import com.tangem.domain.swap.models.*
|
||||
import com.tangem.domain.tokens.utils.CurrencyStatusProxyCreator
|
||||
import com.tangem.domain.tokens.operations.CryptoCurrencyStatusFactory
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.async
|
||||
|
|
@ -49,7 +51,6 @@ internal class DefaultSwapRepositoryV2 @Inject constructor(
|
|||
private val dataSignatureVerifier: DataSignatureVerifier,
|
||||
private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier,
|
||||
private val singleQuoteStatusFetcher: SingleQuoteStatusFetcher,
|
||||
private val currencyStatusProxyCreator: CurrencyStatusProxyCreator,
|
||||
@NetworkMoshi moshi: Moshi,
|
||||
) : SwapRepositoryV2 {
|
||||
|
||||
|
|
@ -391,17 +392,19 @@ internal class DefaultSwapRepositoryV2 @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
return currencyStatusProxyCreator.createCurrencyStatus(
|
||||
val quoteStatus = quote ?: singleQuoteStatusSupplier.getSyncOrNull(
|
||||
params = SingleQuoteStatusProducer.Params(rawCurrencyId = rawCurrencyId),
|
||||
)
|
||||
|
||||
return CryptoCurrencyStatusFactory.create(
|
||||
currency = cryptoCurrency,
|
||||
maybeQuoteStatus = quote?.right() ?: singleQuoteStatusSupplier.getSyncOrNull(
|
||||
params = SingleQuoteStatusProducer.Params(rawCurrencyId = rawCurrencyId),
|
||||
).right(),
|
||||
maybeNetworkStatus = NetworkStatus(
|
||||
network = cryptoCurrency.network,
|
||||
value = NetworkStatus.MissedDerivation, // Caution!!! Do not change this status
|
||||
).right(),
|
||||
maybeYieldBalance = null,
|
||||
).getOrNull()
|
||||
).some(),
|
||||
maybeQuoteStatus = quoteStatus.toOption(),
|
||||
maybeYieldBalance = none(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun parseTxDetails(txDetailsJson: String): TxDetails? {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
|
|||
import com.tangem.domain.swap.SwapErrorResolver
|
||||
import com.tangem.domain.swap.SwapRepositoryV2
|
||||
import com.tangem.domain.swap.SwapTransactionRepository
|
||||
import com.tangem.domain.tokens.utils.CurrencyStatusProxyCreator
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -59,7 +58,6 @@ internal object SwapDataModule {
|
|||
moshi = moshi,
|
||||
singleQuoteStatusSupplier = singleQuoteStatusSupplier,
|
||||
singleQuoteStatusFetcher = singleQuoteStatusFetcher,
|
||||
currencyStatusProxyCreator = CurrencyStatusProxyCreator(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ abstract class BaseCurrencyStatusOperations(
|
|||
private val stakingIdFactory: StakingIdFactory,
|
||||
) {
|
||||
|
||||
protected val currencyStatusProxyCreator = CurrencyStatusProxyCreator()
|
||||
private val currencyStatusProxyCreator = CurrencyStatusProxyCreator()
|
||||
|
||||
abstract fun getCurrenciesStatuses(userWalletId: UserWalletId): LceFlow<TokenListError, List<CryptoCurrencyStatus>>
|
||||
|
||||
|
|
|
|||
|
|
@ -171,29 +171,22 @@ class CachedCurrenciesStatusesOperations(
|
|||
): Lce<TokenListError, List<CryptoCurrencyStatus>> = lce {
|
||||
isLoading.set(isUpdating)
|
||||
|
||||
var quotesRetrievingFailed = false
|
||||
|
||||
val networksStatuses = maybeNetworkStatuses?.bindEither()?.toNonEmptySetOrNull()
|
||||
val yieldBalances = maybeYieldBalances?.bindEither()
|
||||
val quotes = recover({ maybeQuotes?.bind()?.toNonEmptySetOrNull() }) {
|
||||
null
|
||||
}
|
||||
|
||||
if (quotes == null) {
|
||||
quotesRetrievingFailed = true
|
||||
}
|
||||
|
||||
currencies.map { currency ->
|
||||
val quote = quotes?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId }
|
||||
val networkStatus = networksStatuses?.firstOrNull { it.network == currency.network }
|
||||
val yieldBalance = findYieldBalanceOrNull(yieldBalances, currency, networkStatus)
|
||||
|
||||
val currencyStatus = currencyStatusProxyCreator.createCurrencyStatus(
|
||||
val currencyStatus = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
quoteStatus = quote,
|
||||
networkStatus = networkStatus,
|
||||
yieldBalance = yieldBalance,
|
||||
ignoreQuote = quotesRetrievingFailed,
|
||||
maybeNetworkStatus = networkStatus.toOption(),
|
||||
maybeQuoteStatus = quote.toOption(),
|
||||
maybeYieldBalance = yieldBalance.toOption(),
|
||||
)
|
||||
|
||||
currencyStatus
|
||||
|
|
|
|||
|
|
@ -0,0 +1,266 @@
|
|||
package com.tangem.domain.tokens.operations
|
||||
|
||||
import arrow.core.Option
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.NetworkAddress
|
||||
import com.tangem.domain.models.network.NetworkStatus
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.models.quote.QuoteStatus
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.models.yield.supply.YieldSupplyStatus
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Factory to create [CryptoCurrencyStatus] from [NetworkStatus], [QuoteStatus] and [YieldBalance].
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object CryptoCurrencyStatusFactory {
|
||||
|
||||
private val QuoteStatus?.fiatRate: BigDecimal?
|
||||
get() = (this?.value as? QuoteStatus.Data)?.fiatRate
|
||||
|
||||
private val QuoteStatus?.priceChange: BigDecimal?
|
||||
get() = (this?.value as? QuoteStatus.Data)?.priceChange
|
||||
|
||||
/**
|
||||
* Creates [CryptoCurrencyStatus] from [NetworkStatus], [QuoteStatus] and [YieldBalance].
|
||||
*
|
||||
|
||||
* @param maybeNetworkStatus An optional network status containing blockchain information.
|
||||
* @param maybeQuoteStatus An optional quote status containing price information.
|
||||
* @param maybeYieldBalance An optional yield balance containing staking information.
|
||||
*/
|
||||
fun create(
|
||||
currency: CryptoCurrency,
|
||||
maybeNetworkStatus: Option<NetworkStatus>,
|
||||
maybeQuoteStatus: Option<QuoteStatus>,
|
||||
maybeYieldBalance: Option<YieldBalance>,
|
||||
): CryptoCurrencyStatus {
|
||||
return CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = createStatus(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = maybeNetworkStatus,
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
maybeQuoteStatus = maybeQuoteStatus,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createStatus(
|
||||
currency: CryptoCurrency,
|
||||
maybeNetworkStatus: Option<NetworkStatus>,
|
||||
maybeQuoteStatus: Option<QuoteStatus>,
|
||||
maybeYieldBalance: Option<YieldBalance>,
|
||||
): CryptoCurrencyStatus.Value {
|
||||
val quoteStatus = maybeQuoteStatus.getOrNull()
|
||||
|
||||
return when (val status = maybeNetworkStatus.getOrNull()?.value) {
|
||||
is NetworkStatus.MissedDerivation -> createMissedDerivation(quoteStatus)
|
||||
is NetworkStatus.Unreachable -> createUnreachable(status, quoteStatus)
|
||||
is NetworkStatus.NoAccount -> createNoAccount(status, quoteStatus)
|
||||
is NetworkStatus.Verified -> createStatus(currency, status, quoteStatus, maybeYieldBalance)
|
||||
null -> CryptoCurrencyStatus.Loading
|
||||
}
|
||||
}
|
||||
|
||||
private fun createMissedDerivation(quoteStatus: QuoteStatus?): CryptoCurrencyStatus.MissedDerivation {
|
||||
return CryptoCurrencyStatus.MissedDerivation(
|
||||
priceChange = quoteStatus.priceChange,
|
||||
fiatRate = quoteStatus.fiatRate,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createUnreachable(
|
||||
status: NetworkStatus.Unreachable,
|
||||
quoteStatus: QuoteStatus?,
|
||||
): CryptoCurrencyStatus.Unreachable {
|
||||
return CryptoCurrencyStatus.Unreachable(
|
||||
priceChange = quoteStatus.priceChange,
|
||||
fiatRate = quoteStatus.fiatRate,
|
||||
networkAddress = status.address,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createNoAccount(
|
||||
status: NetworkStatus.NoAccount,
|
||||
quoteStatus: QuoteStatus?,
|
||||
): CryptoCurrencyStatus.NoAccount {
|
||||
return CryptoCurrencyStatus.NoAccount(
|
||||
amountToCreateAccount = status.amountToCreateAccount,
|
||||
fiatAmount = BigDecimal.ZERO,
|
||||
priceChange = quoteStatus.priceChange,
|
||||
fiatRate = quoteStatus.fiatRate,
|
||||
networkAddress = status.address,
|
||||
sources = CryptoCurrencyStatus.Sources(
|
||||
networkSource = status.source,
|
||||
quoteSource = quoteStatus?.value?.source ?: StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createStatus(
|
||||
currency: CryptoCurrency,
|
||||
status: NetworkStatus.Verified,
|
||||
quoteStatus: QuoteStatus?,
|
||||
maybeYieldBalance: Option<YieldBalance>,
|
||||
): CryptoCurrencyStatus.Value {
|
||||
val amount = when (val amount = status.amounts[currency.id]) {
|
||||
is NetworkStatus.Amount.Loaded -> amount.value
|
||||
is NetworkStatus.Amount.NotFound -> {
|
||||
return createNoAmount(quoteStatus = quoteStatus)
|
||||
}
|
||||
null -> {
|
||||
return CryptoCurrencyStatus.Loading
|
||||
}
|
||||
}
|
||||
|
||||
val yieldBalance = maybeYieldBalance.getOrNull(id = currency.id, address = status.address)
|
||||
|
||||
if (currency is CryptoCurrency.Token && currency.isCustom) {
|
||||
return createCustom(
|
||||
id = currency.id,
|
||||
status = status,
|
||||
amount = amount,
|
||||
quoteStatus = quoteStatus,
|
||||
yieldBalance = yieldBalance,
|
||||
)
|
||||
}
|
||||
|
||||
// order is important for correct total balance calculation
|
||||
return when (val quoteValue = quoteStatus?.value) {
|
||||
is QuoteStatus.Empty -> {
|
||||
createNoQuote(
|
||||
id = currency.id,
|
||||
status = status,
|
||||
amount = amount,
|
||||
quoteStatus = quoteStatus,
|
||||
yieldBalance = yieldBalance,
|
||||
)
|
||||
}
|
||||
is QuoteStatus.Data -> {
|
||||
createLoaded(
|
||||
id = currency.id,
|
||||
status = status,
|
||||
amount = amount,
|
||||
quoteStatus = quoteValue,
|
||||
yieldBalance = yieldBalance,
|
||||
)
|
||||
}
|
||||
null -> CryptoCurrencyStatus.Loading
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNoAmount(quoteStatus: QuoteStatus?): CryptoCurrencyStatus.NoAmount {
|
||||
return CryptoCurrencyStatus.NoAmount(
|
||||
priceChange = quoteStatus.priceChange,
|
||||
fiatRate = quoteStatus.fiatRate,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createCustom(
|
||||
id: CryptoCurrency.ID,
|
||||
status: NetworkStatus.Verified,
|
||||
amount: BigDecimal,
|
||||
quoteStatus: QuoteStatus?,
|
||||
yieldBalance: YieldBalance.Data?,
|
||||
): CryptoCurrencyStatus.Custom {
|
||||
return CryptoCurrencyStatus.Custom(
|
||||
amount = amount,
|
||||
fiatAmount = quoteStatus.fiatRate?.let { calculateFiatAmount(amount, it) },
|
||||
fiatRate = quoteStatus.fiatRate,
|
||||
priceChange = quoteStatus.priceChange,
|
||||
hasCurrentNetworkTransactions = status.hasCurrentNetworkTransactions(),
|
||||
pendingTransactions = status.getCurrentTransactions(id),
|
||||
networkAddress = status.address,
|
||||
yieldBalance = yieldBalance,
|
||||
yieldSupplyStatus = status.getYieldSupplyStatus(id),
|
||||
sources = CryptoCurrencyStatus.Sources(
|
||||
networkSource = status.source,
|
||||
yieldBalanceSource = yieldBalance?.source ?: StatusSource.ACTUAL,
|
||||
quoteSource = quoteStatus?.value?.source ?: StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createNoQuote(
|
||||
id: CryptoCurrency.ID,
|
||||
status: NetworkStatus.Verified,
|
||||
amount: BigDecimal,
|
||||
quoteStatus: QuoteStatus?,
|
||||
yieldBalance: YieldBalance.Data?,
|
||||
): CryptoCurrencyStatus.NoQuote {
|
||||
return CryptoCurrencyStatus.NoQuote(
|
||||
amount = amount,
|
||||
hasCurrentNetworkTransactions = status.hasCurrentNetworkTransactions(),
|
||||
pendingTransactions = status.getCurrentTransactions(id),
|
||||
networkAddress = status.address,
|
||||
yieldBalance = yieldBalance,
|
||||
yieldSupplyStatus = status.getYieldSupplyStatus(id),
|
||||
sources = CryptoCurrencyStatus.Sources(
|
||||
networkSource = status.source,
|
||||
yieldBalanceSource = yieldBalance?.source ?: StatusSource.ACTUAL,
|
||||
quoteSource = quoteStatus?.value?.source ?: StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createLoaded(
|
||||
id: CryptoCurrency.ID,
|
||||
status: NetworkStatus.Verified,
|
||||
amount: BigDecimal,
|
||||
quoteStatus: QuoteStatus.Data,
|
||||
yieldBalance: YieldBalance.Data?,
|
||||
): CryptoCurrencyStatus.Loaded {
|
||||
return CryptoCurrencyStatus.Loaded(
|
||||
amount = amount,
|
||||
fiatAmount = calculateFiatAmount(amount, quoteStatus.fiatRate),
|
||||
fiatRate = quoteStatus.fiatRate,
|
||||
priceChange = quoteStatus.priceChange,
|
||||
hasCurrentNetworkTransactions = status.hasCurrentNetworkTransactions(),
|
||||
pendingTransactions = status.getCurrentTransactions(id),
|
||||
networkAddress = status.address,
|
||||
yieldBalance = yieldBalance,
|
||||
yieldSupplyStatus = status.getYieldSupplyStatus(id),
|
||||
sources = CryptoCurrencyStatus.Sources(
|
||||
networkSource = status.source,
|
||||
yieldBalanceSource = yieldBalance?.source ?: StatusSource.ACTUAL,
|
||||
quoteSource = quoteStatus.source,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun NetworkStatus.Verified.hasCurrentNetworkTransactions() = pendingTransactions.isNotEmpty()
|
||||
|
||||
private fun NetworkStatus.Verified.getCurrentTransactions(id: CryptoCurrency.ID): Set<TxInfo> {
|
||||
return pendingTransactions.getOrElse(key = id, defaultValue = ::emptySet)
|
||||
}
|
||||
|
||||
private fun NetworkStatus.Verified.getYieldSupplyStatus(id: CryptoCurrency.ID): YieldSupplyStatus? {
|
||||
return yieldSupplyStatuses[id]
|
||||
}
|
||||
|
||||
private fun Option<YieldBalance>.getOrNull(id: CryptoCurrency.ID, address: NetworkAddress): YieldBalance.Data? {
|
||||
val yieldBalance = this.getOrNull() as? YieldBalance.Data ?: return null
|
||||
|
||||
val isCurrentAddressStaking = yieldBalance.stakingId.address == address.defaultAddress.value
|
||||
val filteredTokenBalances = yieldBalance.balance.items.filter {
|
||||
it.token.coinGeckoId == id.rawCurrencyId?.value
|
||||
}
|
||||
|
||||
return if (isCurrentAddressStaking && filteredTokenBalances.isNotEmpty()) {
|
||||
yieldBalance.copy(
|
||||
balance = yieldBalance.balance.copy(items = filteredTokenBalances),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateFiatAmount(amount: BigDecimal, fiatRate: BigDecimal): BigDecimal {
|
||||
return amount * fiatRate
|
||||
}
|
||||
}
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
package com.tangem.domain.tokens.operations
|
||||
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.NetworkStatus
|
||||
import com.tangem.domain.models.quote.QuoteStatus
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class CurrencyStatusOperations(
|
||||
private val currency: CryptoCurrency,
|
||||
private val quoteStatus: QuoteStatus?,
|
||||
private val networkStatus: NetworkStatus?,
|
||||
private val yieldBalance: YieldBalance?,
|
||||
private val ignoreQuote: Boolean,
|
||||
) {
|
||||
|
||||
private val QuoteStatus?.fiatRate: BigDecimal?
|
||||
get() = (this?.value as? QuoteStatus.Data)?.fiatRate
|
||||
|
||||
private val QuoteStatus?.priceChange: BigDecimal?
|
||||
get() = (this?.value as? QuoteStatus.Data)?.priceChange
|
||||
|
||||
fun createTokenStatus(): CryptoCurrencyStatus = CryptoCurrencyStatus(currency, createStatus())
|
||||
|
||||
private fun createStatus(): CryptoCurrencyStatus.Value {
|
||||
return when (val status = networkStatus?.value) {
|
||||
null -> CryptoCurrencyStatus.Loading
|
||||
is NetworkStatus.MissedDerivation -> createMissedDerivationStatus()
|
||||
is NetworkStatus.Unreachable -> createUnreachableStatus(status)
|
||||
is NetworkStatus.NoAccount -> createNoAccountStatus(status)
|
||||
is NetworkStatus.Verified -> createStatus(status, yieldBalance)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createMissedDerivationStatus(): CryptoCurrencyStatus.MissedDerivation =
|
||||
CryptoCurrencyStatus.MissedDerivation(priceChange = quoteStatus?.priceChange, fiatRate = quoteStatus?.fiatRate)
|
||||
|
||||
private fun createUnreachableStatus(status: NetworkStatus.Unreachable): CryptoCurrencyStatus.Unreachable {
|
||||
return CryptoCurrencyStatus.Unreachable(
|
||||
priceChange = quoteStatus?.priceChange,
|
||||
fiatRate = quoteStatus?.fiatRate,
|
||||
networkAddress = status.address,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createNoAccountStatus(status: NetworkStatus.NoAccount): CryptoCurrencyStatus.NoAccount {
|
||||
return CryptoCurrencyStatus.NoAccount(
|
||||
amountToCreateAccount = status.amountToCreateAccount,
|
||||
fiatAmount = if (quoteStatus == null) null else BigDecimal.ZERO,
|
||||
priceChange = quoteStatus?.priceChange,
|
||||
fiatRate = quoteStatus?.fiatRate,
|
||||
networkAddress = status.address,
|
||||
sources = CryptoCurrencyStatus.Sources(
|
||||
networkSource = status.source,
|
||||
quoteSource = quoteStatus?.value?.source ?: StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod", "LongMethod")
|
||||
private fun createStatus(
|
||||
networkStatusValue: NetworkStatus.Verified,
|
||||
yieldBalance: YieldBalance?,
|
||||
): CryptoCurrencyStatus.Value {
|
||||
val amount = when (val amount = networkStatusValue.amounts[currency.id]) {
|
||||
null -> {
|
||||
return CryptoCurrencyStatus.Loading
|
||||
}
|
||||
is NetworkStatus.Amount.NotFound -> {
|
||||
return CryptoCurrencyStatus.NoAmount(
|
||||
priceChange = quoteStatus?.priceChange,
|
||||
fiatRate = quoteStatus?.fiatRate,
|
||||
)
|
||||
}
|
||||
is NetworkStatus.Amount.Loaded -> amount.value
|
||||
}
|
||||
|
||||
val hasCurrentNetworkTransactions = networkStatusValue.pendingTransactions.isNotEmpty()
|
||||
val currentTransactions = networkStatusValue.pendingTransactions.getOrElse(currency.id, ::emptySet)
|
||||
val yieldBalanceData = yieldBalance as? YieldBalance.Data
|
||||
val isCurrentAddressStaking =
|
||||
yieldBalanceData?.stakingId?.address == networkStatusValue.address.defaultAddress.value
|
||||
val filteredTokenBalances = yieldBalanceData?.balance?.items?.filter {
|
||||
it.token.coinGeckoId == currency.id.rawCurrencyId?.value
|
||||
}
|
||||
val currentYieldBalance = if (isCurrentAddressStaking && filteredTokenBalances?.isNotEmpty() == true) {
|
||||
yieldBalanceData.copy(
|
||||
balance = yieldBalanceData.balance.copy(
|
||||
items = filteredTokenBalances,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val yieldSupplyStatus = networkStatusValue.yieldSupplyStatuses[currency.id]
|
||||
|
||||
val quoteValue = quoteStatus?.value
|
||||
|
||||
// order is important for correct total balance calculation
|
||||
return when {
|
||||
currency is CryptoCurrency.Token && currency.isCustom -> CryptoCurrencyStatus.Custom(
|
||||
amount = amount,
|
||||
fiatAmount = calculateFiatAmountOrNull(amount, quoteStatus?.fiatRate),
|
||||
fiatRate = quoteStatus?.fiatRate,
|
||||
priceChange = quoteStatus?.priceChange,
|
||||
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
|
||||
pendingTransactions = currentTransactions,
|
||||
networkAddress = networkStatusValue.address,
|
||||
yieldBalance = currentYieldBalance,
|
||||
yieldSupplyStatus = yieldSupplyStatus,
|
||||
sources = CryptoCurrencyStatus.Sources(
|
||||
networkSource = networkStatusValue.source,
|
||||
quoteSource = quoteStatus?.value?.source ?: StatusSource.ACTUAL,
|
||||
yieldBalanceSource = currentYieldBalance?.source ?: StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
quoteValue is QuoteStatus.Empty || ignoreQuote -> CryptoCurrencyStatus.NoQuote(
|
||||
amount = amount,
|
||||
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
|
||||
pendingTransactions = currentTransactions,
|
||||
networkAddress = networkStatusValue.address,
|
||||
yieldBalance = currentYieldBalance,
|
||||
yieldSupplyStatus = yieldSupplyStatus,
|
||||
sources = CryptoCurrencyStatus.Sources(
|
||||
networkSource = networkStatusValue.source,
|
||||
quoteSource = quoteStatus?.value?.source ?: StatusSource.ACTUAL,
|
||||
yieldBalanceSource = currentYieldBalance?.source ?: StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
quoteValue is QuoteStatus.Data -> CryptoCurrencyStatus.Loaded(
|
||||
amount = amount,
|
||||
fiatAmount = calculateFiatAmount(amount, quoteValue.fiatRate),
|
||||
fiatRate = quoteValue.fiatRate,
|
||||
priceChange = quoteValue.priceChange,
|
||||
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
|
||||
pendingTransactions = currentTransactions,
|
||||
networkAddress = networkStatusValue.address,
|
||||
yieldBalance = currentYieldBalance,
|
||||
yieldSupplyStatus = yieldSupplyStatus,
|
||||
sources = CryptoCurrencyStatus.Sources(
|
||||
networkSource = networkStatusValue.source,
|
||||
quoteSource = quoteValue.source,
|
||||
yieldBalanceSource = currentYieldBalance?.source ?: StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
else -> CryptoCurrencyStatus.Loading
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateFiatAmountOrNull(amount: BigDecimal, fiatRate: BigDecimal?): BigDecimal? {
|
||||
if (fiatRate == null) return null
|
||||
|
||||
return calculateFiatAmount(amount, fiatRate)
|
||||
}
|
||||
|
||||
private fun calculateFiatAmount(amount: BigDecimal, fiatRate: BigDecimal): BigDecimal {
|
||||
return amount * fiatRate
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,9 @@ package com.tangem.domain.tokens.utils
|
|||
import arrow.core.Either
|
||||
import arrow.core.NonEmptyList
|
||||
import arrow.core.raise.either
|
||||
import arrow.core.raise.recover
|
||||
import arrow.core.toNonEmptySetOrNull
|
||||
import arrow.core.toOption
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.NetworkStatus
|
||||
|
|
@ -12,11 +14,11 @@ import com.tangem.domain.models.quote.QuoteStatus
|
|||
import com.tangem.domain.models.staking.StakingID
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.staking.model.StakingIntegrationID
|
||||
import com.tangem.domain.tokens.operations.CryptoCurrencyStatusFactory
|
||||
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error
|
||||
import com.tangem.domain.tokens.operations.CurrencyStatusOperations
|
||||
|
||||
/**
|
||||
* Proxy creator of [CryptoCurrencyStatus]. Used [CurrencyStatusOperations] to create statuses.
|
||||
* Proxy creator of [CryptoCurrencyStatus]. Used [CryptoCurrencyStatusFactory] to create statuses.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
|
@ -28,20 +30,17 @@ class CurrencyStatusProxyCreator {
|
|||
maybeNetworkStatus: Either<Error, NetworkStatus?>,
|
||||
maybeYieldBalance: Either<Error, YieldBalance>?,
|
||||
): Either<Error, CryptoCurrencyStatus> = either {
|
||||
var quoteRetrievingFailed = false
|
||||
|
||||
val networkStatus = maybeNetworkStatus.bind()
|
||||
val quote = arrow.core.raise.recover({ maybeQuoteStatus.bind() }) {
|
||||
quoteRetrievingFailed = true
|
||||
null
|
||||
}
|
||||
val quote = recover(
|
||||
block = { maybeQuoteStatus.bind() },
|
||||
recover = { null },
|
||||
)
|
||||
val yieldBalance = maybeYieldBalance?.getOrNull()
|
||||
|
||||
createCurrencyStatus(
|
||||
currency = currency,
|
||||
quoteStatus = quote,
|
||||
networkStatus = networkStatus,
|
||||
ignoreQuote = quoteRetrievingFailed,
|
||||
yieldBalance = yieldBalance,
|
||||
)
|
||||
}
|
||||
|
|
@ -52,21 +51,8 @@ class CurrencyStatusProxyCreator {
|
|||
maybeNetworkStatuses: Either<Error, Set<NetworkStatus>>,
|
||||
maybeYieldBalances: Either<Error, List<YieldBalance>>,
|
||||
): Either<Error, List<CryptoCurrencyStatus>> = either {
|
||||
var quotesRetrievingFailed = false
|
||||
|
||||
val networksStatuses = maybeNetworkStatuses.bind().toNonEmptySetOrNull()
|
||||
val quoteStatuses: Set<QuoteStatus>? = maybeQuotes?.fold(
|
||||
ifLeft = {
|
||||
quotesRetrievingFailed = true
|
||||
null
|
||||
},
|
||||
ifRight = {
|
||||
it.ifEmpty {
|
||||
quotesRetrievingFailed = true
|
||||
null
|
||||
}
|
||||
},
|
||||
)
|
||||
val quoteStatuses: Set<QuoteStatus>? = maybeQuotes?.getOrNull()?.ifEmpty { null }
|
||||
|
||||
val yieldBalances = maybeYieldBalances.getOrNull()
|
||||
|
||||
|
|
@ -90,27 +76,22 @@ class CurrencyStatusProxyCreator {
|
|||
currency = currency,
|
||||
quoteStatus = quote,
|
||||
networkStatus = networkStatus,
|
||||
ignoreQuote = quotesRetrievingFailed,
|
||||
yieldBalance = yieldBalance,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun createCurrencyStatus(
|
||||
private fun createCurrencyStatus(
|
||||
currency: CryptoCurrency,
|
||||
quoteStatus: QuoteStatus?,
|
||||
networkStatus: NetworkStatus?,
|
||||
ignoreQuote: Boolean,
|
||||
yieldBalance: YieldBalance?,
|
||||
): CryptoCurrencyStatus {
|
||||
val currencyStatusOperations = CurrencyStatusOperations(
|
||||
return CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
quoteStatus = quoteStatus,
|
||||
networkStatus = networkStatus,
|
||||
ignoreQuote = ignoreQuote,
|
||||
yieldBalance = yieldBalance,
|
||||
maybeNetworkStatus = networkStatus.toOption(),
|
||||
maybeQuoteStatus = quoteStatus.toOption(),
|
||||
maybeYieldBalance = yieldBalance.toOption(),
|
||||
)
|
||||
|
||||
return currencyStatusOperations.createTokenStatus()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,686 @@
|
|||
package com.tangem.domain.tokens.operations
|
||||
|
||||
import arrow.core.none
|
||||
import arrow.core.some
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.NetworkAddress
|
||||
import com.tangem.domain.models.network.NetworkStatus
|
||||
import com.tangem.domain.models.network.NetworkStatus.Amount
|
||||
import com.tangem.domain.models.network.TxInfo
|
||||
import com.tangem.domain.models.quote.QuoteStatus
|
||||
import com.tangem.domain.models.staking.BalanceItem
|
||||
import com.tangem.domain.models.staking.StakingID
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.models.staking.YieldBalanceItem
|
||||
import com.tangem.domain.models.yield.supply.YieldSupplyStatus
|
||||
import com.tangem.domain.staking.model.StakingIntegrationID
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class CryptoCurrencyStatusFactoryTest {
|
||||
|
||||
private val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
|
||||
private val currency = cryptoCurrencyFactory.cardano
|
||||
|
||||
private val networkAddress = NetworkAddress.Single(
|
||||
defaultAddress = NetworkAddress.Address(
|
||||
value = "0x123",
|
||||
type = NetworkAddress.Address.Type.Primary,
|
||||
),
|
||||
)
|
||||
|
||||
private val fullQuote = QuoteStatus.Data(
|
||||
fiatRate = 1800.0.toBigDecimal(),
|
||||
priceChange = (-2.5).toBigDecimal(),
|
||||
source = StatusSource.ACTUAL,
|
||||
)
|
||||
private val emptyQuoteStatus = QuoteStatus.Empty.toStatus()
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class MissedDerivation {
|
||||
|
||||
private val networkStatus = NetworkStatus.MissedDerivation.toStatus()
|
||||
private val maybeYieldBalance = none<YieldBalance>() // not relevant for this test
|
||||
|
||||
@Test
|
||||
fun `network is MissedDerivation and QuoteStatus is Data`() {
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = fullQuote.toStatus().some(),
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.MissedDerivation(
|
||||
fiatRate = fullQuote.fiatRate,
|
||||
priceChange = fullQuote.priceChange,
|
||||
).toStatus()
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network is MissedDerivation and QuoteStatus is Empty`() {
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = emptyQuoteStatus.some(),
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.MissedDerivation(fiatRate = null, priceChange = null).toStatus()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network is MissedDerivation and QuoteStatus is null`() {
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = none(),
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.MissedDerivation(fiatRate = null, priceChange = null).toStatus()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class Unreachable {
|
||||
|
||||
private val networkStatus = NetworkStatus.Unreachable(address = networkAddress).toStatus()
|
||||
|
||||
private val maybeYieldBalance = none<YieldBalance>() // not relevant for this test
|
||||
|
||||
@Test
|
||||
fun `network is Unreachable and QuoteStatus is Data`() {
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = fullQuote.toStatus().some(),
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.Unreachable(
|
||||
fiatRate = fullQuote.fiatRate,
|
||||
priceChange = fullQuote.priceChange,
|
||||
networkAddress = networkAddress,
|
||||
).toStatus()
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network is Unreachable and QuoteStatus is Empty`() {
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = emptyQuoteStatus.some(),
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.Unreachable(
|
||||
fiatRate = null,
|
||||
priceChange = null,
|
||||
networkAddress = networkAddress,
|
||||
)
|
||||
.toStatus()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network is Unreachable and QuoteStatus is null`() {
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = none(),
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.Unreachable(
|
||||
fiatRate = null,
|
||||
priceChange = null,
|
||||
networkAddress = networkAddress,
|
||||
).toStatus()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class NoAccount {
|
||||
|
||||
private val networkStatus = NetworkStatus.NoAccount(
|
||||
address = networkAddress,
|
||||
amountToCreateAccount = BigDecimal.ONE,
|
||||
errorMessage = "error message",
|
||||
source = StatusSource.ACTUAL,
|
||||
).toStatus()
|
||||
|
||||
private val maybeYieldBalance = none<YieldBalance>() // not relevant for this test
|
||||
|
||||
@Test
|
||||
fun `network is NoAccount and QuoteStatus is Data`() {
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = fullQuote.toStatus().some(),
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.NoAccount(
|
||||
amountToCreateAccount = BigDecimal.ONE,
|
||||
fiatAmount = BigDecimal.ZERO,
|
||||
priceChange = fullQuote.priceChange,
|
||||
fiatRate = fullQuote.fiatRate,
|
||||
networkAddress = networkAddress,
|
||||
sources = CryptoCurrencyStatus.Sources(),
|
||||
).toStatus()
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network is NoAccount and QuoteStatus is Empty`() {
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = emptyQuoteStatus.some(),
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.NoAccount(
|
||||
amountToCreateAccount = BigDecimal.ONE,
|
||||
fiatAmount = BigDecimal.ZERO,
|
||||
priceChange = null,
|
||||
fiatRate = null,
|
||||
networkAddress = networkAddress,
|
||||
sources = CryptoCurrencyStatus.Sources(),
|
||||
).toStatus()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network is NoAccount and QuoteStatus is null`() {
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = none(),
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.NoAccount(
|
||||
amountToCreateAccount = BigDecimal.ONE,
|
||||
fiatAmount = BigDecimal.ZERO,
|
||||
priceChange = null,
|
||||
fiatRate = null,
|
||||
networkAddress = networkAddress,
|
||||
sources = CryptoCurrencyStatus.Sources(),
|
||||
).toStatus()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class NoAmount {
|
||||
|
||||
private val networkStatus = createVerified(amounts = mapOf(currency.id to Amount.NotFound)).toStatus()
|
||||
|
||||
private val maybeYieldBalance = none<YieldBalance>() // not relevant for this test
|
||||
|
||||
@Test
|
||||
fun `network is Verified with Amount is NotFound and QuoteStatus is Data`() {
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = fullQuote.toStatus().some(),
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.NoAmount(
|
||||
priceChange = fullQuote.priceChange,
|
||||
fiatRate = fullQuote.fiatRate,
|
||||
).toStatus()
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network is Verified with Amount is NotFound and QuoteStatus is Empty`() {
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = emptyQuoteStatus.some(),
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.NoAmount(priceChange = null, fiatRate = null).toStatus()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network is Verified with Amount is NotFound and QuoteStatus is null`() {
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = none(),
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.NoAmount(priceChange = null, fiatRate = null).toStatus()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class Loading {
|
||||
|
||||
private val maybeYieldBalance = none<YieldBalance>() // not relevant for this test
|
||||
|
||||
@Test
|
||||
fun `network is null`() {
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = none(),
|
||||
maybeQuoteStatus = none(), // not relevant for this test,
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.Loading.toStatus()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network is Verified with Amount is null`() {
|
||||
// Arrange
|
||||
val networkStatus = createVerified().toStatus()
|
||||
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = none(), // not relevant for this test,
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.Loading.toStatus()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network is Verified and QuoteStatus is null`() {
|
||||
// Arrange
|
||||
val networkStatus = createVerified(
|
||||
amounts = mapOf(currency.id to Amount.Loaded(value = BigDecimal.ZERO)),
|
||||
).toStatus()
|
||||
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = none(),
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.Loading.toStatus()
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class Custom {
|
||||
|
||||
private val currency = cryptoCurrencyFactory.createToken(blockchain = Blockchain.Cardano).copy(isCustom = true)
|
||||
|
||||
private val networkStatus = createVerified(
|
||||
amounts = mapOf(currency.id to Amount.Loaded(value = BigDecimal.TEN)),
|
||||
).toStatus()
|
||||
|
||||
@Test
|
||||
fun `network is Verified, QuoteStatus is null, YieldBalance is null`() {
|
||||
// Arrange
|
||||
val maybeQuoteStatus = none<QuoteStatus>()
|
||||
val maybeYieldBalance = none<YieldBalance>()
|
||||
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = maybeQuoteStatus,
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Custom(
|
||||
amount = BigDecimal.TEN,
|
||||
fiatAmount = null,
|
||||
fiatRate = null,
|
||||
priceChange = null,
|
||||
yieldBalance = null,
|
||||
yieldSupplyStatus = null,
|
||||
hasCurrentNetworkTransactions = false,
|
||||
pendingTransactions = emptySet(),
|
||||
networkAddress = networkAddress,
|
||||
sources = CryptoCurrencyStatus.Sources(),
|
||||
),
|
||||
)
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network is Verified, QuoteStatus is Data, YieldBalance is Data`() {
|
||||
// Arrange
|
||||
val yieldBalance = YieldBalance.Data(
|
||||
stakingId = StakingID(
|
||||
integrationId = StakingIntegrationID.Coin.Cardano.value,
|
||||
address = networkAddress.defaultAddress.value,
|
||||
),
|
||||
source = StatusSource.ACTUAL,
|
||||
balance = YieldBalanceItem(
|
||||
items = listOf(
|
||||
mockk<BalanceItem> {
|
||||
every { this@mockk.token.coinGeckoId } returns currency.id.rawCurrencyId?.value
|
||||
},
|
||||
mockk<BalanceItem> {
|
||||
every { this@mockk.token.coinGeckoId } returns "unknown"
|
||||
},
|
||||
),
|
||||
integrationId = StakingIntegrationID.Coin.Cardano.value,
|
||||
),
|
||||
)
|
||||
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = fullQuote.toStatus().some(),
|
||||
maybeYieldBalance = yieldBalance.some(),
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = CryptoCurrencyStatus.Custom(
|
||||
amount = BigDecimal.TEN,
|
||||
fiatAmount = BigDecimal.TEN * fullQuote.fiatRate,
|
||||
fiatRate = fullQuote.fiatRate,
|
||||
priceChange = fullQuote.priceChange,
|
||||
yieldBalance = yieldBalance.copy(
|
||||
balance = yieldBalance.balance.copy(
|
||||
items = yieldBalance.balance.items.subList(0, 1),
|
||||
),
|
||||
),
|
||||
yieldSupplyStatus = null,
|
||||
hasCurrentNetworkTransactions = false,
|
||||
pendingTransactions = emptySet(),
|
||||
networkAddress = networkAddress,
|
||||
sources = CryptoCurrencyStatus.Sources(),
|
||||
),
|
||||
)
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class NoQuote {
|
||||
|
||||
private val pendingTransactions = mapOf(currency.id to setOf(mockk<TxInfo>()))
|
||||
private val yieldSupplyStatuses = mapOf(currency.id to mockk<YieldSupplyStatus>())
|
||||
|
||||
private val networkStatus = createVerified(
|
||||
amounts = mapOf(currency.id to Amount.Loaded(value = BigDecimal.TEN)),
|
||||
pendingTransactions = pendingTransactions,
|
||||
yieldSupplyStatuses = yieldSupplyStatuses,
|
||||
).toStatus()
|
||||
|
||||
private val maybeQuoteStatus = emptyQuoteStatus.some()
|
||||
|
||||
@Test
|
||||
fun `network is Verified and YieldBalance is null`() {
|
||||
// Arrange
|
||||
val maybeYieldBalance = none<YieldBalance>()
|
||||
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = maybeQuoteStatus,
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.NoQuote(
|
||||
amount = BigDecimal.TEN,
|
||||
yieldBalance = null,
|
||||
yieldSupplyStatus = yieldSupplyStatuses[currency.id]!!,
|
||||
hasCurrentNetworkTransactions = true,
|
||||
pendingTransactions = pendingTransactions[currency.id]!!,
|
||||
networkAddress = networkAddress,
|
||||
sources = CryptoCurrencyStatus.Sources(),
|
||||
).toStatus()
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network is Verified and YieldBalance is Data`() {
|
||||
// Arrange
|
||||
val yieldBalance = YieldBalance.Data(
|
||||
stakingId = StakingID(
|
||||
integrationId = StakingIntegrationID.Coin.Cardano.value,
|
||||
address = networkAddress.defaultAddress.value,
|
||||
),
|
||||
source = StatusSource.ACTUAL,
|
||||
balance = YieldBalanceItem(
|
||||
items = listOf(
|
||||
mockk<BalanceItem> {
|
||||
every { this@mockk.token.coinGeckoId } returns currency.id.rawCurrencyId?.value
|
||||
},
|
||||
mockk<BalanceItem> {
|
||||
every { this@mockk.token.coinGeckoId } returns "unknown"
|
||||
},
|
||||
),
|
||||
integrationId = StakingIntegrationID.Coin.Cardano.value,
|
||||
),
|
||||
)
|
||||
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = maybeQuoteStatus,
|
||||
maybeYieldBalance = yieldBalance.some(),
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.NoQuote(
|
||||
amount = BigDecimal.TEN,
|
||||
yieldBalance = yieldBalance.copy(
|
||||
balance = yieldBalance.balance.copy(
|
||||
items = yieldBalance.balance.items.subList(0, 1),
|
||||
),
|
||||
),
|
||||
yieldSupplyStatus = yieldSupplyStatuses[currency.id]!!,
|
||||
hasCurrentNetworkTransactions = true,
|
||||
pendingTransactions = pendingTransactions[currency.id]!!,
|
||||
networkAddress = networkAddress,
|
||||
sources = CryptoCurrencyStatus.Sources(),
|
||||
).toStatus()
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
inner class Loaded {
|
||||
|
||||
private val networkStatus = createVerified(
|
||||
amounts = mapOf(currency.id to Amount.Loaded(value = BigDecimal.TEN)),
|
||||
).toStatus()
|
||||
|
||||
private val maybeQuoteStatus = fullQuote.toStatus().some()
|
||||
|
||||
@Test
|
||||
fun `network is Verified and YieldBalance is null`() {
|
||||
// Arrange
|
||||
val maybeYieldBalance = none<YieldBalance>()
|
||||
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = maybeQuoteStatus,
|
||||
maybeYieldBalance = maybeYieldBalance,
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.Loaded(
|
||||
amount = BigDecimal.TEN,
|
||||
fiatAmount = BigDecimal.TEN * fullQuote.fiatRate,
|
||||
fiatRate = fullQuote.fiatRate,
|
||||
priceChange = fullQuote.priceChange,
|
||||
yieldBalance = null,
|
||||
yieldSupplyStatus = null,
|
||||
hasCurrentNetworkTransactions = false,
|
||||
pendingTransactions = emptySet(),
|
||||
networkAddress = networkAddress,
|
||||
sources = CryptoCurrencyStatus.Sources(),
|
||||
).toStatus()
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `network is Verified and YieldBalance is Data`() {
|
||||
// Arrange
|
||||
val yieldBalance = YieldBalance.Data(
|
||||
stakingId = StakingID(
|
||||
integrationId = StakingIntegrationID.Coin.Cardano.value,
|
||||
address = networkAddress.defaultAddress.value,
|
||||
),
|
||||
source = StatusSource.ACTUAL,
|
||||
balance = YieldBalanceItem(
|
||||
items = listOf(
|
||||
mockk<BalanceItem> {
|
||||
every { this@mockk.token.coinGeckoId } returns currency.id.rawCurrencyId?.value
|
||||
},
|
||||
mockk<BalanceItem> {
|
||||
every { this@mockk.token.coinGeckoId } returns "unknown"
|
||||
},
|
||||
),
|
||||
integrationId = StakingIntegrationID.Coin.Cardano.value,
|
||||
),
|
||||
)
|
||||
|
||||
// Act
|
||||
val actual = CryptoCurrencyStatusFactory.create(
|
||||
currency = currency,
|
||||
maybeNetworkStatus = networkStatus.some(),
|
||||
maybeQuoteStatus = fullQuote.toStatus().some(),
|
||||
maybeYieldBalance = yieldBalance.some(),
|
||||
)
|
||||
|
||||
// Assert
|
||||
val expected = CryptoCurrencyStatus.Loaded(
|
||||
amount = BigDecimal.TEN,
|
||||
fiatAmount = BigDecimal.TEN * fullQuote.fiatRate,
|
||||
fiatRate = fullQuote.fiatRate,
|
||||
priceChange = fullQuote.priceChange,
|
||||
yieldBalance = yieldBalance.copy(
|
||||
balance = yieldBalance.balance.copy(
|
||||
items = yieldBalance.balance.items.subList(0, 1),
|
||||
),
|
||||
),
|
||||
yieldSupplyStatus = null,
|
||||
hasCurrentNetworkTransactions = false,
|
||||
pendingTransactions = emptySet(),
|
||||
networkAddress = networkAddress,
|
||||
sources = CryptoCurrencyStatus.Sources(),
|
||||
).toStatus()
|
||||
|
||||
Truth.assertThat(actual).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createVerified(
|
||||
amounts: Map<CryptoCurrency.ID, Amount> = emptyMap(),
|
||||
pendingTransactions: Map<CryptoCurrency.ID, Set<TxInfo>> = emptyMap(),
|
||||
yieldSupplyStatuses: Map<CryptoCurrency.ID, YieldSupplyStatus?> = emptyMap(),
|
||||
): NetworkStatus.Verified {
|
||||
return NetworkStatus.Verified(
|
||||
address = networkAddress,
|
||||
amounts = amounts,
|
||||
pendingTransactions = pendingTransactions,
|
||||
yieldSupplyStatuses = yieldSupplyStatuses,
|
||||
source = StatusSource.ACTUAL,
|
||||
)
|
||||
}
|
||||
|
||||
private fun NetworkStatus.Value.toStatus(): NetworkStatus {
|
||||
return NetworkStatus(network = currency.network, value = this)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.Value.toStatus(): CryptoCurrencyStatus {
|
||||
return CryptoCurrencyStatus(currency = currency, value = this)
|
||||
}
|
||||
|
||||
private fun QuoteStatus.Value.toStatus(): QuoteStatus {
|
||||
return when (this) {
|
||||
is QuoteStatus.Data -> QuoteStatus(rawCurrencyId = currency.id.rawCurrencyId!!, value = this)
|
||||
is QuoteStatus.Empty -> QuoteStatus(rawCurrencyId = currency.id.rawCurrencyId!!, value = this)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue