Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-11 11:42:49 +05:00
parent a13a54accd
commit f023fa7f2c
10 changed files with 267 additions and 90 deletions

View file

@ -0,0 +1,10 @@
package com.tangem.domain.yield.supply.models
data class YieldSupplyRewardBalance(
val fiatBalance: String?,
val cryptoBalance: String?,
) {
companion object {
fun empty() = YieldSupplyRewardBalance(null, null)
}
}

View file

@ -1,11 +1,13 @@
package com.tangem.domain.yield.supply.usecase
import com.tangem.core.ui.format.bigdecimal.anyDecimals
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.yield.supply.models.YieldSupplyRewardBalance
import com.tangem.domain.yield.supply.YieldSupplyRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.delay
@ -22,7 +24,7 @@ class YieldSupplyGetRewardsBalanceUseCase(
private val dispatcherProvider: CoroutineDispatcherProvider,
) {
operator fun invoke(status: CryptoCurrencyStatus, appCurrency: AppCurrency): Flow<String> = flow {
operator fun invoke(status: CryptoCurrencyStatus, appCurrency: AppCurrency): Flow<YieldSupplyRewardBalance> = flow {
val cryptoAmount = status.value.amount
val fiatRate = status.value.fiatRate
@ -30,11 +32,7 @@ class YieldSupplyGetRewardsBalanceUseCase(
return@flow
}
val amount = if (cryptoAmount != null && fiatRate != null) {
cryptoAmount.multiply(fiatRate)
} else {
return@flow
}
if (cryptoAmount == null) return@flow
val tokenAddress = (status.currency as? CryptoCurrency.Token)?.contractAddress ?: return@flow
val apy = try {
@ -50,37 +48,57 @@ class YieldSupplyGetRewardsBalanceUseCase(
return@flow
}
val initialPerTickDelta = amount
.multiply(apyFraction)
.multiply(TICK_SECONDS_BD)
.divide(SECONDS_PER_YEAR_BD, SCALE, RoundingMode.HALF_UP)
.abs()
val initialPerTickDeltaCrypto = perTickDelta(cryptoAmount, apyFraction).abs()
val minVisibleDecimals = calculateMinVisibleDecimals(initialPerTickDelta)
val minVisibleDecimalsCrypto = calculateMinVisibleDecimals(
perTickDeltaAbs = initialPerTickDeltaCrypto,
maxDecimals = status.currency.decimals,
)
var currentBalance: BigDecimal = amount
val fiatAmountStart = fiatRate?.let { cryptoAmount.multiply(it) }
val minVisibleDecimalsFiat = fiatAmountStart?.let { amount ->
val initialPerTickDeltaFiat = perTickDelta(amount, apyFraction).abs()
calculateMinVisibleDecimals(
perTickDeltaAbs = initialPerTickDeltaFiat,
maxDecimals = FIAT_MAX_DECIMALS,
)
}
var currentCryptoBalance: BigDecimal = cryptoAmount
var currentFiatBalance: BigDecimal? = fiatAmountStart
while (true) {
val fiatBalanceFormatted: String? = currentFiatBalance?.format {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
).anyDecimals(decimals = minVisibleDecimalsFiat ?: FIAT_MIN_DECIMALS)
}
val cryptoBalanceFormatted: String = currentCryptoBalance.format {
crypto(status.currency).anyDecimals(
maxDecimals = minVisibleDecimalsCrypto,
minDecimals = minVisibleDecimalsCrypto,
)
}
emit(
currentBalance.format {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
).anyDecimals(decimals = minVisibleDecimals)
},
YieldSupplyRewardBalance(fiatBalance = fiatBalanceFormatted, cryptoBalance = cryptoBalanceFormatted),
)
val perTickDelta = currentBalance
.multiply(apyFraction)
.multiply(TICK_SECONDS_BD)
.divide(SECONDS_PER_YEAR_BD, SCALE, RoundingMode.HALF_UP)
val perTickDeltaCrypto = perTickDelta(currentCryptoBalance, apyFraction)
currentBalance = currentBalance.add(perTickDelta)
currentCryptoBalance = currentCryptoBalance.add(perTickDeltaCrypto)
currentFiatBalance = currentFiatBalance?.let { current ->
val perTickDeltaFiat = perTickDelta(current, apyFraction)
current.add(perTickDeltaFiat)
}
delay(TICK_MILLIS)
}
}.flowOn(dispatcherProvider.default)
private fun calculateMinVisibleDecimals(perTickDeltaAbs: BigDecimal): Int {
private fun calculateMinVisibleDecimals(perTickDeltaAbs: BigDecimal, maxDecimals: Int): Int {
if (perTickDeltaAbs <= BigDecimal.ZERO) return MIN_DECIMALS
val perTickAsDouble = perTickDeltaAbs.toDouble()
@ -88,20 +106,28 @@ class YieldSupplyGetRewardsBalanceUseCase(
val safe = if (perTickAsDouble <= 0.0) EPSILON else perTickAsDouble
val raw = ceil(-ln(safe) / LN_10)
return raw.toInt().coerceIn(MIN_DECIMALS, MAX_DECIMALS)
return raw.toInt().coerceIn(MIN_DECIMALS, maxDecimals)
}
private companion object {
const val TICK_MILLIS: Long = 800
private val TICK_SECONDS_BD = BigDecimal("0.8")
private val SECONDS_PER_YEAR_BD = BigDecimal("31536000") // 365 * 24 * 60 * 60
private val HUNDRED_BD = BigDecimal("100")
private const val SCALE = 18
private fun perTickDelta(amount: BigDecimal, apyFraction: BigDecimal): BigDecimal {
return amount
.multiply(apyFraction)
.multiply(TICK_SECONDS_BD)
.divide(SECONDS_PER_YEAR_BD, SCALE, RoundingMode.HALF_UP)
}
private const val MIN_DECIMALS = 3
private const val MAX_DECIMALS = 12
companion object {
internal const val TICK_MILLIS: Long = 800
internal val TICK_SECONDS_BD: BigDecimal = BigDecimal("0.8")
internal val SECONDS_PER_YEAR_BD: BigDecimal = BigDecimal("31536000") // 365 * 24 * 60 * 60
internal val HUNDRED_BD: BigDecimal = BigDecimal("100")
internal const val SCALE: Int = 18
private val LN_10 = ln(10.0)
private const val EPSILON = 1e-18
internal const val MIN_DECIMALS: Int = 3
internal const val FIAT_MIN_DECIMALS: Int = 2
internal const val FIAT_MAX_DECIMALS: Int = 12
internal val LN_10: Double = ln(10.0)
internal const val EPSILON: Double = 1e-18
}
}

View file

@ -4,6 +4,7 @@ import com.google.common.truth.Truth.assertThat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.anyDecimals
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
@ -11,6 +12,7 @@ import com.tangem.domain.models.network.Network
import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.yield.supply.YieldSupplyRepository
import com.tangem.domain.yield.supply.models.YieldMarketToken
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetRewardsBalanceUseCase.Companion.TICK_MILLIS
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import io.mockk.coEvery
import io.mockk.mockk
@ -26,7 +28,6 @@ import org.junit.jupiter.api.Test
import java.math.BigDecimal
import java.math.RoundingMode
import kotlin.math.ceil
import kotlin.math.ln
class YieldSupplyGetRewardsBalanceUseCaseTest {
@ -165,9 +166,9 @@ class YieldSupplyGetRewardsBalanceUseCaseTest {
val deferred = async { useCase(status, appCurrency).take(3).toList() }
testScheduler.advanceUntilIdle()
advanceTimeBy(300)
advanceTimeBy(TICK_MILLIS)
testScheduler.advanceUntilIdle()
advanceTimeBy(300)
advanceTimeBy(TICK_MILLIS)
testScheduler.advanceUntilIdle()
val collected = deferred.await()
@ -175,25 +176,147 @@ class YieldSupplyGetRewardsBalanceUseCaseTest {
val expectedDecimals = calculateMinVisibleDecimalsForTest(initialPerTickDelta(amount, apy))
val firstExpected = amount.format { fiat(
appCurrency.code,
appCurrency.symbol,
).anyDecimals(decimals = expectedDecimals) }
assertThat(collected[0]).isEqualTo(firstExpected)
val firstExpected = amount.format {
fiat(
appCurrency.code,
appCurrency.symbol,
).anyDecimals(decimals = expectedDecimals)
}
assertThat(collected[0].fiatBalance).isEqualTo(firstExpected)
val firstNext = nextBalance(amount, apy)
val secondExpected = firstNext.format { fiat(
appCurrency.code,
appCurrency.symbol,
).anyDecimals(decimals = expectedDecimals) }
assertThat(collected[1]).isEqualTo(secondExpected)
val secondExpected = firstNext.format {
fiat(
appCurrency.code,
appCurrency.symbol,
).anyDecimals(decimals = expectedDecimals)
}
assertThat(collected[1].fiatBalance).isEqualTo(secondExpected)
val secondNext = nextBalance(firstNext, apy)
val thirdExpected = secondNext.format { fiat(
appCurrency.code,
appCurrency.symbol,
).anyDecimals(decimals = expectedDecimals) }
assertThat(collected[2]).isEqualTo(thirdExpected)
val thirdExpected = secondNext.format {
fiat(
appCurrency.code,
appCurrency.symbol,
).anyDecimals(decimals = expectedDecimals)
}
assertThat(collected[2].fiatBalance).isEqualTo(thirdExpected)
}
@Test
fun `GIVEN polygon USDT0 Loaded status WHEN invoke THEN emit formatted balances`() = runTest {
val network = Network(
id = Network.ID(Network.RawID("POLYGON"), Network.DerivationPath.Card("m/44'/60'/0'/0/0")),
backendId = "polygon-pos",
name = "Polygon",
currencySymbol = "POL",
derivationPath = Network.DerivationPath.Card("m/44'/60'/0'/0/0"),
isTestnet = false,
standardType = Network.StandardType.Unspecified("Polygon"),
hasFiatFeeRate = true,
canHandleTokens = true,
transactionExtrasType = Network.TransactionExtrasType.NONE,
nameResolvingType = Network.NameResolvingType.NONE,
)
val tokenId = CryptoCurrency.ID(
prefix = CryptoCurrency.ID.Prefix.TOKEN_PREFIX,
body = CryptoCurrency.ID.Body.NetworkId(network.rawId),
suffix = CryptoCurrency.ID.Suffix.RawID("usdt0", "0xc2132d05d31c914a87c6611c10748aeb04b58e8f"),
)
val currency = CryptoCurrency.Token(
id = tokenId,
network = network,
name = "USDT0",
symbol = "USDT0",
decimals = 6,
iconUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/usdt0.png",
isCustom = false,
contractAddress = "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
)
val amount = BigDecimal("9.241136")
val fiatRate = BigDecimal("0.9999761277273864")
val status = CryptoCurrencyStatus(
currency = currency,
value = CryptoCurrencyStatus.Loaded(
amount = amount,
fiatAmount = amount.multiply(fiatRate),
fiatRate = fiatRate,
priceChange = BigDecimal("-0.000058200000000008245"),
yieldBalance = null,
yieldSupplyStatus = null,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),
networkAddress = NetworkAddress.Single(
NetworkAddress.Address(
value = "0xb71fa0E20ba8579B3ec51cC79aaa84Bf5982BB49",
type = NetworkAddress.Address.Type.Primary,
),
),
sources = CryptoCurrencyStatus.Sources(),
),
)
val apy = BigDecimal("5.0")
coEvery { repository.getCachedMarkets() } returns listOf(
YieldMarketToken(
tokenAddress = currency.contractAddress,
chainId = 137,
apy = apy,
isActive = true,
maxFeeNative = BigDecimal.ZERO,
maxFeeUSD = BigDecimal.ZERO,
backendId = "polygon-pos",
),
)
val dispatcherProvider = testDispatcherProvider(this)
val useCase = YieldSupplyGetRewardsBalanceUseCase(repository, dispatcherProvider)
val appCurrency = AppCurrency.Default
val deferred = async { useCase(status, appCurrency).take(2).toList() }
testScheduler.advanceUntilIdle()
advanceTimeBy(TICK_MILLIS)
testScheduler.advanceUntilIdle()
val emissions = deferred.await()
assertThat(emissions).hasSize(2)
val apyFraction = apy.divide(BigDecimal("100"), 18, RoundingMode.HALF_UP)
val perTickCrypto = amount.multiply(apyFraction)
.multiply(YieldSupplyGetRewardsBalanceUseCase.TICK_SECONDS_BD)
.divide(
YieldSupplyGetRewardsBalanceUseCase.SECONDS_PER_YEAR_BD,
YieldSupplyGetRewardsBalanceUseCase.SCALE,
RoundingMode.HALF_UP,
)
.abs()
val minCryptoDecimals = calculateMinVisibleDecimalsForTest(perTickCrypto).coerceAtMost(currency.decimals)
val fiatAmountStart = amount.multiply(fiatRate)
val perTickFiat = fiatAmountStart.multiply(apyFraction)
.multiply(YieldSupplyGetRewardsBalanceUseCase.TICK_SECONDS_BD)
.divide(
YieldSupplyGetRewardsBalanceUseCase.SECONDS_PER_YEAR_BD,
YieldSupplyGetRewardsBalanceUseCase.SCALE,
RoundingMode.HALF_UP,
)
.abs()
val minFiatDecimals = calculateMinVisibleDecimalsForTest(perTickFiat)
val expectedCrypto0 = amount.format {
crypto(currency).anyDecimals(
maxDecimals = minCryptoDecimals,
minDecimals = minCryptoDecimals,
)
}
val expectedFiat0 = fiatAmountStart.format {
fiat(appCurrency.code, appCurrency.symbol).anyDecimals(decimals = minFiatDecimals)
}
assertThat(emissions[0].cryptoBalance).isEqualTo(expectedCrypto0)
assertThat(emissions[0].fiatBalance).isEqualTo(expectedFiat0)
}
private fun testDispatcherProvider(scope: TestScope): CoroutineDispatcherProvider {
@ -260,42 +383,48 @@ class YieldSupplyGetRewardsBalanceUseCaseTest {
}
private fun initialPerTickDelta(amount: BigDecimal, apy: BigDecimal): BigDecimal {
val apyFraction = apy.divide(HUNDRED_BD, SCALE, RoundingMode.HALF_UP)
val apyFraction = apy.divide(
YieldSupplyGetRewardsBalanceUseCase.HUNDRED_BD,
YieldSupplyGetRewardsBalanceUseCase.SCALE,
RoundingMode.HALF_UP,
)
return amount
.multiply(apyFraction)
.multiply(TICK_SECONDS_BD)
.divide(SECONDS_PER_YEAR_BD, SCALE, RoundingMode.HALF_UP)
.multiply(YieldSupplyGetRewardsBalanceUseCase.TICK_SECONDS_BD)
.divide(
YieldSupplyGetRewardsBalanceUseCase.SECONDS_PER_YEAR_BD,
YieldSupplyGetRewardsBalanceUseCase.SCALE,
RoundingMode.HALF_UP,
)
.abs()
}
private fun nextBalance(current: BigDecimal, apy: BigDecimal): BigDecimal {
val apyFraction = apy.divide(HUNDRED_BD, SCALE, RoundingMode.HALF_UP)
val apyFraction = apy.divide(
YieldSupplyGetRewardsBalanceUseCase.HUNDRED_BD,
YieldSupplyGetRewardsBalanceUseCase.SCALE,
RoundingMode.HALF_UP,
)
val perTickDelta = current
.multiply(apyFraction)
.multiply(TICK_SECONDS_BD)
.divide(SECONDS_PER_YEAR_BD, SCALE, RoundingMode.HALF_UP)
.multiply(YieldSupplyGetRewardsBalanceUseCase.TICK_SECONDS_BD)
.divide(
YieldSupplyGetRewardsBalanceUseCase.SECONDS_PER_YEAR_BD,
YieldSupplyGetRewardsBalanceUseCase.SCALE,
RoundingMode.HALF_UP,
)
return current.add(perTickDelta)
}
private fun calculateMinVisibleDecimalsForTest(perTickDeltaAbs: BigDecimal): Int {
if (perTickDeltaAbs <= BigDecimal.ZERO) return MIN_DECIMALS
if (perTickDeltaAbs <= BigDecimal.ZERO) return YieldSupplyGetRewardsBalanceUseCase.MIN_DECIMALS
val perTickAsDouble = perTickDeltaAbs.toDouble()
if (perTickAsDouble.isNaN() || perTickAsDouble.isInfinite()) return MIN_DECIMALS
val safe = if (perTickAsDouble <= 0.0) EPSILON else perTickAsDouble
val raw = ceil(-ln(safe) / LN_10)
return raw.toInt().coerceIn(MIN_DECIMALS, MAX_DECIMALS)
}
private companion object {
private const val SCALE = 18
private val TICK_SECONDS_BD = BigDecimal("0.8")
private val SECONDS_PER_YEAR_BD = BigDecimal("31536000")
private val HUNDRED_BD = BigDecimal("100")
private const val MIN_DECIMALS = 3
private const val MAX_DECIMALS = 12
private val LN_10 = ln(10.0)
private const val EPSILON = 1e-18
if (perTickAsDouble.isNaN() || perTickAsDouble.isInfinite()) return YieldSupplyGetRewardsBalanceUseCase.MIN_DECIMALS
val safe = if (perTickAsDouble <= 0.0) YieldSupplyGetRewardsBalanceUseCase.EPSILON else perTickAsDouble
val raw = ceil(-kotlin.math.ln(safe) / YieldSupplyGetRewardsBalanceUseCase.LN_10)
return raw.toInt().coerceIn(
YieldSupplyGetRewardsBalanceUseCase.MIN_DECIMALS,
YieldSupplyGetRewardsBalanceUseCase.FIAT_MAX_DECIMALS,
)
}
}

View file

@ -88,6 +88,7 @@ dependencies {
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models)
implementation(projects.domain.yieldSupply)
implementation(projects.domain.yieldSupply.models)
/** Temp dependency to swap domain */
implementation(projects.features.swap.domain)

View file

@ -121,7 +121,7 @@ internal object TokenDetailsPreviewData {
selectedBalanceType = BalanceType.ALL,
onBalanceSelect = {},
displayCryptoBalance = "966,96 XLM",
displayYeildSupplyCryptoBalance = null,
displayYieldSupplyFiatBalance = null,
displayFiatBalance = "91,50$",
isBalanceSelectorEnabled = true,
isBalanceFlickering = false,

View file

@ -75,6 +75,7 @@ import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase
import com.tangem.domain.wallets.usecase.GetExtendedPublicKeyForCurrencyUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase
import com.tangem.domain.yield.supply.models.YieldSupplyRewardBalance
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetRewardsBalanceUseCase
import com.tangem.feature.tokendetails.deeplink.TokenDetailsDeepLinkActionListener
import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter
@ -416,7 +417,9 @@ internal class TokenDetailsModel @Inject constructor(
.saveIn(yieldSupplyBalanceJobHolder)
} else {
yieldSupplyBalanceJobHolder.cancel()
internalUiState.value = stateFactory.getStateWithUpdatedYieldSupplyDisplayBalance(null)
internalUiState.value = stateFactory.getStateWithUpdatedYieldSupplyDisplayBalance(
YieldSupplyRewardBalance.empty(),
)
}
}

View file

@ -28,7 +28,8 @@ internal sealed class TokenDetailsBalanceBlockState {
val isBalanceSelectorEnabled: Boolean,
val isBalanceFlickering: Boolean,
val yieldSupplyState: TokenDetailsYieldSupplyState = TokenDetailsYieldSupplyState.Empty,
val displayYeildSupplyCryptoBalance: String? = null,
val displayYieldSupplyFiatBalance: String? = null,
val displayYieldSupplyCryptoBalance: String? = null,
) : TokenDetailsBalanceBlockState()
data class Error(

View file

@ -98,8 +98,8 @@ internal class TokenDetailsLoadedBalanceConverter(
stakingCryptoAmount,
currentState.selectedBalanceType,
),
displayYeildSupplyCryptoBalance = (currentState as? TokenDetailsBalanceBlockState.Content)
?.displayYeildSupplyCryptoBalance,
displayYieldSupplyFiatBalance = (currentState as? TokenDetailsBalanceBlockState.Content)
?.displayYieldSupplyFiatBalance,
balanceSegmentedButtonConfig = currentState.balanceSegmentedButtonConfig,
onBalanceSelect = clickIntents::onBalanceSelect,
selectedBalanceType = currentState.selectedBalanceType,

View file

@ -26,6 +26,7 @@ import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase
import com.tangem.domain.yield.supply.models.YieldSupplyRewardBalance
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceSegmentedButtonConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsAppBarMenuConfig
@ -315,13 +316,18 @@ internal class TokenDetailsStateFactory(
return balanceSelectStateConverter.convert(buttonConfig)
}
fun getStateWithUpdatedYieldSupplyDisplayBalance(displayBalance: String?): TokenDetailsState {
fun getStateWithUpdatedYieldSupplyDisplayBalance(
yieldSupplyRewardBalance: YieldSupplyRewardBalance,
): TokenDetailsState {
val state = currentStateProvider()
val balanceState = state.tokenBalanceBlockState
return state.copy(
tokenBalanceBlockState = when (balanceState) {
is TokenDetailsBalanceBlockState.Content ->
balanceState.copy(displayYeildSupplyCryptoBalance = displayBalance)
balanceState.copy(
displayYieldSupplyFiatBalance = yieldSupplyRewardBalance.fiatBalance,
displayYieldSupplyCryptoBalance = yieldSupplyRewardBalance.cryptoBalance,
)
is TokenDetailsBalanceBlockState.Error -> balanceState
is TokenDetailsBalanceBlockState.Loading -> balanceState
},

View file

@ -122,12 +122,12 @@ private fun FiatBalance(
height = TangemTheme.dimens.size32,
),
)
is TokenDetailsBalanceBlockState.Content -> if (state.displayYeildSupplyCryptoBalance != null &&
is TokenDetailsBalanceBlockState.Content -> if (state.displayYieldSupplyFiatBalance != null &&
!isBalanceHidden
) {
TextAnimatedCounter(
modifier = modifier,
text = state.displayYeildSupplyCryptoBalance,
text = state.displayYieldSupplyFiatBalance,
style = TangemTheme.typography.h2.applyBladeBrush(
isEnabled = state.isBalanceFlickering,
textColor = TangemTheme.colors.text.primary1,
@ -136,7 +136,7 @@ private fun FiatBalance(
} else {
Text(
modifier = modifier,
text = (state.displayYeildSupplyCryptoBalance ?: state.displayFiatBalance).orMaskWithStars(
text = (state.displayYieldSupplyFiatBalance ?: state.displayFiatBalance).orMaskWithStars(
isBalanceHidden,
),
style = TangemTheme.typography.h2.applyBladeBrush(
@ -184,8 +184,9 @@ private fun CryptoBalance(
tint = TangemTheme.colors.icon.inactive,
contentDescription = null,
)
Text(
text = state.displayCryptoBalance.orMaskWithStars(isBalanceHidden),
TextAnimatedCounter(
text = (state.displayYieldSupplyCryptoBalance ?: state.displayCryptoBalance)
.orMaskWithStars(isBalanceHidden),
style = TangemTheme.typography.caption2.applyBladeBrush(
isEnabled = state.isBalanceFlickering,
textColor = TangemTheme.colors.text.tertiary,