Updated on 2026-08-14
This commit is contained in:
parent
a13a54accd
commit
f023fa7f2c
10 changed files with 267 additions and 90 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue