Updated on 2026-08-14
This commit is contained in:
parent
0bf2decb83
commit
36e7f2d07f
23 changed files with 212 additions and 70 deletions
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.domain.models
|
||||
|
||||
/**
|
||||
* Source of the status of any loaded data
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
enum class StatusSource {
|
||||
|
||||
/**
|
||||
* Status is loaded from the cache.
|
||||
* In most cases, it's a temporary value, then the source should become either [ACTUAL] or [ONLY_CACHE]
|
||||
*/
|
||||
CACHE,
|
||||
|
||||
/**
|
||||
* Status is updated by a data source that is of actual value.
|
||||
* In most cases, this is a remote data source. But it can also be a value that we have updated programmatically.
|
||||
*/
|
||||
ACTUAL,
|
||||
|
||||
/** Status is loaded from the cache and can't be updated with actual value */
|
||||
ONLY_CACHE,
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ plugins {
|
|||
|
||||
dependencies {
|
||||
implementation(projects.domain.core)
|
||||
implementation(projects.domain.models)
|
||||
|
||||
implementation(deps.kotlin.serialization)
|
||||
implementation(deps.jodatime)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.domain.staking.model.stakekit
|
||||
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import org.joda.time.DateTime
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -13,7 +14,7 @@ sealed class YieldBalance {
|
|||
override val integrationId: String?,
|
||||
override val address: String,
|
||||
val balance: YieldBalanceItem,
|
||||
val isCached: Boolean,
|
||||
val source: StatusSource,
|
||||
) : YieldBalance() {
|
||||
fun getTotalWithRewardsStakingBalance(): BigDecimal {
|
||||
return balance.items.sumOf { it.amount }
|
||||
|
|
@ -42,7 +43,7 @@ sealed class YieldBalance {
|
|||
data class Empty(
|
||||
override val integrationId: String?,
|
||||
override val address: String,
|
||||
val isCached: Boolean,
|
||||
val source: StatusSource,
|
||||
) : YieldBalance()
|
||||
|
||||
data class Error(override val integrationId: String?, override val address: String?) : YieldBalance()
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ dependencies {
|
|||
implementation(projects.core.analytics.models)
|
||||
|
||||
/** Project - Domain */
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.txhistory.models)
|
||||
implementation(projects.domain.staking.models)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.domain.tokens.model
|
||||
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -83,6 +84,7 @@ data class CryptoCurrencyStatus(
|
|||
* Represents a state where there is no account associated with the cryptocurrency
|
||||
*
|
||||
* @property amountToCreateAccount base reserve amount for account creation
|
||||
* @property source source of data
|
||||
*/
|
||||
data class NoAccount(
|
||||
val amountToCreateAccount: BigDecimal,
|
||||
|
|
@ -90,6 +92,7 @@ data class CryptoCurrencyStatus(
|
|||
override val priceChange: BigDecimal?,
|
||||
override val fiatRate: BigDecimal?,
|
||||
override val networkAddress: NetworkAddress,
|
||||
val source: StatusSource,
|
||||
) : Value(isError = false) {
|
||||
|
||||
override val amount: BigDecimal = BigDecimal.ZERO
|
||||
|
|
@ -105,6 +108,7 @@ data class CryptoCurrencyStatus(
|
|||
* @property hasCurrentNetworkTransactions Indicates if there are any transactions in progress related to the
|
||||
* cryptocurrency network.
|
||||
* @property pendingTransactions The current cryptocurrency transactions.
|
||||
* @property source source of data
|
||||
*/
|
||||
data class Loaded(
|
||||
override val amount: BigDecimal,
|
||||
|
|
@ -115,6 +119,7 @@ data class CryptoCurrencyStatus(
|
|||
override val hasCurrentNetworkTransactions: Boolean,
|
||||
override val pendingTransactions: Set<TxHistoryItem>,
|
||||
override val networkAddress: NetworkAddress,
|
||||
val source: StatusSource,
|
||||
) : Value(isError = false)
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.domain.tokens.model
|
||||
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -21,14 +22,14 @@ data class NetworkStatus(
|
|||
*/
|
||||
sealed class Value {
|
||||
|
||||
abstract val isCached: Boolean
|
||||
abstract val source: StatusSource
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the state where the network is refreshing.
|
||||
*/
|
||||
data object Refreshing : Value() {
|
||||
override val isCached: Boolean = false
|
||||
override val source: StatusSource = StatusSource.ACTUAL
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -37,14 +38,14 @@ data class NetworkStatus(
|
|||
* @property address Network addresses.
|
||||
*/
|
||||
data class Unreachable(val address: NetworkAddress?) : Value() {
|
||||
override val isCached: Boolean = false
|
||||
override val source: StatusSource = StatusSource.ACTUAL
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the state where a derivation has been missed.
|
||||
*/
|
||||
data object MissedDerivation : Value() {
|
||||
override val isCached: Boolean = false
|
||||
override val source: StatusSource = StatusSource.ACTUAL
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -54,14 +55,14 @@ data class NetworkStatus(
|
|||
* @property address Network addresses.
|
||||
* @property amounts A map containing the amounts associated with different cryptocurrencies within the network.
|
||||
* @property pendingTransactions A map containing pending transactions associated with different cryptocurrencies
|
||||
* @property isCached flag that determines whether the status is a cache
|
||||
* @property source source of data
|
||||
* within the network.
|
||||
*/
|
||||
data class Verified(
|
||||
val address: NetworkAddress,
|
||||
val amounts: Map<CryptoCurrency.ID, CryptoCurrencyAmountStatus>,
|
||||
val pendingTransactions: Map<CryptoCurrency.ID, Set<TxHistoryItem>>,
|
||||
override val isCached: Boolean,
|
||||
override val source: StatusSource,
|
||||
) : Value()
|
||||
|
||||
/**
|
||||
|
|
@ -70,12 +71,12 @@ data class NetworkStatus(
|
|||
* @property address Network addresses.
|
||||
* @property amountToCreateAccount The amount required to create an account within the network.
|
||||
* @property errorMessage error message
|
||||
* @property isCached flag that determines whether the status is a cache
|
||||
* @property source source of data
|
||||
*/
|
||||
data class NoAccount(
|
||||
val address: NetworkAddress,
|
||||
val amountToCreateAccount: BigDecimal,
|
||||
val errorMessage: String,
|
||||
override val isCached: Boolean,
|
||||
override val source: StatusSource,
|
||||
) : Value()
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.domain.tokens.model
|
||||
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import java.math.BigDecimal
|
||||
|
||||
sealed interface Quote {
|
||||
|
|
@ -19,12 +20,12 @@ sealed interface Quote {
|
|||
* @property rawCurrencyId The unique identifier of the cryptocurrency for which the financial information is provided.
|
||||
* @property fiatRate The current fiat exchange rate for the cryptocurrency.
|
||||
* @property priceChange The price change for the cryptocurrency.
|
||||
* @property isCached flag that determines whether the quote is a cache
|
||||
* @property source source of data
|
||||
*/
|
||||
data class Value(
|
||||
override val rawCurrencyId: CryptoCurrency.RawID,
|
||||
val fiatRate: BigDecimal,
|
||||
val priceChange: BigDecimal,
|
||||
val isCached: Boolean,
|
||||
val source: StatusSource,
|
||||
) : Quote
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.domain.tokens.operations
|
||||
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.tokens.model.*
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -49,17 +50,27 @@ internal class CurrencyStatusOperations(
|
|||
)
|
||||
}
|
||||
|
||||
private fun createNoAccountStatus(status: NetworkStatus.NoAccount): CryptoCurrencyStatus.NoAccount =
|
||||
CryptoCurrencyStatus.NoAccount(
|
||||
private fun createNoAccountStatus(status: NetworkStatus.NoAccount): CryptoCurrencyStatus.NoAccount {
|
||||
return CryptoCurrencyStatus.NoAccount(
|
||||
amountToCreateAccount = status.amountToCreateAccount,
|
||||
fiatAmount = if (quote == null) null else BigDecimal.ZERO,
|
||||
priceChange = quote?.priceChange,
|
||||
fiatRate = quote?.fiatRate,
|
||||
networkAddress = status.address,
|
||||
source = getResultStatusSource(
|
||||
sources = listOf(
|
||||
status.source,
|
||||
(quote as? Quote.Value)?.source ?: StatusSource.ACTUAL,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createStatus(status: NetworkStatus.Verified, yieldBalance: YieldBalance?): CryptoCurrencyStatus.Value {
|
||||
val amount = when (val amount = status.amounts[currency.id]) {
|
||||
private fun createStatus(
|
||||
networkStatusValue: NetworkStatus.Verified,
|
||||
yieldBalance: YieldBalance?,
|
||||
): CryptoCurrencyStatus.Value {
|
||||
val amount = when (val amount = networkStatusValue.amounts[currency.id]) {
|
||||
null -> {
|
||||
return CryptoCurrencyStatus.Loading
|
||||
}
|
||||
|
|
@ -69,10 +80,10 @@ internal class CurrencyStatusOperations(
|
|||
is CryptoCurrencyAmountStatus.Loaded -> amount.value
|
||||
}
|
||||
|
||||
val hasCurrentNetworkTransactions = status.pendingTransactions.isNotEmpty()
|
||||
val currentTransactions = status.pendingTransactions.getOrElse(currency.id, ::emptySet)
|
||||
val hasCurrentNetworkTransactions = networkStatusValue.pendingTransactions.isNotEmpty()
|
||||
val currentTransactions = networkStatusValue.pendingTransactions.getOrElse(currency.id, ::emptySet)
|
||||
val yieldBalanceData = yieldBalance as? YieldBalance.Data
|
||||
val isCurrentAddressStaking = yieldBalanceData?.address == status.address.defaultAddress.value
|
||||
val isCurrentAddressStaking = yieldBalanceData?.address == networkStatusValue.address.defaultAddress.value
|
||||
val filteredTokenBalances = yieldBalanceData?.balance?.items?.filter {
|
||||
it.token.coinGeckoId == currency.id.rawCurrencyId?.value
|
||||
}
|
||||
|
|
@ -94,14 +105,14 @@ internal class CurrencyStatusOperations(
|
|||
priceChange = quote?.priceChange,
|
||||
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
|
||||
pendingTransactions = currentTransactions,
|
||||
networkAddress = status.address,
|
||||
networkAddress = networkStatusValue.address,
|
||||
yieldBalance = currentYieldBalance,
|
||||
)
|
||||
quote is Quote.Empty || ignoreQuote -> CryptoCurrencyStatus.NoQuote(
|
||||
amount = amount,
|
||||
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
|
||||
pendingTransactions = currentTransactions,
|
||||
networkAddress = status.address,
|
||||
networkAddress = networkStatusValue.address,
|
||||
yieldBalance = currentYieldBalance,
|
||||
)
|
||||
quote is Quote.Value -> CryptoCurrencyStatus.Loaded(
|
||||
|
|
@ -111,8 +122,15 @@ internal class CurrencyStatusOperations(
|
|||
priceChange = quote.priceChange,
|
||||
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
|
||||
pendingTransactions = currentTransactions,
|
||||
networkAddress = status.address,
|
||||
networkAddress = networkStatusValue.address,
|
||||
yieldBalance = currentYieldBalance,
|
||||
source = getResultStatusSource(
|
||||
sources = listOf(
|
||||
networkStatusValue.source,
|
||||
currentYieldBalance?.source ?: StatusSource.ACTUAL,
|
||||
quote.source,
|
||||
),
|
||||
),
|
||||
)
|
||||
else -> CryptoCurrencyStatus.Loading
|
||||
}
|
||||
|
|
@ -127,4 +145,18 @@ internal class CurrencyStatusOperations(
|
|||
private fun calculateFiatAmount(amount: BigDecimal, fiatRate: BigDecimal): BigDecimal {
|
||||
return amount * fiatRate
|
||||
}
|
||||
|
||||
/*
|
||||
* ACTUAL, ACTUAL, ACTUAL -> ACTUAL
|
||||
* ACTUAL, ACTUAL, CACHE -> CACHE
|
||||
* ACTUAL, ACTUAL, ONLY_CACHE -> ONLY_CACHE
|
||||
* ACTUAL, CACHE, ONLY_CACHE -> ONLY_CACHE
|
||||
*/
|
||||
private fun getResultStatusSource(sources: List<StatusSource>): StatusSource {
|
||||
return when {
|
||||
sources.any { it == StatusSource.ONLY_CACHE } -> StatusSource.ONLY_CACHE
|
||||
sources.any { it == StatusSource.CACHE } -> StatusSource.CACHE
|
||||
else -> StatusSource.ACTUAL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.tokens.mock
|
|||
|
||||
import arrow.core.NonEmptySet
|
||||
import arrow.core.nonEmptySetOf
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyAmountStatus
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.model.NetworkAddress
|
||||
|
|
@ -76,7 +77,7 @@ internal object MockNetworks {
|
|||
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
|
||||
),
|
||||
errorMessage = "",
|
||||
isCached = false,
|
||||
source = StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -92,7 +93,7 @@ internal object MockNetworks {
|
|||
address = NetworkAddress.Single(
|
||||
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
|
||||
),
|
||||
isCached = false,
|
||||
source = StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -108,7 +109,7 @@ internal object MockNetworks {
|
|||
address = NetworkAddress.Single(
|
||||
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
|
||||
),
|
||||
isCached = false,
|
||||
source = StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -125,7 +126,7 @@ internal object MockNetworks {
|
|||
address = NetworkAddress.Single(
|
||||
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
|
||||
),
|
||||
isCached = false,
|
||||
source = StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.tokens.mock
|
||||
|
||||
import arrow.core.nonEmptySetOf
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Quote
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -12,70 +13,70 @@ internal object MockQuotes {
|
|||
rawCurrencyId = MockTokens.token1.id.rawCurrencyId!!,
|
||||
fiatRate = BigDecimal("1.23"),
|
||||
priceChange = BigDecimal("0.01"),
|
||||
isCached = false,
|
||||
source = StatusSource.ACTUAL,
|
||||
)
|
||||
|
||||
val quote2 = Quote.Value(
|
||||
rawCurrencyId = MockTokens.token2.id.rawCurrencyId!!,
|
||||
fiatRate = BigDecimal("2.34"),
|
||||
priceChange = BigDecimal("-0.02"),
|
||||
isCached = false,
|
||||
source = StatusSource.ACTUAL,
|
||||
)
|
||||
|
||||
val quote3 = Quote.Value(
|
||||
rawCurrencyId = MockTokens.token3.id.rawCurrencyId!!,
|
||||
fiatRate = BigDecimal("3.45"),
|
||||
priceChange = BigDecimal("0.03"),
|
||||
isCached = false,
|
||||
source = StatusSource.ACTUAL,
|
||||
)
|
||||
|
||||
val quote4 = Quote.Value(
|
||||
rawCurrencyId = MockTokens.token4.id.rawCurrencyId!!,
|
||||
fiatRate = BigDecimal("4.56"),
|
||||
priceChange = BigDecimal("-0.04"),
|
||||
isCached = false,
|
||||
source = StatusSource.ACTUAL,
|
||||
)
|
||||
|
||||
val quote5 = Quote.Value(
|
||||
rawCurrencyId = MockTokens.token5.id.rawCurrencyId!!,
|
||||
fiatRate = BigDecimal("5.67"),
|
||||
priceChange = BigDecimal("0.05"),
|
||||
isCached = false,
|
||||
source = StatusSource.ACTUAL,
|
||||
)
|
||||
|
||||
val quote6 = Quote.Value(
|
||||
rawCurrencyId = MockTokens.token6.id.rawCurrencyId!!,
|
||||
fiatRate = BigDecimal("6.78"),
|
||||
priceChange = BigDecimal("-0.06"),
|
||||
isCached = false,
|
||||
source = StatusSource.ACTUAL,
|
||||
)
|
||||
|
||||
val quote7 = Quote.Value(
|
||||
rawCurrencyId = MockTokens.token7.id.rawCurrencyId!!,
|
||||
fiatRate = BigDecimal("7.89"),
|
||||
priceChange = BigDecimal("0.07"),
|
||||
isCached = false,
|
||||
source = StatusSource.ACTUAL,
|
||||
)
|
||||
|
||||
val quote8 = Quote.Value(
|
||||
rawCurrencyId = MockTokens.token8.id.rawCurrencyId!!,
|
||||
fiatRate = BigDecimal("8.90"),
|
||||
priceChange = BigDecimal("-0.08"),
|
||||
isCached = false,
|
||||
source = StatusSource.ACTUAL,
|
||||
)
|
||||
|
||||
val quote9 = Quote.Value(
|
||||
rawCurrencyId = MockTokens.token9.id.rawCurrencyId!!,
|
||||
fiatRate = BigDecimal("9.01"),
|
||||
priceChange = BigDecimal("0.09"),
|
||||
isCached = false,
|
||||
source = StatusSource.ACTUAL,
|
||||
)
|
||||
|
||||
val quote10 = Quote.Value(
|
||||
rawCurrencyId = MockTokens.token10.id.rawCurrencyId!!,
|
||||
fiatRate = BigDecimal("10.12"),
|
||||
priceChange = BigDecimal("-0.10"),
|
||||
isCached = false,
|
||||
source = StatusSource.ACTUAL,
|
||||
)
|
||||
|
||||
val quote11 = Quote.Empty(CryptoCurrency.RawID("null"))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.tokens.mock
|
||||
|
||||
import arrow.core.nonEmptyListOf
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.tokens.model.*
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -74,6 +75,7 @@ internal object MockTokensStates {
|
|||
networkAddress = NetworkAddress.Single(
|
||||
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
|
||||
),
|
||||
source = StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -87,6 +89,7 @@ internal object MockTokensStates {
|
|||
networkAddress = NetworkAddress.Single(
|
||||
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
|
||||
),
|
||||
source = StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -100,6 +103,7 @@ internal object MockTokensStates {
|
|||
networkAddress = NetworkAddress.Single(
|
||||
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
|
||||
),
|
||||
source = StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -113,6 +117,7 @@ internal object MockTokensStates {
|
|||
networkAddress = NetworkAddress.Single(
|
||||
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
|
||||
),
|
||||
source = StatusSource.ACTUAL,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -157,6 +162,7 @@ internal object MockTokensStates {
|
|||
hasCurrentNetworkTransactions = false,
|
||||
networkAddress = requireNotNull(networkStatus.value as? NetworkStatus.Verified).address,
|
||||
yieldBalance = null,
|
||||
source = StatusSource.ACTUAL,
|
||||
)
|
||||
}
|
||||
status.copy(value = value)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue