Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-26 11:03:16 +01:00
parent 88f7205293
commit f9f2ac60d0
8 changed files with 221 additions and 116 deletions

View file

@ -104,6 +104,8 @@ data class TokenMarketInfoResponse(
data class Metrics(
@Json(name = "market_rating")
val marketRating: Int?,
@Json(name = "market_rating_change_24h")
val marketRatingChange24h: Int?,
@Json(name = "circulating_supply")
val circulatingSupply: BigDecimal?,
@Json(name = "market_cap")

View file

@ -834,6 +834,7 @@
<string name="markets_token_details_max_supply_description">The maximum number of coins or tokens that can ever exist for a particular cryptocurrency</string>
<string name="markets_token_details_max_supply_full">Max supply</string>
<string name="markets_token_details_metrics">Metrics</string>
<string name="markets_token_details_metrics_no_limited">No limited</string>
<string name="markets_token_details_official_links">Official links</string>
<string name="markets_token_details_price_performance">Price performance</string>
<string name="markets_token_details_repository">Repository</string>

View file

@ -114,6 +114,18 @@ class TangemTypography2 internal constructor(
),
)
val headingSemibold22: TextStyle = TextStyle(
fontFamily = fontFamily,
fontSize = 22.sp,
fontWeight = FontWeight.SemiBold,
letterSpacing = TextUnit(value = 0.38f, type = TextUnitType.Sp),
lineHeight = TextUnit(value = 28f, type = TextUnitType.Sp),
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
),
)
val headingRegular20: TextStyle = TextStyle(
fontFamily = fontFamily,
fontSize = 20.sp,

View file

@ -105,6 +105,7 @@ internal class TokenMarketInfoConverter(
private fun TokenMarketInfoResponse.Metrics.convert(): TokenMarketInfo.Metrics {
return TokenMarketInfo.Metrics(
marketRating = marketRating,
marketRatingChange24h = marketRatingChange24h,
circulatingSupply = circulatingSupply,
marketCap = marketCap,
volume24h = volume24h,

View file

@ -49,6 +49,7 @@ data class TokenMarketInfo(
data class Metrics(
val marketRating: Int?,
val marketRatingChange24h: Int?,
val circulatingSupply: BigDecimal?,
val marketCap: BigDecimal?,
val volume24h: BigDecimal?,

View file

@ -1,6 +1,7 @@
package com.tangem.features.feed.model.market.details.converter
import androidx.compose.runtime.Stable
import com.tangem.core.ui.extensions.combinedReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
@ -37,7 +38,7 @@ internal class MetricsConverter(
metrics = persistentListOf(
InfoPointUM(
title = resourceReference(R.string.markets_token_details_market_capitalization),
value = marketCap.formatAmount(),
value = marketCap.formatAmount() ?: StringsSigns.DASH_SIGN,
onInfoClick = {
onInfoClick(
InfoBottomSheetContent(
@ -65,7 +66,7 @@ internal class MetricsConverter(
),
InfoPointUM(
title = resourceReference(R.string.markets_token_details_trading_volume),
value = volume24h.formatAmount(),
value = volume24h.formatAmount() ?: StringsSigns.DASH_SIGN,
onInfoClick = {
onInfoClick(
InfoBottomSheetContent(
@ -79,7 +80,7 @@ internal class MetricsConverter(
),
InfoPointUM(
title = resourceReference(R.string.markets_token_details_fully_diluted_valuation),
value = fullyDilutedValuation.formatAmount(),
value = fullyDilutedValuation.formatAmount() ?: StringsSigns.DASH_SIGN,
onInfoClick = {
onInfoClick(
InfoBottomSheetContent(
@ -95,7 +96,7 @@ internal class MetricsConverter(
),
InfoPointUM(
title = resourceReference(R.string.markets_token_details_circulating_supply),
value = circulatingSupply.formatAmount(crypto = true),
value = circulatingSupply.formatAmount(crypto = true) ?: StringsSigns.DASH_SIGN,
onInfoClick = {
onInfoClick(
InfoBottomSheetContent(
@ -109,7 +110,7 @@ internal class MetricsConverter(
),
InfoPointUM(
title = resourceReference(R.string.markets_token_details_max_supply),
value = maxSupply.formatMaxSupply(),
value = maxSupply.formatMaxSupply() ?: StringsSigns.DASH_SIGN,
onInfoClick = {
onInfoClick(
InfoBottomSheetContent(
@ -135,7 +136,7 @@ internal class MetricsConverter(
val liquidity = getLiquidity(value.volume24h, value.marketCap)
persistentListOf(
InfoPointUMV2.MarketCap(
capitalizationValue = stringReference(marketCap.formatAmount()),
capitalizationValue = marketCap.formatAmount()?.let(::stringReference),
onInfoClick = {
onInfoClick(
InfoBottomSheetContent(
@ -150,7 +151,7 @@ internal class MetricsConverter(
},
),
InfoPointUMV2.TradingVolume(
tradingValue = stringReference(volume24h.formatAmount()),
tradingValue = volume24h.formatAmount()?.let(::stringReference),
liquidity = liquidity,
trendingVolumeLiquidityType = getTrendingVolumeLiquidityType(liquidity),
onInfoClick = {
@ -165,7 +166,9 @@ internal class MetricsConverter(
},
),
InfoPointUMV2.MarketPosition(
position = marketRating?.toString() ?: StringsSigns.DASH_SIGN,
position = marketRating?.let {
stringReference(it.toString())
},
rangeValue = getMarketRatingRangeValue(marketRating),
marketRatingType = getMarketRatingType(marketRating),
onInfoClick = {
@ -176,12 +179,15 @@ internal class MetricsConverter(
),
)
},
marketRatingChange24H = getMarketRatingChange(marketRatingChange24h),
),
InfoPointUMV2.FullyDilutedValuation(
value = resourceReference(
R.string.markets_token_details_valuation_value_in_total,
wrappedList(fullyDilutedValuation.formatAmount()),
),
value = fullyDilutedValuation?.formatAmount()?.let { formatted ->
resourceReference(
id = R.string.markets_token_details_valuation_value_in_total,
formatArgs = wrappedList(formatted),
)
},
onInfoClick = {
onInfoClick(
InfoBottomSheetContent(
@ -196,20 +202,29 @@ internal class MetricsConverter(
},
),
InfoPointUMV2.CirculatingSupply(
currentValue = stringReference(circulatingSupply.formatAmount(crypto = true)),
maxValue = maxSupply?.let { supply ->
if (supply > BigDecimal.ZERO) {
stringReference(supply.formatMaxSupply())
} else {
null
currentValue = circulatingSupply?.formatAmount(true)?.let(::stringReference),
maxValue = when (maxSupply) {
null -> null
BigDecimal.ZERO -> {
resourceReference(
R.string
.markets_token_details_metrics_no_limited,
)
}
else -> maxSupply.formatAmount(true)?.let(::stringReference)
},
fillValue = getCirculatingSupplyFillValue(circulatingSupply, maxSupply),
onInfoClick = {
onInfoClick(
InfoBottomSheetContent(
title = resourceReference(R.string.markets_token_details_max_supply_full),
body = resourceReference(R.string.markets_token_details_total_supply_description),
title = resourceReference(
R.string.markets_token_details_max_supply_and_circulation_full,
),
body = combinedReference(
resourceReference(R.string.markets_token_details_circulating_supply_description),
stringReference("\n\n"),
resourceReference(R.string.markets_token_details_total_supply_description),
),
),
)
},
@ -234,7 +249,7 @@ internal class MetricsConverter(
)
}
private fun BigDecimal?.formatMaxSupply(): String {
private fun BigDecimal?.formatMaxSupply(): String? {
when (this) {
null -> return StringsSigns.DASH_SIGN
BigDecimal.ZERO -> return StringsSigns.INFINITY_SIGN
@ -243,8 +258,8 @@ internal class MetricsConverter(
return this.formatAmount(crypto = true)
}
private fun BigDecimal?.formatAmount(crypto: Boolean = false): String {
if (this == null) return StringsSigns.DASH_SIGN
private fun BigDecimal?.formatAmount(crypto: Boolean = false): String? {
if (this == null) return null
return if (crypto) {
format {
@ -266,9 +281,8 @@ internal class MetricsConverter(
}
@Suppress("MagicNumber")
private fun getLiquidity(volume24h: BigDecimal?, marketCap: BigDecimal?): Float {
if (volume24h == null || marketCap == null || marketCap == BigDecimal.ZERO) return 0f
private fun getLiquidity(volume24h: BigDecimal?, marketCap: BigDecimal?): Float? {
if (volume24h == null || marketCap == null || marketCap == BigDecimal.ZERO) return null
val ratio = volume24h
.divide(marketCap, 6, RoundingMode.HALF_UP)
.toFloat()
@ -277,8 +291,9 @@ internal class MetricsConverter(
}
@Suppress("MagicNumber")
private fun getTrendingVolumeLiquidityType(liquidity: Float): TrendingVolumeLiquidityType {
private fun getTrendingVolumeLiquidityType(liquidity: Float?): TrendingVolumeLiquidityType {
return when {
liquidity == null -> TrendingVolumeLiquidityType.UNKNOWN
liquidity >= 0.5f -> TrendingVolumeLiquidityType.HIGH
liquidity in 0.2f..<0.5f -> TrendingVolumeLiquidityType.MEDIUM
else -> TrendingVolumeLiquidityType.LOW
@ -296,8 +311,8 @@ internal class MetricsConverter(
}
@Suppress("MagicNumber")
private fun getMarketRatingRangeValue(marketRating: Int?): Float {
if (marketRating == null) return 0f
private fun getMarketRatingRangeValue(marketRating: Int?): Float? {
if (marketRating == null) return null
return when {
marketRating <= 20 -> {
@ -352,4 +367,12 @@ internal class MetricsConverter(
return ratio.coerceIn(0f, 1f)
}
private fun getMarketRatingChange(change: Int?): MarketRatingChange24H {
return when {
change == null || change == 0 -> MarketRatingChange24H.NoChanges
change < 0 -> MarketRatingChange24H.Down(-change)
else -> MarketRatingChange24H.Up(change)
}
}
}

View file

@ -21,14 +21,12 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerW
import com.tangem.core.ui.components.progressbar.TangemLinearProgressIndicator
import com.tangem.core.ui.ds.progress.TangemLinearProgressIndicatorWithDot
import com.tangem.core.ui.ds.row.TangemRowContainer
import com.tangem.core.ui.ds.row.TangemRowLayoutId
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.LocalIsInDarkTheme
import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
@ -36,6 +34,7 @@ import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.features.feed.impl.R
import com.tangem.features.feed.ui.components.MetricsCard
import com.tangem.features.feed.ui.market.detailed.state.InfoPointUMV2
import com.tangem.features.feed.ui.market.detailed.state.MarketRatingChange24H
import com.tangem.features.feed.ui.market.detailed.state.MarketRatingType
import com.tangem.features.feed.ui.market.detailed.state.TrendingVolumeLiquidityType
@ -45,15 +44,7 @@ internal fun MarketCapCard(item: InfoPointUMV2.MarketCap) {
modifier = Modifier
.heightIn(120.dp)
.fillMaxWidth(),
title = {
Text(
text = item.capitalizationValue.resolveReference(),
style = TangemTheme.typography2.headingSemibold20,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
},
title = { MetricValueText(value = item.capitalizationValue) },
content = {
InformationTextBlock(
text = resourceReference(R.string.markets_token_details_market_capitalization),
@ -69,25 +60,22 @@ internal fun TradingVolumeCard(item: InfoPointUMV2.TradingVolume) {
TrendingVolumeLiquidityType.HIGH -> TangemTheme.colors2.markers.backgroundSolidGreen
TrendingVolumeLiquidityType.MEDIUM -> TangemTheme.colors2.graphic.status.attention
TrendingVolumeLiquidityType.LOW -> TangemTheme.colors2.graphic.status.warning
TrendingVolumeLiquidityType.UNKNOWN -> TangemTheme.colors2.surface.level3
}
val valueColor = metricValueColor(hasData = item.tradingValue != null)
MetricsCard(
modifier = Modifier
.heightIn(120.dp)
.fillMaxWidth(),
title = {
Row {
Text(
text = item.tradingValue.resolveReference(),
style = TangemTheme.typography2.headingSemibold20,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
MetricValueText(item.tradingValue)
Text(
modifier = Modifier.padding(TangemTheme.dimens2.x1),
text = stringResourceSafe(R.string.markets_token_details_trading_interval),
style = TangemTheme.typography2.captionSemibold11,
color = TangemTheme.colors2.text.neutral.primary,
color = valueColor,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
@ -95,14 +83,17 @@ internal fun TradingVolumeCard(item: InfoPointUMV2.TradingVolume) {
},
content = {
Column(modifier = Modifier.fillMaxWidth()) {
TangemLinearProgressIndicator(
modifier = Modifier
.fillMaxWidth()
.height(6.dp),
progress = { item.liquidity },
color = tradingColor,
backgroundColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant.copy(alpha = .1f),
)
if (item.liquidity != null) {
TangemLinearProgressIndicator(
modifier = Modifier
.fillMaxWidth()
.height(6.dp),
progress = { item.liquidity },
color = tradingColor,
backgroundColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant
.copy(alpha = .1f),
)
}
SpacerH(12.dp)
InformationTextBlock(
text = resourceReference(R.string.markets_token_details_trading_volume),
@ -127,37 +118,25 @@ internal fun MarketPositionCard(item: InfoPointUMV2.MarketPosition) {
.fillMaxWidth(),
title = {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_big_laurel_left_20),
tint = ratingColor,
contentDescription = null,
)
Text(
textAlign = TextAlign.Center,
text = item.position,
color = ratingColor,
style = TangemTheme.typography2.headingSemibold20.copy(letterSpacing = 0.sp),
maxLines = 1,
)
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_big_laurel_right_20),
tint = ratingColor,
contentDescription = null,
)
MarketPositionValue(position = item.position, ratingColor = ratingColor)
if (item.position != null) {
SpacerW(6.dp)
RatingChangeIndicator(change = item.marketRatingChange24H)
}
}
},
content = {
Column(modifier = Modifier.fillMaxWidth()) {
TangemLinearProgressIndicatorWithDot(
modifier = Modifier
.fillMaxWidth()
.height(6.dp),
progress = { item.rangeValue },
dotColor = TangemTheme.colors2.fill.neutral.primaryInvertedConstant,
backgroundColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant.copy(alpha = .1f),
)
if (item.rangeValue != null) {
TangemLinearProgressIndicatorWithDot(
modifier = Modifier
.fillMaxWidth()
.height(6.dp),
progress = { item.rangeValue },
dotColor = TangemTheme.colors2.fill.neutral.primaryInvertedConstant,
backgroundColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant.copy(alpha = .1f),
)
}
SpacerH(12.dp)
InformationTextBlock(
text = resourceReference(R.string.markets_token_details_market_rating),
@ -177,15 +156,7 @@ internal fun FDVCard(item: InfoPointUMV2.FullyDilutedValuation) {
modifier = Modifier
.heightIn(120.dp)
.fillMaxWidth(),
title = {
Text(
text = item.value.resolveReference(),
style = TangemTheme.typography2.headingSemibold20,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
},
title = { MetricValueText(value = item.value) },
content = {
InformationTextBlock(
text = resourceReference(R.string.markets_token_details_fully_diluted_valuation),
@ -212,15 +183,11 @@ internal fun CirculatingSupplyCard(item: InfoPointUMV2.CirculatingSupply) {
overflow = TextOverflow.Ellipsis,
)
Text(
MetricValueText(
value = item.currentValue,
modifier = Modifier
.padding(top = 12.dp)
.layoutId(TangemRowLayoutId.START_BOTTOM),
text = item.currentValue.resolveReference(),
style = TangemTheme.typography2.headingSemibold20,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
@ -238,7 +205,7 @@ internal fun CirculatingSupplyCard(item: InfoPointUMV2.CirculatingSupply) {
.padding(top = 12.dp)
.layoutId(TangemRowLayoutId.END_BOTTOM),
text = item.maxValue.resolveReference(),
style = TangemTheme.typography2.headingSemibold20,
style = TangemTheme.typography2.headingSemibold22,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
@ -253,7 +220,8 @@ internal fun CirculatingSupplyCard(item: InfoPointUMV2.CirculatingSupply) {
.fillMaxWidth()
.height(6.dp),
color = TangemTheme.colors2.graphic.status.accent,
trackColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant.copy(alpha = .1f),
trackColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant
.copy(alpha = .1f),
progress = { item.fillValue },
strokeCap = StrokeCap.Round,
drawStopIndicator = {},
@ -265,6 +233,87 @@ internal fun CirculatingSupplyCard(item: InfoPointUMV2.CirculatingSupply) {
)
}
// region Private helpers
@Composable
private fun MetricValueText(value: TextReference?, modifier: Modifier = Modifier) {
Text(
modifier = modifier,
text = value?.resolveReference() ?: stringResourceSafe(R.string.token_market_metrics_no_data),
style = TangemTheme.typography2.headingSemibold22,
color = metricValueColor(hasData = value != null),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
@Composable
private fun metricValueColor(hasData: Boolean): Color {
return if (hasData) TangemTheme.colors2.text.neutral.primary else TangemTheme.colors2.text.neutral.tertiary
}
@Composable
private fun MarketPositionValue(position: TextReference?, ratingColor: Color) {
if (position != null) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_big_laurel_left_20),
tint = ratingColor,
contentDescription = null,
)
Text(
textAlign = TextAlign.Center,
text = position.resolveReference(),
color = ratingColor,
style = TangemTheme.typography2.headingSemibold22.copy(letterSpacing = 0.sp),
maxLines = 1,
)
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_big_laurel_right_20),
tint = ratingColor,
contentDescription = null,
)
} else {
MetricValueText(value = null)
}
}
@Composable
private fun RatingChangeIndicator(change: MarketRatingChange24H) {
when (change) {
is MarketRatingChange24H.Up -> RatingChangeContent(
iconRes = R.drawable.ic_arrow_up_8,
iconTint = TangemTheme.colors2.markers.iconGreen,
changeValue = change.changeValue.toString(),
textColor = TangemTheme.colors2.text.status.positive,
)
is MarketRatingChange24H.Down -> RatingChangeContent(
iconRes = R.drawable.ic_arrow_down_8,
iconTint = TangemTheme.colors2.markers.iconRed,
changeValue = change.changeValue.toString(),
textColor = TangemTheme.colors2.text.status.warning,
)
MarketRatingChange24H.NoChanges -> Unit
}
}
@Composable
private fun RatingChangeContent(iconRes: Int, iconTint: Color, changeValue: String, textColor: Color) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
modifier = Modifier.size(TangemTheme.dimens2.x3),
imageVector = ImageVector.vectorResource(id = iconRes),
tint = iconTint,
contentDescription = null,
)
SpacerW(2.dp)
Text(
text = changeValue,
style = TangemTheme.typography2.captionSemibold12,
color = textColor,
)
}
}
@Composable
private fun MarketRatingType.baseColor(): Color {
val isDarkTheme = LocalIsInDarkTheme.current
@ -295,6 +344,8 @@ private fun mapRatingToCardColor(marketRatingType: MarketRatingType): Color {
}
}
// endregion
private const val GOLD_PLACE_COLOR_NIGHT = 0xFFFBEE76
private const val GOLD_PLACE_COLOR_LIGHT = 0xFFD9B900
private const val SILVER_PLACE_COLOR_NIGHT = 0xFFAABEF7
@ -353,17 +404,19 @@ private fun MetricsCardsPreview() {
MarketPositionCard(
item = InfoPointUMV2.MarketPosition(
position = "1",
position = stringReference("1"),
rangeValue = 0.02f,
marketRatingType = MarketRatingType.GOLD,
onInfoClick = {},
marketRatingChange24H = MarketRatingChange24H.NoChanges,
),
)
MarketPositionCard(
item = InfoPointUMV2.MarketPosition(
position = "2",
position = stringReference("2"),
rangeValue = 0.05f,
marketRatingChange24H = MarketRatingChange24H.Up(1),
marketRatingType = MarketRatingType.SILVER,
onInfoClick = {},
),
@ -371,19 +424,21 @@ private fun MetricsCardsPreview() {
MarketPositionCard(
item = InfoPointUMV2.MarketPosition(
position = "3",
rangeValue = 0.08f,
marketRatingType = MarketRatingType.BRONZE,
position = null,
rangeValue = null,
marketRatingType = MarketRatingType.OTHER,
onInfoClick = {},
marketRatingChange24H = MarketRatingChange24H.NoChanges,
),
)
MarketPositionCard(
item = InfoPointUMV2.MarketPosition(
position = "42",
position = stringReference("42"),
rangeValue = 0.42f,
marketRatingType = MarketRatingType.OTHER,
onInfoClick = {},
marketRatingChange24H = MarketRatingChange24H.Down(15),
),
)

View file

@ -14,35 +14,36 @@ internal sealed interface InfoPointUMV2 {
@Immutable
data class MarketCap(
val capitalizationValue: TextReference,
val capitalizationValue: TextReference?,
val onInfoClick: () -> Unit,
) : InfoPointUMV2
@Immutable
data class TradingVolume(
val tradingValue: TextReference,
val liquidity: Float,
val tradingValue: TextReference?,
val liquidity: Float?,
val trendingVolumeLiquidityType: TrendingVolumeLiquidityType,
val onInfoClick: () -> Unit,
) : InfoPointUMV2
@Immutable
data class MarketPosition(
val position: String,
val rangeValue: Float,
val position: TextReference?,
val rangeValue: Float?,
val marketRatingChange24H: MarketRatingChange24H,
val marketRatingType: MarketRatingType,
val onInfoClick: () -> Unit,
) : InfoPointUMV2
@Immutable
data class FullyDilutedValuation(
val value: TextReference,
val value: TextReference?,
val onInfoClick: () -> Unit,
) : InfoPointUMV2
@Immutable
data class CirculatingSupply(
val currentValue: TextReference,
val currentValue: TextReference?,
val maxValue: TextReference?,
val fillValue: Float?,
val onInfoClick: () -> Unit,
@ -60,9 +61,18 @@ internal data class MetricsV2UM(
}
internal enum class TrendingVolumeLiquidityType {
HIGH, MEDIUM, LOW,
HIGH, MEDIUM, LOW, UNKNOWN
}
internal enum class MarketRatingType {
GOLD, SILVER, BRONZE, OTHER
}
internal sealed interface MarketRatingChange24H {
data class Up(val changeValue: Int) : MarketRatingChange24H
data class Down(val changeValue: Int) : MarketRatingChange24H
data object NoChanges : MarketRatingChange24H
}