Skip to content

Commit

Permalink
Merge pull request #1773 from novasamatech/myth_staking/select_collator
Browse files Browse the repository at this point in the history
Myth staking/select collator
  • Loading branch information
valentunn authored Jan 27, 2025
2 parents cf205af + f43db62 commit 84358bc
Show file tree
Hide file tree
Showing 52 changed files with 1,253 additions and 262 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.app.root.navigation.navigators.staking.mythos.MythosStakingNavigator
import io.novafoundation.nova.app.root.navigation.navigators.staking.mythos.SelectMythCollatorSettingsInterScreenCommunicatorImpl
import io.novafoundation.nova.app.root.navigation.navigators.staking.mythos.SelectMythosCollatorInterScreenCommunicatorImpl
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_staking_impl.presentation.MythosStakingRouter
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.SelectMythosInterScreenCommunicator
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.selectCollatorSettings.SelectMythCollatorSettingsInterScreenCommunicator

@Module
class MythosStakingNavigationModule {
Expand All @@ -17,4 +21,16 @@ class MythosStakingNavigationModule {
): MythosStakingRouter {
return MythosStakingNavigator(navigationHoldersRegistry)
}

@Provides
@ApplicationScope
fun provideSelectCollatorCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): SelectMythosInterScreenCommunicator {
return SelectMythosCollatorInterScreenCommunicatorImpl(navigationHoldersRegistry)
}

@Provides
@ApplicationScope
fun provideSelectSettingsCollatorCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): SelectMythCollatorSettingsInterScreenCommunicator {
return SelectMythCollatorSettingsInterScreenCommunicatorImpl(navigationHoldersRegistry)
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
package io.novafoundation.nova.app.root.navigation.navigators.staking.mythos

import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.navigators.BaseNavigator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_staking_impl.presentation.MythosStakingRouter
import io.novafoundation.nova.feature_staking_impl.presentation.validators.details.StakeTargetDetailsPayload
import io.novafoundation.nova.feature_staking_impl.presentation.validators.details.ValidatorDetailsFragment

class MythosStakingNavigator(
navigationHoldersRegistry: NavigationHoldersRegistry,
) : BaseNavigator(navigationHoldersRegistry), MythosStakingRouter
) : BaseNavigator(navigationHoldersRegistry), MythosStakingRouter {

override fun openCollatorDetails(payload: StakeTargetDetailsPayload) {
navigationBuilder()
.action(R.id.open_validator_details)
.setArgs(ValidatorDetailsFragment.getBundle(payload))
.navigateInFirstAttachedContext()
}

override fun returnToStartStaking() {
navigationBuilder()
.action(R.id.action_return_to_start_staking)
.navigateInFirstAttachedContext()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.novafoundation.nova.app.root.navigation.navigators.staking.mythos

import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.NavStackInterScreenCommunicator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.selectCollatorSettings.SelectMythCollatorSettingsFragment
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.selectCollatorSettings.SelectMythCollatorSettingsInterScreenCommunicator
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.selectCollatorSettings.model.MythCollatorRecommendationConfigParcel

class SelectMythCollatorSettingsInterScreenCommunicatorImpl(navigationHoldersRegistry: NavigationHoldersRegistry) :
SelectMythCollatorSettingsInterScreenCommunicator,
NavStackInterScreenCommunicator<MythCollatorRecommendationConfigParcel, MythCollatorRecommendationConfigParcel>(navigationHoldersRegistry) {

override fun openRequest(request: MythCollatorRecommendationConfigParcel) {
super.openRequest(request)

val bundle = SelectMythCollatorSettingsFragment.getBundle(request)
navController.navigate(R.id.action_selectMythosCollatorFragment_to_selectMythCollatorSettingsFragment, bundle)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.novafoundation.nova.app.root.navigation.navigators.staking.mythos

import io.novafoundation.nova.app.R
import io.novafoundation.nova.app.root.navigation.NavStackInterScreenCommunicator
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.SelectMythosInterScreenCommunicator
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.SelectMythosInterScreenCommunicator.Request
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.selectCollator.model.MythosCollatorParcel

class SelectMythosCollatorInterScreenCommunicatorImpl(navigationHoldersRegistry: NavigationHoldersRegistry) :
SelectMythosInterScreenCommunicator,
NavStackInterScreenCommunicator<Request, MythosCollatorParcel>(navigationHoldersRegistry) {

override fun respond(response: MythosCollatorParcel) {
val responseEntry = navController.getBackStackEntry(R.id.startMythosStakingFragment)
saveResultTo(responseEntry, response)
}

override fun openRequest(request: Request) {
super.openRequest(request)

navController.navigate(R.id.action_startMythosStakingFragment_to_selectMythosCollatorFragment)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class SelectCollatorSettingsInterScreenCommunicatorImpl(navigationHoldersRegistr
NavStackInterScreenCommunicator<Request, Response>(navigationHoldersRegistry) {

override fun openRequest(request: Request) {
super.openRequest(request)

val bundle = SelectCollatorSettingsFragment.getBundle(request.currentConfig)
navController.navigate(R.id.action_selectCollatorFragment_to_selectCollatorSettingsFragment, bundle)
}
Expand Down
26 changes: 26 additions & 0 deletions app/src/main/res/navigation/staking_mythos_start_graph.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,31 @@
android:name="io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.setup.SetupStartMythosStakingFragment"
android:label="StartMythosStakingFragment"
tools:layout="@layout/fragment_parachain_staking_start">

<action
android:id="@+id/action_startMythosStakingFragment_to_selectMythosCollatorFragment"
app:destination="@id/selectMythosCollatorFragment"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_close_enter"
app:popExitAnim="@anim/fragment_close_exit" />
</fragment>

<fragment
android:id="@+id/selectMythosCollatorFragment"
android:name="io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.selectCollator.SelectMythosCollatorFragment"
android:label="SelectMythosCollatorFragment">

<action
android:id="@+id/action_selectMythosCollatorFragment_to_selectMythCollatorSettingsFragment"
app:destination="@id/selectMythCollatorSettingsFragment"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_close_enter"
app:popExitAnim="@anim/fragment_close_exit" />
</fragment>
<fragment
android:id="@+id/selectMythCollatorSettingsFragment"
android:name="io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.selectCollatorSettings.SelectMythCollatorSettingsFragment"
android:label="SelectMythCollatorSettingsFragment" />
</navigation>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.novafoundation.nova.common.address

import io.novasama.substrate_sdk_android.extensions.toHexString
import io.novasama.substrate_sdk_android.runtime.AccountId

class AccountIdKey(val value: AccountId) {
Expand All @@ -17,6 +18,10 @@ class AccountIdKey(val value: AccountId) {

fun AccountId.intoKey() = AccountIdKey(this)

fun AccountIdKey.toHex(): String {
return value.toHexString()
}

operator fun <T> Map<AccountIdKey, T>.get(key: AccountId) = get(AccountIdKey(key))
fun <T> Map<AccountIdKey, T>.getValue(key: AccountId) = getValue(AccountIdKey(key))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.novafoundation.nova.common.address

import android.os.Parcelable
import io.novasama.substrate_sdk_android.extensions.fromHex
import io.novasama.substrate_sdk_android.runtime.AccountId
import kotlinx.android.parcel.Parcelize

@Parcelize
class AccountIdParcel(private val value: AccountId) : Parcelable {

companion object {

fun fromHex(hexAccountId: String): AccountIdParcel {
return AccountIdParcel(hexAccountId.fromHex())
}
}

val accountId: AccountIdKey
get() = value.intoKey()
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ fun BigDecimal?.orZero(): BigDecimal = this ?: 0.toBigDecimal()

fun Double?.orZero(): Double = this ?: 0.0

fun Int?.orZero(): Int = this ?: 0

fun BigInteger.divideToDecimal(divisor: BigInteger, mathContext: MathContext = MathContext.DECIMAL64): BigDecimal {
return toBigDecimal().divide(divisor.toBigDecimal(), mathContext)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ interface MythosStakingRepository {

fun minStakeFlow(chainId: ChainId): Flow<Balance>

suspend fun maxCandidatesPerDelegator(chainId: ChainId): Int

suspend fun unstakeDurationInSessions(chainId: ChainId): Int
}

Expand All @@ -37,6 +39,12 @@ class RealMythosStakingRepository @Inject constructor(
}
}

override suspend fun maxCandidatesPerDelegator(chainId: ChainId): Int {
return chainRegistry.withRuntime(chainId) {
metadata.collatorStaking().numberConstant("MaxStakedCandidates").toInt()
}
}

override suspend fun unstakeDurationInSessions(chainId: ChainId): Int {
return chainRegistry.withRuntime(chainId) {
metadata.collatorStaking().numberConstant("StakeUnlockDelay").toInt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ import io.novafoundation.nova.feature_staking_impl.presentation.validators.chang
import io.novafoundation.nova.feature_staking_impl.presentation.validators.change.confirm.nominations.di.ConfirmNominationsComponent
import io.novafoundation.nova.feature_staking_impl.presentation.dashboard.main.di.StakingDashboardComponent
import io.novafoundation.nova.feature_staking_impl.presentation.dashboard.more.di.MoreStakingOptionsComponent
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.SelectMythosInterScreenCommunicator
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.selectCollator.di.SelectMythosCollatorComponent
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.selectCollatorSettings.SelectMythCollatorSettingsInterScreenCommunicator
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.selectCollatorSettings.di.SelectMythCollatorSettingsComponent
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.setup.di.SetupStartMythosStakingComponent
import io.novafoundation.nova.feature_staking_impl.presentation.nominationPools.bondMore.confirm.di.NominationPoolsConfirmBondMoreComponent
import io.novafoundation.nova.feature_staking_impl.presentation.nominationPools.bondMore.setup.di.NominationPoolsSetupBondMoreComponent
Expand Down Expand Up @@ -241,18 +245,25 @@ interface StakingFeatureComponent : StakingFeatureApi {

fun startMythosStakingFactory(): SetupStartMythosStakingComponent.Factory

fun selectMythosCollatorFactory(): SelectMythosCollatorComponent.Factory

fun selectMythosSettingsFactory(): SelectMythCollatorSettingsComponent.Factory

@Component.Factory
interface Factory {

fun create(
@BindsInstance router: StakingRouter,

@BindsInstance parachainStaking: ParachainStakingRouter,
@BindsInstance mythosStakingRouter: MythosStakingRouter,
@BindsInstance selectCollatorInterScreenCommunicator: SelectCollatorInterScreenCommunicator,
@BindsInstance selectCollatorSettingsInterScreenCommunicator: SelectCollatorSettingsInterScreenCommunicator,
@BindsInstance selectAddressCommunicator: SelectAddressCommunicator,

@BindsInstance mythosStakingRouter: MythosStakingRouter,
@BindsInstance selectMythosCollatorInterScreenCommunicator: SelectMythosInterScreenCommunicator,
@BindsInstance selectMythosCollatorSettingsInterScreenCommunicator: SelectMythCollatorSettingsInterScreenCommunicator,

@BindsInstance nominationPoolsRouter: NominationPoolsRouter,

@BindsInstance startMultiStakingRouter: StartMultiStakingRouter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import io.novafoundation.nova.feature_staking_impl.presentation.ParachainStaking
import io.novafoundation.nova.feature_staking_impl.presentation.StakingDashboardRouter
import io.novafoundation.nova.feature_staking_impl.presentation.StakingRouter
import io.novafoundation.nova.feature_staking_impl.presentation.StartMultiStakingRouter
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.SelectMythosInterScreenCommunicator
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.selectCollatorSettings.SelectMythCollatorSettingsInterScreenCommunicator
import io.novafoundation.nova.feature_staking_impl.presentation.parachainStaking.collator.common.SelectCollatorInterScreenCommunicator
import io.novafoundation.nova.feature_staking_impl.presentation.parachainStaking.collator.settings.SelectCollatorSettingsInterScreenCommunicator
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
Expand All @@ -32,6 +34,8 @@ class StakingFeatureHolder @Inject constructor(
private val selectAddressCommunicator: SelectAddressCommunicator,
private val selectCollatorInterScreenCommunicator: SelectCollatorInterScreenCommunicator,
private val selectCollatorSettingsInterScreenCommunicator: SelectCollatorSettingsInterScreenCommunicator,
private val selectMythosCollatorInterScreenCommunicator: SelectMythosInterScreenCommunicator,
private val selectMythosCollatorSettingsInterScreenCommunicator: SelectMythCollatorSettingsInterScreenCommunicator,
) : FeatureApiHolder(featureContainer) {

override fun initializeDependencies(): Any {
Expand All @@ -56,6 +60,8 @@ class StakingFeatureHolder @Inject constructor(
nominationPoolsRouter = nominationPoolsRouter,
startMultiStakingRouter = startMultiStakingRouter,
stakingDashboardRouter = stakingDashboardRouter,
selectMythosCollatorInterScreenCommunicator = selectMythosCollatorInterScreenCommunicator,
selectMythosCollatorSettingsInterScreenCommunicator = selectMythosCollatorSettingsInterScreenCommunicator,
deps = dependencies
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.novafoundation.nova.feature_staking_impl.domain.common.singleSelect

import io.novafoundation.nova.common.data.memory.ComputationalScope
import io.novafoundation.nova.feature_staking_impl.data.StakingOption
import io.novafoundation.nova.feature_staking_impl.domain.common.singleSelect.recommendations.SingleSelectRecommendatorConfig

interface SingleSelectRecommendator<T> {

Expand All @@ -11,5 +12,7 @@ interface SingleSelectRecommendator<T> {
suspend fun create(stakingOption: StakingOption, computationalScope: ComputationalScope): SingleSelectRecommendator<T>
}

fun recommendations(config: SingleSelectRecommendatorConfig<T>): List<T>

fun defaultRecommendation(): T?
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ class FilteringSingleSelectRecommendator<T : WithAccountId>(
private val excluded: Set<AccountIdKey>
) : SingleSelectRecommendator<T> {

fun recommendations(sorting: Comparator<T>): List<T> {
override fun recommendations(config: SingleSelectRecommendatorConfig<T>): List<T> {
return allTargets.filter { it.accountId !in excluded }
.sortedWith(
// accounts from recommended list first
compareByDescending<T> { it.accountId in recommended }
// then by the supplied sorting rule
.then(sorting)
.then(config)
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package io.novafoundation.nova.feature_staking_impl.domain.common.singleSelect.recommendations

typealias SingleSelectRecommendatorConfig<T> = Comparator<T>
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class RealMythosCollatorProvider @Inject constructor(
totalStake = collatorStake.stake,
delegators = collatorStake.stakers,
// TODO APY calculation
apy = Fraction.ZERO
apr = Fraction.ZERO
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class MythosCollator(
val identity: OnChainIdentity?,
val totalStake: Balance,
val delegators: Int,
val apy: Fraction?,
val apr: Fraction?,
) : Identifiable, WithAccountId {

override val identifier: String = accountId.value.toHexString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ fun MythosDelegatorState.stakeByCollator(): Map<AccountIdKey, Balance> {
}
}

fun MythosDelegatorState.stakedCollatorsCount(): Int {
return when (this) {
is MythosDelegatorState.Locked.Delegating -> userStakeInfo.candidates.size
MythosDelegatorState.NotStarted, is MythosDelegatorState.Locked.NotDelegating -> 0
}
}

fun MythosDelegatorState.hasActiveValidators(sessionValidators: SessionValidators): Boolean {
return when (this) {
is MythosDelegatorState.Locked.Delegating -> userStakeInfo.hasActiveValidators(sessionValidators)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import io.novafoundation.nova.feature_staking_impl.domain.mythos.common.model.My

enum class MythosCollatorSorting(private val collatorComparator: Comparator<MythosCollator>) : Comparator<MythosCollator> by collatorComparator {

REWARDS(compareByDescending { it.apy }),
REWARDS(compareByDescending { it.apr }),
TOTAL_STAKE(compareByDescending { it.totalStake }),
}

Expand Down
Loading

0 comments on commit 84358bc

Please sign in to comment.