Updated on 2026-08-14
This commit is contained in:
parent
072d04125d
commit
e00fd4599e
14 changed files with 492 additions and 253 deletions
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.tangem.domain.models.staking
|
||||||
|
|
||||||
|
import com.tangem.domain.models.staking.StakingEntryType.Companion.fromBalanceType
|
||||||
|
|
||||||
|
fun BalanceItem.toStakingBalanceEntry(validatorName: String? = null): StakingBalanceEntry {
|
||||||
|
return StakingBalanceEntry(
|
||||||
|
id = groupId,
|
||||||
|
type = fromBalanceType(type),
|
||||||
|
amount = amount,
|
||||||
|
validator = validatorAddress?.let {
|
||||||
|
ValidatorInfo(address = it, name = validatorName)
|
||||||
|
},
|
||||||
|
date = date,
|
||||||
|
actions = StakingEntryActions.StakeKit(
|
||||||
|
pendingActions = pendingActions,
|
||||||
|
pendingActionsConstraints = pendingActionsConstraints,
|
||||||
|
),
|
||||||
|
isPending = isPending,
|
||||||
|
rawCurrencyId = rawCurrencyId,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<BalanceItem>.toStakingBalanceEntries(
|
||||||
|
validatorNameResolver: (String?) -> String? = { null },
|
||||||
|
): List<StakingBalanceEntry> {
|
||||||
|
return map { it.toStakingBalanceEntry(validatorNameResolver(it.validatorAddress)) }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
package com.tangem.domain.models.staking
|
||||||
|
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
fun P2PEthPoolStakingAccount.toStakingBalanceEntries(vaultName: String? = null): List<StakingBalanceEntry> {
|
||||||
|
return buildList {
|
||||||
|
if (stake.assets > BigDecimal.ZERO) {
|
||||||
|
add(
|
||||||
|
StakingBalanceEntry(
|
||||||
|
id = vaultAddress,
|
||||||
|
type = StakingEntryType.STAKED,
|
||||||
|
amount = stake.assets,
|
||||||
|
validator = ValidatorInfo(address = vaultAddress, name = vaultName),
|
||||||
|
date = null,
|
||||||
|
actions = StakingEntryActions.P2PEthPool(
|
||||||
|
ticket = null,
|
||||||
|
estimatedWithdrawalDate = null,
|
||||||
|
isClaimable = false,
|
||||||
|
),
|
||||||
|
isPending = false,
|
||||||
|
rawCurrencyId = null,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
exitQueue.requests.forEach { request ->
|
||||||
|
add(
|
||||||
|
StakingBalanceEntry(
|
||||||
|
id = "${vaultAddress}_${request.ticket}",
|
||||||
|
type = StakingEntryType.UNSTAKING,
|
||||||
|
amount = request.totalAssets,
|
||||||
|
validator = ValidatorInfo(address = vaultAddress, name = vaultName),
|
||||||
|
date = request.withdrawalTimestamp,
|
||||||
|
actions = StakingEntryActions.P2PEthPool(
|
||||||
|
ticket = request.ticket,
|
||||||
|
estimatedWithdrawalDate = request.withdrawalTimestamp,
|
||||||
|
isClaimable = request.isClaimable,
|
||||||
|
),
|
||||||
|
isPending = false,
|
||||||
|
rawCurrencyId = null,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availableToWithdraw > BigDecimal.ZERO) {
|
||||||
|
add(
|
||||||
|
StakingBalanceEntry(
|
||||||
|
id = "${vaultAddress}_withdrawable",
|
||||||
|
type = StakingEntryType.WITHDRAWABLE,
|
||||||
|
amount = availableToWithdraw,
|
||||||
|
validator = ValidatorInfo(address = vaultAddress, name = vaultName),
|
||||||
|
date = null,
|
||||||
|
actions = StakingEntryActions.P2PEthPool(
|
||||||
|
ticket = null,
|
||||||
|
estimatedWithdrawalDate = null,
|
||||||
|
isClaimable = true,
|
||||||
|
),
|
||||||
|
isPending = false,
|
||||||
|
rawCurrencyId = null,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.tangem.domain.models.staking
|
package com.tangem.domain.models.staking
|
||||||
|
|
||||||
import com.tangem.domain.models.StatusSource
|
import com.tangem.domain.models.StatusSource
|
||||||
|
import com.tangem.domain.models.serialization.SerializedBigDecimal
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
|
@ -21,6 +22,9 @@ sealed interface StakingBalance {
|
||||||
@Serializable
|
@Serializable
|
||||||
sealed interface Data : StakingBalance {
|
sealed interface Data : StakingBalance {
|
||||||
|
|
||||||
|
/** Provider-agnostic list of balance entries for UI display */
|
||||||
|
val entries: List<StakingBalanceEntry>
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class StakeKit(
|
data class StakeKit(
|
||||||
override val stakingId: StakingID,
|
override val stakingId: StakingID,
|
||||||
|
|
@ -28,25 +32,23 @@ sealed interface StakingBalance {
|
||||||
val balance: YieldBalanceItem,
|
val balance: YieldBalanceItem,
|
||||||
) : Data {
|
) : Data {
|
||||||
|
|
||||||
override val totalStaked: BigDecimal
|
override val totalStaked: SerializedBigDecimal = balance.items
|
||||||
get() = balance.items
|
|
||||||
.filter { it.type == BalanceType.STAKED }
|
.filter { it.type == BalanceType.STAKED }
|
||||||
.sumOf { it.amount }
|
.sumOf { it.amount }
|
||||||
|
|
||||||
override val totalRewards: BigDecimal
|
override val totalRewards: SerializedBigDecimal = balance.items
|
||||||
get() = balance.items
|
|
||||||
.filter { it.type == BalanceType.REWARDS }
|
.filter { it.type == BalanceType.REWARDS }
|
||||||
.sumOf { it.amount }
|
.sumOf { it.amount }
|
||||||
|
|
||||||
override val unstakingAmount: BigDecimal
|
override val unstakingAmount: SerializedBigDecimal = balance.items
|
||||||
get() = balance.items
|
|
||||||
.filter { it.type == BalanceType.UNSTAKING || it.type == BalanceType.UNLOCKING }
|
.filter { it.type == BalanceType.UNSTAKING || it.type == BalanceType.UNLOCKING }
|
||||||
.sumOf { it.amount }
|
.sumOf { it.amount }
|
||||||
|
|
||||||
override val withdrawableAmount: BigDecimal
|
override val withdrawableAmount: SerializedBigDecimal = balance.items
|
||||||
get() = balance.items
|
|
||||||
.filter { it.type == BalanceType.UNSTAKED }
|
.filter { it.type == BalanceType.UNSTAKED }
|
||||||
.sumOf { it.amount }
|
.sumOf { it.amount }
|
||||||
|
|
||||||
|
override val entries: List<StakingBalanceEntry> = balance.items.toStakingBalanceEntries()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
@ -56,17 +58,15 @@ sealed interface StakingBalance {
|
||||||
val account: P2PEthPoolStakingAccount,
|
val account: P2PEthPoolStakingAccount,
|
||||||
) : Data {
|
) : Data {
|
||||||
|
|
||||||
override val totalStaked: BigDecimal
|
override val totalStaked: SerializedBigDecimal = account.stake.assets
|
||||||
get() = account.stake.assets
|
|
||||||
|
|
||||||
override val totalRewards: BigDecimal
|
override val totalRewards: SerializedBigDecimal = account.stake.totalEarnedAssets
|
||||||
get() = account.stake.totalEarnedAssets
|
|
||||||
|
|
||||||
override val unstakingAmount: BigDecimal
|
override val unstakingAmount: SerializedBigDecimal = account.exitQueue.total
|
||||||
get() = account.exitQueue.total
|
|
||||||
|
|
||||||
override val withdrawableAmount: BigDecimal
|
override val withdrawableAmount: SerializedBigDecimal = account.availableToWithdraw
|
||||||
get() = account.availableToWithdraw
|
|
||||||
|
override val entries: List<StakingBalanceEntry> = account.toStakingBalanceEntries()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.tangem.domain.models.staking
|
||||||
|
|
||||||
|
import com.tangem.domain.models.serialization.SerializedBigDecimal
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class StakingBalanceEntry(
|
||||||
|
val id: String,
|
||||||
|
val type: StakingEntryType,
|
||||||
|
val amount: SerializedBigDecimal,
|
||||||
|
val validator: ValidatorInfo?,
|
||||||
|
val date: Instant?,
|
||||||
|
val actions: StakingEntryActions,
|
||||||
|
val isPending: Boolean,
|
||||||
|
val rawCurrencyId: String?,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ValidatorInfo(
|
||||||
|
val address: String,
|
||||||
|
val name: String?,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.tangem.domain.models.staking
|
||||||
|
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
sealed interface StakingEntryActions {
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class StakeKit(
|
||||||
|
val pendingActions: List<PendingAction>,
|
||||||
|
val pendingActionsConstraints: List<PendingActionConstraints>,
|
||||||
|
) : StakingEntryActions {
|
||||||
|
val hasPendingActions: Boolean get() = pendingActions.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class P2PEthPool(
|
||||||
|
val ticket: String?,
|
||||||
|
val estimatedWithdrawalDate: Instant?,
|
||||||
|
val isClaimable: Boolean,
|
||||||
|
) : StakingEntryActions
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.tangem.domain.models.staking
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
enum class StakingEntryType {
|
||||||
|
AVAILABLE,
|
||||||
|
STAKED,
|
||||||
|
PREPARING,
|
||||||
|
LOCKED,
|
||||||
|
UNSTAKING,
|
||||||
|
UNLOCKING,
|
||||||
|
WITHDRAWABLE,
|
||||||
|
REWARDS,
|
||||||
|
UNKNOWN,
|
||||||
|
;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromBalanceType(type: BalanceType): StakingEntryType = when (type) {
|
||||||
|
BalanceType.AVAILABLE -> AVAILABLE
|
||||||
|
BalanceType.STAKED -> STAKED
|
||||||
|
BalanceType.PREPARING -> PREPARING
|
||||||
|
BalanceType.LOCKED -> LOCKED
|
||||||
|
BalanceType.UNSTAKING -> UNSTAKING
|
||||||
|
BalanceType.UNLOCKING -> UNLOCKING
|
||||||
|
BalanceType.UNSTAKED -> WITHDRAWABLE // stakekit's UNSTAKED = ready to withdraw
|
||||||
|
BalanceType.REWARDS -> REWARDS
|
||||||
|
BalanceType.UNKNOWN -> UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -428,10 +428,10 @@ class CryptoCurrencyStatusFactoryTest {
|
||||||
source = StatusSource.ACTUAL,
|
source = StatusSource.ACTUAL,
|
||||||
balance = YieldBalanceItem(
|
balance = YieldBalanceItem(
|
||||||
items = listOf(
|
items = listOf(
|
||||||
mockk<BalanceItem> {
|
mockk<BalanceItem>(relaxed = true) {
|
||||||
every { this@mockk.token.coinGeckoId } returns currency.id.rawCurrencyId?.value
|
every { this@mockk.token.coinGeckoId } returns currency.id.rawCurrencyId?.value
|
||||||
},
|
},
|
||||||
mockk<BalanceItem> {
|
mockk<BalanceItem>(relaxed = true) {
|
||||||
every { this@mockk.token.coinGeckoId } returns "unknown"
|
every { this@mockk.token.coinGeckoId } returns "unknown"
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -525,10 +525,10 @@ class CryptoCurrencyStatusFactoryTest {
|
||||||
source = StatusSource.ACTUAL,
|
source = StatusSource.ACTUAL,
|
||||||
balance = YieldBalanceItem(
|
balance = YieldBalanceItem(
|
||||||
items = listOf(
|
items = listOf(
|
||||||
mockk<BalanceItem> {
|
mockk<BalanceItem>(relaxed = true) {
|
||||||
every { this@mockk.token.coinGeckoId } returns currency.id.rawCurrencyId?.value
|
every { this@mockk.token.coinGeckoId } returns currency.id.rawCurrencyId?.value
|
||||||
},
|
},
|
||||||
mockk<BalanceItem> {
|
mockk<BalanceItem>(relaxed = true) {
|
||||||
every { this@mockk.token.coinGeckoId } returns "unknown"
|
every { this@mockk.token.coinGeckoId } returns "unknown"
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -614,10 +614,10 @@ class CryptoCurrencyStatusFactoryTest {
|
||||||
source = StatusSource.ACTUAL,
|
source = StatusSource.ACTUAL,
|
||||||
balance = YieldBalanceItem(
|
balance = YieldBalanceItem(
|
||||||
items = listOf(
|
items = listOf(
|
||||||
mockk<BalanceItem> {
|
mockk<BalanceItem>(relaxed = true) {
|
||||||
every { this@mockk.token.coinGeckoId } returns currency.id.rawCurrencyId?.value
|
every { this@mockk.token.coinGeckoId } returns currency.id.rawCurrencyId?.value
|
||||||
},
|
},
|
||||||
mockk<BalanceItem> {
|
mockk<BalanceItem>(relaxed = true) {
|
||||||
every { this@mockk.token.coinGeckoId } returns "unknown"
|
every { this@mockk.token.coinGeckoId } returns "unknown"
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -589,11 +589,11 @@ class TotalFiatBalanceCalculatorTest {
|
||||||
|
|
||||||
private fun createStakeKitBalance(amount: BigDecimal, balanceType: BalanceType): StakingBalance.Data.StakeKit {
|
private fun createStakeKitBalance(amount: BigDecimal, balanceType: BalanceType): StakingBalance.Data.StakeKit {
|
||||||
return StakingBalance.Data.StakeKit(
|
return StakingBalance.Data.StakeKit(
|
||||||
stakingId = mockk(),
|
stakingId = mockk(relaxed = true),
|
||||||
source = StatusSource.ACTUAL,
|
source = StatusSource.ACTUAL,
|
||||||
balance = YieldBalanceItem(
|
balance = YieldBalanceItem(
|
||||||
items = listOf(
|
items = listOf(
|
||||||
mockk<BalanceItem> {
|
mockk<BalanceItem>(relaxed = true) {
|
||||||
every { this@mockk.amount } returns amount
|
every { this@mockk.amount } returns amount
|
||||||
every { this@mockk.type } returns balanceType
|
every { this@mockk.type } returns balanceType
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -199,14 +199,21 @@ internal class StakingModel @Inject constructor(
|
||||||
}
|
}
|
||||||
private var appCurrency: AppCurrency by Delegates.notNull()
|
private var appCurrency: AppCurrency by Delegates.notNull()
|
||||||
|
|
||||||
private val balancesToShow: List<BalanceItem>
|
private val balancesToShow: List<StakingBalanceEntry>
|
||||||
get() {
|
get() {
|
||||||
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
|
val stakingBalance = cryptoCurrencyStatus.value.stakingBalance
|
||||||
return invalidatePendingTransactionsUseCase(
|
return when (stakingBalance) {
|
||||||
balanceItems = stakeKitBalance?.balance?.items.orEmpty(),
|
is StakingBalance.Data.StakeKit -> {
|
||||||
|
val invalidatedItems = invalidatePendingTransactionsUseCase(
|
||||||
|
balanceItems = stakingBalance.balance.items,
|
||||||
stakingActions = stakingActions,
|
stakingActions = stakingActions,
|
||||||
token = integration.token,
|
token = integration.token,
|
||||||
).getOrElse { emptyList() }
|
).getOrElse { emptyList() }
|
||||||
|
invalidatedItems.toStakingBalanceEntries()
|
||||||
|
}
|
||||||
|
is StakingBalance.Data.P2PEthPool -> stakingBalance.entries
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isInitialInfoAnalyticSent: Boolean = false
|
private var isInitialInfoAnalyticSent: Boolean = false
|
||||||
|
|
|
||||||
|
|
@ -1,177 +0,0 @@
|
||||||
package com.tangem.features.staking.impl.presentation.state.converters
|
|
||||||
|
|
||||||
import com.tangem.core.ui.extensions.*
|
|
||||||
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.core.ui.utils.parseBigDecimal
|
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
|
||||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
|
||||||
import com.tangem.domain.models.staking.BalanceItem
|
|
||||||
import com.tangem.domain.models.staking.BalanceType
|
|
||||||
import com.tangem.domain.models.staking.BalanceType.Companion.isClickable
|
|
||||||
import com.tangem.domain.models.staking.StakingBalance
|
|
||||||
import com.tangem.domain.models.staking.action.StakingActionType
|
|
||||||
import com.tangem.domain.staking.model.StakingIntegration
|
|
||||||
import com.tangem.domain.staking.utils.getRewardStakingBalance
|
|
||||||
import com.tangem.features.staking.impl.R
|
|
||||||
import com.tangem.features.staking.impl.presentation.state.BalanceState
|
|
||||||
import com.tangem.lib.crypto.BlockchainUtils
|
|
||||||
import com.tangem.lib.crypto.BlockchainUtils.isTon
|
|
||||||
import com.tangem.utils.Provider
|
|
||||||
import com.tangem.utils.converter.Converter
|
|
||||||
import com.tangem.utils.extensions.orZero
|
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
|
||||||
import kotlinx.datetime.Instant
|
|
||||||
import java.math.BigDecimal
|
|
||||||
import java.util.Calendar
|
|
||||||
|
|
||||||
internal class BalanceItemConverter(
|
|
||||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
|
||||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
|
||||||
private val integration: StakingIntegration,
|
|
||||||
) : Converter<BalanceItem, BalanceState?> {
|
|
||||||
|
|
||||||
override fun convert(value: BalanceItem): BalanceState? {
|
|
||||||
val appCurrency = appCurrencyProvider()
|
|
||||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
|
||||||
|
|
||||||
val target = integration.targets.firstOrNull {
|
|
||||||
value.validatorAddress?.contains(it.address, ignoreCase = true) == true
|
|
||||||
}
|
|
||||||
val cryptoAmount = value.getBalanceValue()
|
|
||||||
val fiatAmount = cryptoCurrencyStatus.value.fiatRate?.times(cryptoAmount)
|
|
||||||
|
|
||||||
val title = value.type.getTitle(target?.name)
|
|
||||||
return title?.let {
|
|
||||||
BalanceState(
|
|
||||||
groupId = value.groupId,
|
|
||||||
target = target,
|
|
||||||
title = title,
|
|
||||||
subtitle = getSubtitle(value),
|
|
||||||
type = value.type,
|
|
||||||
cryptoValue = cryptoAmount.parseBigDecimal(cryptoCurrency.decimals),
|
|
||||||
cryptoAmount = cryptoAmount,
|
|
||||||
formattedCryptoAmount = stringReference(
|
|
||||||
cryptoAmount.format { crypto(cryptoCurrency) },
|
|
||||||
),
|
|
||||||
fiatAmount = fiatAmount,
|
|
||||||
formattedFiatAmount = stringReference(
|
|
||||||
fiatAmount.format {
|
|
||||||
fiat(
|
|
||||||
fiatCurrencyCode = appCurrency.code,
|
|
||||||
fiatCurrencySymbol = appCurrency.symbol,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
rawCurrencyId = value.rawCurrencyId,
|
|
||||||
pendingActions = value.pendingActions.toPersistentList(),
|
|
||||||
isClickable = value.isClickable(),
|
|
||||||
isPending = value.isPending,
|
|
||||||
targetAddress = value.validatorAddress,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun BalanceItem.getBalanceValue(): BigDecimal {
|
|
||||||
val isIncludeStakingTotalBalance = BlockchainUtils.isIncludeStakingTotalBalance(
|
|
||||||
blockchainId = cryptoCurrencyStatus.currency.network.rawId,
|
|
||||||
)
|
|
||||||
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
|
|
||||||
return if (isIncludeStakingTotalBalance) {
|
|
||||||
amount
|
|
||||||
} else {
|
|
||||||
amount - stakeKitBalance?.getRewardStakingBalance().orZero()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun BalanceType.getTitle(validatorName: String?) = when (this) {
|
|
||||||
BalanceType.PREPARING,
|
|
||||||
BalanceType.STAKED,
|
|
||||||
-> validatorName?.let { stringReference(it) }
|
|
||||||
BalanceType.UNSTAKED -> resourceReference(R.string.staking_unstaked)
|
|
||||||
BalanceType.UNSTAKING -> resourceReference(R.string.staking_unstaking)
|
|
||||||
BalanceType.LOCKED -> resourceReference(R.string.staking_locked)
|
|
||||||
BalanceType.AVAILABLE,
|
|
||||||
BalanceType.REWARDS,
|
|
||||||
BalanceType.UNLOCKING,
|
|
||||||
BalanceType.UNKNOWN,
|
|
||||||
-> null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getSubtitle(balance: BalanceItem) = when (balance.type) {
|
|
||||||
BalanceType.UNSTAKING -> getUnbondingDate(balance.date)
|
|
||||||
BalanceType.UNSTAKED -> resourceReference(R.string.staking_tap_to_withdraw)
|
|
||||||
BalanceType.LOCKED -> if (balance.pendingActions.any { it.type == StakingActionType.VOTE_LOCKED }) {
|
|
||||||
resourceReference(R.string.staking_tap_to_unlock_or_vote)
|
|
||||||
} else {
|
|
||||||
resourceReference(R.string.staking_tap_to_unlock)
|
|
||||||
}
|
|
||||||
BalanceType.PREPARING -> {
|
|
||||||
val warmupPeriod = integration.warmupPeriodDays
|
|
||||||
combinedReference(
|
|
||||||
resourceReference(R.string.staking_details_warmup_period),
|
|
||||||
stringReference(" "),
|
|
||||||
pluralReference(R.plurals.common_days, warmupPeriod, wrappedList(warmupPeriod)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
BalanceType.AVAILABLE,
|
|
||||||
BalanceType.STAKED,
|
|
||||||
BalanceType.UNLOCKING,
|
|
||||||
BalanceType.REWARDS,
|
|
||||||
BalanceType.UNKNOWN,
|
|
||||||
-> null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getUnbondingDate(date: Instant?): TextReference? {
|
|
||||||
val unbondingPeriod = integration.cooldownPeriodDays ?: return null
|
|
||||||
if (date == null) {
|
|
||||||
return combinedReference(
|
|
||||||
resourceReference(R.string.staking_details_unbonding_period),
|
|
||||||
stringReference(" "),
|
|
||||||
pluralReference(R.plurals.common_days, unbondingPeriod, wrappedList(unbondingPeriod)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val nowCalendar = Calendar.getInstance()
|
|
||||||
nowCalendar.resetHours()
|
|
||||||
|
|
||||||
val endDate = Calendar.getInstance()
|
|
||||||
endDate.timeInMillis = date.toEpochMilliseconds()
|
|
||||||
endDate.resetHours()
|
|
||||||
|
|
||||||
val days = ((endDate.timeInMillis - nowCalendar.timeInMillis) / DAY_IN_MILLIS).toInt()
|
|
||||||
return if (days > 0) {
|
|
||||||
resourceReference(
|
|
||||||
R.string.common_left,
|
|
||||||
wrappedList(
|
|
||||||
pluralReference(R.plurals.common_days, days, wrappedList(days)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
resourceReference(R.string.common_today)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun BalanceItem.isClickable(): Boolean {
|
|
||||||
val networkId = cryptoCurrencyStatus.currency.network.rawId
|
|
||||||
return when {
|
|
||||||
// TON allows withdrawing funds in the preparing state, unlike other networks.
|
|
||||||
isTon(networkId) && this.type == BalanceType.PREPARING -> {
|
|
||||||
pendingActions.any { it.type == StakingActionType.WITHDRAW }
|
|
||||||
}
|
|
||||||
else -> this.type.isClickable() && !this.isPending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Calendar.resetHours() {
|
|
||||||
this[Calendar.HOUR_OF_DAY] = 0
|
|
||||||
this[Calendar.MINUTE] = 0
|
|
||||||
this[Calendar.SECOND] = 0
|
|
||||||
this[Calendar.MILLISECOND] = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
private companion object {
|
|
||||||
const val DAY_IN_MILLIS = 24 * 60 * 60 * 1000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -24,9 +24,11 @@ internal class RewardsValidatorStateConverter(
|
||||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||||
private val integration: StakingIntegration,
|
private val integration: StakingIntegration,
|
||||||
) : Converter<Unit, StakingStates.RewardsValidatorsState> {
|
) : Converter<Unit, StakingStates.RewardsValidatorsState> {
|
||||||
|
|
||||||
override fun convert(value: Unit): StakingStates.RewardsValidatorsState {
|
override fun convert(value: Unit): StakingStates.RewardsValidatorsState {
|
||||||
val stakingBalance = cryptoCurrencyStatus.value.stakingBalance
|
val stakingBalance = cryptoCurrencyStatus.value.stakingBalance
|
||||||
return if (stakingBalance is StakingBalance.Data.StakeKit) {
|
return when (stakingBalance) {
|
||||||
|
is StakingBalance.Data.StakeKit -> {
|
||||||
val balances = stakingBalance.balance.items
|
val balances = stakingBalance.balance.items
|
||||||
StakingStates.RewardsValidatorsState.Data(
|
StakingStates.RewardsValidatorsState.Data(
|
||||||
isPrimaryButtonEnabled = true,
|
isPrimaryButtonEnabled = true,
|
||||||
|
|
@ -35,9 +37,9 @@ internal class RewardsValidatorStateConverter(
|
||||||
.mapRewardBalances(cryptoCurrencyStatus)
|
.mapRewardBalances(cryptoCurrencyStatus)
|
||||||
.toPersistentList(),
|
.toPersistentList(),
|
||||||
)
|
)
|
||||||
} else {
|
}
|
||||||
// TODO p2p
|
is StakingBalance.Data.P2PEthPool -> StakingStates.RewardsValidatorsState.Empty()
|
||||||
StakingStates.RewardsValidatorsState.Empty()
|
else -> StakingStates.RewardsValidatorsState.Empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
package com.tangem.features.staking.impl.presentation.state.converters
|
||||||
|
|
||||||
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
|
import com.tangem.core.ui.extensions.pluralReference
|
||||||
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
|
import com.tangem.core.ui.extensions.stringReference
|
||||||
|
import com.tangem.core.ui.extensions.wrappedList
|
||||||
|
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.core.ui.utils.parseBigDecimal
|
||||||
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
|
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||||
|
import com.tangem.domain.models.staking.BalanceType
|
||||||
|
import com.tangem.domain.models.staking.PendingAction
|
||||||
|
import com.tangem.domain.models.staking.StakingBalance
|
||||||
|
import com.tangem.domain.models.staking.StakingBalanceEntry
|
||||||
|
import com.tangem.domain.models.staking.StakingEntryActions
|
||||||
|
import com.tangem.domain.models.staking.StakingEntryType
|
||||||
|
import com.tangem.domain.models.staking.action.StakingActionType
|
||||||
|
import com.tangem.domain.staking.model.StakingIntegration
|
||||||
|
import com.tangem.domain.staking.model.StakingTarget
|
||||||
|
import com.tangem.features.staking.impl.R
|
||||||
|
import com.tangem.features.staking.impl.presentation.state.BalanceState
|
||||||
|
import com.tangem.lib.crypto.BlockchainUtils
|
||||||
|
import com.tangem.lib.crypto.BlockchainUtils.isTon
|
||||||
|
import com.tangem.utils.Provider
|
||||||
|
import com.tangem.utils.converter.Converter
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.util.Calendar
|
||||||
|
|
||||||
|
internal class StakingBalanceEntryConverter(
|
||||||
|
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||||
|
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||||
|
private val integration: StakingIntegration,
|
||||||
|
) : Converter<StakingBalanceEntry, BalanceState?> {
|
||||||
|
|
||||||
|
override fun convert(value: StakingBalanceEntry): BalanceState? {
|
||||||
|
val appCurrency = appCurrencyProvider()
|
||||||
|
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||||
|
|
||||||
|
val target = findTarget(value)
|
||||||
|
val cryptoAmount = value.getBalanceValue()
|
||||||
|
val fiatAmount = cryptoCurrencyStatus.value.fiatRate?.times(cryptoAmount)
|
||||||
|
|
||||||
|
val title = value.type.getTitle(target?.name ?: value.validator?.name)
|
||||||
|
return title?.let {
|
||||||
|
BalanceState(
|
||||||
|
groupId = value.id,
|
||||||
|
target = target,
|
||||||
|
title = title,
|
||||||
|
subtitle = getSubtitle(value),
|
||||||
|
type = value.type.toBalanceType(),
|
||||||
|
cryptoValue = cryptoAmount.parseBigDecimal(cryptoCurrency.decimals),
|
||||||
|
cryptoAmount = cryptoAmount,
|
||||||
|
formattedCryptoAmount = stringReference(
|
||||||
|
cryptoAmount.format { crypto(cryptoCurrency) },
|
||||||
|
),
|
||||||
|
fiatAmount = fiatAmount,
|
||||||
|
formattedFiatAmount = stringReference(
|
||||||
|
fiatAmount.format {
|
||||||
|
fiat(
|
||||||
|
fiatCurrencyCode = appCurrency.code,
|
||||||
|
fiatCurrencySymbol = appCurrency.symbol,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
rawCurrencyId = value.rawCurrencyId,
|
||||||
|
pendingActions = value.getPendingActions().toPersistentList(),
|
||||||
|
isClickable = value.isClickable(),
|
||||||
|
isPending = value.isPending,
|
||||||
|
targetAddress = value.validator?.address,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findTarget(entry: StakingBalanceEntry): StakingTarget? {
|
||||||
|
return integration.targets.firstOrNull {
|
||||||
|
entry.validator?.address?.contains(it.address, ignoreCase = true) == true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StakingBalanceEntry.getBalanceValue(): BigDecimal {
|
||||||
|
val isIncludeStakingTotalBalance = BlockchainUtils.isIncludeStakingTotalBalance(
|
||||||
|
blockchainId = cryptoCurrencyStatus.currency.network.rawId,
|
||||||
|
)
|
||||||
|
return if (isIncludeStakingTotalBalance) {
|
||||||
|
amount
|
||||||
|
} else {
|
||||||
|
val stakingBalance = cryptoCurrencyStatus.value.stakingBalance
|
||||||
|
if (stakingBalance is StakingBalance.Data.StakeKit) {
|
||||||
|
amount - stakingBalance.totalRewards
|
||||||
|
} else {
|
||||||
|
amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StakingEntryType.getTitle(validatorName: String?): TextReference? = when (this) {
|
||||||
|
StakingEntryType.PREPARING,
|
||||||
|
StakingEntryType.STAKED,
|
||||||
|
-> validatorName?.let { stringReference(it) }
|
||||||
|
StakingEntryType.WITHDRAWABLE -> resourceReference(R.string.staking_unstaked)
|
||||||
|
StakingEntryType.UNSTAKING -> resourceReference(R.string.staking_unstaking)
|
||||||
|
StakingEntryType.LOCKED -> resourceReference(R.string.staking_locked)
|
||||||
|
StakingEntryType.AVAILABLE,
|
||||||
|
StakingEntryType.REWARDS,
|
||||||
|
StakingEntryType.UNLOCKING,
|
||||||
|
StakingEntryType.UNKNOWN,
|
||||||
|
-> null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSubtitle(entry: StakingBalanceEntry): TextReference? = when (entry.type) {
|
||||||
|
StakingEntryType.UNSTAKING -> getUnbondingDate(entry.date)
|
||||||
|
StakingEntryType.WITHDRAWABLE -> resourceReference(R.string.staking_tap_to_withdraw)
|
||||||
|
StakingEntryType.LOCKED -> {
|
||||||
|
val hasVoteLocked = entry.getPendingActions().any { it.type == StakingActionType.VOTE_LOCKED }
|
||||||
|
if (hasVoteLocked) {
|
||||||
|
resourceReference(R.string.staking_tap_to_unlock_or_vote)
|
||||||
|
} else {
|
||||||
|
resourceReference(R.string.staking_tap_to_unlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StakingEntryType.PREPARING -> {
|
||||||
|
val warmupPeriod = integration.warmupPeriodDays
|
||||||
|
TextReference.Combined(
|
||||||
|
wrappedList(
|
||||||
|
resourceReference(R.string.staking_details_warmup_period),
|
||||||
|
stringReference(" "),
|
||||||
|
pluralReference(R.plurals.common_days, warmupPeriod, wrappedList(warmupPeriod)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
StakingEntryType.AVAILABLE,
|
||||||
|
StakingEntryType.STAKED,
|
||||||
|
StakingEntryType.UNLOCKING,
|
||||||
|
StakingEntryType.REWARDS,
|
||||||
|
StakingEntryType.UNKNOWN,
|
||||||
|
-> null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getUnbondingDate(date: Instant?): TextReference? {
|
||||||
|
val unbondingPeriod = integration.cooldownPeriodDays ?: return null
|
||||||
|
if (date == null) {
|
||||||
|
return TextReference.Combined(
|
||||||
|
wrappedList(
|
||||||
|
resourceReference(R.string.staking_details_unbonding_period),
|
||||||
|
stringReference(" "),
|
||||||
|
pluralReference(R.plurals.common_days, unbondingPeriod, wrappedList(unbondingPeriod)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val nowCalendar = Calendar.getInstance()
|
||||||
|
nowCalendar.resetHours()
|
||||||
|
|
||||||
|
val endDate = Calendar.getInstance()
|
||||||
|
endDate.timeInMillis = date.toEpochMilliseconds()
|
||||||
|
endDate.resetHours()
|
||||||
|
|
||||||
|
val days = ((endDate.timeInMillis - nowCalendar.timeInMillis) / DAY_IN_MILLIS).toInt()
|
||||||
|
return if (days > 0) {
|
||||||
|
resourceReference(
|
||||||
|
R.string.common_left,
|
||||||
|
wrappedList(
|
||||||
|
pluralReference(R.plurals.common_days, days, wrappedList(days)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
resourceReference(R.string.common_today)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StakingBalanceEntry.isClickable(): Boolean {
|
||||||
|
val networkId = cryptoCurrencyStatus.currency.network.rawId
|
||||||
|
return when {
|
||||||
|
// TON allows withdrawing funds in the preparing state, unlike other networks.
|
||||||
|
isTon(networkId) && this.type == StakingEntryType.PREPARING -> {
|
||||||
|
getPendingActions().any { it.type == StakingActionType.WITHDRAW }
|
||||||
|
}
|
||||||
|
else -> this.type.isClickableType() && !this.isPending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StakingEntryType.isClickableType(): Boolean = when (this) {
|
||||||
|
StakingEntryType.STAKED,
|
||||||
|
StakingEntryType.WITHDRAWABLE,
|
||||||
|
StakingEntryType.LOCKED,
|
||||||
|
-> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StakingBalanceEntry.getPendingActions(): List<PendingAction> {
|
||||||
|
return when (val actions = this.actions) {
|
||||||
|
is StakingEntryActions.StakeKit -> actions.pendingActions
|
||||||
|
is StakingEntryActions.P2PEthPool -> persistentListOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StakingEntryType.toBalanceType(): BalanceType {
|
||||||
|
return when (this) {
|
||||||
|
StakingEntryType.AVAILABLE -> BalanceType.AVAILABLE
|
||||||
|
StakingEntryType.STAKED -> BalanceType.STAKED
|
||||||
|
StakingEntryType.PREPARING -> BalanceType.PREPARING
|
||||||
|
StakingEntryType.LOCKED -> BalanceType.LOCKED
|
||||||
|
StakingEntryType.UNSTAKING -> BalanceType.UNSTAKING
|
||||||
|
StakingEntryType.UNLOCKING -> BalanceType.UNLOCKING
|
||||||
|
StakingEntryType.WITHDRAWABLE -> BalanceType.UNSTAKED
|
||||||
|
StakingEntryType.REWARDS -> BalanceType.REWARDS
|
||||||
|
StakingEntryType.UNKNOWN -> BalanceType.UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Calendar.resetHours() {
|
||||||
|
this[Calendar.HOUR_OF_DAY] = 0
|
||||||
|
this[Calendar.MINUTE] = 0
|
||||||
|
this[Calendar.SECOND] = 0
|
||||||
|
this[Calendar.MILLISECOND] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val DAY_IN_MILLIS = 24 * 60 * 60 * 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,13 +6,13 @@ import com.tangem.core.ui.format.bigdecimal.fiat
|
||||||
import com.tangem.core.ui.format.bigdecimal.format
|
import com.tangem.core.ui.format.bigdecimal.format
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.models.staking.BalanceItem
|
|
||||||
import com.tangem.domain.models.staking.BalanceType
|
import com.tangem.domain.models.staking.BalanceType
|
||||||
import com.tangem.domain.models.staking.RewardBlockType
|
import com.tangem.domain.models.staking.RewardBlockType
|
||||||
import com.tangem.domain.models.staking.StakingBalance
|
import com.tangem.domain.models.staking.StakingBalance
|
||||||
|
import com.tangem.domain.models.staking.StakingBalanceEntry
|
||||||
|
import com.tangem.domain.models.staking.StakingEntryType
|
||||||
import com.tangem.domain.models.staking.action.StakingActionType
|
import com.tangem.domain.models.staking.action.StakingActionType
|
||||||
import com.tangem.domain.staking.model.StakingIntegration
|
import com.tangem.domain.staking.model.StakingIntegration
|
||||||
import com.tangem.domain.staking.utils.getRewardStakingBalance
|
|
||||||
import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState
|
import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState
|
||||||
import com.tangem.features.staking.impl.presentation.state.YieldReward
|
import com.tangem.features.staking.impl.presentation.state.YieldReward
|
||||||
import com.tangem.lib.crypto.BlockchainUtils
|
import com.tangem.lib.crypto.BlockchainUtils
|
||||||
|
|
@ -24,23 +24,23 @@ import kotlinx.collections.immutable.toPersistentList
|
||||||
internal class YieldBalancesConverter(
|
internal class YieldBalancesConverter(
|
||||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||||
private val balancesToShowProvider: Provider<List<BalanceItem>>,
|
private val balancesToShowProvider: Provider<List<StakingBalanceEntry>>,
|
||||||
private val integration: StakingIntegration,
|
private val integration: StakingIntegration,
|
||||||
) : Converter<Unit, InnerYieldBalanceState> {
|
) : Converter<Unit, InnerYieldBalanceState> {
|
||||||
|
|
||||||
private val balanceItemConverter by lazy(LazyThreadSafetyMode.NONE) {
|
private val balanceEntryConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
BalanceItemConverter(cryptoCurrencyStatus, appCurrencyProvider, integration)
|
StakingBalanceEntryConverter(cryptoCurrencyStatus, appCurrencyProvider, integration)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun convert(value: Unit): InnerYieldBalanceState {
|
override fun convert(value: Unit): InnerYieldBalanceState {
|
||||||
val appCurrency = appCurrencyProvider()
|
val appCurrency = appCurrencyProvider()
|
||||||
|
|
||||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||||
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
|
val stakingBalance = cryptoCurrencyStatus.value.stakingBalance
|
||||||
val balanceToShowItems = balancesToShowProvider()
|
val balanceEntries = balancesToShowProvider()
|
||||||
|
val hasStakingData = stakingBalance is StakingBalance.Data
|
||||||
|
|
||||||
return if (stakeKitBalance != null || balanceToShowItems.any { it.isPending }) {
|
return if (hasStakingData || balanceEntries.any { it.isPending }) {
|
||||||
val cryptoRewardsValue = stakeKitBalance?.getRewardStakingBalance()
|
val cryptoRewardsValue = (stakingBalance as? StakingBalance.Data)?.totalRewards
|
||||||
|
|
||||||
val fiatRate = cryptoCurrencyStatus.value.fiatRate
|
val fiatRate = cryptoCurrencyStatus.value.fiatRate
|
||||||
val fiatRewardsValue = if (fiatRate != null && cryptoRewardsValue != null) {
|
val fiatRewardsValue = if (fiatRate != null && cryptoRewardsValue != null) {
|
||||||
|
|
@ -48,14 +48,11 @@ internal class YieldBalancesConverter(
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
val type = getRewardBlockType()
|
val type = getRewardBlockType(stakingBalance)
|
||||||
val pendingRewardsConstraints = stakeKitBalance?.balance?.items
|
val pendingRewardsConstraints = getRewardConstraints(stakingBalance)
|
||||||
?.firstOrNull { it.type == BalanceType.REWARDS }
|
|
||||||
?.pendingActionsConstraints
|
|
||||||
?.firstOrNull { it.type == StakingActionType.CLAIM_REWARDS }
|
|
||||||
|
|
||||||
InnerYieldBalanceState.Data(
|
InnerYieldBalanceState.Data(
|
||||||
integrationId = stakeKitBalance?.stakingId?.integrationId,
|
integrationId = stakingBalance?.stakingId?.integrationId,
|
||||||
reward = YieldReward(
|
reward = YieldReward(
|
||||||
rewardsCrypto = cryptoRewardsValue.format { crypto(cryptoCurrency) },
|
rewardsCrypto = cryptoRewardsValue.format { crypto(cryptoCurrency) },
|
||||||
rewardsFiat = fiatRewardsValue.format {
|
rewardsFiat = fiatRewardsValue.format {
|
||||||
|
|
@ -68,24 +65,32 @@ internal class YieldBalancesConverter(
|
||||||
rewardConstraints = pendingRewardsConstraints,
|
rewardConstraints = pendingRewardsConstraints,
|
||||||
),
|
),
|
||||||
isActionable = type.isActionable,
|
isActionable = type.isActionable,
|
||||||
balances = balanceToShowItems.mapBalances(),
|
balances = balanceEntries.mapBalances(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// TODO p2p
|
|
||||||
InnerYieldBalanceState.Empty
|
InnerYieldBalanceState.Empty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<BalanceItem>.mapBalances() = asSequence()
|
private fun List<StakingBalanceEntry>.mapBalances() = asSequence()
|
||||||
.filterNot { it.amount.isZero() || it.type == BalanceType.REWARDS }
|
.filterNot { it.amount.isZero() || it.type == StakingEntryType.REWARDS }
|
||||||
.mapNotNull(balanceItemConverter::convert)
|
.mapNotNull(balanceEntryConverter::convert)
|
||||||
.sortedByDescending { it.cryptoAmount }
|
.sortedByDescending { it.cryptoAmount }
|
||||||
.sortedBy { it.type.order }
|
.sortedBy { it.type.order }
|
||||||
.toPersistentList()
|
.toPersistentList()
|
||||||
|
|
||||||
private fun getRewardBlockType(): RewardBlockType {
|
private fun getRewardBlockType(stakingBalance: StakingBalance?): RewardBlockType {
|
||||||
val blockchainId = cryptoCurrencyStatus.currency.network.rawId
|
val blockchainId = cryptoCurrencyStatus.currency.network.rawId
|
||||||
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
|
|
||||||
|
if (stakingBalance is StakingBalance.Data.P2PEthPool) {
|
||||||
|
return if (isStakingRewardUnavailable(blockchainId)) {
|
||||||
|
RewardBlockType.RewardUnavailable.DefaultRewardUnavailable
|
||||||
|
} else {
|
||||||
|
RewardBlockType.NoRewards
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val stakeKitBalance = stakingBalance as? StakingBalance.Data.StakeKit
|
||||||
val rewards = stakeKitBalance?.balance?.items
|
val rewards = stakeKitBalance?.balance?.items
|
||||||
?.filter { it.type == BalanceType.REWARDS && !it.amount.isZero() }
|
?.filter { it.type == BalanceType.REWARDS && !it.amount.isZero() }
|
||||||
|
|
||||||
|
|
@ -105,4 +110,11 @@ internal class YieldBalancesConverter(
|
||||||
else -> RewardBlockType.NoRewards
|
else -> RewardBlockType.NoRewards
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getRewardConstraints(stakingBalance: StakingBalance?) =
|
||||||
|
(stakingBalance as? StakingBalance.Data.StakeKit)
|
||||||
|
?.balance?.items
|
||||||
|
?.firstOrNull { it.type == BalanceType.REWARDS }
|
||||||
|
?.pendingActionsConstraints
|
||||||
|
?.firstOrNull { it.type == StakingActionType.CLAIM_REWARDS }
|
||||||
}
|
}
|
||||||
|
|
@ -17,7 +17,7 @@ import com.tangem.core.ui.format.bigdecimal.percent
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
import com.tangem.domain.models.account.Account
|
import com.tangem.domain.models.account.Account
|
||||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.models.staking.BalanceItem
|
import com.tangem.domain.models.staking.StakingBalanceEntry
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.staking.model.StakingIntegration
|
import com.tangem.domain.staking.model.StakingIntegration
|
||||||
import com.tangem.domain.staking.model.StakingTarget
|
import com.tangem.domain.staking.model.StakingTarget
|
||||||
|
|
@ -49,7 +49,7 @@ internal class SetInitialDataStateTransformer(
|
||||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||||
private val userWalletProvider: Provider<UserWallet>,
|
private val userWalletProvider: Provider<UserWallet>,
|
||||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||||
private val balancesToShowProvider: Provider<List<BalanceItem>>,
|
private val balancesToShowProvider: Provider<List<StakingBalanceEntry>>,
|
||||||
private val isAccountsModeEnabled: Boolean,
|
private val isAccountsModeEnabled: Boolean,
|
||||||
private val account: Account.CryptoPortfolio?,
|
private val account: Account.CryptoPortfolio?,
|
||||||
private val isBalanceHidden: Boolean,
|
private val isBalanceHidden: Boolean,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue