Updated on 2026-08-14

This commit is contained in:
Tangem 2023-10-20 19:39:03 +03:00
parent 732452b611
commit d92b32d25d
7 changed files with 71 additions and 92 deletions

View file

@ -1,74 +0,0 @@
package com.tangem.tap.domain
import com.tangem.common.services.Result
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.domain.common.ThrottlerWithValues
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import java.math.BigDecimal
class RatesRepository(
private val tangemTechApi: TangemTechApi,
private val dispatchers: CoroutineDispatcherProvider,
) {
private val throttler = ThrottlerWithValues<Currency, Result<BigDecimal>?>(60000)
suspend fun loadFiatRate(currencyId: String, coinsList: List<Currency>): Result<RatesResult> =
withContext(dispatchers.io) {
// get and submit previous result of equivalents.
val throttledResult = coinsList.filter { throttler.isStillThrottled(it) }.map {
Pair(it, throttler.geValue(it))
}
val currenciesToUpdate = coinsList.filter { !throttler.isStillThrottled(it) }
val coinIds = currenciesToUpdate.mapNotNull { it.coinId }.distinct()
if (coinIds.isEmpty()) return@withContext handleFiatRatesResult(throttledResult.toMap())
runCatching { tangemTechApi.getRates(currencyId.lowercase(), coinIds.joinToString(",")) }
.onSuccess { response ->
val ratesResultList: Map<String, Result<BigDecimal>> = response.rates.mapValues {
Result.Success(it.value.toBigDecimal())
}
val updatedCurrencies = throttledResult.toMap().toMutableMap()
coinsList.forEach { currency ->
ratesResultList[currency.coinId]?.let {
updatedCurrencies[currency] = it
throttler.updateThrottlingTo(currency)
throttler.setValue(currency, it)
}
}
return@withContext handleFiatRatesResult(updatedCurrencies)
}
.onFailure { Result.Failure(it) }
error("Unreachable code because runCatching must return result")
}
private fun handleFiatRatesResult(rates: Map<Currency, Result<BigDecimal>?>): Result.Success<RatesResult> {
val success = mutableMapOf<Currency, BigDecimal>()
val failures = mutableMapOf<Currency, Throwable>()
rates.mapNotNull { (currency, priceResult) ->
when (priceResult) {
is Result.Success -> success[currency] = priceResult.data
is Result.Failure -> failures[currency] = priceResult.error
else -> null
}
}
return Result.Success(success to failures)
}
fun clear() {
throttler.clear()
}
}
typealias RatesResult = Pair<MutableMap<Currency, BigDecimal>, MutableMap<Currency, Throwable>>
val RatesResult.loadedRates
get() = this.first
val RatesResult.failedRates
get() = this.second

View file

@ -9,7 +9,7 @@
},
{
"name": "REDESIGNED_WALLET_SCREEN_ENABLED",
"version": "undefined"
"version": "1.0.0"
},
{
"name": "1INCH_LEARN_2_EARN_ENABLED",
@ -21,10 +21,10 @@
},
{
"name": "REDESIGNED_APP_CURRENCY_SELECTOR_ENABLED",
"version": "undefined"
"version": "1.0.0"
},
{
"name": "DARK_THEME_ENABLED",
"version": "undefined"
"version": "1.0.0"
}
]

View file

@ -51,19 +51,14 @@ fun getActiveIconRes(blockchainId: String): Int {
@Suppress("ComplexMethod")
@DrawableRes
fun getActiveIconResByCoinId(coinId: String, networkId: String): Int {
fun getActiveIconResByCoinId(coinId: String): Int {
return when (coinId) {
"binancecoin" -> R.drawable.img_bsc_22
"bitcoin" -> R.drawable.img_btc_22
"bitcoin-cash" -> R.drawable.img_btc_cash_22
"ethereum" -> {
when (networkId) {
"ethereum" -> R.drawable.img_eth_22
"arbitrum-one" -> R.drawable.img_arbitrum_22
"optimistic-ethereum" -> R.drawable.img_optimism_22
else -> R.drawable.ic_alert_24
}
}
"ethereum" -> R.drawable.img_eth_22
"arbitrum-one" -> R.drawable.img_arbitrum_22
"optimistic-ethereum" -> R.drawable.img_optimism_22
"ethereum-classic" -> R.drawable.img_eth_classic_22
"stellar" -> R.drawable.img_stellar_22
"cardano" -> R.drawable.img_cardano_22

View file

@ -3,6 +3,7 @@ package com.tangem.data.tokens.repository
import com.tangem.data.common.api.safeApiCall
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.data.tokens.utils.QuotesConverter
import com.tangem.data.tokens.utils.QuotesUnsupportedCurrenciesIdAdapter
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore
import com.tangem.datasource.local.quote.QuotesStore
@ -23,6 +24,7 @@ internal class DefaultQuotesRepository(
) : QuotesRepository {
private val quotesConverter = QuotesConverter()
private val quotesUnsupportedCurrenciesAdapter = QuotesUnsupportedCurrenciesIdAdapter()
@Volatile
private var quotesFetchedForAppCurrency: String? = null
@ -71,9 +73,10 @@ internal class DefaultQuotesRepository(
}
private suspend fun fetchQuotes(rawCurrenciesIds: Set<String>, appCurrencyId: String) {
val replacementIdsResult = quotesUnsupportedCurrenciesAdapter.replaceUnsupportedCurrencies(rawCurrenciesIds)
val response = safeApiCall(
call = {
val coinIds = rawCurrenciesIds.joinToString(separator = ",")
val coinIds = replacementIdsResult.idsForRequest.joinToString(separator = ",")
tangemTechApi.getQuotes(appCurrencyId, coinIds).bind()
},
onError = {
@ -83,7 +86,11 @@ internal class DefaultQuotesRepository(
)
if (response != null) {
quotesStore.store(response)
val updatedResponse = quotesUnsupportedCurrenciesAdapter.getResponseWithUnsupportedCurrencies(
response,
replacementIdsResult.idsFiltered,
)
quotesStore.store(updatedResponse)
}
}

View file

@ -0,0 +1,51 @@
package com.tangem.data.tokens.utils
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
/**
* Adapter to replace unsupported currencies for quotes request if it necessary
*/
class QuotesUnsupportedCurrenciesIdAdapter {
/**
* Replaces unsupported currencies id to it replacements for request
*/
fun replaceUnsupportedCurrencies(currenciesIds: Set<String>): ReplacementResult {
val idsForRequest = mutableSetOf<String>()
val idsFiltered = mutableSetOf<String>()
currenciesIds.forEach { currencyId ->
unsupportedMapWithReplacement[currencyId]?.let {
idsFiltered.add(currencyId)
idsForRequest.add(it)
} ?: idsForRequest.add(currencyId)
}
return ReplacementResult(idsForRequest = idsForRequest, idsFiltered = idsFiltered)
}
/**
* Recover previously replaced currencies to response that uses in application
*/
fun getResponseWithUnsupportedCurrencies(response: QuotesResponse, filteredIds: Set<String>): QuotesResponse {
val updatedQuotes = mutableMapOf<String, QuotesResponse.Quote>()
filteredIds.forEach { filteredId ->
unsupportedMapWithReplacement[filteredId]?.let { baseId ->
response.quotes[baseId]?.let { quote ->
updatedQuotes[filteredId] = quote
}
}
}
return response.copy(quotes = updatedQuotes + response.quotes)
}
data class ReplacementResult(val idsForRequest: Set<String>, val idsFiltered: Set<String>)
companion object {
/**
* Map that contains unsupported currencies and their replacement for request
*/
private val unsupportedMapWithReplacement = mapOf(
"optimistic-ethereum" to "ethereum",
"arbitrum-one" to "ethereum",
)
}
}

View file

@ -159,7 +159,7 @@ fun Blockchain.toCoinId(): String {
Blockchain.Stellar, Blockchain.StellarTestnet -> "stellar"
Blockchain.Cardano -> "cardano"
Blockchain.Polygon, Blockchain.PolygonTestnet -> "matic-network"
Blockchain.Arbitrum, Blockchain.ArbitrumTestnet -> "ethereum"
Blockchain.Arbitrum, Blockchain.ArbitrumTestnet -> "arbitrum-one"
Blockchain.Avalanche, Blockchain.AvalancheTestnet -> "avalanche-2"
Blockchain.Solana, Blockchain.SolanaTestnet -> "solana"
Blockchain.Fantom, Blockchain.FantomTestnet -> "fantom"
@ -175,7 +175,7 @@ fun Blockchain.toCoinId(): String {
Blockchain.EthereumPow, Blockchain.EthereumPowTestnet -> "ethereum-pow-iou"
Blockchain.EthereumFair -> "ethereumfair"
Blockchain.Kusama -> "kusama"
Blockchain.Optimism, Blockchain.OptimismTestnet -> "ethereum"
Blockchain.Optimism, Blockchain.OptimismTestnet -> "optimistic-ethereum"
Blockchain.Dash -> "dash"
Blockchain.Kaspa -> "kaspa"
Blockchain.TON, Blockchain.TONTestnet -> "the-open-network"

View file

@ -156,7 +156,7 @@ private fun MainInfo(state: SwapStateHolder) {
priceImpact = priceImpactWarning,
networkIconRes = if (state.sendCardData.isNotNativeToken) networkIconRes else null,
iconPlaceholder = state.sendCardData.coinId?.let {
getActiveIconResByCoinId(it, state.networkId)
getActiveIconResByCoinId(it)
},
onChangeTokenClick = if (state.sendCardData.canSelectAnotherToken) state.onSelectTokenClick else null,
modifier = Modifier.constrainAs(topCard) {
@ -174,7 +174,7 @@ private fun MainInfo(state: SwapStateHolder) {
priceImpact = priceImpactWarning,
networkIconRes = if (state.receiveCardData.isNotNativeToken) networkIconRes else null,
iconPlaceholder = state.receiveCardData.coinId?.let {
getActiveIconResByCoinId(it, state.networkId)
getActiveIconResByCoinId(it)
},
onChangeTokenClick = if (state.receiveCardData.canSelectAnotherToken) {
state.onSelectTokenClick