diff --git a/build.gradle b/build.gradle index 0f179c8215..6f173ef9ff 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,8 @@ buildscript { ext { // App version - versionName = '7.11.2' - versionCode = 131 + versionName = '7.12.0' + versionCode = 132 applicationId = "io.novafoundation.nova" releaseApplicationSuffix = "market" diff --git a/common/src/main/java/io/novafoundation/nova/common/data/network/runtime/binding/Floats.kt b/common/src/main/java/io/novafoundation/nova/common/data/network/runtime/binding/Floats.kt index 3f8107fe68..83bdee4bba 100644 --- a/common/src/main/java/io/novafoundation/nova/common/data/network/runtime/binding/Floats.kt +++ b/common/src/main/java/io/novafoundation/nova/common/data/network/runtime/binding/Floats.kt @@ -9,6 +9,7 @@ typealias FixedI64 = BigDecimal const val PERBILL_MANTISSA_SIZE = 9 const val PERMILL_MANTISSA_SIZE = 6 +const val PERQUINTILL_MANTISSA_SIZE = 18 @HelperBinding fun bindPerbillNumber(value: BigInteger, mantissa: Int = PERBILL_MANTISSA_SIZE): Perbill { @@ -34,3 +35,7 @@ fun bindPerbillTyped(dynamic: Any?, mantissa: Int = PERBILL_MANTISSA_SIZE): Perb fun bindPermill(dynamic: Any?): PerbillTyped { return bindPerbillTyped(dynamic, mantissa = PERMILL_MANTISSA_SIZE) } + +fun BigInteger.asPerQuintill(): PerbillTyped { + return PerbillTyped(toBigDecimal(scale = PERQUINTILL_MANTISSA_SIZE).toDouble()) +} diff --git a/core-db/src/main/java/io/novafoundation/nova/core_db/converters/LongMathConverters.kt b/core-db/src/main/java/io/novafoundation/nova/core_db/converters/LongMathConverters.kt index 4e061f9b3c..56b540be7a 100644 --- a/core-db/src/main/java/io/novafoundation/nova/core_db/converters/LongMathConverters.kt +++ b/core-db/src/main/java/io/novafoundation/nova/core_db/converters/LongMathConverters.kt @@ -23,6 +23,16 @@ class LongMathConverters { @TypeConverter fun toBigInteger(balance: String?): BigInteger? { - return balance?.let { BigInteger(it) } + return balance?.let { + // When using aggregates like SUM in SQL queries, SQLite might return the result in a scientific notation especially if aggregation is done + // BigInteger, which is stored as a string and SQLite casts it to REAL which causing the scientific notation on big numbers + // This can be avoided by adjusting the query but we keep the fallback to BigDecimal parsing here anyways to avoid unpleasant crashes + // It doesn't bring much impact since try-catch doesn't have an overhead unless the exception is thrown + try { + BigInteger(it) + } catch (e: NumberFormatException) { + BigDecimal(it).toBigInteger() + } + } } } diff --git a/feature-buy-impl/src/main/java/io/novafoundation/nova/feature_buy_impl/di/BuyFeatureModule.kt b/feature-buy-impl/src/main/java/io/novafoundation/nova/feature_buy_impl/di/BuyFeatureModule.kt index aabf5389d4..12fd44232f 100644 --- a/feature-buy-impl/src/main/java/io/novafoundation/nova/feature_buy_impl/di/BuyFeatureModule.kt +++ b/feature-buy-impl/src/main/java/io/novafoundation/nova/feature_buy_impl/di/BuyFeatureModule.kt @@ -57,9 +57,9 @@ class BuyFeatureModule { ): BuyTokenRegistry { return RealBuyTokenRegistry( providers = listOf( + mercuryoProvider, transakProvider, banxaProvider, - mercuryoProvider ) ) } diff --git a/feature-push-notifications/src/main/java/io/novafoundation/nova/feature_push_notifications/data/settings/RealPushSettingsProvider.kt b/feature-push-notifications/src/main/java/io/novafoundation/nova/feature_push_notifications/data/settings/RealPushSettingsProvider.kt index c9610e432e..79a052f1c5 100644 --- a/feature-push-notifications/src/main/java/io/novafoundation/nova/feature_push_notifications/data/settings/RealPushSettingsProvider.kt +++ b/feature-push-notifications/src/main/java/io/novafoundation/nova/feature_push_notifications/data/settings/RealPushSettingsProvider.kt @@ -33,7 +33,7 @@ class RealPushSettingsProvider( sentTokensEnabled = true, receivedTokensEnabled = true, subscribedMetaAccounts = setOf(accountRepository.getSelectedMetaAccount().id), - stakingReward = PushSettings.ChainFeature.Concrete(emptyList()), + stakingReward = PushSettings.ChainFeature.All, governance = emptyMap() ) } diff --git a/feature-staking-api/src/main/java/io/novafoundation/nova/feature_staking_api/domain/model/Validator.kt b/feature-staking-api/src/main/java/io/novafoundation/nova/feature_staking_api/domain/model/Validator.kt index 08625cb264..2aaf4624a2 100644 --- a/feature-staking-api/src/main/java/io/novafoundation/nova/feature_staking_api/domain/model/Validator.kt +++ b/feature-staking-api/src/main/java/io/novafoundation/nova/feature_staking_api/domain/model/Validator.kt @@ -16,6 +16,7 @@ class Validator( val prefs: ValidatorPrefs?, val electedInfo: ElectedInfo?, val identity: OnChainIdentity?, + val isNovaValidator: Boolean ) : Identifiable { class ElectedInfo( diff --git a/feature-staking-impl/build.gradle b/feature-staking-impl/build.gradle index 4e74c2a4df..d8831fda10 100644 --- a/feature-staking-impl/build.gradle +++ b/feature-staking-impl/build.gradle @@ -14,7 +14,7 @@ android { testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - buildConfigField "String", "DASHBOARD_SUBQUERY_URL", "\"https://api.subquery.network/sq/nova-wallet/subquery-staking\"" + buildConfigField "String", "GLOBAL_CONFIG_URL", "\"https://raw.githubusercontent.com/novasamatech/nova-utils/master/staking/global_config_dev.json\"" } buildTypes { @@ -22,7 +22,7 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - buildConfigField "String", "DASHBOARD_SUBQUERY_URL", "\"https://api.subquery.network/sq/nova-wallet/subquery-staking\"" + buildConfigField "String", "GLOBAL_CONFIG_URL", "\"https://raw.githubusercontent.com/novasamatech/nova-utils/master/staking/global_config.json\"" } } diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/collators/KnownNovaCollators.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/collators/KnownNovaCollators.kt deleted file mode 100644 index a2a09c628b..0000000000 --- a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/collators/KnownNovaCollators.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.novafoundation.nova.feature_staking_impl.data.collators - -import io.novafoundation.nova.runtime.ext.Geneses -import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain -import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId - -interface KnownNovaCollators { - - fun getCollatorIds(chainId: ChainId): List -} - -class FixedKnownNovaCollators : KnownNovaCollators { - - private val novaValidators by lazy { - mapOf( - Chain.Geneses.POLIMEC to listOf("5A5Qgq3wn6JeH8Qtu7rakxULpBhtyqyX8iNj1XV8WFg3U58T") - ) - } - - override fun getCollatorIds(chainId: ChainId): List { - return novaValidators[chainId].orEmpty() - } -} diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/config/StakingGlobalConfig.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/config/StakingGlobalConfig.kt new file mode 100644 index 0000000000..18d33f78e8 --- /dev/null +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/config/StakingGlobalConfig.kt @@ -0,0 +1,5 @@ +package io.novafoundation.nova.feature_staking_impl.data.config + +class StakingGlobalConfig( + val multiStakingApiUrl: String, +) diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/config/api/StakingGlobalConfigApi.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/config/api/StakingGlobalConfigApi.kt new file mode 100644 index 0000000000..93c927fed1 --- /dev/null +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/config/api/StakingGlobalConfigApi.kt @@ -0,0 +1,11 @@ +package io.novafoundation.nova.feature_staking_impl.data.config.api + +import io.novafoundation.nova.feature_staking_impl.BuildConfig +import io.novafoundation.nova.feature_staking_impl.data.config.api.response.StakingGlobalConfigRemote +import retrofit2.http.GET + +interface StakingGlobalConfigApi { + + @GET(BuildConfig.GLOBAL_CONFIG_URL) + suspend fun getStakingGlobalConfig(): StakingGlobalConfigRemote +} diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/config/api/response/StakingGlobalConfigRemote.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/config/api/response/StakingGlobalConfigRemote.kt new file mode 100644 index 0000000000..09b4837b48 --- /dev/null +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/config/api/response/StakingGlobalConfigRemote.kt @@ -0,0 +1,5 @@ +package io.novafoundation.nova.feature_staking_impl.data.config.api.response + +class StakingGlobalConfigRemote( + val multiStakingApiUrl: String +) diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/dashboard/network/stats/StakingStatsDataSource.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/dashboard/network/stats/StakingStatsDataSource.kt index 4b335cd857..7a364723ce 100644 --- a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/dashboard/network/stats/StakingStatsDataSource.kt +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/dashboard/network/stats/StakingStatsDataSource.kt @@ -15,6 +15,7 @@ import io.novafoundation.nova.feature_staking_impl.data.dashboard.network.stats. import io.novafoundation.nova.feature_staking_impl.data.dashboard.network.stats.api.StakingStatsResponse.WithStakingId import io.novafoundation.nova.feature_staking_impl.data.dashboard.network.stats.api.StakingStatsRewards import io.novafoundation.nova.feature_staking_impl.data.dashboard.network.stats.api.mapSubQueryIdToStakingType +import io.novafoundation.nova.feature_staking_impl.data.repository.StakingGlobalConfigRepository import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.types.Balance import io.novafoundation.nova.runtime.ext.UTILITY_ASSET_ID import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain @@ -28,7 +29,7 @@ interface StakingStatsDataSource { class RealStakingStatsDataSource( private val api: StakingStatsApi, - private val dashboardApiUrl: String, + private val stakingGlobalConfigRepository: StakingGlobalConfigRepository ) : StakingStatsDataSource { override suspend fun fetchStakingStats( @@ -37,6 +38,7 @@ class RealStakingStatsDataSource( ): MultiChainStakingStats = withContext(Dispatchers.IO) { retryUntilDone { val request = StakingStatsRequest(stakingAccounts, stakingChains) + val dashboardApiUrl = stakingGlobalConfigRepository.getStakingGlobalConfig().multiStakingApiUrl val response = api.fetchStakingStats(request, dashboardApiUrl).data val earnings = response.stakingApies.associatedById() diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/nominationPools/pool/KnownNovaPools.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/nominationPools/pool/KnownNovaPools.kt index 9c6074783c..bc89c47569 100644 --- a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/nominationPools/pool/KnownNovaPools.kt +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/nominationPools/pool/KnownNovaPools.kt @@ -17,7 +17,8 @@ class FixedKnownNovaPools : KnownNovaPools { override val novaPoolIds: Set> = setOf( key(Chain.Geneses.POLKADOT, 54), key(Chain.Geneses.KUSAMA, 160), - key(Chain.Geneses.ALEPH_ZERO, 74) + key(Chain.Geneses.ALEPH_ZERO, 74), + key(Chain.Geneses.VARA, 65) ) private fun key(chainId: ChainId, poolId: Int) = chainId to PoolId(poolId) diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/repository/StakingGlobalConfigRepository.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/repository/StakingGlobalConfigRepository.kt new file mode 100644 index 0000000000..65192821f3 --- /dev/null +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/repository/StakingGlobalConfigRepository.kt @@ -0,0 +1,36 @@ +package io.novafoundation.nova.feature_staking_impl.data.repository + +import io.novafoundation.nova.feature_staking_impl.data.config.StakingGlobalConfig +import io.novafoundation.nova.feature_staking_impl.data.config.api.StakingGlobalConfigApi +import io.novafoundation.nova.feature_staking_impl.data.config.api.response.StakingGlobalConfigRemote +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock + +interface StakingGlobalConfigRepository { + + suspend fun getStakingGlobalConfig(): StakingGlobalConfig +} + +class RealStakingGlobalConfigRepository( + private val api: StakingGlobalConfigApi +) : StakingGlobalConfigRepository { + + private val mutex = Mutex() + private var cache: StakingGlobalConfig? = null + + override suspend fun getStakingGlobalConfig(): StakingGlobalConfig { + return mutex.withLock { + if (cache == null) { + cache = api.getStakingGlobalConfig().toDomain() + } + + cache!! + } + } + + private fun StakingGlobalConfigRemote.toDomain(): StakingGlobalConfig { + return StakingGlobalConfig( + multiStakingApiUrl = multiStakingApiUrl + ) + } +} diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/repository/VaraRepository.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/repository/VaraRepository.kt new file mode 100644 index 0000000000..68698a3746 --- /dev/null +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/repository/VaraRepository.kt @@ -0,0 +1,38 @@ +package io.novafoundation.nova.feature_staking_impl.data.repository + +import io.novafoundation.nova.common.data.network.runtime.binding.asPerQuintill +import io.novafoundation.nova.common.utils.Perbill +import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry +import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId +import io.novafoundation.nova.runtime.multiNetwork.getSocket +import io.novasama.substrate_sdk_android.wsrpc.SocketService +import io.novasama.substrate_sdk_android.wsrpc.executeAsync +import io.novasama.substrate_sdk_android.wsrpc.mappers.nonNull +import io.novasama.substrate_sdk_android.wsrpc.mappers.pojo +import io.novasama.substrate_sdk_android.wsrpc.request.runtime.RuntimeRequest +import java.math.BigInteger + +interface VaraRepository { + + suspend fun getVaraInflation(chainId: ChainId): Perbill +} + +class RealVaraRepository( + private val chainRegistry: ChainRegistry +) : VaraRepository { + + override suspend fun getVaraInflation(chainId: ChainId): Perbill { + return chainRegistry.getSocket(chainId).inflationInfo().inflation.asPerQuintill() + } + + private suspend fun SocketService.inflationInfo(): InflationInfo { + return executeAsync(InflationInfoRequest(), mapper = pojo().nonNull()) + } + + private class InflationInfoRequest : RuntimeRequest( + method = "stakingRewards_inflationInfo", + params = emptyList() + ) + + private class InflationInfo(val inflation: BigInteger) +} diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/validators/KnownNovaValidators.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/validators/KnownNovaValidators.kt index 7ae221d28d..b5ad74964e 100644 --- a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/validators/KnownNovaValidators.kt +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/validators/KnownNovaValidators.kt @@ -1,41 +1,61 @@ package io.novafoundation.nova.feature_staking_impl.data.validators -import io.novafoundation.nova.common.utils.toHexAccountId -import io.novafoundation.nova.runtime.ext.Geneses +import io.novafoundation.nova.common.utils.filterNotNull +import io.novafoundation.nova.runtime.ext.accountIdOf +import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId +import io.novafoundation.nova.runtime.multiNetwork.chainsById +import io.novasama.substrate_sdk_android.extensions.toHexString +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock interface KnownNovaValidators { - fun getValidatorIds(chainId: ChainId): Set + suspend fun getValidatorIds(chainId: ChainId): List } -class FixedKnownNovaValidators : KnownNovaValidators { +class RemoteKnownNovaValidators( + private val validatorsApi: NovaValidatorsApi, + private val chainRegistry: ChainRegistry, +) : KnownNovaValidators { - private val novaValidators by lazy { - val sharedAccounts = sharedValidatorsAccountIdsHex() + private var validatorsByNetwork: Map>? = null + private val validatorsMutex = Mutex() - mapOf( - Chain.Geneses.POLKADOT to sharedAccounts, - Chain.Geneses.KUSAMA to kusamaValidators(), - Chain.Geneses.ALEPH_ZERO to sharedAccounts - ) + override suspend fun getValidatorIds(chainId: ChainId): List { + return getValidators()[chainId].orEmpty() } - override fun getValidatorIds(chainId: ChainId): Set { - return novaValidators[chainId].orEmpty() + private suspend fun getValidators(): Map> { + return validatorsMutex.withLock { + if (validatorsByNetwork == null) { + validatorsByNetwork = fetchValidators() + } + + requireNotNull(validatorsByNetwork) + } + } + + private suspend fun fetchValidators(): Map> { + return runCatching { + val chainsById = chainRegistry.chainsById() + + validatorsApi.getValidators().mapValues { (chainId, addresses) -> + chainsById[chainId]?.let { chain -> + addresses.convertAddressesToAccountIds(chain) + } + }.filterNotNull() + }.getOrDefault(emptyMap()) } - private fun sharedValidatorsAccountIdsHex(): Set { - return setOf( - "127zarPDhVzmCXVQ7Kfr1yyaa9wsMuJ74GJW9Q7ezHfQEgh6".toHexAccountId() - ) + private fun List.convertAddressesToAccountIds(chain: Chain): List { + return mapNotNull { + chain.tryConvertAddressToAccountIdHex(it) + } } - private fun kusamaValidators(): Set { - return setOf( - "DhK6qU2U5kDWeJKvPRtmnWRs8ETUGZ9S9QmNmQFuzrNoKm4".toHexAccountId(), - "EtETk1FbrDg7FoAfkREuXT7xHxCjbEf28sBvWf6zfB5wFyV".toHexAccountId() - ) + private fun Chain.tryConvertAddressToAccountIdHex(address: String): String? { + return runCatching { accountIdOf(address).toHexString() }.getOrNull() } } diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/validators/NovaValidatorsApi.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/validators/NovaValidatorsApi.kt new file mode 100644 index 0000000000..4e17fa9245 --- /dev/null +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/data/validators/NovaValidatorsApi.kt @@ -0,0 +1,9 @@ +package io.novafoundation.nova.feature_staking_impl.data.validators + +import retrofit2.http.GET + +interface NovaValidatorsApi { + + @GET("https://raw.githubusercontent.com/novasamatech/nova-utils/master/staking/nova_validators.json") + suspend fun getValidators(): Map> +} diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/di/StakingFeatureModule.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/di/StakingFeatureModule.kt index 00330dc08f..207bf08991 100644 --- a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/di/StakingFeatureModule.kt +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/di/StakingFeatureModule.kt @@ -27,32 +27,35 @@ import io.novafoundation.nova.feature_proxy_api.data.repository.GetProxyReposito import io.novafoundation.nova.feature_proxy_api.data.repository.ProxyConstantsRepository import io.novafoundation.nova.feature_staking_api.data.network.blockhain.updaters.PooledBalanceUpdaterFactory import io.novafoundation.nova.feature_staking_api.data.nominationPools.pool.PoolAccountDerivation -import io.novafoundation.nova.feature_staking_impl.domain.staking.delegation.proxy.AddStakingProxyInteractor import io.novafoundation.nova.feature_staking_api.domain.api.StakingRepository import io.novafoundation.nova.feature_staking_api.presentation.nominationPools.display.PoolDisplayUseCase import io.novafoundation.nova.feature_staking_impl.data.StakingSharedState +import io.novafoundation.nova.feature_staking_impl.data.config.api.StakingGlobalConfigApi import io.novafoundation.nova.feature_staking_impl.data.dashboard.repository.StakingDashboardRepository import io.novafoundation.nova.feature_staking_impl.data.network.subquery.StakingApi import io.novafoundation.nova.feature_staking_impl.data.network.subquery.SubQueryValidatorSetFetcher import io.novafoundation.nova.feature_staking_impl.data.nominationPools.network.blockhain.updater.RealPooledBalanceUpdaterFactory import io.novafoundation.nova.feature_staking_impl.data.nominationPools.repository.NominationPoolStateRepository import io.novafoundation.nova.feature_staking_impl.data.parachainStaking.RoundDurationEstimator -import io.novafoundation.nova.feature_staking_impl.domain.staking.delegation.proxy.RealAddStakingProxyInteractor import io.novafoundation.nova.feature_staking_impl.data.repository.BagListRepository import io.novafoundation.nova.feature_staking_impl.data.repository.LocalBagListRepository import io.novafoundation.nova.feature_staking_impl.data.repository.ParasRepository import io.novafoundation.nova.feature_staking_impl.data.repository.PayoutRepository import io.novafoundation.nova.feature_staking_impl.data.repository.RealParasRepository import io.novafoundation.nova.feature_staking_impl.data.repository.RealSessionRepository +import io.novafoundation.nova.feature_staking_impl.data.repository.RealStakingGlobalConfigRepository import io.novafoundation.nova.feature_staking_impl.data.repository.RealStakingPeriodRepository import io.novafoundation.nova.feature_staking_impl.data.repository.RealStakingRewardsRepository import io.novafoundation.nova.feature_staking_impl.data.repository.RealStakingVersioningRepository +import io.novafoundation.nova.feature_staking_impl.data.repository.RealVaraRepository import io.novafoundation.nova.feature_staking_impl.data.repository.SessionRepository import io.novafoundation.nova.feature_staking_impl.data.repository.StakingConstantsRepository +import io.novafoundation.nova.feature_staking_impl.data.repository.StakingGlobalConfigRepository import io.novafoundation.nova.feature_staking_impl.data.repository.StakingPeriodRepository import io.novafoundation.nova.feature_staking_impl.data.repository.StakingRepositoryImpl import io.novafoundation.nova.feature_staking_impl.data.repository.StakingRewardsRepository import io.novafoundation.nova.feature_staking_impl.data.repository.StakingVersioningRepository +import io.novafoundation.nova.feature_staking_impl.data.repository.VaraRepository import io.novafoundation.nova.feature_staking_impl.data.repository.consensus.AuraSession import io.novafoundation.nova.feature_staking_impl.data.repository.consensus.BabeSession import io.novafoundation.nova.feature_staking_impl.data.repository.consensus.ElectionsSessionRegistry @@ -65,8 +68,9 @@ import io.novafoundation.nova.feature_staking_impl.data.repository.datasource.re import io.novafoundation.nova.feature_staking_impl.data.repository.datasource.reward.PoolStakingRewardsDataSource import io.novafoundation.nova.feature_staking_impl.data.repository.datasource.reward.RealStakingRewardsDataSourceRegistry import io.novafoundation.nova.feature_staking_impl.data.repository.datasource.reward.StakingRewardsDataSourceRegistry -import io.novafoundation.nova.feature_staking_impl.data.validators.FixedKnownNovaValidators import io.novafoundation.nova.feature_staking_impl.data.validators.KnownNovaValidators +import io.novafoundation.nova.feature_staking_impl.data.validators.NovaValidatorsApi +import io.novafoundation.nova.feature_staking_impl.data.validators.RemoteKnownNovaValidators import io.novafoundation.nova.feature_staking_impl.di.staking.DefaultBulkRetriever import io.novafoundation.nova.feature_staking_impl.di.staking.PayoutsBulkRetriever import io.novafoundation.nova.feature_staking_impl.domain.StakingInteractor @@ -83,6 +87,8 @@ import io.novafoundation.nova.feature_staking_impl.domain.rewards.RewardCalculat import io.novafoundation.nova.feature_staking_impl.domain.setup.ChangeValidatorsInteractor import io.novafoundation.nova.feature_staking_impl.domain.staking.bond.BondMoreInteractor import io.novafoundation.nova.feature_staking_impl.domain.staking.delegation.controller.ControllerInteractor +import io.novafoundation.nova.feature_staking_impl.domain.staking.delegation.proxy.AddStakingProxyInteractor +import io.novafoundation.nova.feature_staking_impl.domain.staking.delegation.proxy.RealAddStakingProxyInteractor import io.novafoundation.nova.feature_staking_impl.domain.staking.delegation.proxy.list.RealStakingProxyListInteractor import io.novafoundation.nova.feature_staking_impl.domain.staking.delegation.proxy.list.StakingProxyListInteractor import io.novafoundation.nova.feature_staking_impl.domain.staking.delegation.proxy.remove.RealRemoveStakingProxyInteractor @@ -294,6 +300,10 @@ class StakingFeatureModule { totalIssuanceRepository ) + @Provides + @FeatureScope + fun provideVaraRepository(chainRegistry: ChainRegistry): VaraRepository = RealVaraRepository(chainRegistry) + @Provides @FeatureScope fun provideRewardCalculatorFactory( @@ -301,11 +311,27 @@ class StakingFeatureModule { totalIssuanceRepository: TotalIssuanceRepository, stakingSharedComputation: dagger.Lazy, parasRepository: ParasRepository, - ) = RewardCalculatorFactory(repository, totalIssuanceRepository, stakingSharedComputation, parasRepository) + varaRepository: VaraRepository + ) = RewardCalculatorFactory( + stakingRepository = repository, + totalIssuanceRepository = totalIssuanceRepository, + shareStakingSharedComputation = stakingSharedComputation, + parasRepository = parasRepository, + varaRepository = varaRepository + ) + + @Provides + @FeatureScope + fun provideNovaValidatorsApi(apiCreator: NetworkApiCreator): NovaValidatorsApi { + return apiCreator.create(NovaValidatorsApi::class.java) + } @Provides @FeatureScope - fun provideKnownNovaValidators(): KnownNovaValidators = FixedKnownNovaValidators() + fun provideKnownNovaValidators( + novaValidatorsApi: NovaValidatorsApi, + chainRegistry: ChainRegistry + ): KnownNovaValidators = RemoteKnownNovaValidators(novaValidatorsApi, chainRegistry) @Provides @FeatureScope @@ -652,4 +678,18 @@ class StakingFeatureModule { extrinsicService, proxySyncService ) + + @Provides + @FeatureScope + fun provideStakingGlobalConfigApi(apiCreator: NetworkApiCreator): StakingGlobalConfigApi { + return apiCreator.create(StakingGlobalConfigApi::class.java) + } + + @Provides + @FeatureScope + fun provideStakingGlobalConfigRepository( + api: StakingGlobalConfigApi + ): StakingGlobalConfigRepository { + return RealStakingGlobalConfigRepository(api) + } } diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/di/staking/dashboard/StakingDashboardModule.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/di/staking/dashboard/StakingDashboardModule.kt index 957602df23..5a64f0bc7b 100644 --- a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/di/staking/dashboard/StakingDashboardModule.kt +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/di/staking/dashboard/StakingDashboardModule.kt @@ -9,8 +9,8 @@ import io.novafoundation.nova.core_db.dao.StakingDashboardDao import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository import io.novafoundation.nova.feature_dapp_api.data.repository.DAppMetadataRepository import io.novafoundation.nova.feature_staking_api.data.dashboard.StakingDashboardUpdateSystem +import io.novafoundation.nova.feature_staking_api.data.nominationPools.pool.PoolAccountDerivation import io.novafoundation.nova.feature_staking_api.domain.dashboard.StakingDashboardInteractor -import io.novafoundation.nova.feature_staking_impl.BuildConfig import io.novafoundation.nova.feature_staking_impl.data.dashboard.cache.RealStakingDashboardCache import io.novafoundation.nova.feature_staking_impl.data.dashboard.cache.StakingDashboardCache import io.novafoundation.nova.feature_staking_impl.data.dashboard.network.stats.RealStakingStatsDataSource @@ -22,8 +22,8 @@ import io.novafoundation.nova.feature_staking_impl.data.dashboard.repository.Rea import io.novafoundation.nova.feature_staking_impl.data.dashboard.repository.RealTotalStakeChainComparatorProvider import io.novafoundation.nova.feature_staking_impl.data.dashboard.repository.StakingDashboardRepository import io.novafoundation.nova.feature_staking_impl.data.dashboard.repository.TotalStakeChainComparatorProvider -import io.novafoundation.nova.feature_staking_api.data.nominationPools.pool.PoolAccountDerivation import io.novafoundation.nova.feature_staking_impl.data.nominationPools.repository.NominationPoolStateRepository +import io.novafoundation.nova.feature_staking_impl.data.repository.StakingGlobalConfigRepository import io.novafoundation.nova.feature_staking_impl.domain.dashboard.RealStakingDashboardInteractor import io.novafoundation.nova.feature_wallet_api.domain.interfaces.WalletRepository import io.novafoundation.nova.runtime.di.REMOTE_STORAGE_SOURCE @@ -47,10 +47,13 @@ class StakingDashboardModule { @Provides @FeatureScope - fun provideStakingStatsDataSource(api: StakingStatsApi): StakingStatsDataSource { + fun provideStakingStatsDataSource( + api: StakingStatsApi, + globalConfigRepository: StakingGlobalConfigRepository + ): StakingStatsDataSource { return RealStakingStatsDataSource( api = api, - dashboardApiUrl = BuildConfig.DASHBOARD_SUBQUERY_URL + stakingGlobalConfigRepository = globalConfigRepository ) } diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/di/staking/parachain/ParachainStakingModule.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/di/staking/parachain/ParachainStakingModule.kt index ec9645735e..77eab9ede3 100644 --- a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/di/staking/parachain/ParachainStakingModule.kt +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/di/staking/parachain/ParachainStakingModule.kt @@ -9,8 +9,6 @@ import io.novafoundation.nova.common.resources.ResourceManager import io.novafoundation.nova.feature_account_api.data.repository.OnChainIdentityRepository import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository import io.novafoundation.nova.feature_staking_impl.data.StakingSharedState -import io.novafoundation.nova.feature_staking_impl.data.collators.FixedKnownNovaCollators -import io.novafoundation.nova.feature_staking_impl.data.collators.KnownNovaCollators import io.novafoundation.nova.feature_staking_impl.data.parachainStaking.RealRoundDurationEstimator import io.novafoundation.nova.feature_staking_impl.data.parachainStaking.RoundDurationEstimator import io.novafoundation.nova.feature_staking_impl.data.parachainStaking.repository.CandidatesRepository @@ -26,6 +24,7 @@ import io.novafoundation.nova.feature_staking_impl.data.parachainStaking.reposit import io.novafoundation.nova.feature_staking_impl.data.parachainStaking.turing.repository.TuringStakingRewardsRepository import io.novafoundation.nova.feature_staking_impl.data.repository.StakingPeriodRepository import io.novafoundation.nova.feature_staking_impl.data.repository.StakingRewardsRepository +import io.novafoundation.nova.feature_staking_impl.data.validators.KnownNovaValidators import io.novafoundation.nova.feature_staking_impl.di.staking.parachain.start.StartParachainStakingFlowModule import io.novafoundation.nova.feature_staking_impl.di.staking.parachain.turing.TuringStakingModule import io.novafoundation.nova.feature_staking_impl.di.staking.parachain.unbond.ParachainStakingUnbondModule @@ -145,16 +144,12 @@ class ParachainStakingModule { roundDurationEstimator: RoundDurationEstimator ) = ParachainStakingHintsUseCase(stakingSharedState, resourceManager, roundDurationEstimator) - @Provides - @FeatureScope - fun provideKnownNovaCollators(): KnownNovaCollators = FixedKnownNovaCollators() - @Provides @FeatureScope fun provideCollatorRecommendatorFactory( collatorProvider: CollatorProvider, computationalCache: ComputationalCache, - knownNovaCollators: KnownNovaCollators + knownNovaCollators: KnownNovaValidators ) = CollatorRecommendatorFactory(collatorProvider, computationalCache, knownNovaCollators) @Provides diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/parachainStaking/common/recommendations/CollatorRecommendator.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/parachainStaking/common/recommendations/CollatorRecommendator.kt index eba508df93..859595fea5 100644 --- a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/parachainStaking/common/recommendations/CollatorRecommendator.kt +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/parachainStaking/common/recommendations/CollatorRecommendator.kt @@ -4,7 +4,7 @@ import io.novafoundation.nova.common.data.memory.ComputationalCache import io.novafoundation.nova.common.utils.indexOfOrNull import io.novafoundation.nova.feature_staking_impl.data.StakingOption import io.novafoundation.nova.feature_staking_impl.data.chain -import io.novafoundation.nova.feature_staking_impl.data.collators.KnownNovaCollators +import io.novafoundation.nova.feature_staking_impl.data.validators.KnownNovaValidators import io.novafoundation.nova.feature_staking_impl.domain.parachainStaking.common.CollatorProvider import io.novafoundation.nova.feature_staking_impl.domain.parachainStaking.common.CollatorProvider.CollatorSource import io.novafoundation.nova.feature_staking_impl.domain.parachainStaking.common.model.Collator @@ -29,13 +29,13 @@ private const val COLLATORS_CACHE = "COLLATORS_CACHE" class CollatorRecommendatorFactory( private val collatorProvider: CollatorProvider, private val computationalCache: ComputationalCache, - private val knownNovaCollators: KnownNovaCollators + private val knownNovaValidators: KnownNovaValidators ) { suspend fun create(stakingOption: StakingOption, scope: CoroutineScope) = computationalCache.useCache(COLLATORS_CACHE, scope) { val collators = collatorProvider.getCollators(stakingOption, CollatorSource.Elected) - val knownNovaCollators = knownNovaCollators.getCollatorIds(stakingOption.chain.id) + val knownNovaCollators = knownNovaValidators.getValidatorIds(stakingOption.chain.id) CollatorRecommendator(collators, knownNovaCollators) } diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/recommendations/ValidatorRecommender.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/recommendations/ValidatorRecommender.kt index 1fdc9ad598..f019461729 100644 --- a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/recommendations/ValidatorRecommender.kt +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/recommendations/ValidatorRecommender.kt @@ -1,18 +1,15 @@ package io.novafoundation.nova.feature_staking_impl.domain.recommendations import io.novafoundation.nova.common.utils.applyFilters -import io.novafoundation.nova.common.utils.ceil import io.novafoundation.nova.feature_staking_api.domain.model.Validator import io.novafoundation.nova.feature_staking_impl.domain.recommendations.settings.RecommendationSettings import io.novafoundation.nova.feature_staking_impl.domain.recommendations.settings.RecommendationSorting import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -private const val MAX_NOVA_VALIDATORS_FRACTION = 0.2 - class ValidatorRecommender( val availableValidators: List, - private val novaValidatorIds: Set, + private val novaValidatorIds: List, ) { suspend fun recommendations(settings: RecommendationSettings) = withContext(Dispatchers.Default) { @@ -34,13 +31,10 @@ class ValidatorRecommender( if (isEmpty()) return emptyList() val (novaValidators, others) = partition { it.accountIdHex in novaValidatorIds } - val maxNovaValidators = maxNovaValidators(limit) - val cappedNovaValidators = novaValidators.take(maxNovaValidators) + val cappedNovaValidators = novaValidators.take(limit) val cappedOthers = others.take(limit - cappedNovaValidators.size) return (cappedNovaValidators + cappedOthers).sortedWith(sorting) } - - private fun maxNovaValidators(limit: Int): Int = (limit * MAX_NOVA_VALIDATORS_FRACTION).ceil().toInt() } diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/recommendations/settings/postprocessors/RemoveClusteringPostprocessor.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/recommendations/settings/postprocessors/RemoveClusteringPostprocessor.kt index b617f12766..8e43859dfe 100644 --- a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/recommendations/settings/postprocessors/RemoveClusteringPostprocessor.kt +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/recommendations/settings/postprocessors/RemoveClusteringPostprocessor.kt @@ -17,6 +17,8 @@ object RemoveClusteringPostprocessor : RecommendationPostProcessor { val clusterCounter = mutableMapOf() return original.filter { validator -> + if (validator.shouldSkipClusteringFiltering()) return@filter true + validator.clusterIdentity()?.let { val currentCounter = clusterCounter.getOrDefault(it, 0) @@ -38,4 +40,8 @@ object RemoveClusteringPostprocessor : RecommendationPostProcessor { else -> null } } + + private fun Validator.shouldSkipClusteringFiltering(): Boolean { + return isNovaValidator + } } diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/rewards/RewardCalculatorFactory.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/rewards/RewardCalculatorFactory.kt index 47f4327363..e4cbeb12bf 100644 --- a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/rewards/RewardCalculatorFactory.kt +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/rewards/RewardCalculatorFactory.kt @@ -1,5 +1,7 @@ package io.novafoundation.nova.feature_staking_impl.domain.rewards +import android.util.Log +import io.novafoundation.nova.common.utils.LOG_TAG import io.novafoundation.nova.feature_account_api.data.model.AccountIdMap import io.novafoundation.nova.feature_staking_api.domain.api.StakingRepository import io.novafoundation.nova.feature_staking_api.domain.model.Exposure @@ -7,6 +9,7 @@ import io.novafoundation.nova.feature_staking_api.domain.model.ValidatorPrefs import io.novafoundation.nova.feature_staking_impl.data.StakingOption import io.novafoundation.nova.feature_staking_impl.data.chain import io.novafoundation.nova.feature_staking_impl.data.repository.ParasRepository +import io.novafoundation.nova.feature_staking_impl.data.repository.VaraRepository import io.novafoundation.nova.feature_staking_impl.data.stakingType import io.novafoundation.nova.feature_staking_impl.data.unwrapNominationPools import io.novafoundation.nova.feature_staking_impl.domain.common.StakingSharedComputation @@ -33,6 +36,7 @@ class RewardCalculatorFactory( private val totalIssuanceRepository: TotalIssuanceRepository, private val shareStakingSharedComputation: dagger.Lazy, private val parasRepository: ParasRepository, + private val varaRepository: VaraRepository, ) { suspend fun create( @@ -68,20 +72,51 @@ class RewardCalculatorFactory( private suspend fun StakingOption.createRewardCalculator(validators: List, totalIssuance: BigInteger): RewardCalculator { return when (unwrapNominationPools().stakingType) { RELAYCHAIN, RELAYCHAIN_AURA -> { + val custom = customRelayChainCalculator(validators, totalIssuance) + if (custom != null) return custom + val activePublicParachains = parasRepository.activePublicParachains(assetWithChain.chain.id) val inflationConfig = InflationConfig.create(chain.id, activePublicParachains) RewardCurveInflationRewardCalculator(validators, totalIssuance, inflationConfig) } + ALEPH_ZERO -> AlephZeroRewardCalculator(validators, chainAsset = assetWithChain.asset) NOMINATION_POOLS, UNSUPPORTED, PARACHAIN, TURING -> throw IllegalStateException("Unknown staking type in RelaychainRewardFactory") } } + private suspend fun StakingOption.customRelayChainCalculator( + validators: List, + totalIssuance: BigInteger + ): RewardCalculator? { + return when (chain.id) { + Chain.Geneses.VARA -> Vara(chain.id, validators, totalIssuance) + else -> null + } + } + private fun InflationConfig.Companion.create(chainId: ChainId, activePublicParachains: Int?): InflationConfig { return when (chainId) { Chain.Geneses.POLKADOT -> Polkadot(activePublicParachains) + Chain.Geneses.AVAIL_TURING_TESTNET, Chain.Geneses.AVAIL -> Avail() else -> Default(activePublicParachains) } } + + private suspend fun Vara( + chainId: ChainId, + validators: List, + totalIssuance: BigInteger + ): RewardCalculator? { + return runCatching { + val inflationInfo = varaRepository.getVaraInflation(chainId) + + VaraRewardCalculator(validators, totalIssuance, inflationInfo) + } + .onFailure { + Log.e(LOG_TAG, "Failed to create Vara reward calculator, fallbacking to default", it) + } + .getOrNull() + } } diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/rewards/RewardCurveInflationRewardCalculator.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/rewards/RewardCurveInflationRewardCalculator.kt index f0636f8959..c195868b27 100644 --- a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/rewards/RewardCurveInflationRewardCalculator.kt +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/rewards/RewardCurveInflationRewardCalculator.kt @@ -48,6 +48,15 @@ class InflationConfig( ) } ) + + // Source: https://github.com/availproject/avail/blob/main/runtime/src/constants.rs#L223 + fun Avail() = InflationConfig( + falloff = 0.05, + maxInflation = 0.05, + minInflation = 0.01, + stakeTarget = 0.50, + parachainAdjust = null + ) } } diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/rewards/VaraRewardCalculator.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/rewards/VaraRewardCalculator.kt new file mode 100644 index 0000000000..bf1cbbfe19 --- /dev/null +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/rewards/VaraRewardCalculator.kt @@ -0,0 +1,15 @@ +package io.novafoundation.nova.feature_staking_impl.domain.rewards + +import io.novafoundation.nova.common.utils.Perbill +import java.math.BigInteger + +class VaraRewardCalculator( + validators: List, + totalIssuance: BigInteger, + private val inflation: Perbill +) : InflationBasedRewardCalculator(validators, totalIssuance) { + + override fun calculateYearlyInflation(stakedPortion: Double): Double { + return inflation.value + } +} diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/validators/ValidatorProvider.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/validators/ValidatorProvider.kt index 22ab8674f6..af5e116bbc 100644 --- a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/validators/ValidatorProvider.kt +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/domain/validators/ValidatorProvider.kt @@ -44,9 +44,10 @@ class ValidatorProvider( val chain = stakingOption.assetWithChain.chain val chainId = chain.id + val novaValidatorIds = knownNovaValidators.getValidatorIds(chainId).toSet() val electedValidatorExposures = stakingSharedComputation.electedExposuresInActiveEra(chainId, scope) - val requestedValidatorIds = sources.allValidatorIds(chainId, electedValidatorExposures) + val requestedValidatorIds = sources.allValidatorIds(chainId, electedValidatorExposures, novaValidatorIds) // we always need validator prefs for elected validators to construct reward calculator val validatorIdsToQueryPrefs = electedValidatorExposures.keys + requestedValidatorIds @@ -74,7 +75,8 @@ class ValidatorProvider( electedInfo = electedInfo, prefs = validatorPrefs[accountIdHex], identity = identities[accountIdHex], - address = chain.addressOf(accountIdHex.fromHex()) + address = chain.addressOf(accountIdHex.fromHex()), + isNovaValidator = accountIdHex in novaValidatorIds ) } } @@ -89,31 +91,36 @@ class ValidatorProvider( val slashes = stakingRepository.getSlashes(chainId, accountIdBridged) + val novaValidatorIds = knownNovaValidators.getValidatorIds(chainId).toSet() + return Validator( slashed = slashes.getOrDefault(accountId, false), accountIdHex = accountId, address = address, prefs = prefs, identity = identity, - electedInfo = null + electedInfo = null, + isNovaValidator = accountId in novaValidatorIds ) } private fun List.allValidatorIds( chainId: ChainId, - electedExposures: AccountIdMap + electedExposures: AccountIdMap, + novaValidatorIds: Set, ): Set { - return foldToSet { it.validatorIds(chainId, electedExposures) } + return foldToSet { it.validatorIds(chainId, electedExposures, novaValidatorIds) } } private fun ValidatorSource.validatorIds( chainId: ChainId, - electedExposures: AccountIdMap + electedExposures: AccountIdMap, + novaValidatorIds: Set, ): Set { return when (this) { is ValidatorSource.Custom -> validatorIds ValidatorSource.Elected -> electedExposures.keys - ValidatorSource.NovaValidators -> knownNovaValidators.getValidatorIds(chainId) + ValidatorSource.NovaValidators -> novaValidatorIds } } } diff --git a/runtime/build.gradle b/runtime/build.gradle index 76f0316325..a055d684dc 100644 --- a/runtime/build.gradle +++ b/runtime/build.gradle @@ -13,7 +13,7 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - buildConfigField "String", "CHAINS_URL", "\"https://raw.githubusercontent.com/novasamatech/nova-utils/master/chains/v18/chains_dev.json\"" + buildConfigField "String", "CHAINS_URL", "\"https://raw.githubusercontent.com/novasamatech/nova-utils/master/chains/v19/chains_dev.json\"" buildConfigField "String", "EVM_ASSETS_URL", "\"https://raw.githubusercontent.com/novasamatech/nova-utils/master/assets/evm/v2/assets_dev.json\"" buildConfigField "String", "TEST_CHAINS_URL", "\"https://raw.githubusercontent.com/novasamatech/nova-utils/master/tests/chains_for_testBalance.json\"" @@ -31,7 +31,7 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - buildConfigField "String", "CHAINS_URL", "\"https://raw.githubusercontent.com/novasamatech/nova-utils/master/chains/v18/chains.json\"" + buildConfigField "String", "CHAINS_URL", "\"https://raw.githubusercontent.com/novasamatech/nova-utils/master/chains/v19/chains.json\"" buildConfigField "String", "EVM_ASSETS_URL", "\"https://raw.githubusercontent.com/novasamatech/nova-utils/master/assets/evm/v2/assets.json\"" } } diff --git a/runtime/src/main/java/io/novafoundation/nova/runtime/ext/ChainExt.kt b/runtime/src/main/java/io/novafoundation/nova/runtime/ext/ChainExt.kt index a4c22d9eb0..351dbdb94e 100644 --- a/runtime/src/main/java/io/novafoundation/nova/runtime/ext/ChainExt.kt +++ b/runtime/src/main/java/io/novafoundation/nova/runtime/ext/ChainExt.kt @@ -29,6 +29,7 @@ import io.novasama.substrate_sdk_android.extensions.asEthereumAddress import io.novasama.substrate_sdk_android.extensions.asEthereumPublicKey import io.novasama.substrate_sdk_android.extensions.fromHex import io.novasama.substrate_sdk_android.extensions.isValid +import io.novasama.substrate_sdk_android.extensions.requireHexPrefix import io.novasama.substrate_sdk_android.extensions.toAccountId import io.novasama.substrate_sdk_android.extensions.toAddress import io.novasama.substrate_sdk_android.extensions.toHexString @@ -39,7 +40,6 @@ import io.novasama.substrate_sdk_android.runtime.definitions.types.toHexUntyped import io.novasama.substrate_sdk_android.ss58.SS58Encoder.addressPrefix import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAddress -import io.novasama.substrate_sdk_android.extensions.requireHexPrefix val Chain.typesUsage: TypesUsage get() = when { @@ -322,6 +322,11 @@ object ChainGeneses { const val WESTMINT = "67f9723393ef76214df0118c34bbbd3dbebc8ed46a10973a8c969d48fe7598c9" const val HYDRA_DX = "afdc188f45c71dacbaa0b62e16a91f726c7b8699a9748cdf715459de6b7f366d" + + const val AVAIL_TURING_TESTNET = "d3d2f3a3495dc597434a99d7d449ebad6616db45e4e4f178f31cc6fa14378b70" + const val AVAIL = "128ea318539862c0a06b745981300d527c1041c6f3388a8c49565559e3ea3d10" + + const val VARA = "fe1b4c55fd4d668101126434206571a7838a8b6b93a6d1b95d607e78e6c53763" } object ChainIds { diff --git a/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/CustomSignedExtensions.kt b/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/CustomSignedExtensions.kt index 93d8f7e1a5..6e5560af1b 100644 --- a/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/CustomSignedExtensions.kt +++ b/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/CustomSignedExtensions.kt @@ -19,6 +19,17 @@ object CustomSignedExtensions { signedExtra = assetTxPaymentPayload(assetId = null) ) } + }, + + // Signed extension for Avail related to Data Availability Transactions. + // We set it to 0 which is the default value provided by Avail team + CHECK_APP_ID("CheckAppId") { + + override fun createPayload(): SignedExtensionValue { + return SignedExtensionValue( + signedExtra = BigInteger.ZERO + ) + } }; abstract fun createPayload(): SignedExtensionValue