Updated on 2026-08-14

This commit is contained in:
Tangem 2024-07-09 12:45:17 +03:00
parent 46ff455a6f
commit e3eada68fd
35 changed files with 757 additions and 13 deletions

View file

@ -0,0 +1,41 @@
package com.tangem.datasource.api.markets
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.markets.models.response.*
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query
interface TangemTechMarketsApi {
@Suppress("LongParameterList")
@GET("coins/list")
suspend fun getCoinsList(
@Query("currency") currency: String,
@Query("interval") interval: String,
@Query("offset") offset: Int,
@Query("limit") limit: Int,
@Query("order") order: String,
@Query("general_coins") generalCoins: Boolean,
@Query("search") search: String?,
): ApiResponse<TokenMarketListResponse>
@GET("coins/{coin_id}")
suspend fun getCoinMarketData(
@Path("coin_id") coinId: String,
@Query("currency") currency: String,
): ApiResponse<TokenMarketDetailsResponse>
@GET("coins/{coin_id}/history")
suspend fun getCoinChart(
@Query("currency") currency: String,
@Query("interval") interval: String,
): ApiResponse<TokenMarketChartResponse>
@GET("coins/history_preview")
suspend fun getCoinsListCharts(
@Query("coin_ids") coinIds: List<String>,
@Query("currency") currency: String,
@Query("interval") interval: String,
): ApiResponse<TokenMarketChartListResponse>
}

View file

@ -0,0 +1,8 @@
package com.tangem.datasource.api.markets.models.response
import com.squareup.moshi.Json
class TokenMarketChartListResponse(
@Json(name = "tokens")
val tokens: Map<String, TokenMarketChartResponse>,
)

View file

@ -0,0 +1,9 @@
package com.tangem.datasource.api.markets.models.response
import com.squareup.moshi.Json
import java.math.BigDecimal
data class TokenMarketChartResponse(
@Json(name = "prices")
val prices: Map<Long, BigDecimal>,
)

View file

@ -0,0 +1,132 @@
package com.tangem.datasource.api.markets.models.response
import com.squareup.moshi.Json
import java.math.BigDecimal
data class TokenMarketDetailsResponse(
@Json(name = "id")
val id: String,
@Json(name = "name")
val name: String,
@Json(name = "symbol")
val symbol: String,
@Json(name = "active")
val active: Boolean,
@Json(name = "current_price")
val currentPrice: BigDecimal,
@Json(name = "price_change_percentage")
val priceChangePercentage: PriceChangePercentage,
@Json(name = "networks")
val networks: List<Network>,
@Json(name = "short_description")
val shortDescription: String?,
@Json(name = "full_description")
val fullDescription: String?,
@Json(name = "insights")
val insights: List<Insight>?,
@Json(name = "metrics")
val metrics: Metrics,
@Json(name = "links")
val links: Links,
@Json(name = "price_performance")
val pricePerformance: PricePerformance,
) {
data class PriceChangePercentage(
@Json(name = "24h")
val h24: BigDecimal,
@Json(name = "1w")
val week1: BigDecimal,
@Json(name = "1m")
val month1: BigDecimal,
@Json(name = "3m")
val month3: BigDecimal,
@Json(name = "6m")
val month6: BigDecimal,
@Json(name = "1y")
val year1: BigDecimal,
@Json(name = "all_time")
val allTime: BigDecimal,
)
data class Network(
@Json(name = "network_id")
val networkId: String,
@Json(name = "exchangeable")
val exchangeable: Boolean,
@Json(name = "contract_address")
val contractAddress: String,
@Json(name = "decimalCount")
val decimalCount: Int,
)
data class Insight(
@Json(name = "holders_change")
val holdersChange: Change,
@Json(name = "liquidity_change")
val liquidityChange: Change,
@Json(name = "buy_pressure_change")
val buyPressureChange: Change,
@Json(name = "experienced_buyer_change")
val experiencedBuyerChange: Change,
) {
data class Change(
@Json(name = "1d")
val day1: Int,
@Json(name = "1w")
val week1: Int,
@Json(name = "1m")
val month1: Int,
)
}
data class Metrics(
@Json(name = "market_rating")
val marketRating: Int,
@Json(name = "circulating_supply")
val circulatingSupply: BigDecimal,
@Json(name = "market_cap")
val marketCap: BigDecimal,
@Json(name = "volume_24h")
val volume24h: BigDecimal,
@Json(name = "total_supply")
val totalSupply: BigDecimal,
@Json(name = "fully_diluted_valuation")
val fullyDilutedValuation: BigDecimal,
)
data class Links(
@Json(name = "official_links")
val officialLinks: List<Link> = emptyList(),
@Json(name = "social")
val social: List<Link> = emptyList(),
@Json(name = "repository")
val repository: List<Link> = emptyList(),
@Json(name = "blockchain_site")
val blockchainSite: List<Link> = emptyList(),
)
data class Link(
@Json(name = "title")
val title: String?,
@Json(name = "id")
val id: String,
@Json(name = "link")
val url: String,
)
data class PricePerformance(
@Json(name = "high_price")
val highPrice: Price,
@Json(name = "low_price")
val lowPrice: Price,
) {
data class Price(
@Json(name = "24h")
val h24: BigDecimal,
@Json(name = "1m")
val month1: BigDecimal,
@Json(name = "all_time")
val allTime: BigDecimal,
)
}
}

View file

@ -0,0 +1,43 @@
package com.tangem.datasource.api.markets.models.response
import com.squareup.moshi.Json
import java.math.BigDecimal
data class TokenMarketListResponse(
@Json(name = "imageHost")
val imageHost: String,
@Json(name = "tokens")
val tokens: List<Token>,
@Json(name = "total")
val total: Int,
@Json(name = "limit")
val limit: Int,
@Json(name = "offset")
val offset: Int,
) {
data class Token(
@Json(name = "id")
val id: String,
@Json(name = "name")
val name: String,
@Json(name = "symbol")
val symbol: String,
@Json(name = "current_price")
val currentPrice: BigDecimal,
@Json(name = "price_change_percentage")
val priceChangePercentage: PriceChangePercentage,
@Json(name = "market_rating")
val marketRating: Int?,
@Json(name = "market_cap")
val marketCap: BigDecimal?,
) {
data class PriceChangePercentage(
@Json(name = "24h")
val h24: BigDecimal,
@Json(name = "1w")
val week1: BigDecimal,
@Json(name = "30d")
val day30: BigDecimal,
)
}
}

View file

@ -12,6 +12,10 @@ data class QuotesResponse(
@Json(name = "price")
val price: BigDecimal?,
@Json(name = "priceChange24h")
val priceChange: BigDecimal?,
val priceChange24h: BigDecimal?,
@Json(name = "priceChange1w")
val priceChange1w: BigDecimal?,
@Json(name = "priceChange30d")
val priceChange30d: BigDecimal?,
)
}

View file

@ -5,6 +5,7 @@ import com.squareup.moshi.Moshi
import com.tangem.datasource.BuildConfig
import com.tangem.datasource.api.common.response.ApiResponseCallAdapterFactory
import com.tangem.datasource.api.express.TangemExpressApi
import com.tangem.datasource.api.markets.TangemTechMarketsApi
import com.tangem.datasource.api.stakekit.StakeKitApi
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.TangemTechApiV2
@ -128,6 +129,23 @@ class NetworkModule {
)
}
@Provides
@DevTangemApi
@Singleton
fun provideCoinMarketsApi(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
appVersionProvider: AppVersionProvider,
): TangemTechMarketsApi {
return provideTangemTechApiInternal(
moshi = moshi,
context = context,
appVersionProvider = appVersionProvider,
baseUrl = DEV_V1_TANGEM_TECH_BASE_URL,
requestHeaders = listOf(AppVersionPlatformHeaders(appVersionProvider)),
)
}
private inline fun <reified T> provideTangemTechApiInternal(
moshi: Moshi,
context: Context,

View file

@ -16,7 +16,7 @@ sealed class BatchFetchResult<out TData> {
*/
data class Success<TData>(
val data: TData,
val last: Boolean = false,
val last: Boolean,
) : BatchFetchResult<TData>()
/**

View file

@ -269,10 +269,14 @@ private class DefaultBatchListSource<TKey, TData, TRequestParams : Any, TUpdate>
val batches = state.value.data
val batchesToUpdate = batches.filter { action.keys.contains(it.key) }
val result = updateFetcher.fetchUpdate(
val result = try {
updateFetcher.fetchUpdate(
toUpdate = batchesToUpdate,
updateRequest = action.updateRequest,
)
} catch (t: Throwable) {
BatchUpdateResult.Error(t)
}
if (result is BatchUpdateResult.Success) {
state.update { currentState ->

View file

@ -0,0 +1,17 @@
package com.tangem.pagination
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
fun <TKey, TData, TUpdate> BatchListSource<TKey, TData, TUpdate>.toBatchFlow() =
object : BatchFlow<TKey, TData, TUpdate> {
override val state: StateFlow<BatchListState<TKey, TData>>
get() = this@toBatchFlow.state
override val updateResults: SharedFlow<Pair<TUpdate, BatchUpdateResult<TKey, TData>>>
get() = this@toBatchFlow.updateResults
}
interface BatchFlow<TKey, TData, TUpdate> {
val state: StateFlow<BatchListState<TKey, TData>>
val updateResults: SharedFlow<Pair<TUpdate, BatchUpdateResult<TKey, TData>>>
}

View file

@ -35,7 +35,10 @@ class LimitOffsetBatchFetcher<TRequestParams : Any, TData>(
request = requestParams,
)
val res = fetch(req)
val res = runCatching {
fetch(req)
}.getOrElse { BatchFetchResult.Error(it) }
lastRequest.value = req
return res
}
@ -61,7 +64,10 @@ class LimitOffsetBatchFetcher<TRequestParams : Any, TData>(
last
}
val res = fetch(req)
val res = runCatching {
fetch(req)
}.getOrElse { BatchFetchResult.Error(it) }
lastRequest.value = req
return res
}

1
data/markets/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,32 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
alias(deps.plugins.kotlin.kapt)
alias(deps.plugins.hilt.android)
id("configuration")
}
android {
namespace = "com.tangem.data.markets"
}
dependencies {
implementation(projects.core.datasource)
implementation(projects.core.utils)
implementation(projects.core.pagination)
implementation(projects.domain.tokens.models)
implementation(projects.domain.markets)
// region DI
implementation(deps.hilt.android)
kapt(deps.hilt.kapt)
// endregion
// region Others dependencies
implementation(deps.kotlin.coroutines)
implementation(deps.moshi)
implementation(deps.moshi.kotlin)
implementation(projects.libs.blockchainSdk)
// endregion
}

View file

@ -0,0 +1,121 @@
package com.tangem.data.markets
import com.tangem.data.markets.converters.*
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.markets.TangemTechMarketsApi
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.domain.markets.*
import com.tangem.domain.markets.repositories.MarketsTokenRepository
import com.tangem.pagination.*
import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
internal class DefaultMarketsTokenRepository(
private val marketsApi: TangemTechMarketsApi,
private val tangemTechApi: TangemTechApi,
private val dispatcherProvider: CoroutineDispatcherProvider,
) : MarketsTokenRepository {
private val tokenListConverter = TokenMarketListConverter()
private val tokenListChartsConverter = TokenMarketChartsConverter(TokenListChartConverter())
private val tokenQuotesConverter = TokenQuotesConverter()
private val tokenMarketsFetcher
get() = LimitOffsetBatchFetcher<TokenMarketListConfig, List<TokenMarket>>(
prefetchDistance = 50,
batchSize = 30,
fetch = { params ->
withContext(dispatcherProvider.io) {
val res = marketsApi.getCoinsList(
currency = params.request.fiatPriceCurrency,
interval = params.request.priceChangeInterval.toRequestParam(),
order = params.request.priceChangeInterval.toRequestParam(),
search = params.request.searchText,
generalCoins = params.request.showUnder100kMarketCapTokens.not(),
offset = params.offset,
limit = params.limit,
).getOrThrow()
val last = res.tokens.size < params.limit
BatchFetchResult.Success(
data = tokenListConverter.convert(res),
last = last,
)
}
},
)
private val tokenMarketsUpdateFetcher
get() = BatchUpdateFetcher<Int, List<TokenMarket>, TokenMarketUpdateRequest> { toUpdate, updateRequest ->
withContext(dispatcherProvider.io) {
val idsToUpdate = toUpdate.map { batch ->
batch.data.map { it.id }
}.flatten()
val updatedBatches = when (updateRequest) {
is TokenMarketUpdateRequest.UpdateChart -> {
val res = marketsApi.getCoinsListCharts(
coinIds = idsToUpdate,
interval = updateRequest.interval.toRequestParam(),
currency = updateRequest.currency,
).getOrThrow()
toUpdate.map { batch ->
batch.copy(
data = batch.data.map {
it.copy(
tokenCharts = tokenListChartsConverter.convert(
chartsToCopy = it.tokenCharts,
tokenId = it.id,
interval = updateRequest.interval,
value = res,
),
)
},
)
}
}
is TokenMarketUpdateRequest.UpdateQuotes -> {
val quotesRes = tangemTechApi.getQuotes(
currencyId = updateRequest.currencyId,
coinIds = idsToUpdate.joinToString(separator = ","),
fields = quoteFields.joinToString(separator = ","),
).getOrThrow()
toUpdate.map { batch ->
batch.copy(
data = batch.data.map {
it.copy(tokenQuotes = tokenQuotesConverter.convert(it.id, quotesRes))
},
)
}
}
}
BatchUpdateResult.Success(updatedBatches)
}
}
override suspend fun getTokenListFlow(
batchingContext: BatchingContext<Int, TokenMarketListConfig, TokenMarketUpdateRequest>,
): BatchFlow<Int, List<TokenMarket>, TokenMarketUpdateRequest> {
return BatchListSource(
fetchDispatcher = dispatcherProvider.io,
context = batchingContext,
generateNewKey = { it.size },
batchFetcher = tokenMarketsFetcher,
updateFetcher = tokenMarketsUpdateFetcher,
).toBatchFlow()
}
companion object {
private val quoteFields = listOf(
"price",
"priceChange24h",
"priceChange1w",
"priceChange30d",
)
}
}

View file

@ -0,0 +1,16 @@
package com.tangem.data.markets.converters
import com.tangem.datasource.api.markets.models.response.TokenMarketChartResponse
import com.tangem.domain.markets.PriceChangeInterval
import com.tangem.domain.markets.TokenChart
class TokenListChartConverter {
fun convert(interval: PriceChangeInterval, value: TokenMarketChartResponse): TokenChart {
return TokenChart(
interval = interval,
priceY = value.prices.values.toList(),
timeStamp = value.prices.keys.toList(),
)
}
}

View file

@ -0,0 +1,28 @@
package com.tangem.data.markets.converters
import com.tangem.domain.markets.PriceChangeInterval
import com.tangem.domain.markets.TokenMarketListConfig
fun TokenMarketListConfig.Interval.toRequestParam(): String = when (this) {
TokenMarketListConfig.Interval.H24 -> "24h"
TokenMarketListConfig.Interval.WEEK -> "1w"
TokenMarketListConfig.Interval.MONTH -> "30d"
}
fun TokenMarketListConfig.Order.toRequestParam(): String = when (this) {
TokenMarketListConfig.Order.ByRating -> "rating"
TokenMarketListConfig.Order.Trending -> "trending"
TokenMarketListConfig.Order.Buyers -> "buyers"
TokenMarketListConfig.Order.TopGainers -> "gainers"
TokenMarketListConfig.Order.TopLosers -> "losers"
}
fun PriceChangeInterval.toRequestParam(): String = when (this) {
PriceChangeInterval.H24 -> "24h"
PriceChangeInterval.WEEK -> "1w"
PriceChangeInterval.MONTH -> "1m"
PriceChangeInterval.MONTH3 -> "3m"
PriceChangeInterval.MONTH6 -> "6m"
PriceChangeInterval.YEAR -> "1y"
PriceChangeInterval.ALL_TIME -> "all"
}

View file

@ -0,0 +1,33 @@
package com.tangem.data.markets.converters
import com.tangem.datasource.api.markets.models.response.TokenMarketChartListResponse
import com.tangem.domain.markets.PriceChangeInterval
import com.tangem.domain.markets.TokenMarket
class TokenMarketChartsConverter(
private val tokenListChartConverter: TokenListChartConverter,
) {
fun convert(
chartsToCopy: TokenMarket.Charts,
tokenId: String,
interval: PriceChangeInterval,
value: TokenMarketChartListResponse,
): TokenMarket.Charts {
val prices = requireNotNull(value.tokens[tokenId]) {
"$tokenId is not found in the response. This shouldn't have happened."
}
return when (interval) {
PriceChangeInterval.H24 -> chartsToCopy.copy(
h24 = tokenListChartConverter.convert(interval, prices),
)
PriceChangeInterval.WEEK -> chartsToCopy.copy(
week = tokenListChartConverter.convert(interval, prices),
)
PriceChangeInterval.MONTH -> chartsToCopy.copy(
month = tokenListChartConverter.convert(interval, prices),
)
else -> error("unsupported interval=$interval. This shouldn't have happened.")
}
}
}

View file

@ -0,0 +1,32 @@
package com.tangem.data.markets.converters
import com.tangem.datasource.api.markets.models.response.TokenMarketListResponse
import com.tangem.domain.markets.PriceChangeInterval
import com.tangem.domain.markets.TokenMarket
import com.tangem.domain.markets.TokenQuotes
import com.tangem.utils.converter.Converter
class TokenMarketListConverter : Converter<TokenMarketListResponse, List<TokenMarket>> {
override fun convert(value: TokenMarketListResponse): List<TokenMarket> {
return value.tokens.map { token ->
TokenMarket(
id = token.id,
name = token.name,
symbol = token.symbol,
marketRating = token.marketRating,
marketCap = token.marketCap,
imageHost = value.imageHost,
tokenQuotes = TokenQuotes(
currentPrice = token.currentPrice,
priceChanges = mapOf(
PriceChangeInterval.H24 to token.priceChangePercentage.h24,
PriceChangeInterval.WEEK to token.priceChangePercentage.week1,
PriceChangeInterval.MONTH to token.priceChangePercentage.day30,
),
),
tokenCharts = TokenMarket.Charts(null, null, null),
)
}
}
}

View file

@ -0,0 +1,30 @@
package com.tangem.data.markets.converters
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
import com.tangem.domain.markets.PriceChangeInterval
import com.tangem.domain.markets.TokenQuotes
class TokenQuotesConverter {
fun convert(tokenId: String, value: QuotesResponse): TokenQuotes {
val quote = requireNotNull(value.quotes[tokenId]) {
"$tokenId is not found in the response. This shouldn't have happened."
}
return TokenQuotes(
currentPrice = requireNotNull(quote.price) {
"Price is not found in the QuotesResponse. This shouldn't have happened."
},
priceChanges = mapOf(
PriceChangeInterval.H24 to requireNotNull(quote.priceChange1w) {
"priceChange1w is not found in the QuotesResponse. This shouldn't have happened."
},
PriceChangeInterval.WEEK to requireNotNull(quote.priceChange1w) {
"priceChange1w is not found in the QuotesResponse. This shouldn't have happened."
},
PriceChangeInterval.MONTH to requireNotNull(quote.priceChange30d) {
"priceChange30d is not found in the QuotesResponse. This shouldn't have happened."
},
),
)
}
}

View file

@ -0,0 +1,31 @@
package com.tangem.data.markets.di
import com.tangem.data.markets.DefaultMarketsTokenRepository
import com.tangem.datasource.api.markets.TangemTechMarketsApi
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.domain.markets.repositories.MarketsTokenRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object MarketsDataModule {
@Provides
@Singleton
fun provideMarketsRepository(
marketsApi: TangemTechMarketsApi,
tangemTechApi: TangemTechApi,
dispatchers: CoroutineDispatcherProvider,
): MarketsTokenRepository {
return DefaultMarketsTokenRepository(
marketsApi = marketsApi,
tangemTechApi = tangemTechApi,
dispatcherProvider = dispatchers,
)
}
}

View file

@ -13,7 +13,7 @@ internal class QuotesConverter : Converter<StoredQuote, Quote> {
return Quote(
rawCurrencyId = rawCurrencyId,
fiatRate = responseQuote.price ?: BigDecimal.ZERO,
priceChange = (responseQuote.priceChange ?: BigDecimal.ZERO).movePointLeft(2),
priceChange = (responseQuote.priceChange24h ?: BigDecimal.ZERO).movePointLeft(2),
)
}
}

1
domain/markets/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,20 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
alias(deps.plugins.kotlin.serialization)
id("configuration")
}
android {
namespace = "com.tangem.domain.markets"
}
dependencies {
api(projects.domain.markets.models)
api(projects.domain.core)
api(projects.core.pagination)
implementation(deps.kotlin.serialization)
implementation(projects.domain.tokens.models)
}

1
domain/markets/models/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,12 @@
plugins {
alias(deps.plugins.kotlin.jvm)
alias(deps.plugins.kotlin.serialization)
id("configuration")
}
dependencies {
implementation(projects.domain.core)
implementation(deps.kotlin.serialization)
implementation(deps.jodatime)
}

View file

@ -0,0 +1,5 @@
package com.tangem.domain.markets
enum class PriceChangeInterval {
H24, WEEK, MONTH, MONTH3, MONTH6, YEAR, ALL_TIME
}

View file

@ -0,0 +1,13 @@
package com.tangem.domain.markets
import java.math.BigDecimal
data class TokenChart(
val interval: PriceChangeInterval,
val priceY: List<BigDecimal>,
val timeStamp: List<Long>,
) {
init {
require(priceY.size == timeStamp.size)
}
}

View file

@ -0,0 +1,33 @@
package com.tangem.domain.markets
import java.math.BigDecimal
data class TokenMarket(
val id: String,
val name: String,
val symbol: String,
val marketRating: Int?,
val marketCap: BigDecimal?,
val tokenQuotes: TokenQuotes,
val tokenCharts: Charts,
private val imageHost: String,
) {
data class Charts(
val h24: TokenChart?,
val week: TokenChart?,
val month: TokenChart?,
)
// 25x25
val imageUrlThumb =
"$imageHost/:thumb/:$id.png"
// 50x50
val imageUrlSmall =
"$imageHost/:small/:$id.png"
// 250x250
val imageUrlLarge =
"$imageHost/:large/:$id.png"
}

View file

@ -0,0 +1,18 @@
package com.tangem.domain.markets
data class TokenMarketListConfig(
val fiatPriceCurrency: String,
val searchText: String?,
val showUnder100kMarketCapTokens: Boolean,
val priceChangeInterval: Interval,
val order: Order,
) {
enum class Order {
ByRating, Trending, Buyers, TopGainers, TopLosers
}
enum class Interval {
H24, WEEK, MONTH,
}
}

View file

@ -0,0 +1,13 @@
package com.tangem.domain.markets
sealed class TokenMarketUpdateRequest {
data class UpdateQuotes(
val currencyId: String,
) : TokenMarketUpdateRequest()
data class UpdateChart(
val interval: PriceChangeInterval,
val currency: String,
) : TokenMarketUpdateRequest()
}

View file

@ -0,0 +1,8 @@
package com.tangem.domain.markets
import java.math.BigDecimal
data class TokenQuotes(
val currentPrice: BigDecimal,
val priceChanges: Map<PriceChangeInterval, BigDecimal>,
)

View file

@ -0,0 +1,14 @@
package com.tangem.domain.markets.repositories
import com.tangem.domain.markets.TokenMarket
import com.tangem.domain.markets.TokenMarketListConfig
import com.tangem.domain.markets.TokenMarketUpdateRequest
import com.tangem.pagination.BatchFlow
import com.tangem.pagination.BatchingContext
interface MarketsTokenRepository {
suspend fun getTokenListFlow(
batchingContext: BatchingContext<Int, TokenMarketListConfig, TokenMarketUpdateRequest>,
): BatchFlow<Int, List<TokenMarket>, TokenMarketUpdateRequest>
}

View file

@ -8,12 +8,9 @@ import java.math.BigDecimal
* @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 values The values representing the cryptocurrency's price changes over a 24-hour period,
* suitable for chart plotting.
*/
data class Quote(
val rawCurrencyId: String,
val fiatRate: BigDecimal,
val priceChange: BigDecimal,
val values: List<Double>? = null,
)

View file

@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs = -Xmx4096m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.jvmargs = -Xmx6144m -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects

View file

@ -224,6 +224,8 @@ include(":domain:qr-scanning:models")
include(":domain:staking")
include(":domain:staking:models")
include(":domain:wallet-connect")
include(":domain:markets")
include(":domain:markets:models")
// endregion Domain modules
// region Data modules
@ -245,4 +247,5 @@ include(":data:feedback")
include(":data:qr-scanning")
include(":data:staking")
include(":data:wallet-connect")
include(":data:markets")
// endregion Data modules