Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-06 18:48:35 +03:00
parent 4765a7be43
commit d5fcf732e5
15 changed files with 142 additions and 53 deletions

View file

@ -164,7 +164,11 @@ internal class DefaultStakingRepository(
val yield = getYield(cryptoCurrencyId, symbol)
StakingEntryInfo(
apr = requireNotNull(yield.preferredValidators.maxByOrNull { it.apr.orZero() }?.apr),
rewardInfo = requireNotNull(
yield
.preferredValidators
.maxByOrNull { it.rewardInfo?.rate.orZero() }?.rewardInfo,
),
rewardSchedule = yield.metadata.rewardSchedule,
tokenSymbol = yield.token.symbol,
)

View file

@ -10,7 +10,9 @@ import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.model.stakekit.Yield.Metadata.RewardSchedule
import com.tangem.domain.staking.model.stakekit.Yield.Validator.ValidatorStatus
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.orZero
import kotlinx.collections.immutable.toImmutableList
import java.math.RoundingMode
internal object YieldConverter : Converter<YieldDTO, Yield> {
@ -21,7 +23,10 @@ internal object YieldConverter : Converter<YieldDTO, Yield> {
private val PARTNERS_NAMES = listOf("Meria")
private const val DIVIDE_SCALE = 8
override fun convert(value: YieldDTO): Yield {
val rewardType = convertRewardType(value.rewardType.asMandatory("rewardType"))
return Yield(
id = value.id.asMandatory("id"),
token = YieldTokenConverter.convert(value.token.asMandatory("token")),
@ -30,14 +35,14 @@ internal object YieldConverter : Converter<YieldDTO, Yield> {
status = convertStatus(value.status.asMandatory("status")),
apy = value.apy.asMandatory("apy"),
rewardRate = value.rewardRate.asMandatory("rewardRate"),
rewardType = convertRewardType(value.rewardType.asMandatory("rewardType")),
rewardType = rewardType,
metadata = convertMetadata(value.metadata.asMandatory("metadata")),
validators = value.validators.asMandatory("validators")
.asSequence()
.distinctBy { it.address }
.filter { it.status == ValidatorStatusDTO.ACTIVE }
.map { convertValidator(it) }
.sortedByDescending { it.apr }
.map { convertValidator(validatorDTO = it, rewardType = rewardType) }
.sortedByDescending { it.rewardInfo?.rate?.orZero() }
.sortedByDescending { it.isStrategicPartner }
.toImmutableList(),
isAvailable = value.isAvailable.asMandatory("isAvailable"),
@ -119,15 +124,16 @@ internal object YieldConverter : Converter<YieldDTO, Yield> {
)
}
private fun convertValidator(validatorDTO: YieldDTO.ValidatorDTO): Yield.Validator {
private fun convertValidator(validatorDTO: YieldDTO.ValidatorDTO, rewardType: Yield.RewardType): Yield.Validator {
val address = validatorDTO.address.asMandatory("address")
return Yield.Validator(
address = address,
status = convertValidatorStatus(validatorDTO.status.asMandatory("status")),
name = validatorDTO.name.asMandatory("name"),
image = validatorDTO.image,
website = validatorDTO.website,
apr = validatorDTO.apr,
rewardInfo = createRewardInfo(validatorDTO, rewardType),
commission = validatorDTO.commission,
stakedBalance = validatorDTO.stakedBalance,
votingPower = validatorDTO.votingPower,
@ -136,6 +142,32 @@ internal object YieldConverter : Converter<YieldDTO, Yield> {
)
}
private fun createRewardInfo(validatorDTO: YieldDTO.ValidatorDTO, rewardType: Yield.RewardType): Yield.RewardInfo? {
val aprOrApy = validatorDTO.apr
val commission = validatorDTO.commission
// gross = net / (1 - commission)
return try {
val netApy = aprOrApy
val grossAprOrApy = if (netApy != null && commission != null) {
val commissionFraction = commission.toBigDecimal()
if (commissionFraction < 1.toBigDecimal()) {
netApy.divide(
1.toBigDecimal() - commissionFraction,
DIVIDE_SCALE,
RoundingMode.HALF_UP,
)
} else {
netApy
}
} else {
netApy
}
grossAprOrApy?.let { Yield.RewardInfo(rate = it, type = rewardType) }
} catch (_: Exception) {
aprOrApy?.let { Yield.RewardInfo(rate = it, type = rewardType) }
}
}
private fun convertRewardType(rewardTypeDTO: YieldDTO.RewardTypeDTO): Yield.RewardType {
return when (rewardTypeDTO) {
YieldDTO.RewardTypeDTO.APY -> Yield.RewardType.APY