Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-25 09:42:40 +02:00
parent 072d04125d
commit e00fd4599e
14 changed files with 492 additions and 253 deletions

View file

@ -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)) }
}

View file

@ -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,
),
)
}
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.domain.models.staking
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.serialization.SerializedBigDecimal
import kotlinx.serialization.Serializable
import java.math.BigDecimal
@ -21,6 +22,9 @@ sealed interface StakingBalance {
@Serializable
sealed interface Data : StakingBalance {
/** Provider-agnostic list of balance entries for UI display */
val entries: List<StakingBalanceEntry>
@Serializable
data class StakeKit(
override val stakingId: StakingID,
@ -28,25 +32,23 @@ sealed interface StakingBalance {
val balance: YieldBalanceItem,
) : Data {
override val totalStaked: BigDecimal
get() = balance.items
.filter { it.type == BalanceType.STAKED }
.sumOf { it.amount }
override val totalStaked: SerializedBigDecimal = balance.items
.filter { it.type == BalanceType.STAKED }
.sumOf { it.amount }
override val totalRewards: BigDecimal
get() = balance.items
.filter { it.type == BalanceType.REWARDS }
.sumOf { it.amount }
override val totalRewards: SerializedBigDecimal = balance.items
.filter { it.type == BalanceType.REWARDS }
.sumOf { it.amount }
override val unstakingAmount: BigDecimal
get() = balance.items
.filter { it.type == BalanceType.UNSTAKING || it.type == BalanceType.UNLOCKING }
.sumOf { it.amount }
override val unstakingAmount: SerializedBigDecimal = balance.items
.filter { it.type == BalanceType.UNSTAKING || it.type == BalanceType.UNLOCKING }
.sumOf { it.amount }
override val withdrawableAmount: BigDecimal
get() = balance.items
.filter { it.type == BalanceType.UNSTAKED }
.sumOf { it.amount }
override val withdrawableAmount: SerializedBigDecimal = balance.items
.filter { it.type == BalanceType.UNSTAKED }
.sumOf { it.amount }
override val entries: List<StakingBalanceEntry> = balance.items.toStakingBalanceEntries()
}
@Serializable
@ -56,17 +58,15 @@ sealed interface StakingBalance {
val account: P2PEthPoolStakingAccount,
) : Data {
override val totalStaked: BigDecimal
get() = account.stake.assets
override val totalStaked: SerializedBigDecimal = account.stake.assets
override val totalRewards: BigDecimal
get() = account.stake.totalEarnedAssets
override val totalRewards: SerializedBigDecimal = account.stake.totalEarnedAssets
override val unstakingAmount: BigDecimal
get() = account.exitQueue.total
override val unstakingAmount: SerializedBigDecimal = account.exitQueue.total
override val withdrawableAmount: BigDecimal
get() = account.availableToWithdraw
override val withdrawableAmount: SerializedBigDecimal = account.availableToWithdraw
override val entries: List<StakingBalanceEntry> = account.toStakingBalanceEntries()
}
}

View file

@ -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?,
)

View file

@ -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
}

View file

@ -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
}
}
}