Updated on 2026-08-14

This commit is contained in:
Tangem 2025-05-30 17:36:17 +04:00
parent 9cc51216bc
commit c8c122b848
34 changed files with 383 additions and 303 deletions

View file

@ -1,14 +1,14 @@
package com.tangem.common.test.data.quote
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
import com.tangem.datasource.local.quote.converter.QuoteConverter
import com.tangem.datasource.local.quote.converter.QuoteStatusConverter
import com.tangem.domain.models.StatusSource
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
fun QuotesResponse.Quote.toDomain(rawCurrencyId: String, source: StatusSource = StatusSource.ACTUAL): Quote {
return QuoteConverter(source = source).convert(value = mapOf(rawCurrencyId to this).entries.first())
fun QuotesResponse.Quote.toDomain(rawCurrencyId: String, source: StatusSource = StatusSource.ACTUAL): QuoteStatus {
return QuoteStatusConverter(source = source).convert(value = mapOf(rawCurrencyId to this).entries.first())
}
fun Pair<String, QuotesResponse.Quote>.toDomain(source: StatusSource = StatusSource.ACTUAL): Quote {
return QuoteConverter(source = source).convert(value = mapOf(this).entries.first())
fun Pair<String, QuotesResponse.Quote>.toDomain(source: StatusSource = StatusSource.ACTUAL): QuoteStatus {
return QuoteStatusConverter(source = source).convert(value = mapOf(this).entries.first())
}

View file

@ -3,21 +3,20 @@ package com.tangem.datasource.local.quote.converter
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.orZero
/**
* Converter from [QuotesResponse.Quote] to [Quote.Value]
* Converter from [QuotesResponse.Quote] to [QuoteStatus]
*
* @property source status source
*
[REDACTED_AUTHOR]
*/
class QuoteConverter(
class QuoteStatusConverter(
private val source: StatusSource,
) :
Converter<Map.Entry<String, QuotesResponse.Quote>, Quote.Value> {
) : Converter<Map.Entry<String, QuotesResponse.Quote>, QuoteStatus> {
/**
* Secondary constructor
@ -28,14 +27,16 @@ class QuoteConverter(
source = if (isCached) StatusSource.CACHE else StatusSource.ACTUAL,
)
override fun convert(value: Map.Entry<String, QuotesResponse.Quote>): Quote.Value {
override fun convert(value: Map.Entry<String, QuotesResponse.Quote>): QuoteStatus {
val (currencyId, quote) = value
return Quote.Value(
return QuoteStatus(
rawCurrencyId = CryptoCurrency.RawID(currencyId),
fiatRate = quote.price.orZero(),
priceChange = quote.priceChange24h.orZero().movePointLeft(2),
source = source,
value = QuoteStatus.Data(
source = source,
fiatRate = quote.price.orZero(),
priceChange = quote.priceChange24h.orZero().movePointLeft(2),
),
)
}
}

View file

@ -11,7 +11,7 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.onramp.model.HotCryptoCurrency
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.utils.converter.Converter
import java.math.BigDecimal
@ -40,7 +40,7 @@ internal class HotCryptoCurrencyConverter(
return HotCryptoCurrency(
cryptoCurrency = currency.setupIconUrl(id = rawId.value),
quote = createQuote(
quoteStatus = createQuote(
fiatRate = value.currentPrice,
priceChange = value.priceChangePercentage,
rawCurrencyId = rawId,
@ -89,16 +89,18 @@ internal class HotCryptoCurrencyConverter(
fiatRate: BigDecimal?,
priceChange: BigDecimal?,
rawCurrencyId: CryptoCurrency.RawID,
): Quote {
): QuoteStatus {
return if (fiatRate != null && priceChange != null) {
Quote.Value(
QuoteStatus(
rawCurrencyId = rawCurrencyId,
fiatRate = fiatRate,
priceChange = priceChange.movePointLeft(2),
source = StatusSource.ACTUAL, // It doesn't matter
value = QuoteStatus.Data(
fiatRate = fiatRate,
priceChange = priceChange.movePointLeft(2),
source = StatusSource.ACTUAL, // It doesn't matter
),
)
} else {
Quote.Empty(rawCurrencyId)
QuoteStatus(rawCurrencyId = rawCurrencyId)
}
}

View file

@ -3,7 +3,7 @@ package com.tangem.data.quotes
import com.tangem.data.quotes.store.QuotesStoreV2
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.quotes.QuotesRepositoryV2
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import javax.inject.Inject
/**
@ -17,9 +17,10 @@ internal class DefaultQuotesRepositoryV2 @Inject constructor(
private val quotesStore: QuotesStoreV2,
) : QuotesRepositoryV2 {
override suspend fun getMultiQuoteSyncOrNull(currenciesIds: Set<CryptoCurrency.RawID>): Set<Quote>? {
override suspend fun getMultiQuoteSyncOrNull(currenciesIds: Set<CryptoCurrency.RawID>): Set<QuoteStatus>? {
return quotesStore.getAllSyncOrNull()?.mapTo(hashSetOf()) {
it.takeIf { it.rawCurrencyId in currenciesIds } ?: Quote.Empty(it.rawCurrencyId)
it.takeIf { it.rawCurrencyId in currenciesIds }
?: QuoteStatus(rawCurrencyId = it.rawCurrencyId)
}
}
}

View file

@ -7,7 +7,7 @@ import com.tangem.datasource.appcurrency.AppCurrencyResponseStore
import com.tangem.domain.core.utils.EitherFlow
import com.tangem.domain.quotes.multi.MultiQuoteFetcher
import com.tangem.domain.quotes.multi.MultiQuoteUpdater
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
@ -61,7 +61,7 @@ internal class DefaultMultiQuoteUpdater @Inject constructor(
.filterNotNull()
.mapLatest { appCurrency ->
val currenciesIds = quotesStore.getAllSyncOrNull().orEmpty()
.mapTo(destination = hashSetOf(), transform = Quote::rawCurrencyId)
.mapTo(destination = hashSetOf(), transform = QuoteStatus::rawCurrencyId)
multiQuoteFetcher(
params = MultiQuoteFetcher.Params(currenciesIds = currenciesIds, appCurrencyId = appCurrency.id),

View file

@ -2,7 +2,7 @@ package com.tangem.data.quotes.single
import com.tangem.data.quotes.store.QuotesStoreV2
import com.tangem.domain.quotes.single.SingleQuoteProducer
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@ -24,9 +24,9 @@ internal class DefaultSingleQuoteProducer @AssistedInject constructor(
private val dispatchers: CoroutineDispatcherProvider,
) : SingleQuoteProducer {
override val fallback: Quote = Quote.Empty(rawCurrencyId = params.rawCurrencyId)
override val fallback: QuoteStatus = QuoteStatus(rawCurrencyId = params.rawCurrencyId)
override fun produce(): Flow<Quote> {
override fun produce(): Flow<QuoteStatus> {
return quotesStore.get()
.mapNotNull { quotes -> quotes.firstOrNull { it.rawCurrencyId == params.rawCurrencyId } }
.distinctUntilChanged()

View file

@ -3,10 +3,10 @@ package com.tangem.data.quotes.store
import androidx.datastore.core.DataStore
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.datasource.local.quote.converter.QuoteConverter
import com.tangem.datasource.local.quote.converter.QuoteStatusConverter
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.addOrReplace
import kotlinx.coroutines.CoroutineScope
@ -26,7 +26,7 @@ internal typealias CurrencyIdWithQuote = Map<String, QuotesResponse.Quote>
* @param dispatchers dispatchers
*/
internal class DefaultQuotesStoreV2(
private val runtimeStore: RuntimeSharedStore<Set<Quote>>,
private val runtimeStore: RuntimeSharedStore<Set<QuoteStatus>>,
private val persistenceDataStore: DataStore<CurrencyIdWithQuote>,
dispatchers: CoroutineDispatcherProvider,
) : QuotesStoreV2 {
@ -40,14 +40,14 @@ internal class DefaultQuotesStoreV2(
if (cachedStatuses.isNullOrEmpty()) return@launch
runtimeStore.store(
value = QuoteConverter(isCached = true).convertSet(input = cachedStatuses.entries),
value = QuoteStatusConverter(isCached = true).convertSet(input = cachedStatuses.entries),
)
}
}
override fun get(): Flow<Set<Quote>> = runtimeStore.get()
override fun get(): Flow<Set<QuoteStatus>> = runtimeStore.get()
override suspend fun getAllSyncOrNull(): Set<Quote>? = runtimeStore.getSyncOrNull()
override suspend fun getAllSyncOrNull(): Set<QuoteStatus>? = runtimeStore.getSyncOrNull()
override suspend fun refresh(currenciesIds: Set<CryptoCurrency.RawID>) {
updateStatusSourceInRuntime(currenciesIds = currenciesIds, source = StatusSource.CACHE)
@ -56,7 +56,7 @@ internal class DefaultQuotesStoreV2(
override suspend fun storeActual(values: Map<String, QuotesResponse.Quote>) {
coroutineScope {
launch {
val quotes = QuoteConverter(isCached = false).convertSet(input = values.entries)
val quotes = QuoteStatusConverter(isCached = false).convertSet(input = values.entries)
storeInRuntimeStore(values = quotes)
}
launch { storeInPersistenceStore(values = values) }
@ -66,14 +66,14 @@ internal class DefaultQuotesStoreV2(
override suspend fun storeError(currenciesIds: Set<CryptoCurrency.RawID>) {
updateStatusSourceInRuntime(
currenciesIds = currenciesIds,
ifNotFound = Quote::Empty,
ifNotFound = ::QuoteStatus,
source = StatusSource.ONLY_CACHE,
)
}
private suspend fun updateStatusSourceInRuntime(
currenciesIds: Set<CryptoCurrency.RawID>,
ifNotFound: (CryptoCurrency.RawID) -> Quote? = { null },
ifNotFound: (CryptoCurrency.RawID) -> QuoteStatus? = { null },
source: StatusSource,
) {
runtimeStore.update(default = emptySet()) { stored ->
@ -82,14 +82,14 @@ internal class DefaultQuotesStoreV2(
?: ifNotFound(id)
?: return@mapNotNullTo null
quote.copySealed(source = source)
quote.copy(value = quote.value.copySealed(source = source))
}
stored.addOrReplace(items = updatedQuotes) { old, new -> old.rawCurrencyId == new.rawCurrencyId }
}
}
private suspend fun storeInRuntimeStore(values: Set<Quote>) {
private suspend fun storeInRuntimeStore(values: Set<QuoteStatus>) {
runtimeStore.update(default = emptySet()) { saved ->
saved.addOrReplace(items = values) { prev, new -> prev.rawCurrencyId == new.rawCurrencyId }
}

View file

@ -2,17 +2,17 @@ package com.tangem.data.quotes.store
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import kotlinx.coroutines.flow.Flow
/** Store of [Quote]'es set */
/** Store of [QuoteStatus]'es set */
internal interface QuotesStoreV2 {
/** Get flow of quotes */
fun get(): Flow<Set<Quote>>
fun get(): Flow<Set<QuoteStatus>>
/** Get all quotes synchronously or null */
suspend fun getAllSyncOrNull(): Set<Quote>?
suspend fun getAllSyncOrNull(): Set<QuoteStatus>?
/** Refresh status of [currenciesIds] */
suspend fun refresh(currenciesIds: Set<CryptoCurrency.RawID>)

View file

@ -23,7 +23,7 @@ import java.math.BigDecimal
/**
[REDACTED_AUTHOR]
*/
internal class DefaultMultiQuoteFetcherTest {
internal class DefaultMultiQuoteStatusFetcherTest {
private val tangemTechApi = mockk<TangemTechApi>(relaxed = true)
private val appCurrencyResponseStore = mockk<AppCurrencyResponseStore>(relaxed = true)

View file

@ -16,7 +16,7 @@ import org.junit.Test
/**
[REDACTED_AUTHOR]
*/
internal class DefaultMultiQuoteUpdaterTest {
internal class DefaultMultiQuoteStatusUpdaterTest {
private val appCurrencyResponseStore: AppCurrencyResponseStore = mockk()
private val quotesStore: QuotesStoreV2 = mockk()

View file

@ -21,7 +21,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Test
import java.math.BigDecimal
internal class DefaultSingleQuoteFetcherTest {
internal class DefaultSingleQuoteStatusFetcherTest {
private val tangemTechApi = mockk<TangemTechApi>(relaxed = true)
private val appCurrencyResponseStore = mockk<AppCurrencyResponseStore>(relaxed = true)

View file

@ -6,7 +6,7 @@ import com.tangem.data.quotes.store.QuotesStoreV2
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.quotes.single.SingleQuoteProducer
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.every
import io.mockk.mockk
@ -19,7 +19,7 @@ import java.math.BigDecimal
/**
[REDACTED_AUTHOR]
*/
internal class DefaultSingleQuoteProducerTest {
internal class DefaultSingleQuoteStatusProducerTest {
private val params = SingleQuoteProducer.Params(
rawCurrencyId = CryptoCurrency.RawID(value = "BTC"),
@ -35,11 +35,11 @@ internal class DefaultSingleQuoteProducerTest {
@Test
fun `test that flow is mapped for network from params`() = runTest {
val status = Quote.Empty(rawCurrencyId = params.rawCurrencyId)
val status = QuoteStatus(rawCurrencyId = params.rawCurrencyId)
val storeQuote = flowOf(
setOf(
status,
Quote.Empty(rawCurrencyId = CryptoCurrency.RawID(value = "ETH")),
QuoteStatus(rawCurrencyId = CryptoCurrency.RawID(value = "ETH")),
),
)
@ -57,7 +57,7 @@ internal class DefaultSingleQuoteProducerTest {
@Test
fun `test that flow is updated if quote is updated`() = runTest {
val storeQuote = MutableSharedFlow<Set<Quote>>(replay = 2, extraBufferCapacity = 1)
val storeQuote = MutableSharedFlow<Set<QuoteStatus>>(replay = 2, extraBufferCapacity = 1)
every { quotesStore.get() } returns storeQuote
@ -66,7 +66,7 @@ internal class DefaultSingleQuoteProducerTest {
verify { quotesStore.get() }
// first emit
val status = Quote.Empty(rawCurrencyId = params.rawCurrencyId)
val status = QuoteStatus(rawCurrencyId = params.rawCurrencyId)
storeQuote.emit(value = setOf(status))
val values1 = getEmittedValues(flow = actual)
@ -75,11 +75,13 @@ internal class DefaultSingleQuoteProducerTest {
Truth.assertThat(values1).isEqualTo(listOf(status))
// second emit
val updatedStatus = Quote.Value(
val updatedStatus = QuoteStatus(
rawCurrencyId = params.rawCurrencyId,
fiatRate = BigDecimal.ONE,
priceChange = BigDecimal.ZERO,
source = StatusSource.ACTUAL,
value = QuoteStatus.Data(
fiatRate = BigDecimal.ONE,
priceChange = BigDecimal.ZERO,
source = StatusSource.ACTUAL,
),
)
storeQuote.emit(value = setOf(updatedStatus))
@ -91,7 +93,7 @@ internal class DefaultSingleQuoteProducerTest {
@Test
fun `test that flow is filtered the same status`() = runTest {
val storeQuote = MutableSharedFlow<Set<Quote>>(replay = 2, extraBufferCapacity = 1)
val storeQuote = MutableSharedFlow<Set<QuoteStatus>>(replay = 2, extraBufferCapacity = 1)
every { quotesStore.get() } returns storeQuote
@ -100,7 +102,7 @@ internal class DefaultSingleQuoteProducerTest {
verify { quotesStore.get() }
// first emit
val status = Quote.Empty(rawCurrencyId = params.rawCurrencyId)
val status = QuoteStatus(rawCurrencyId = params.rawCurrencyId)
storeQuote.emit(value = setOf(status))
val values1 = getEmittedValues(flow = actual)
@ -120,11 +122,13 @@ internal class DefaultSingleQuoteProducerTest {
@Test
fun `test if flow throws exception`() = runTest {
val exception = IllegalStateException()
val status = Quote.Value(
val status = QuoteStatus(
rawCurrencyId = params.rawCurrencyId,
fiatRate = BigDecimal.ONE,
priceChange = BigDecimal.ZERO,
source = StatusSource.ACTUAL,
value = QuoteStatus.Data(
fiatRate = BigDecimal.ONE,
priceChange = BigDecimal.ZERO,
source = StatusSource.ACTUAL,
),
)
val innerFlow = MutableStateFlow(value = false)
@ -146,7 +150,7 @@ internal class DefaultSingleQuoteProducerTest {
val values1 = getEmittedValues(flow = actual)
Truth.assertThat(values1.size).isEqualTo(1)
val fallbackStatus = Quote.Empty(rawCurrencyId = params.rawCurrencyId)
val fallbackStatus = QuoteStatus(rawCurrencyId = params.rawCurrencyId)
Truth.assertThat(values1).isEqualTo(listOf(fallbackStatus))
innerFlow.emit(value = true)
@ -160,7 +164,7 @@ internal class DefaultSingleQuoteProducerTest {
fun `test if flow doesn't contain network from params`() = runTest {
val storeFlow = flowOf(
setOf(
Quote.Empty(rawCurrencyId = CryptoCurrency.RawID(value = "ETH")),
QuoteStatus(rawCurrencyId = CryptoCurrency.RawID(value = "ETH")),
),
)

View file

@ -6,7 +6,7 @@ import com.tangem.common.test.data.quote.toDomain
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.common.test.utils.getEmittedValues
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import kotlinx.coroutines.test.runTest
import org.junit.Test
@ -17,7 +17,7 @@ import java.math.BigDecimal
*/
internal class QuotesStoreGetMethodTest {
private val runtimeStore = RuntimeSharedStore<Set<Quote>>()
private val runtimeStore = RuntimeSharedStore<Set<QuoteStatus>>()
private val persistenceStore = MockStateDataStore<CurrencyIdWithQuote>(default = emptyMap())
private val store = DefaultQuotesStoreV2(
@ -32,7 +32,7 @@ internal class QuotesStoreGetMethodTest {
val values = getEmittedValues(flow = actual)
Truth.assertThat(values).isEqualTo(emptyList<Set<Quote>>())
Truth.assertThat(values).isEqualTo(emptyList<Set<QuoteStatus>>())
}
@Test
@ -43,7 +43,7 @@ internal class QuotesStoreGetMethodTest {
val values = getEmittedValues(flow = actual)
Truth.assertThat(values).isEqualTo(listOf(emptySet<Quote>()))
Truth.assertThat(values).isEqualTo(listOf(emptySet<QuoteStatus>()))
}
@Test
@ -74,7 +74,7 @@ internal class QuotesStoreGetMethodTest {
val actual = store.getAllSyncOrNull()
Truth.assertThat(actual).isEqualTo(emptySet<Quote>())
Truth.assertThat(actual).isEqualTo(emptySet<QuoteStatus>())
}
@Test

View file

@ -7,7 +7,7 @@ import com.tangem.common.test.data.quote.toDomain
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.StatusSource
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.every
import io.mockk.mockk
@ -23,7 +23,7 @@ internal class QuotesStoreInitializationTest {
@Test
fun `test initialization if cache store is empty`() = runTest {
val runtimeStore = RuntimeSharedStore<Set<Quote>>()
val runtimeStore = RuntimeSharedStore<Set<QuoteStatus>>()
val persistenceStore: DataStore<CurrencyIdWithQuote> = mockk()
every { persistenceStore.data } returns emptyFlow()
@ -39,7 +39,7 @@ internal class QuotesStoreInitializationTest {
@Test
fun `test initialization if cache store contains empty map`() = runTest {
val runtimeStore = RuntimeSharedStore<Set<Quote>>()
val runtimeStore = RuntimeSharedStore<Set<QuoteStatus>>()
val persistenceStore = MockStateDataStore<CurrencyIdWithQuote>(default = emptyMap())
DefaultQuotesStoreV2(
@ -53,7 +53,7 @@ internal class QuotesStoreInitializationTest {
@Test
fun `test initialization if cache store is not empty`() = runTest {
val runtimeStore = RuntimeSharedStore<Set<Quote>>()
val runtimeStore = RuntimeSharedStore<Set<QuoteStatus>>()
val persistenceStore = MockStateDataStore<CurrencyIdWithQuote>(default = emptyMap())
val btcQuote = "BTC" to MockQuoteResponseFactory.createSinglePrice(BigDecimal.ZERO)

View file

@ -7,7 +7,7 @@ import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.test.runTest
@ -19,7 +19,7 @@ import java.math.BigDecimal
*/
internal class QuotesStoreUpdateMethodsTest {
private val runtimeStore = RuntimeSharedStore<Set<Quote>>()
private val runtimeStore = RuntimeSharedStore<Set<QuoteStatus>>()
private val persistenceStore = MockStateDataStore<CurrencyIdWithQuote>(default = emptyMap())
private val store = DefaultQuotesStoreV2(
@ -37,8 +37,8 @@ internal class QuotesStoreUpdateMethodsTest {
store.refresh(currenciesIds = currenciesIds)
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(emptySet<Quote>())
Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap<String, Set<Quote>>())
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(emptySet<QuoteStatus>())
Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap<String, Set<QuoteStatus>>())
}
@Test
@ -50,10 +50,12 @@ internal class QuotesStoreUpdateMethodsTest {
store.refresh(currenciesIds = setOf(quote.rawCurrencyId))
val runtimeExpected = setOf(quote.copySealed(source = StatusSource.CACHE))
val runtimeExpected = setOf(
quote.copy(value = quote.value.copySealed(source = StatusSource.CACHE)),
)
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected)
Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap<String, Set<Quote>>())
Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap<String, Set<QuoteStatus>>())
}
@Test
@ -94,10 +96,12 @@ internal class QuotesStoreUpdateMethodsTest {
store.storeError(currenciesIds = currenciesIds)
val runtimeExpected = currenciesIds.map(Quote::Empty).toSet()
val runtimeExpected = currenciesIds
.map { QuoteStatus(rawCurrencyId = it, value = QuoteStatus.Empty) }
.toSet()
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected)
Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap<String, Set<Quote>>())
Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap<String, Set<QuoteStatus>>())
}
@Test
@ -107,7 +111,7 @@ internal class QuotesStoreUpdateMethodsTest {
runtimeStore.store(
value = setOf(
status.toDomain(rawCurrencyId = "BTC", source = StatusSource.CACHE),
Quote.Empty(rawCurrencyId = CryptoCurrency.RawID(value = "ETH")),
QuoteStatus(rawCurrencyId = CryptoCurrency.RawID(value = "ETH"), value = QuoteStatus.Empty),
),
)
@ -120,10 +124,10 @@ internal class QuotesStoreUpdateMethodsTest {
val runtimeExpected = setOf(
status.toDomain(rawCurrencyId = "BTC", source = StatusSource.ONLY_CACHE),
Quote.Empty(rawCurrencyId = CryptoCurrency.RawID(value = "ETH")),
QuoteStatus(rawCurrencyId = CryptoCurrency.RawID(value = "ETH"), value = QuoteStatus.Empty),
)
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected)
Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap<String, Set<Quote>>())
Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap<String, Set<QuoteStatus>>())
}
}

View file

@ -6,7 +6,7 @@ import arrow.core.toOption
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.quotes.single.SingleQuoteProducer
import com.tangem.domain.quotes.single.SingleQuoteSupplier
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flowOf
@ -20,13 +20,13 @@ class GetCurrencyQuotesUseCase(
currencyID: CryptoCurrency.ID,
interval: PriceChangeInterval,
refresh: Boolean,
): Flow<Option<Quote.Value>> {
): Flow<Option<QuoteStatus.Data>> {
val rawId = currencyID.rawCurrencyId ?: return flowOf(None)
return singleQuoteSupplier(
params = SingleQuoteProducer.Params(rawCurrencyId = rawId),
)
.map { (it as? Quote.Value).toOption() }
.map { (it.value as? QuoteStatus.Data).toOption() }
.catch { emit(None) }
}
}

View file

@ -6,7 +6,7 @@ import com.tangem.domain.nft.models.NFTSalePrice
import com.tangem.domain.nft.repository.NFTRepository
import com.tangem.domain.quotes.single.SingleQuoteProducer
import com.tangem.domain.quotes.single.SingleQuoteSupplier
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.fold
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@ -31,12 +31,15 @@ class GetNFTPriceUseCase(
assetId = nftAsset.id,
)
val quoteValue = quote as? Quote.Value
if (nftPrice !is NFTSalePrice.Value) {
nftPrice
} else {
nftPrice.copy(fiatValue = quoteValue?.fiatRate?.multiply(nftPrice.value))
nftPrice.copy(
fiatValue = quote.fold(
onData = { fiatRate.multiply(nftPrice.value) },
onEmpty = { null },
),
)
}
}
}

View file

@ -1,17 +1,17 @@
package com.tangem.domain.onramp.model
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
/**
* Hot crypto currency
*
* @property cryptoCurrency crypto currency
* @property quote quote
* @property quoteStatus quote status
*
[REDACTED_AUTHOR]
*/
data class HotCryptoCurrency(
val cryptoCurrency: CryptoCurrency,
val quote: Quote,
val quoteStatus: QuoteStatus,
)

View file

@ -1,7 +1,7 @@
package com.tangem.domain.quotes
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
/**
* Quotes repository
@ -11,5 +11,5 @@ import com.tangem.domain.tokens.model.Quote
interface QuotesRepositoryV2 {
/** Get quotes by [currenciesIds] synchronously or null */
suspend fun getMultiQuoteSyncOrNull(currenciesIds: Set<CryptoCurrency.RawID>): Set<Quote>?
suspend fun getMultiQuoteSyncOrNull(currenciesIds: Set<CryptoCurrency.RawID>): Set<QuoteStatus>?
}

View file

@ -2,14 +2,14 @@ package com.tangem.domain.quotes.single
import com.tangem.domain.core.flow.FlowProducer
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
/**
* Producer of quote [CryptoCurrency.RawID]
*
[REDACTED_AUTHOR]
*/
interface SingleQuoteProducer : FlowProducer<Quote> {
interface SingleQuoteProducer : FlowProducer<QuoteStatus> {
data class Params(val rawCurrencyId: CryptoCurrency.RawID)

View file

@ -1,7 +1,7 @@
package com.tangem.domain.quotes.single
import com.tangem.domain.core.flow.FlowCachingSupplier
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
/**
* Supplier of quote [SingleQuoteProducer.Params]
@ -14,4 +14,4 @@ import com.tangem.domain.tokens.model.Quote
abstract class SingleQuoteSupplier(
override val factory: SingleQuoteProducer.Factory,
override val keyCreator: (SingleQuoteProducer.Params) -> String,
) : FlowCachingSupplier<SingleQuoteProducer, SingleQuoteProducer.Params, Quote>()
) : FlowCachingSupplier<SingleQuoteProducer, SingleQuoteProducer.Params, QuoteStatus>()

View file

@ -1,39 +0,0 @@
package com.tangem.domain.tokens.model
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrency
import java.math.BigDecimal
sealed interface Quote {
val rawCurrencyId: CryptoCurrency.RawID
fun copySealed(source: StatusSource): Quote {
return when (this) {
is Empty -> this
is Value -> copy(source = source)
}
}
/**
* Represents unknown financial information for a specific cryptocurrency.
*
* @property rawCurrencyId The raw cryptocurrency ID.
*/
data class Empty(override val rawCurrencyId: CryptoCurrency.RawID) : Quote
/**
* Represents financial information for a specific cryptocurrency, including its fiat exchange rate and price change.
*
* @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 source source of data
*/
data class Value(
override val rawCurrencyId: CryptoCurrency.RawID,
val fiatRate: BigDecimal,
val priceChange: BigDecimal,
val source: StatusSource,
) : Quote
}

View file

@ -0,0 +1,69 @@
package com.tangem.domain.tokens.model
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrency
import java.math.BigDecimal
/**
* Represents the status of a specific currency quote
*
* @property rawCurrencyId the unique identifier of the cryptocurrency for which the financial information is provided
* @property value the specific status value
*/
data class QuoteStatus(val rawCurrencyId: CryptoCurrency.RawID, val value: Value) {
/** Constructor for creating the [Empty] status of a specific currency quote */
constructor(rawCurrencyId: CryptoCurrency.RawID) : this(rawCurrencyId = rawCurrencyId, value = Empty)
/** Represents the various possible statuses of a quote */
sealed interface Value {
/** Status source */
val source: StatusSource
fun copySealed(source: StatusSource): Value {
return when (this) {
is Empty -> this
is Data -> copy(source = source)
}
}
}
/** Represents unknown financial information for a specific cryptocurrency */
data object Empty : Value {
override val source: StatusSource = StatusSource.ACTUAL
}
/**
* Represents financial information for a specific cryptocurrency, including its fiat exchange rate and
* price change.
*
* @property source status source
* @property fiatRate the current fiat exchange rate for the cryptocurrency
* @property priceChange the price change for the cryptocurrency
*/
data class Data(
override val source: StatusSource,
val fiatRate: BigDecimal,
val priceChange: BigDecimal,
) : Value
}
/** Applies the given [function] if [QuoteStatus.Value] is [QuoteStatus.Data] or does nothing */
inline fun QuoteStatus.mapData(function: QuoteStatus.Data.() -> Unit): QuoteStatus {
if (value is QuoteStatus.Data) value.function()
return this
}
/**
* Applies the given [onData] or [onEmpty] functions depending on [QuoteStatus.Data] or [QuoteStatus.Empty]
*
* @param T type of result
*/
inline fun <T> QuoteStatus.fold(onData: QuoteStatus.Data.() -> T, onEmpty: QuoteStatus.Empty.() -> T): T {
return when (value) {
is QuoteStatus.Data -> value.onData()
is QuoteStatus.Empty -> value.onEmpty()
}
}

View file

@ -6,7 +6,7 @@ package com.tangem.domain.tokens.model
* @property id The unique identifier of the token.
* @property networks List of networks associated with the token.
* @property isAvailable Indicates whether this token is supported in Tangem app.
* @property quote Equivalent prices for the token.
* @property quoteStatus Equivalent prices for the token.
* @property name The name of the token.
* @property symbol The brief name of the token, e.g., "BTC".
* @property iconUrl URL of the token's icon.
@ -15,7 +15,7 @@ data class Token(
val id: String,
val networks: List<Network>,
val isAvailable: Boolean,
val quote: Quote?,
val quoteStatus: QuoteStatus?,
val name: String,
val symbol: String,
val iconUrl: String,

View file

@ -23,7 +23,7 @@ import com.tangem.domain.staking.single.SingleYieldBalanceProducer
import com.tangem.domain.staking.single.SingleYieldBalanceSupplier
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.utils.CurrencyStatusProxyCreator
@ -52,7 +52,7 @@ abstract class BaseCurrencyStatusOperations(
protected val currencyStatusProxyCreator = CurrencyStatusProxyCreator(stakingRepository)
protected abstract fun getQuotes(id: CryptoCurrency.RawID): Flow<Either<Error, Set<Quote>>>
protected abstract fun getQuotes(id: CryptoCurrency.RawID): Flow<Either<Error, Set<QuoteStatus>>>
protected abstract fun getNetworksStatuses(
userWalletId: UserWalletId,
@ -120,7 +120,7 @@ abstract class BaseCurrencyStatusOperations(
combine(quoteFlow, statusFlow, yieldBalanceFlow) { maybeQuote, maybeNetworkStatus, maybeYieldBalance ->
currencyStatusProxyCreator.createCurrencyStatus(
currency = currency,
maybeQuote = maybeQuote,
maybeQuoteStatus = maybeQuote,
maybeNetworkStatus = maybeNetworkStatus,
maybeYieldBalance = maybeYieldBalance,
)
@ -129,7 +129,7 @@ abstract class BaseCurrencyStatusOperations(
combine(quoteFlow, statusFlow) { maybeQuote, maybeNetworkStatus ->
currencyStatusProxyCreator.createCurrencyStatus(
currency = currency,
maybeQuote = maybeQuote,
maybeQuoteStatus = maybeQuote,
maybeNetworkStatus = maybeNetworkStatus,
maybeYieldBalance = null,
)
@ -210,7 +210,7 @@ abstract class BaseCurrencyStatusOperations(
return currencyStatusProxyCreator.createCurrencyStatus(
currency = currency,
maybeQuote = quote,
maybeQuoteStatus = quote,
maybeNetworkStatus = networkStatuses,
maybeYieldBalance = yieldBalances,
)
@ -303,7 +303,7 @@ abstract class BaseCurrencyStatusOperations(
return currencyStatusProxyCreator.createCurrencyStatus(
currency = currency,
maybeQuote = quotes,
maybeQuoteStatus = quotes,
maybeNetworkStatus = networkStatus,
maybeYieldBalance = yieldBalances,
)

View file

@ -31,7 +31,7 @@ import com.tangem.domain.staking.single.SingleYieldBalanceSupplier
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.utils.extractAddress
@ -121,7 +121,7 @@ class CachedCurrenciesStatusesOperations(
val (networks, currenciesIds) = getIds(currencies)
fun createCurrenciesStatuses(
maybeQuotes: Either<TokenListError, Set<Quote>>?,
maybeQuotes: Either<TokenListError, Set<QuoteStatus>>?,
maybeNetworkStatuses: Either<TokenListError, Set<NetworkStatus>>?,
maybeYieldBalances: Either<TokenListError, YieldBalanceList>?,
isUpdating: Boolean,
@ -212,7 +212,7 @@ class CachedCurrenciesStatusesOperations(
private fun createCurrenciesStatuses(
currencies: NonEmptyList<CryptoCurrency>,
maybeQuotes: Either<TokenListError, Set<Quote>>?,
maybeQuotes: Either<TokenListError, Set<QuoteStatus>>?,
maybeNetworkStatuses: Either<TokenListError, Set<NetworkStatus>>?,
maybeYieldBalances: Either<TokenListError, YieldBalanceList>?,
isUpdating: Boolean,
@ -238,7 +238,7 @@ class CachedCurrenciesStatusesOperations(
val currencyStatus = currencyStatusProxyCreator.createCurrencyStatus(
currency = currency,
quote = quote,
quoteStatus = quote,
networkStatus = networkStatus,
yieldBalance = yieldBalance,
ignoreQuote = quotesRetrievingFailed,
@ -272,7 +272,7 @@ class CachedCurrenciesStatusesOperations(
.distinctUntilChanged()
}
private fun getQuotes(tokensIds: NonEmptySet<CryptoCurrency.ID>): Flow<Either<TokenListError, Set<Quote>>> {
private fun getQuotes(tokensIds: NonEmptySet<CryptoCurrency.ID>): Flow<Either<TokenListError, Set<QuoteStatus>>> {
return getQuotesUpdates(
rawCurrencyIds = tokensIds.mapNotNullTo(
destination = hashSetOf(),
@ -281,11 +281,11 @@ class CachedCurrenciesStatusesOperations(
)
}
override fun getQuotes(id: CryptoCurrency.RawID): Flow<Either<Error, Set<Quote>>> {
override fun getQuotes(id: CryptoCurrency.RawID): Flow<Either<Error, Set<QuoteStatus>>> {
return singleQuoteSupplier(
params = SingleQuoteProducer.Params(rawCurrencyId = id),
)
.map<Quote, Either<Error, Set<Quote>>> { setOf(it).right() }
.map<QuoteStatus, Either<Error, Set<QuoteStatus>>> { setOf(it).right() }
.distinctUntilChanged()
}
@ -353,9 +353,11 @@ class CachedCurrenciesStatusesOperations(
}
// temporary code because token list is built using networks list
private fun getQuotesUpdates(rawCurrencyIds: Set<CryptoCurrency.RawID>): EitherFlow<TokenListError, Set<Quote>> {
private fun getQuotesUpdates(
rawCurrencyIds: Set<CryptoCurrency.RawID>,
): EitherFlow<TokenListError, Set<QuoteStatus>> {
return channelFlow {
val state = MutableStateFlow(emptySet<Quote>())
val state = MutableStateFlow(emptySet<QuoteStatus>())
rawCurrencyIds.onEach {
launch {
@ -375,7 +377,7 @@ class CachedCurrenciesStatusesOperations(
.onEach(::send)
.launchIn(scope = this)
}
.map<Set<Quote>, Either<TokenListError, Set<Quote>>> { it.right() }
.map<Set<QuoteStatus>, Either<TokenListError, Set<QuoteStatus>>> { it.right() }
.distinctUntilChanged()
}

View file

@ -5,28 +5,22 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import java.math.BigDecimal
internal class CurrencyStatusOperations(
private val currency: CryptoCurrency,
private val quote: Quote?,
private val quoteStatus: QuoteStatus?,
private val networkStatus: NetworkStatus?,
private val yieldBalance: YieldBalance?,
private val ignoreQuote: Boolean,
) {
private val Quote?.fiatRate: BigDecimal?
get() = when (this) {
is Quote.Value -> this.fiatRate
is Quote.Empty, null -> null
}
private val QuoteStatus?.fiatRate: BigDecimal?
get() = (this?.value as? QuoteStatus.Data)?.fiatRate
private val Quote?.priceChange: BigDecimal?
get() = when (this) {
is Quote.Value -> this.priceChange
is Quote.Empty, null -> null
}
private val QuoteStatus?.priceChange: BigDecimal?
get() = (this?.value as? QuoteStatus.Data)?.priceChange
fun createTokenStatus(): CryptoCurrencyStatus = CryptoCurrencyStatus(currency, createStatus())
@ -41,12 +35,12 @@ internal class CurrencyStatusOperations(
}
private fun createMissedDerivationStatus(): CryptoCurrencyStatus.MissedDerivation =
CryptoCurrencyStatus.MissedDerivation(priceChange = quote?.priceChange, fiatRate = quote?.fiatRate)
CryptoCurrencyStatus.MissedDerivation(priceChange = quoteStatus?.priceChange, fiatRate = quoteStatus?.fiatRate)
private fun createUnreachableStatus(status: NetworkStatus.Unreachable): CryptoCurrencyStatus.Unreachable {
return CryptoCurrencyStatus.Unreachable(
priceChange = quote?.priceChange,
fiatRate = quote?.fiatRate,
priceChange = quoteStatus?.priceChange,
fiatRate = quoteStatus?.fiatRate,
networkAddress = status.address,
)
}
@ -54,13 +48,13 @@ internal class CurrencyStatusOperations(
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,
fiatAmount = if (quoteStatus == null) null else BigDecimal.ZERO,
priceChange = quoteStatus?.priceChange,
fiatRate = quoteStatus?.fiatRate,
networkAddress = status.address,
sources = CryptoCurrencyStatus.Sources(
networkSource = status.source,
quoteSource = (quote as? Quote.Value)?.source ?: StatusSource.ACTUAL,
quoteSource = quoteStatus?.value?.source ?: StatusSource.ACTUAL,
),
)
}
@ -75,7 +69,10 @@ internal class CurrencyStatusOperations(
return CryptoCurrencyStatus.Loading
}
is NetworkStatus.Amount.NotFound -> {
return CryptoCurrencyStatus.NoAmount(priceChange = quote?.priceChange, fiatRate = quote?.fiatRate)
return CryptoCurrencyStatus.NoAmount(
priceChange = quoteStatus?.priceChange,
fiatRate = quoteStatus?.fiatRate,
)
}
is NetworkStatus.Amount.Loaded -> amount.value
}
@ -96,24 +93,27 @@ internal class CurrencyStatusOperations(
} else {
null
}
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, quote?.fiatRate),
fiatRate = quote?.fiatRate,
priceChange = quote?.priceChange,
fiatAmount = calculateFiatAmountOrNull(amount, quoteStatus?.fiatRate),
fiatRate = quoteStatus?.fiatRate,
priceChange = quoteStatus?.priceChange,
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
pendingTransactions = currentTransactions,
networkAddress = networkStatusValue.address,
yieldBalance = currentYieldBalance,
sources = CryptoCurrencyStatus.Sources(
networkSource = networkStatusValue.source,
quoteSource = (quote as? Quote.Value)?.source ?: StatusSource.ACTUAL,
quoteSource = quoteStatus?.value?.source ?: StatusSource.ACTUAL,
yieldBalanceSource = currentYieldBalance?.source ?: StatusSource.ACTUAL,
),
)
quote is Quote.Empty || ignoreQuote -> CryptoCurrencyStatus.NoQuote(
quoteValue is QuoteStatus.Empty || ignoreQuote -> CryptoCurrencyStatus.NoQuote(
amount = amount,
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
pendingTransactions = currentTransactions,
@ -121,22 +121,22 @@ internal class CurrencyStatusOperations(
yieldBalance = currentYieldBalance,
sources = CryptoCurrencyStatus.Sources(
networkSource = networkStatusValue.source,
quoteSource = (quote as? Quote.Value)?.source ?: StatusSource.ACTUAL,
quoteSource = quoteStatus?.value?.source ?: StatusSource.ACTUAL,
yieldBalanceSource = currentYieldBalance?.source ?: StatusSource.ACTUAL,
),
)
quote is Quote.Value -> CryptoCurrencyStatus.Loaded(
quoteValue is QuoteStatus.Data -> CryptoCurrencyStatus.Loaded(
amount = amount,
fiatAmount = calculateFiatAmount(amount, quote.fiatRate),
fiatRate = quote.fiatRate,
priceChange = quote.priceChange,
fiatAmount = calculateFiatAmount(amount, quoteValue.fiatRate),
fiatRate = quoteValue.fiatRate,
priceChange = quoteValue.priceChange,
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
pendingTransactions = currentTransactions,
networkAddress = networkStatusValue.address,
yieldBalance = currentYieldBalance,
sources = CryptoCurrencyStatus.Sources(
networkSource = networkStatusValue.source,
quoteSource = quote.source,
quoteSource = quoteValue.source,
yieldBalanceSource = currentYieldBalance?.source ?: StatusSource.ACTUAL,
),
)

View file

@ -10,7 +10,7 @@ import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.staking.model.stakekit.YieldBalanceList
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error
import com.tangem.domain.tokens.operations.CurrencyStatusOperations
@ -27,14 +27,14 @@ class CurrencyStatusProxyCreator(
fun createCurrencyStatus(
currency: CryptoCurrency,
maybeQuote: Either<Error, Quote?>,
maybeQuoteStatus: Either<Error, QuoteStatus?>,
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({ maybeQuote.bind() }) {
val quote = arrow.core.raise.recover({ maybeQuoteStatus.bind() }) {
quoteRetrievingFailed = true
null
}
@ -42,7 +42,7 @@ class CurrencyStatusProxyCreator(
createCurrencyStatus(
currency = currency,
quote = quote,
quoteStatus = quote,
networkStatus = networkStatus,
ignoreQuote = quoteRetrievingFailed,
yieldBalance = yieldBalance,
@ -51,14 +51,14 @@ class CurrencyStatusProxyCreator(
fun createCurrenciesStatuses(
currencies: NonEmptyList<CryptoCurrency>,
maybeQuotes: Either<Error, Set<Quote>>?,
maybeQuotes: Either<Error, Set<QuoteStatus>>?,
maybeNetworkStatuses: Either<Error, Set<NetworkStatus>>?,
maybeYieldBalances: Either<Error, YieldBalanceList>?,
): Either<Error, List<CryptoCurrencyStatus>> = either {
var quotesRetrievingFailed = false
val networksStatuses = maybeNetworkStatuses?.bind()?.toNonEmptySetOrNull()
val quotes: Set<Quote>? = maybeQuotes?.fold(
val quoteStatuses: Set<QuoteStatus>? = maybeQuotes?.fold(
ifLeft = {
quotesRetrievingFailed = true
null
@ -74,7 +74,7 @@ class CurrencyStatusProxyCreator(
val yieldBalances = maybeYieldBalances?.getOrNull()
currencies.map { currency ->
val quote = quotes?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId }
val quote = quoteStatuses?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId }
val networkStatus = networksStatuses?.firstOrNull { it.network == currency.network }
val address = extractAddress(networkStatus)
@ -89,7 +89,7 @@ class CurrencyStatusProxyCreator(
}
createCurrencyStatus(
currency = currency,
quote = quote,
quoteStatus = quote,
networkStatus = networkStatus,
ignoreQuote = quotesRetrievingFailed,
yieldBalance = yieldBalance,
@ -99,14 +99,14 @@ class CurrencyStatusProxyCreator(
fun createCurrencyStatus(
currency: CryptoCurrency,
quote: Quote?,
quoteStatus: QuoteStatus?,
networkStatus: NetworkStatus?,
ignoreQuote: Boolean,
yieldBalance: YieldBalance?,
): CryptoCurrencyStatus {
val currencyStatusOperations = CurrencyStatusOperations(
currency = currency,
quote = quote,
quoteStatus = quoteStatus,
networkStatus = networkStatus,
ignoreQuote = ignoreQuote,
yieldBalance = yieldBalance,

View file

@ -3,83 +3,103 @@ package com.tangem.domain.tokens.mock
import arrow.core.nonEmptySetOf
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import java.math.BigDecimal
@Suppress("MemberVisibilityCanBePrivate")
internal object MockQuotes {
val quote1 = Quote.Value(
val quote1 = QuoteStatus(
rawCurrencyId = MockTokens.token1.id.rawCurrencyId!!,
fiatRate = BigDecimal("1.23"),
priceChange = BigDecimal("0.01"),
source = StatusSource.ACTUAL,
value = QuoteStatus.Data(
fiatRate = BigDecimal("1.23"),
priceChange = BigDecimal("0.01"),
source = StatusSource.ACTUAL,
),
)
val quote2 = Quote.Value(
val quote2 = QuoteStatus(
rawCurrencyId = MockTokens.token2.id.rawCurrencyId!!,
fiatRate = BigDecimal("2.34"),
priceChange = BigDecimal("-0.02"),
source = StatusSource.ACTUAL,
value = QuoteStatus.Data(
fiatRate = BigDecimal("2.34"),
priceChange = BigDecimal("-0.02"),
source = StatusSource.ACTUAL,
),
)
val quote3 = Quote.Value(
val quote3 = QuoteStatus(
rawCurrencyId = MockTokens.token3.id.rawCurrencyId!!,
fiatRate = BigDecimal("3.45"),
priceChange = BigDecimal("0.03"),
source = StatusSource.ACTUAL,
value = QuoteStatus.Data(
fiatRate = BigDecimal("3.45"),
priceChange = BigDecimal("0.03"),
source = StatusSource.ACTUAL,
),
)
val quote4 = Quote.Value(
val quote4 = QuoteStatus(
rawCurrencyId = MockTokens.token4.id.rawCurrencyId!!,
fiatRate = BigDecimal("4.56"),
priceChange = BigDecimal("-0.04"),
source = StatusSource.ACTUAL,
value = QuoteStatus.Data(
fiatRate = BigDecimal("4.56"),
priceChange = BigDecimal("-0.04"),
source = StatusSource.ACTUAL,
),
)
val quote5 = Quote.Value(
val quote5 = QuoteStatus(
rawCurrencyId = MockTokens.token5.id.rawCurrencyId!!,
fiatRate = BigDecimal("5.67"),
priceChange = BigDecimal("0.05"),
source = StatusSource.ACTUAL,
value = QuoteStatus.Data(
fiatRate = BigDecimal("5.67"),
priceChange = BigDecimal("0.05"),
source = StatusSource.ACTUAL,
),
)
val quote6 = Quote.Value(
val quote6 = QuoteStatus(
rawCurrencyId = MockTokens.token6.id.rawCurrencyId!!,
fiatRate = BigDecimal("6.78"),
priceChange = BigDecimal("-0.06"),
source = StatusSource.ACTUAL,
value = QuoteStatus.Data(
fiatRate = BigDecimal("6.78"),
priceChange = BigDecimal("-0.06"),
source = StatusSource.ACTUAL,
),
)
val quote7 = Quote.Value(
val quote7 = QuoteStatus(
rawCurrencyId = MockTokens.token7.id.rawCurrencyId!!,
fiatRate = BigDecimal("7.89"),
priceChange = BigDecimal("0.07"),
source = StatusSource.ACTUAL,
value = QuoteStatus.Data(
fiatRate = BigDecimal("7.89"),
priceChange = BigDecimal("0.07"),
source = StatusSource.ACTUAL,
),
)
val quote8 = Quote.Value(
val quote8 = QuoteStatus(
rawCurrencyId = MockTokens.token8.id.rawCurrencyId!!,
fiatRate = BigDecimal("8.90"),
priceChange = BigDecimal("-0.08"),
source = StatusSource.ACTUAL,
value = QuoteStatus.Data(
fiatRate = BigDecimal("8.90"),
priceChange = BigDecimal("-0.08"),
source = StatusSource.ACTUAL,
),
)
val quote9 = Quote.Value(
val quote9 = QuoteStatus(
rawCurrencyId = MockTokens.token9.id.rawCurrencyId!!,
fiatRate = BigDecimal("9.01"),
priceChange = BigDecimal("0.09"),
source = StatusSource.ACTUAL,
value = QuoteStatus.Data(
fiatRate = BigDecimal("9.01"),
priceChange = BigDecimal("0.09"),
source = StatusSource.ACTUAL,
),
)
val quote10 = Quote.Value(
val quote10 = QuoteStatus(
rawCurrencyId = MockTokens.token10.id.rawCurrencyId!!,
fiatRate = BigDecimal("10.12"),
priceChange = BigDecimal("-0.10"),
source = StatusSource.ACTUAL,
value = QuoteStatus.Data(
fiatRate = BigDecimal("10.12"),
priceChange = BigDecimal("-0.10"),
source = StatusSource.ACTUAL,
),
)
val quote11 = Quote.Empty(CryptoCurrency.RawID("null"))
val quote11 = QuoteStatus(rawCurrencyId = CryptoCurrency.RawID("null"), value = QuoteStatus.Empty)
val quotes = nonEmptySetOf(
quote1, quote2, quote3, quote4, quote5, quote6, quote7, quote8, quote9, quote10,

View file

@ -4,7 +4,8 @@ import arrow.core.nonEmptyListOf
import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.domain.tokens.model.fold
import java.math.BigDecimal
@Suppress("MemberVisibilityCanBePrivate")
@ -13,8 +14,8 @@ internal object MockTokensStates {
val tokenState1 = CryptoCurrencyStatus(
currency = MockTokens.token1,
value = CryptoCurrencyStatus.Unreachable(
priceChange = MockQuotes.quote1.priceChange,
fiatRate = MockQuotes.quote1.fiatRate,
priceChange = MockQuotes.quote1.getPriceChange(),
fiatRate = MockQuotes.quote1.getFiatRate(),
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
),
@ -24,8 +25,8 @@ internal object MockTokensStates {
val tokenState2 = CryptoCurrencyStatus(
currency = MockTokens.token2,
value = CryptoCurrencyStatus.Unreachable(
priceChange = MockQuotes.quote2.priceChange,
fiatRate = MockQuotes.quote2.fiatRate,
priceChange = MockQuotes.quote2.getPriceChange(),
fiatRate = MockQuotes.quote2.getFiatRate(),
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
),
@ -35,8 +36,8 @@ internal object MockTokensStates {
val tokenState3 = CryptoCurrencyStatus(
currency = MockTokens.token3,
value = CryptoCurrencyStatus.Unreachable(
priceChange = MockQuotes.quote3.priceChange,
fiatRate = MockQuotes.quote3.fiatRate,
priceChange = MockQuotes.quote3.getPriceChange(),
fiatRate = MockQuotes.quote3.getFiatRate(),
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
),
@ -46,24 +47,24 @@ internal object MockTokensStates {
val tokenState4 = CryptoCurrencyStatus(
currency = MockTokens.token4,
value = CryptoCurrencyStatus.MissedDerivation(
priceChange = MockQuotes.quote4.priceChange,
fiatRate = MockQuotes.quote4.fiatRate,
priceChange = MockQuotes.quote4.getPriceChange(),
fiatRate = MockQuotes.quote4.getFiatRate(),
),
)
val tokenState5 = CryptoCurrencyStatus(
currency = MockTokens.token5,
value = CryptoCurrencyStatus.MissedDerivation(
priceChange = MockQuotes.quote5.priceChange,
fiatRate = MockQuotes.quote5.fiatRate,
priceChange = MockQuotes.quote5.getPriceChange(),
fiatRate = MockQuotes.quote5.getFiatRate(),
),
)
val tokenState6 = CryptoCurrencyStatus(
currency = MockTokens.token6,
value = CryptoCurrencyStatus.MissedDerivation(
priceChange = MockQuotes.quote6.priceChange,
fiatRate = MockQuotes.quote6.fiatRate,
priceChange = MockQuotes.quote6.getPriceChange(),
fiatRate = MockQuotes.quote6.getFiatRate(),
),
)
@ -71,8 +72,8 @@ internal object MockTokensStates {
currency = MockTokens.token7,
value = CryptoCurrencyStatus.NoAccount(
fiatAmount = BigDecimal.ZERO,
priceChange = MockQuotes.quote7.priceChange,
fiatRate = MockQuotes.quote7.fiatRate,
priceChange = MockQuotes.quote7.getPriceChange(),
fiatRate = MockQuotes.quote7.getFiatRate(),
amountToCreateAccount = MockNetworks.amountToCreateAccount,
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
@ -85,8 +86,8 @@ internal object MockTokensStates {
currency = MockTokens.token8,
value = CryptoCurrencyStatus.NoAccount(
fiatAmount = BigDecimal.ZERO,
priceChange = MockQuotes.quote8.priceChange,
fiatRate = MockQuotes.quote8.fiatRate,
priceChange = MockQuotes.quote8.getPriceChange(),
fiatRate = MockQuotes.quote8.getFiatRate(),
amountToCreateAccount = MockNetworks.amountToCreateAccount,
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
@ -99,8 +100,8 @@ internal object MockTokensStates {
currency = MockTokens.token9,
value = CryptoCurrencyStatus.NoAccount(
fiatAmount = BigDecimal.ZERO,
priceChange = MockQuotes.quote9.priceChange,
fiatRate = MockQuotes.quote9.fiatRate,
priceChange = MockQuotes.quote9.getPriceChange(),
fiatRate = MockQuotes.quote9.getFiatRate(),
amountToCreateAccount = MockNetworks.amountToCreateAccount,
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
@ -113,8 +114,8 @@ internal object MockTokensStates {
currency = MockTokens.token10,
value = CryptoCurrencyStatus.NoAccount(
fiatAmount = BigDecimal.ZERO,
priceChange = MockQuotes.quote10.priceChange,
fiatRate = MockQuotes.quote10.fiatRate,
priceChange = MockQuotes.quote10.getPriceChange(),
fiatRate = MockQuotes.quote10.getFiatRate(),
amountToCreateAccount = MockNetworks.amountToCreateAccount,
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
@ -145,8 +146,8 @@ internal object MockTokensStates {
)?.value ?: BigDecimal.ZERO
val quote = MockQuotes.quotes.first { it.rawCurrencyId == status.currency.id.rawCurrencyId }
val value = when (quote) {
is Quote.Empty -> CryptoCurrencyStatus.NoQuote(
val value = when (val value = quote.value) {
is QuoteStatus.Empty -> CryptoCurrencyStatus.NoQuote(
amount = status.value.amount!!,
pendingTransactions = emptySet(),
hasCurrentNetworkTransactions = false,
@ -156,11 +157,11 @@ internal object MockTokensStates {
yieldBalance = null,
sources = CryptoCurrencyStatus.Sources(),
)
is Quote.Value -> CryptoCurrencyStatus.Loaded(
is QuoteStatus.Data -> CryptoCurrencyStatus.Loaded(
amount = amount,
fiatAmount = amount * quote.fiatRate,
fiatRate = quote.fiatRate,
priceChange = quote.priceChange,
fiatAmount = amount * value.fiatRate,
fiatRate = value.fiatRate,
priceChange = value.priceChange,
pendingTransactions = emptySet(),
hasCurrentNetworkTransactions = false,
networkAddress = requireNotNull(networkStatus.value as? NetworkStatus.Verified).address,
@ -187,4 +188,7 @@ internal object MockTokensStates {
),
)
}
private fun QuoteStatus.getPriceChange(): BigDecimal? = fold(onData = { priceChange }, onEmpty = { null })
private fun QuoteStatus.getFiatRate(): BigDecimal? = fold(onData = { fiatRate }, onEmpty = { null })
}

View file

@ -10,7 +10,8 @@ import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.percent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.onramp.model.HotCryptoCurrency
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.domain.tokens.model.fold
import com.tangem.utils.converter.Converter
import java.math.BigDecimal
@ -30,7 +31,7 @@ internal class HotTokenItemStateConverter(
id = value.cryptoCurrency.id.value,
iconState = CryptoCurrencyToIconStateConverter().convert(value.cryptoCurrency),
titleState = TokenItemState.TitleState.Content(text = stringReference(value.cryptoCurrency.name)),
subtitleState = value.quote.getCryptoPriceState(appCurrency),
subtitleState = value.quoteStatus.getCryptoPriceState(appCurrency),
fiatAmountState = null,
subtitle2State = null,
onItemClick = onItemClick.let { onItemClick -> { onItemClick(it, value) } },
@ -38,17 +39,17 @@ internal class HotTokenItemStateConverter(
)
}
private fun Quote.getCryptoPriceState(appCurrency: AppCurrency): TokenItemState.SubtitleState {
return when (this) {
is Quote.Empty -> TokenItemState.SubtitleState.Unknown
is Quote.Value -> {
private fun QuoteStatus.getCryptoPriceState(appCurrency: AppCurrency): TokenItemState.SubtitleState {
return fold(
onData = {
TokenItemState.SubtitleState.CryptoPriceContent(
price = fiatRate.getFormattedCryptoPrice(appCurrency),
priceChangePercent = priceChange.format { percent() },
type = priceChange.getPriceChangeType(),
)
}
}
},
onEmpty = { TokenItemState.SubtitleState.Unknown },
)
}
private fun BigDecimal.getFormattedCryptoPrice(appCurrency: AppCurrency): String {

View file

@ -25,7 +25,7 @@ import com.tangem.domain.tokens.GetCurrencyCheckUseCase
import com.tangem.domain.tokens.GetMultiCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.FeePaidCurrency
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
@ -1034,7 +1034,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
val rates = getQuotes(fromToken.currency.id)
val fromTokenSwapInfo = TokenSwapInfo(
tokenAmount = amount,
amountFiat = rates[fromToken.currency.id]?.fiatRate?.multiply(amount.value)
amountFiat = rates[fromToken.currency.id]?.multiply(amount.value)
?: BigDecimal.ZERO,
cryptoCurrencyStatus = fromToken,
)
@ -1130,7 +1130,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
else -> getNativeToken(networkId = fromToken.network.backendId).id
}
val rates = getQuotes(feeCurrencyId)
return rates[feeCurrencyId]?.fiatRate?.let { rate ->
return rates[feeCurrencyId]?.let { rate ->
fees.map { fee ->
rate.multiply(fee).format {
fiat(
@ -1233,7 +1233,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
val rates = getQuotes(fromToken.currency.id)
val fromTokenSwapInfo = TokenSwapInfo(
tokenAmount = amount,
amountFiat = rates[fromToken.currency.id]?.fiatRate?.multiply(amount.value)
amountFiat = rates[fromToken.currency.id]?.multiply(amount.value)
?: BigDecimal.ZERO,
cryptoCurrencyStatus = fromToken,
)
@ -1307,20 +1307,20 @@ internal class SwapInteractorImpl @AssistedInject constructor(
fromTokenInfo = TokenSwapInfo(
tokenAmount = fromTokenAmount,
cryptoCurrencyStatus = fromTokenStatus,
amountFiat = rates[fromToken.id]?.fiatRate?.multiply(fromTokenAmount.value)
amountFiat = rates[fromToken.id]?.multiply(fromTokenAmount.value)
?: BigDecimal.ZERO,
),
toTokenInfo = TokenSwapInfo(
tokenAmount = toTokenAmount,
cryptoCurrencyStatus = toTokenStatus,
amountFiat = rates[toToken.id]?.fiatRate?.multiply(toTokenAmount.value)
amountFiat = rates[toToken.id]?.multiply(toTokenAmount.value)
?: BigDecimal.ZERO,
),
priceImpact = calculatePriceImpact(
fromTokenAmount = fromTokenAmount.value,
fromRate = rates[fromToken.id]?.fiatRate?.toDouble() ?: 0.0,
fromRate = rates[fromToken.id]?.toDouble() ?: 0.0,
toTokenAmount = toTokenAmount.value,
toRate = rates[toToken.id]?.fiatRate?.toDouble() ?: 0.0,
toRate = rates[toToken.id]?.toDouble() ?: 0.0,
),
swapDataModel = swapData,
txFee = txFeeState,
@ -1795,18 +1795,20 @@ internal class SwapInteractorImpl @AssistedInject constructor(
return PriceImpact.Value(value)
}
private suspend fun getQuotes(vararg ids: CryptoCurrency.ID): Map<CryptoCurrency.ID, Quote.Value> {
private suspend fun getQuotes(vararg ids: CryptoCurrency.ID): Map<CryptoCurrency.ID, BigDecimal> {
val set = ids.mapNotNull { it.rawCurrencyId }
.toSet()
.getQuotesOrEmpty()
.filterIsInstance<Quote.Value>()
return ids
.mapNotNull { id -> set.find { it.rawCurrencyId == id.rawCurrencyId }?.let { id to it } }
.mapNotNull { id ->
set.find { it.rawCurrencyId == id.rawCurrencyId && it.value is QuoteStatus.Data }
?.let { id to (it.value as QuoteStatus.Data).fiatRate }
}
.toMap()
}
private suspend fun Set<CryptoCurrency.RawID>.getQuotesOrEmpty(): Set<Quote> {
private suspend fun Set<CryptoCurrency.RawID>.getQuotesOrEmpty(): Set<QuoteStatus> {
return runCatching { quotesRepositoryV2.getMultiQuoteSyncOrNull(currenciesIds = this) }
.getOrNull()
.orEmpty()

View file

@ -17,8 +17,9 @@ import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
import com.tangem.core.ui.utils.toTimeFormat
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent
import com.tangem.domain.tokens.model.mapData
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus.Companion.isFailed
import com.tangem.feature.swap.domain.models.domain.ExchangeStatusModel
@ -57,7 +58,7 @@ internal class TokenDetailsSwapTransactionsStateConverter(
fun convert(
savedTransactions: List<SavedSwapTransactionListModel>,
quotes: Set<Quote>,
quoteStatuses: Set<QuoteStatus>,
): PersistentList<ExchangeUM> {
val result = mutableListOf<ExchangeUM>()
@ -73,12 +74,17 @@ internal class TokenDetailsSwapTransactionsStateConverter(
val fromAmount = transaction.fromCryptoAmount
var toFiatAmount: BigDecimal? = null
var fromFiatAmount: BigDecimal? = null
quotes.forEach { quote ->
if (quote is Quote.Value && quote.rawCurrencyId == toCryptoCurrencyRawId) {
toFiatAmount = quote.fiatRate.multiply(toAmount)
quoteStatuses.forEach { quote ->
quote.mapData {
if (quote.rawCurrencyId == toCryptoCurrencyRawId) {
toFiatAmount = fiatRate.multiply(toAmount)
}
}
if (quote is Quote.Value && quote.rawCurrencyId == fromCryptoCurrencyRawId) {
fromFiatAmount = quote.fiatRate.multiply(fromAmount)
quote.mapData {
if (quote.rawCurrencyId == fromCryptoCurrencyRawId) {
fromFiatAmount = fiatRate.multiply(fromAmount)
}
}
}
val statusModel = transaction.status

View file

@ -8,7 +8,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.quotes.QuotesRepositoryV2
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
import com.tangem.domain.tokens.model.Quote
import com.tangem.domain.tokens.model.QuoteStatus
import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.swap.domain.SwapTransactionRepository
@ -66,7 +66,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
getExchangeStatusState(
savedTransactions = savedTransactions,
quotes = quotes,
quoteStatuses = quotes,
)
}
}
@ -155,7 +155,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
private fun getExchangeStatusState(
savedTransactions: List<SavedSwapTransactionListModel>?,
quotes: Set<Quote>,
quoteStatuses: Set<QuoteStatus>,
): PersistentList<ExchangeUM> {
if (savedTransactions == null) {
return persistentListOf()
@ -163,7 +163,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
return swapTransactionsStateConverter.convert(
savedTransactions = savedTransactions,
quotes = quotes,
quoteStatuses = quoteStatuses,
)
}
@ -187,7 +187,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
}
}
private suspend fun Set<CryptoCurrency.ID>.getQuotesOrEmpty(): Set<Quote> {
private suspend fun Set<CryptoCurrency.ID>.getQuotesOrEmpty(): Set<QuoteStatus> {
val rawIds = mapNotNull { it.rawCurrencyId }.toSet()
return runCatching { quotesRepositoryV2.getMultiQuoteSyncOrNull(currenciesIds = rawIds) }