Fix nomination pool interactors to use parent chain for exposures

For Asset Hub nomination pools, elected exposures must be queried from
the parent relay chain where validators actually exist.
This commit is contained in:
2026-02-09 21:06:14 +03:00
parent 97d2c803bf
commit 4cd4999e04
4 changed files with 28 additions and 6 deletions
@@ -56,6 +56,7 @@ import io.novafoundation.nova.feature_staking_impl.domain.nominationPools.pools.
import io.novafoundation.nova.feature_staking_impl.presentation.nominationPools.common.PoolDisplayFormatter
import io.novafoundation.nova.feature_staking_impl.presentation.nominationPools.common.RealPoolDisplayFormatter
import io.novafoundation.nova.runtime.call.MultiChainRuntimeCallsApi
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
import io.novafoundation.nova.runtime.di.LOCAL_STORAGE_SOURCE
import io.novafoundation.nova.runtime.di.REMOTE_STORAGE_SOURCE
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
@@ -151,12 +152,14 @@ class NominationPoolModule {
poolAccountDerivation: PoolAccountDerivation,
relaychainStakingInteractor: StakingInteractor,
nominationPoolMemberUseCase: NominationPoolMemberUseCase,
chainRegistry: ChainRegistry,
): NominationPoolsNetworkInfoInteractor = RealNominationPoolsNetworkInfoInteractor(
relaychainStakingSharedComputation = relaychainStakingSharedComputation,
nominationPoolGlobalsRepository = nominationPoolGlobalsRepository,
poolAccountDerivation = poolAccountDerivation,
relaychainStakingInteractor = relaychainStakingInteractor,
nominationPoolMemberUseCase = nominationPoolMemberUseCase
nominationPoolMemberUseCase = nominationPoolMemberUseCase,
chainRegistry = chainRegistry
)
@Provides
@@ -18,6 +18,7 @@ import io.novafoundation.nova.feature_staking_impl.domain.nominationPools.common
import io.novafoundation.nova.feature_staking_impl.domain.nominationPools.main.alerts.NominationPoolAlert.RedeemTokens
import io.novafoundation.nova.feature_staking_impl.domain.nominationPools.main.alerts.NominationPoolAlert.WaitingForNextEra
import io.novafoundation.nova.feature_staking_impl.domain.nominationPools.main.alerts.RealNominationPoolsAlertsInteractor.AlertsResolutionContext.PoolContext
import io.novafoundation.nova.feature_staking_impl.domain.nominationPools.stakingBackingChainId
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import io.novasama.substrate_sdk_android.hash.isPositive
import io.novasama.substrate_sdk_android.runtime.AccountId
@@ -49,11 +50,13 @@ class RealNominationPoolsAlertsInteractor(
return flowOfAll {
val poolId = poolMember.poolId
val poolStash = poolAccountDerivation.bondedAccountOf(poolId, chain.id)
// For nomination pools on parachains (like Asset Hub), get exposures from parent relay chain
val stakingBackingChainId = chain.stakingBackingChainId(Chain.Asset.StakingType.NOMINATION_POOLS)
combine(
nominationPoolsSharedComputation.participatingPoolNominationsFlow(poolStash, poolId, chain.id, shareComputationScope),
nominationPoolsSharedComputation.unbondingPoolsFlow(poolId, chain.id, shareComputationScope),
stakingSharedComputation.electedExposuresWithActiveEraFlow(chain.id, shareComputationScope),
stakingSharedComputation.electedExposuresWithActiveEraFlow(stakingBackingChainId, shareComputationScope),
) { poolNominations, unbondingPools, (eraStakers, activeEra) ->
val alertsContext = AlertsResolutionContext(
eraStakers = eraStakers,
@@ -12,6 +12,9 @@ import io.novafoundation.nova.feature_staking_impl.domain.StakingInteractor
import io.novafoundation.nova.feature_staking_impl.domain.common.StakingSharedComputation
import io.novafoundation.nova.feature_staking_impl.domain.common.electedExposuresInActiveEraFlow
import io.novafoundation.nova.feature_staking_impl.domain.model.NetworkInfo
import io.novafoundation.nova.feature_staking_impl.domain.nominationPools.stakingBackingChainId
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import io.novafoundation.nova.feature_staking_impl.domain.model.StakingPeriod
import io.novafoundation.nova.feature_staking_impl.domain.nominationPools.common.NominationPoolMemberUseCase
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.types.Balance
@@ -19,6 +22,8 @@ import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import java.math.BigInteger
@@ -35,6 +40,7 @@ class RealNominationPoolsNetworkInfoInteractor(
private val poolAccountDerivation: PoolAccountDerivation,
private val relaychainStakingInteractor: StakingInteractor,
private val nominationPoolMemberUseCase: NominationPoolMemberUseCase,
private val chainRegistry: ChainRegistry,
) : NominationPoolsNetworkInfoInteractor {
override fun observeShouldShowNetworkInfo(): Flow<Boolean> {
@@ -44,9 +50,13 @@ class RealNominationPoolsNetworkInfoInteractor(
override fun observeNetworkInfo(
chainId: ChainId,
sharedComputationScope: CoroutineScope
): Flow<NetworkInfo> {
return combine(
relaychainStakingSharedComputation.electedExposuresInActiveEraFlow(chainId, sharedComputationScope),
): Flow<NetworkInfo> = flow {
// For nomination pools on parachains (like Asset Hub), get exposures from parent relay chain
val chain = chainRegistry.getChain(chainId)
val stakingBackingChainId = chain.stakingBackingChainId(Chain.Asset.StakingType.NOMINATION_POOLS)
val networkInfoFlow = combine(
relaychainStakingSharedComputation.electedExposuresInActiveEraFlow(stakingBackingChainId, sharedComputationScope),
nominationPoolGlobalsRepository.observeMinJoinBond(chainId),
nominationPoolGlobalsRepository.lastPoolIdFlow(chainId),
lockupDurationFlow(sharedComputationScope),
@@ -59,6 +69,8 @@ class RealNominationPoolsNetworkInfoInteractor(
nominatorsCount = null
)
}
emitAll(networkInfoFlow)
}
private fun lockupDurationFlow(sharedComputationScope: CoroutineScope) = flowOf { relaychainStakingInteractor.getLockupDuration(sharedComputationScope) }
@@ -9,8 +9,10 @@ import io.novafoundation.nova.feature_staking_api.domain.model.EraIndex
import io.novafoundation.nova.feature_staking_api.domain.model.Exposure
import io.novafoundation.nova.feature_staking_api.domain.model.Nominations
import io.novafoundation.nova.feature_staking_impl.data.StakingOption
import io.novafoundation.nova.feature_staking_impl.data.stakingType
import io.novafoundation.nova.feature_staking_impl.data.nominationPools.network.blockhain.models.PoolMember
import io.novafoundation.nova.feature_staking_impl.domain.common.StakingSharedComputation
import io.novafoundation.nova.feature_staking_impl.domain.nominationPools.stakingBackingChainId
import io.novafoundation.nova.feature_staking_impl.domain.common.isWaiting
import io.novafoundation.nova.feature_staking_impl.domain.model.StakeSummary
import io.novafoundation.nova.feature_staking_impl.domain.nominationPools.common.NominationPoolSharedComputation
@@ -48,12 +50,14 @@ class RealNominationPoolStakeSummaryInteractor(
sharedComputationScope: CoroutineScope,
): Flow<StakeSummary<PoolMemberStatus>> = flowOfAll {
val chainId = stakingOption.assetWithChain.chain.id
// For nomination pools on parachains (like Asset Hub), get exposures from parent relay chain
val stakingBackingChainId = stakingOption.assetWithChain.chain.stakingBackingChainId(stakingOption.stakingType)
val poolStash = poolAccountDerivation.bondedAccountOf(poolMember.poolId, chainId)
combineTransform(
nominationPoolSharedComputation.participatingBondedPoolStateFlow(poolStash, poolMember.poolId, chainId, sharedComputationScope),
nominationPoolSharedComputation.participatingPoolNominationsFlow(poolStash, poolMember.poolId, chainId, sharedComputationScope),
stakingSharedComputation.electedExposuresWithActiveEraFlow(chainId, sharedComputationScope)
stakingSharedComputation.electedExposuresWithActiveEraFlow(stakingBackingChainId, sharedComputationScope)
) { bondedPoolState, poolNominations, (eraStakers, activeEra) ->
val activeStaked = bondedPoolState.amountOf(poolMember.points)