diff --git a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt index 273839cc0e..9ba606d735 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt @@ -1,5 +1,6 @@ package com.tangem.common.ui.tokens +import com.tangem.blockchain.common.Blockchain import com.tangem.common.ui.R import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter @@ -21,6 +22,7 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.currency.yieldSupplyKey import com.tangem.domain.models.staking.YieldBalance +import com.tangem.domain.staking.model.isStakingSupported import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance import com.tangem.lib.crypto.BlockchainUtils @@ -211,15 +213,22 @@ class TokenItemStateConverter( } if (stakingApyMap.isNotEmpty()) { - val (stakingRate, hasStaked) = findStakingRate( + val stakingInfo = findStakingRate( currencyStatus = cryptoCurrencyStatus, stakingApyMap = stakingApyMap, ) - if (stakingRate != null) { + val rewardTypeRes = when (stakingInfo.rewardType) { + Yield.RewardType.APR -> R.string.staking_apr_earn_badge + Yield.RewardType.UNKNOWN, + Yield.RewardType.APY, + null, + -> R.string.yield_module_earn_badge + } + if (stakingInfo.rate != null) { return resourceReference( - R.string.yield_module_earn_badge, - wrappedList(stakingRate.format { percent(withPercentSign = false) }), - ) to hasStaked + rewardTypeRes, + wrappedList(stakingInfo.rate.format { percent(withPercentSign = false) }), + ) to stakingInfo.isActive } } @@ -229,21 +238,46 @@ class TokenItemStateConverter( private fun findStakingRate( currencyStatus: CryptoCurrencyStatus, stakingApyMap: Map>, - ): Pair { + ): StakingLocalInfo { val stakingKey = currencyStatus.currency.stakingKey() - val validators = stakingApyMap[stakingKey] ?: return null to false + val validators = stakingApyMap[stakingKey] + ?: return StakingLocalInfo(rate = null, isActive = false, rewardType = null) + val yieldBalance = currencyStatus.value.yieldBalance val hasStakedBalance = yieldBalance is YieldBalance.Data - val rate = if (hasStakedBalance) { + + val rateInfo: Pair? = if (hasStakedBalance) { val validatorsByAddress = validators.associateBy { it.address } yieldBalance.balance.items .mapNotNull { it.validatorAddress } - .firstNotNullOfOrNull { address -> validatorsByAddress[address]?.rewardInfo?.rate } - ?: validators.mapNotNull { it.rewardInfo?.rate }.maxOrNull() + .firstNotNullOfOrNull { address -> + val validator = validatorsByAddress[address] + validator?.rewardInfo?.rate?.let { rate -> + rate to validator.rewardInfo?.type + } + } + ?: validators + .filter { it.preferred } + .mapNotNull { validator -> + validator.rewardInfo?.rate?.let { rate -> rate to validator.rewardInfo?.type } + } + .maxByOrNull { it.first } } else { - validators.mapNotNull { it.rewardInfo?.rate }.maxOrNull() + validators + .filter { it.preferred } + .mapNotNull { validator -> + validator.rewardInfo?.rate?.let { rate -> + rate to validator.rewardInfo?.type + } + } + .maxByOrNull { it.first } } - return rate to hasStakedBalance + + return StakingLocalInfo( + rate = rateInfo?.first, + isActive = hasStakedBalance, + rewardType = rateInfo?.second, + ) } private fun createSubtitleState( @@ -353,7 +387,21 @@ class TokenItemStateConverter( fun CryptoCurrencyStatus.Value.isFlickering(): Boolean = sources.total == StatusSource.CACHE private fun CryptoCurrency.stakingKey(): String { - return "${network.backendId}_$symbol" + if (this is CryptoCurrency.Coin && !network.isStakingSupported) return "" + + if (network.isStakingSupported && this !is CryptoCurrency.Coin) { + val isPolygonTokenOnEthereum = this is CryptoCurrency.Token && + this.network.id.rawId.value == Blockchain.Ethereum.id && + this.symbol == Blockchain.Polygon.currency + if (!isPolygonTokenOnEthereum) return "" + } + return "${id.rawCurrencyId}_$symbol" } } + + private data class StakingLocalInfo( + val rate: BigDecimal?, + val isActive: Boolean, + val rewardType: Yield.RewardType?, + ) } \ No newline at end of file diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 8b5e486e5f..143869f7f9 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -1155,6 +1155,7 @@ The amount to stake must be at least %s Staking amount will be rounded to %1$s TRX due to network rules. Unstaking amount will be rounded to %1$s TRX due to network rules. + APR %1$s%% Claim unstaked Stake account fee A staking account is a special account where staked SOL tokens are stored. It is created when you delegate your tokens to a validator to participate in transaction validation and receive rewards. A small fee is charged for creating the staking account, which is returned after the staking is completed. diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/model/StakingIntegrationID.kt b/domain/staking/src/main/java/com/tangem/domain/staking/model/StakingIntegrationID.kt index ff38b75b71..43e2488895 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/model/StakingIntegrationID.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/model/StakingIntegrationID.kt @@ -1,8 +1,10 @@ package com.tangem.domain.staking.model import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchainsdk.utils.toBlockchain import com.tangem.blockchainsdk.utils.toMigratedCoinId import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.network.Network /** * Represents a staking integration identifier. @@ -143,6 +145,9 @@ sealed interface StakingIntegrationID { } } +val Network.isStakingSupported: Boolean + get() = this.toBlockchain().isStakingSupported + /** * Extension property to check if staking is supported for a blockchain. * diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/usecase/StakingApyFlowUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/usecase/StakingApyFlowUseCase.kt index 5b277e5aed..3b50f3a63c 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/usecase/StakingApyFlowUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/usecase/StakingApyFlowUseCase.kt @@ -1,8 +1,5 @@ package com.tangem.domain.staking.usecase -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchainsdk.utils.toNetworkId -import com.tangem.domain.staking.model.StakingIntegrationID import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.repositories.StakingRepository import kotlinx.coroutines.flow.Flow @@ -21,22 +18,10 @@ class StakingApyFlowUseCase(private val stakingRepository: StakingRepository) { return stakingRepository.getEnabledYields() .map { yields -> yields.associate { yield -> - val key = composeKey(yield) + val key = "${yield.token.coinGeckoId}_${yield.token.symbol}" val apy = yield.validators key to apy } } } - - // so that cause for api.stakek.it coinGeckoId on BNB Smart chain different how we receive our backendId - // coinGeckoId - binance - // our backendId BNB Smart Chain - binance-smart-chain - // our backendId BNB Beacon Chain - binance - private fun composeKey(yield: Yield): String { - return if (yield.token.symbol == StakingIntegrationID.Coin.BSC.blockchain.currency) { - "${Blockchain.BSC.toNetworkId()}_${yield.token.symbol}" - } else { - "${yield.token.coinGeckoId}_${yield.token.symbol}" - } - } } \ No newline at end of file