diff --git a/.github/workflows/manual_firebase_distribution.yml b/.github/workflows/manual_firebase_distribution.yml
index e53734f4b9..0da2f31778 100644
--- a/.github/workflows/manual_firebase_distribution.yml
+++ b/.github/workflows/manual_firebase_distribution.yml
@@ -36,7 +36,7 @@ jobs:
uses: ./.github/workflows/upload-to-firebase
with:
appId: ${{ secrets.ANDROID_DEVELOP_FIREBASE_APP_ID }}
- firebase-token: ${{ secrets.ANDROID_DEV_FIREBASE_TOKEN }}
+ firebase-token: ${{ secrets.CREDENTIAL_FILE_CONTENT }}
releaseNotes: ${{ github.event.head_commit.message }}
test-groups: ${{ github.event.inputs.firebase_group }}
upload-file: app/develop/app-develop.apk
diff --git a/.github/workflows/push_develop.yml b/.github/workflows/push_develop.yml
index dd6bdaf41d..dc1e55f3ed 100644
--- a/.github/workflows/push_develop.yml
+++ b/.github/workflows/push_develop.yml
@@ -29,7 +29,7 @@ jobs:
uses: ./.github/workflows/upload-to-firebase
with:
appId: ${{ secrets.ANDROID_DEVELOP_FIREBASE_APP_ID }}
- firebase-token: ${{ secrets.ANDROID_DEV_FIREBASE_TOKEN }}
+ firebase-token: ${{ secrets.CREDENTIAL_FILE_CONTENT }}
releaseNotes: ${{ github.event.head_commit.message }}
test-groups: dev-team
upload-file: app/develop/app-develop.apk
diff --git a/.github/workflows/upload-to-firebase/action.yml b/.github/workflows/upload-to-firebase/action.yml
index f8fd0b2ead..ef654256a7 100644
--- a/.github/workflows/upload-to-firebase/action.yml
+++ b/.github/workflows/upload-to-firebase/action.yml
@@ -24,10 +24,10 @@ runs:
- name: Upload artifact to Firebase App Distribution
id: upload
continue-on-error: true
- uses: wzieba/Firebase-Distribution-Github-Action@v1
+ uses: wzieba/Firebase-Distribution-Github-Action@v1.7.0
with:
appId: ${{ inputs.appId }}
- token: ${{ inputs.firebase-token }}
+ serviceCredentialsFileContent: ${{ inputs.firebase-token }}
releaseNotes: ${{ inputs.releaseNotes }}
groups: ${{ inputs.test-groups }}
file: ${{ inputs.upload-file }}
@@ -40,10 +40,10 @@ runs:
- name: Retry upload artifacts
if: steps.upload.outcome=='failure'
- uses: wzieba/Firebase-Distribution-Github-Action@v1
+ uses: wzieba/Firebase-Distribution-Github-Action@v1.7.0
with:
appId: ${{ inputs.appId }}
- token: ${{ inputs.firebase-token }}
+ serviceCredentialsFileContent: ${{ inputs.firebase-token }}
releaseNotes: ${{ inputs.releaseNotes }}
groups: ${{ inputs.test-groups }}
file: ${{ inputs.upload-file }}
diff --git a/app/src/androidTest/java/io/novafoundation/nova/SwapServiceIntegrationTest.kt b/app/src/androidTest/java/io/novafoundation/nova/SwapServiceIntegrationTest.kt
index 978370d7b8..df3c39ad02 100644
--- a/app/src/androidTest/java/io/novafoundation/nova/SwapServiceIntegrationTest.kt
+++ b/app/src/androidTest/java/io/novafoundation/nova/SwapServiceIntegrationTest.kt
@@ -4,6 +4,7 @@ import android.util.Log
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.common.utils.Percent
import io.novafoundation.nova.feature_swap_api.di.SwapFeatureApi
+import io.novafoundation.nova.feature_swap_api.domain.model.QuotePath
import io.novafoundation.nova.feature_swap_api.domain.model.SwapDirection
import io.novafoundation.nova.feature_swap_api.domain.model.SwapExecuteArgs
import io.novafoundation.nova.feature_swap_api.domain.model.SwapLimit
@@ -88,7 +89,8 @@ class SwapServiceIntegrationTest : BaseIntegrationTest() {
expectedAmountOut = Balance.ZERO
),
customFeeAsset = null,
- nativeAsset = arbitraryAssetUseCase.assetFlow(westmint.commissionAsset).first()
+ nativeAsset = arbitraryAssetUseCase.assetFlow(westmint.commissionAsset).first(),
+ path = QuotePath(emptyList())
)
val fee = swapService.estimateFee(swapArgs)
@@ -111,7 +113,8 @@ class SwapServiceIntegrationTest : BaseIntegrationTest() {
expectedAmountOut = Balance.ZERO
),
customFeeAsset = siri,
- nativeAsset = arbitraryAssetUseCase.assetFlow(westmint.commissionAsset).first()
+ nativeAsset = arbitraryAssetUseCase.assetFlow(westmint.commissionAsset).first(),
+ path = QuotePath(emptyList())
)
val fee = swapService.estimateFee(swapArgs)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 71de0f9b2c..639c1427de 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -19,6 +19,7 @@
+
@@ -76,6 +77,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/io/novafoundation/nova/app/root/presentation/RootViewModel.kt b/app/src/main/java/io/novafoundation/nova/app/root/presentation/RootViewModel.kt
index ba2c21d6a6..2f2827d2f1 100644
--- a/app/src/main/java/io/novafoundation/nova/app/root/presentation/RootViewModel.kt
+++ b/app/src/main/java/io/novafoundation/nova/app/root/presentation/RootViewModel.kt
@@ -15,7 +15,6 @@ import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.common.sequrity.SafeModeService
import io.novafoundation.nova.common.utils.coroutines.RootScope
import io.novafoundation.nova.common.utils.inBackground
-import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
import io.novafoundation.nova.common.utils.sequrity.BackgroundAccessObserver
import io.novafoundation.nova.core.updater.Updater
import io.novafoundation.nova.feature_crowdloan_api.domain.contributions.ContributionsInteractor
@@ -44,7 +43,6 @@ class RootViewModel(
private val walletConnectService: WalletConnectService,
private val walletConnectSessionsUseCase: WalletConnectSessionsUseCase,
private val deepLinkHandler: DeepLinkHandler,
- private val automaticInteractionGate: AutomaticInteractionGate,
private val rootScope: RootScope,
private val compoundRequestBusHandler: CompoundRequestBusHandler
) : BaseViewModel(), NetworkStateUi by networkStateMixin {
diff --git a/app/src/main/java/io/novafoundation/nova/app/root/presentation/deepLinks/RootDeepLinkHandler.kt b/app/src/main/java/io/novafoundation/nova/app/root/presentation/deepLinks/RootDeepLinkHandler.kt
index cfac9aad8e..cf7afb27e8 100644
--- a/app/src/main/java/io/novafoundation/nova/app/root/presentation/deepLinks/RootDeepLinkHandler.kt
+++ b/app/src/main/java/io/novafoundation/nova/app/root/presentation/deepLinks/RootDeepLinkHandler.kt
@@ -17,7 +17,11 @@ class RootDeepLinkHandler(
}
override suspend fun handleDeepLink(data: Uri) {
- nestedHandlers.find { it.matches(data) }
- ?.handleDeepLink(data)
+ runCatching {
+ nestedHandlers.find {
+ runCatching { it.matches(data) }.getOrDefault(false)
+ }
+ ?.handleDeepLink(data)
+ }
}
}
diff --git a/app/src/main/java/io/novafoundation/nova/app/root/presentation/deepLinks/handlers/ReferendumDeepLinkHandler.kt b/app/src/main/java/io/novafoundation/nova/app/root/presentation/deepLinks/handlers/ReferendumDeepLinkHandler.kt
index 869b02e206..c8664115e9 100644
--- a/app/src/main/java/io/novafoundation/nova/app/root/presentation/deepLinks/handlers/ReferendumDeepLinkHandler.kt
+++ b/app/src/main/java/io/novafoundation/nova/app/root/presentation/deepLinks/handlers/ReferendumDeepLinkHandler.kt
@@ -6,7 +6,6 @@ import io.novafoundation.nova.app.root.presentation.deepLinks.DeepLinkHandler
import io.novafoundation.nova.app.root.presentation.deepLinks.common.DeepLinkHandlingException.ReferendumHandlingException
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
import io.novafoundation.nova.common.utils.sequrity.awaitInteractionAllowed
-import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
import io.novafoundation.nova.feature_governance_api.data.MutableGovernanceState
import io.novafoundation.nova.feature_governance_impl.presentation.GovernanceRouter
import io.novafoundation.nova.feature_governance_impl.presentation.referenda.details.ReferendumDetailsPayload
@@ -14,8 +13,8 @@ import io.novafoundation.nova.runtime.ext.utilityAsset
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import io.novafoundation.nova.runtime.multiNetwork.getChainOrNull
-import java.math.BigInteger
import kotlinx.coroutines.flow.MutableSharedFlow
+import java.math.BigInteger
private const val GOV_DEEP_LINK_PREFIX = "/open/gov"
@@ -23,7 +22,6 @@ class ReferendumDeepLinkHandler(
private val governanceRouter: GovernanceRouter,
private val chainRegistry: ChainRegistry,
private val mutableGovernanceState: MutableGovernanceState,
- private val accountRepository: AccountRepository,
private val automaticInteractionGate: AutomaticInteractionGate
) : DeepLinkHandler {
@@ -57,7 +55,7 @@ class ReferendumDeepLinkHandler(
?.toBigIntegerOrNull()
}
- private suspend fun Uri.getGovernanceType(chain: Chain): Chain.Governance {
+ private fun Uri.getGovernanceType(chain: Chain): Chain.Governance {
val supportedGov = chain.governance
val govType = getQueryParameter("type")
?.toIntOrNull()
@@ -74,8 +72,4 @@ class ReferendumDeepLinkHandler(
else -> throw ReferendumHandlingException.GovernanceTypeIsNotSupported
}
}
-
- private fun Chain.Governance.takeIfContainedIn(list: List): Chain.Governance? {
- return list.firstOrNull { it == this }
- }
}
diff --git a/app/src/main/java/io/novafoundation/nova/app/root/presentation/deepLinks/handlers/walletConnect/WalletConnectPairDeeplinkHandler.kt b/app/src/main/java/io/novafoundation/nova/app/root/presentation/deepLinks/handlers/walletConnect/WalletConnectPairDeeplinkHandler.kt
new file mode 100644
index 0000000000..9c88b89550
--- /dev/null
+++ b/app/src/main/java/io/novafoundation/nova/app/root/presentation/deepLinks/handlers/walletConnect/WalletConnectPairDeeplinkHandler.kt
@@ -0,0 +1,31 @@
+package io.novafoundation.nova.app.root.presentation.deepLinks.handlers.walletConnect
+
+import android.net.Uri
+import io.novafoundation.nova.app.root.presentation.deepLinks.CallbackEvent
+import io.novafoundation.nova.app.root.presentation.deepLinks.DeepLinkHandler
+import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
+import io.novafoundation.nova.common.utils.sequrity.awaitInteractionAllowed
+import io.novafoundation.nova.feature_wallet_connect_api.presentation.WalletConnectService
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+
+class WalletConnectPairDeeplinkHandler(
+ private val walletConnectService: WalletConnectService,
+ private val automaticInteractionGate: AutomaticInteractionGate
+) : DeepLinkHandler {
+
+ override val callbackFlow: Flow = emptyFlow()
+
+ override suspend fun matches(data: Uri): Boolean {
+ val newLinkMatch = data.scheme == "novawallet" && data.host == "wc"
+ // Older version of wc send both pair and sign requests through `wc:` deeplink so we additionaly check for `symKey` which is only present in pairing url
+ val oldLinkMatch = data.scheme == "wc" && "symKey" in data.toString()
+
+ return newLinkMatch || oldLinkMatch
+ }
+
+ override suspend fun handleDeepLink(data: Uri) {
+ automaticInteractionGate.awaitInteractionAllowed()
+ walletConnectService.pair(data.toString())
+ }
+}
diff --git a/app/src/main/java/io/novafoundation/nova/app/root/presentation/di/DeepLinkModule.kt b/app/src/main/java/io/novafoundation/nova/app/root/presentation/di/DeepLinkModule.kt
index 00158d5005..d8fd8bb5a3 100644
--- a/app/src/main/java/io/novafoundation/nova/app/root/presentation/di/DeepLinkModule.kt
+++ b/app/src/main/java/io/novafoundation/nova/app/root/presentation/di/DeepLinkModule.kt
@@ -11,6 +11,7 @@ import io.novafoundation.nova.app.root.presentation.deepLinks.handlers.DAppDeepL
import io.novafoundation.nova.app.root.presentation.deepLinks.handlers.ImportMnemonicDeepLinkHandler
import io.novafoundation.nova.app.root.presentation.deepLinks.handlers.ReferendumDeepLinkHandler
import io.novafoundation.nova.app.root.presentation.deepLinks.handlers.StakingDashboardDeepLinkHandler
+import io.novafoundation.nova.app.root.presentation.deepLinks.handlers.walletConnect.WalletConnectPairDeeplinkHandler
import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
import io.novafoundation.nova.feature_account_api.domain.account.common.EncryptionDefaults
@@ -21,6 +22,7 @@ import io.novafoundation.nova.feature_dapp_api.data.repository.DAppMetadataRepos
import io.novafoundation.nova.feature_dapp_impl.DAppRouter
import io.novafoundation.nova.feature_governance_api.data.MutableGovernanceState
import io.novafoundation.nova.feature_governance_impl.presentation.GovernanceRouter
+import io.novafoundation.nova.feature_wallet_connect_api.presentation.WalletConnectService
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
@Module
@@ -71,14 +73,12 @@ class DeepLinkModule {
governanceRouter: GovernanceRouter,
chainRegistry: ChainRegistry,
mutableGovernanceState: MutableGovernanceState,
- accountRepository: AccountRepository,
automaticInteractionGate: AutomaticInteractionGate
): DeepLinkHandler {
return ReferendumDeepLinkHandler(
governanceRouter,
chainRegistry,
mutableGovernanceState,
- accountRepository,
automaticInteractionGate
)
}
@@ -92,6 +92,15 @@ class DeepLinkModule {
return BuyCallbackDeepLinkHandler(interactor, resourceManager)
}
+ @Provides
+ @IntoSet
+ fun provideWalletConnectPairDeepLinkHandler(
+ walletConnectService: WalletConnectService,
+ automaticInteractionGate: AutomaticInteractionGate
+ ): DeepLinkHandler {
+ return WalletConnectPairDeeplinkHandler(walletConnectService, automaticInteractionGate)
+ }
+
@Provides
fun provideRootDeepLinkHandler(
deepLinkHandlers: Set<@JvmSuppressWildcards DeepLinkHandler>
diff --git a/app/src/main/java/io/novafoundation/nova/app/root/presentation/di/RootActivityModule.kt b/app/src/main/java/io/novafoundation/nova/app/root/presentation/di/RootActivityModule.kt
index a86dc36655..15f78f2c64 100644
--- a/app/src/main/java/io/novafoundation/nova/app/root/presentation/di/RootActivityModule.kt
+++ b/app/src/main/java/io/novafoundation/nova/app/root/presentation/di/RootActivityModule.kt
@@ -16,7 +16,6 @@ import io.novafoundation.nova.common.di.viewmodel.ViewModelModule
import io.novafoundation.nova.common.mixin.api.NetworkStateMixin
import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.common.sequrity.SafeModeService
-import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
import io.novafoundation.nova.common.utils.coroutines.RootScope
import io.novafoundation.nova.common.utils.sequrity.BackgroundAccessObserver
import io.novafoundation.nova.feature_crowdloan_api.domain.contributions.ContributionsInteractor
@@ -52,7 +51,6 @@ class RootActivityModule {
walletConnectService: WalletConnectService,
walletConnectSessionsUseCase: WalletConnectSessionsUseCase,
deepLinkHandler: RootDeepLinkHandler,
- automaticInteractionGate: AutomaticInteractionGate,
rootScope: RootScope,
compoundRequestBusHandler: CompoundRequestBusHandler
): ViewModel {
@@ -70,7 +68,6 @@ class RootActivityModule {
walletConnectService,
walletConnectSessionsUseCase,
deepLinkHandler,
- automaticInteractionGate,
rootScope,
compoundRequestBusHandler
)
diff --git a/app/src/main/res/navigation/main_nav_graph.xml b/app/src/main/res/navigation/main_nav_graph.xml
index 41cab41e5b..140943c881 100644
--- a/app/src/main/res/navigation/main_nav_graph.xml
+++ b/app/src/main/res/navigation/main_nav_graph.xml
@@ -867,6 +867,7 @@
android:label="SelectWalletFragment" />
diff --git a/build.gradle b/build.gradle
index 01b7b74e35..a4f1278c1e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,8 +1,8 @@
buildscript {
ext {
// App version
- versionName = '7.8.0'
- versionCode = 115
+ versionName = '7.9.0'
+ versionCode = 117
applicationId = "io.novafoundation.nova"
releaseApplicationSuffix = "market"
@@ -51,7 +51,7 @@ buildscript {
web3jVersion = '4.9.5'
- fearlessLibVersion = '1.11.1'
+ fearlessLibVersion = '1.11.2'
gifVersion = '1.2.19'
@@ -83,8 +83,8 @@ buildscript {
markwonVersion = '4.6.2'
- walletConnectCoreVersion = "1.13.0"
- walletConnectWalletVersion = "1.6.0"
+ walletConnectCoreVersion = "1.27.2"
+ walletConnectWalletVersion = "1.20.2"
withoutTransitiveAndroidX = {
exclude group: "androidx.appcompat", module: "appcompat"
@@ -208,6 +208,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath 'com.google.gms:google-services:4.3.14'
+ classpath 'org.mozilla.rust-android-gradle:plugin:0.9.3'
classpath "com.google.firebase:firebase-appdistribution-gradle:$firebaseAppDistrVersion"
classpath "com.github.triplet.gradle:play-publisher:$playPublisherVersion"
}
diff --git a/common/src/main/java/io/novafoundation/nova/common/data/network/runtime/binding/AccountInfo.kt b/common/src/main/java/io/novafoundation/nova/common/data/network/runtime/binding/AccountInfo.kt
index cb5b0d9665..d1f610e775 100644
--- a/common/src/main/java/io/novafoundation/nova/common/data/network/runtime/binding/AccountInfo.kt
+++ b/common/src/main/java/io/novafoundation/nova/common/data/network/runtime/binding/AccountInfo.kt
@@ -26,6 +26,8 @@ open class AccountBalance(
}
}
+fun AccountBalance?.orEmpty(): AccountBalance = this ?: AccountBalance.empty()
+
class AccountData(
free: BigInteger,
reserved: BigInteger,
@@ -113,7 +115,13 @@ fun bindNonce(dynamicInstance: Any?): BigInteger {
fun bindAccountInfo(scale: String, runtime: RuntimeSnapshot): AccountInfo {
val type = runtime.metadata.system().storage("Account").returnType()
- val dynamicInstance = type.fromHexOrNull(runtime, scale).cast()
+ val dynamicInstance = type.fromHexOrNull(runtime, scale)
+
+ return bindAccountInfo(dynamicInstance)
+}
+
+fun bindAccountInfo(decoded: Any?): AccountInfo {
+ val dynamicInstance = decoded.cast()
return AccountInfo(
consumers = dynamicInstance.getTyped("consumers").orZero(),
@@ -122,3 +130,17 @@ fun bindAccountInfo(scale: String, runtime: RuntimeSnapshot): AccountInfo {
data = bindAccountData(dynamicInstance.getTyped("data"))
)
}
+
+fun bindOrmlAccountBalanceOrEmpty(decoded: Any?): AccountBalance {
+ return decoded?.let { bindOrmlAccountData(decoded) } ?: AccountBalance.empty()
+}
+
+fun bindOrmlAccountData(decoded: Any?): AccountBalance {
+ val dynamicInstance = decoded.cast()
+
+ return AccountBalance(
+ free = bindNumber(dynamicInstance["free"]),
+ reserved = bindNumber(dynamicInstance["reserved"]),
+ frozen = bindNumber(dynamicInstance["frozen"]),
+ )
+}
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 bc08b05c3a..3f8107fe68 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
@@ -7,15 +7,16 @@ import io.novafoundation.nova.common.utils.Perbill as PerbillTyped
typealias Perbill = BigDecimal
typealias FixedI64 = BigDecimal
-private const val FLOAT_MANTISSA_SIZE = 9
+const val PERBILL_MANTISSA_SIZE = 9
+const val PERMILL_MANTISSA_SIZE = 6
@HelperBinding
-fun bindPerbillNumber(value: BigInteger): Perbill {
- return value.toBigDecimal(scale = FLOAT_MANTISSA_SIZE)
+fun bindPerbillNumber(value: BigInteger, mantissa: Int = PERBILL_MANTISSA_SIZE): Perbill {
+ return value.toBigDecimal(scale = mantissa)
}
-fun bindPerbill(dynamic: Any?): Perbill {
- return bindPerbillNumber(dynamic.cast())
+fun bindPerbill(dynamic: Any?, mantissa: Int = PERBILL_MANTISSA_SIZE): Perbill {
+ return bindPerbillNumber(dynamic.cast(), mantissa)
}
fun bindFixedI64Number(value: BigInteger): FixedI64 {
@@ -26,6 +27,10 @@ fun bindFixedI64(dynamic: Any?): FixedI64 {
return bindPerbill(dynamic)
}
-fun bindPerbillTyped(dynamic: Any?): PerbillTyped {
- return PerbillTyped(bindPerbill(dynamic).toDouble())
+fun bindPerbillTyped(dynamic: Any?, mantissa: Int = PERBILL_MANTISSA_SIZE): PerbillTyped {
+ return PerbillTyped(bindPerbill(dynamic, mantissa).toDouble())
+}
+
+fun bindPermill(dynamic: Any?): PerbillTyped {
+ return bindPerbillTyped(dynamic, mantissa = PERMILL_MANTISSA_SIZE)
}
diff --git a/common/src/main/java/io/novafoundation/nova/common/data/network/runtime/binding/GenericCall.kt b/common/src/main/java/io/novafoundation/nova/common/data/network/runtime/binding/GenericCall.kt
index 146a0fe1a6..5c43b8cd03 100644
--- a/common/src/main/java/io/novafoundation/nova/common/data/network/runtime/binding/GenericCall.kt
+++ b/common/src/main/java/io/novafoundation/nova/common/data/network/runtime/binding/GenericCall.kt
@@ -5,3 +5,7 @@ import jp.co.soramitsu.fearless_utils.runtime.definitions.types.generics.Generic
fun bindGenericCall(decoded: Any?): GenericCall.Instance {
return decoded.cast()
}
+
+fun bindGenericCallList(decoded: Any?): List {
+ return bindList(decoded, ::bindGenericCall)
+}
diff --git a/common/src/main/java/io/novafoundation/nova/common/utils/CollectionDiffer.kt b/common/src/main/java/io/novafoundation/nova/common/utils/CollectionDiffer.kt
index 1bc9957f5b..b1cdf00203 100644
--- a/common/src/main/java/io/novafoundation/nova/common/utils/CollectionDiffer.kt
+++ b/common/src/main/java/io/novafoundation/nova/common/utils/CollectionDiffer.kt
@@ -5,8 +5,10 @@ interface Identifiable {
val identifier: String
}
-fun List.findById(other: Identifiable?): T? = find { it.identifier == other?.identifier }
-fun List.findById(id: String): T? = find { it.identifier == id }
+fun Iterable.findById(other: Identifiable?): T? = find { it.identifier == other?.identifier }
+fun Iterable.findById(id: String): T? = find { it.identifier == id }
+
+fun Iterable.firstById(id: String): T = first { it.identifier == id }
fun CollectionDiffer.Diff<*>.hasDifference() = newOrUpdated.isNotEmpty() || removed.isNotEmpty()
diff --git a/common/src/main/java/io/novafoundation/nova/common/utils/FearlessLibExt.kt b/common/src/main/java/io/novafoundation/nova/common/utils/FearlessLibExt.kt
index 3b5563dc96..11b25b347b 100644
--- a/common/src/main/java/io/novafoundation/nova/common/utils/FearlessLibExt.kt
+++ b/common/src/main/java/io/novafoundation/nova/common/utils/FearlessLibExt.kt
@@ -23,6 +23,7 @@ import jp.co.soramitsu.fearless_utils.runtime.RuntimeSnapshot
import jp.co.soramitsu.fearless_utils.runtime.definitions.types.RuntimeType
import jp.co.soramitsu.fearless_utils.runtime.definitions.types.bytesOrNull
import jp.co.soramitsu.fearless_utils.runtime.definitions.types.composite.Struct
+import jp.co.soramitsu.fearless_utils.runtime.definitions.types.fromByteArrayOrNull
import jp.co.soramitsu.fearless_utils.runtime.definitions.types.generics.DefaultSignedExtensions
import jp.co.soramitsu.fearless_utils.runtime.definitions.types.generics.Extrinsic
import jp.co.soramitsu.fearless_utils.runtime.definitions.types.generics.Extrinsic.EncodingInstance.CallRepresentation
@@ -48,6 +49,7 @@ import jp.co.soramitsu.fearless_utils.runtime.metadata.storageOrNull
import jp.co.soramitsu.fearless_utils.scale.EncodableStruct
import jp.co.soramitsu.fearless_utils.scale.Schema
import jp.co.soramitsu.fearless_utils.scale.dataType.DataType
+import jp.co.soramitsu.fearless_utils.scale.utils.toUnsignedBytes
import jp.co.soramitsu.fearless_utils.ss58.SS58Encoder
import jp.co.soramitsu.fearless_utils.ss58.SS58Encoder.addressPrefix
import jp.co.soramitsu.fearless_utils.ss58.SS58Encoder.toAccountId
@@ -95,6 +97,10 @@ val Short.bigEndianBytes
fun ByteArray.toBigEndianShort(): Short = ByteBuffer.wrap(this).order(ByteOrder.BIG_ENDIAN).short
fun ByteArray.toBigEndianU16(): UShort = toBigEndianShort().toUShort()
+fun BigInteger.toUnsignedLittleEndian(): ByteArray {
+ return toUnsignedBytes().reversedArray()
+}
+
fun ByteArray.toBigEndianU32(): UInt = ByteBuffer.wrap(this).order(ByteOrder.BIG_ENDIAN).int.toUInt()
fun DataType.fromHex(hex: String): T {
@@ -166,6 +172,10 @@ fun Module.optionalNumberConstant(name: String, runtimeSnapshot: RuntimeSnapshot
fun Constant.asNumber(runtimeSnapshot: RuntimeSnapshot) = bindNumberConstant(this, runtimeSnapshot)
+fun Constant.decoded(runtimeSnapshot: RuntimeSnapshot): Any? {
+ return type?.fromByteArrayOrNull(runtimeSnapshot, value)
+}
+
fun Module.constantOrNull(name: String) = constants[name]
fun RuntimeMetadata.staking() = module(Modules.STAKING)
@@ -181,6 +191,8 @@ fun RuntimeMetadata.eqBalances() = module(Modules.EQ_BALANCES)
fun RuntimeMetadata.tokens() = module(Modules.TOKENS)
+fun RuntimeMetadata.assetRegistry() = module(Modules.ASSET_REGISTRY)
+
fun RuntimeMetadata.currencies() = module(Modules.CURRENCIES)
fun RuntimeMetadata.currenciesOrNull() = moduleOrNull(Modules.CURRENCIES)
fun RuntimeMetadata.crowdloan() = module(Modules.CROWDLOAN)
@@ -233,12 +245,30 @@ fun RuntimeMetadata.nominationPoolsOrNull() = moduleOrNull(Modules.NOMINATION_PO
fun RuntimeMetadata.assetConversionOrNull() = moduleOrNull(Modules.ASSET_CONVERSION)
+fun RuntimeMetadata.omnipoolOrNull() = moduleOrNull(Modules.OMNIPOOL)
+
+fun RuntimeMetadata.omnipool() = module(Modules.OMNIPOOL)
+
+fun RuntimeMetadata.stableSwapOrNull() = moduleOrNull(Modules.STABLE_SWAP)
+
+fun RuntimeMetadata.stableSwap() = module(Modules.STABLE_SWAP)
+
+fun RuntimeMetadata.dynamicFeesOrNull() = moduleOrNull(Modules.DYNAMIC_FEES)
+
+fun RuntimeMetadata.dynamicFees() = module(Modules.DYNAMIC_FEES)
+
+fun RuntimeMetadata.multiTransactionPayment() = module(Modules.MULTI_TRANSACTION_PAYMENT)
+
+fun RuntimeMetadata.referralsOrNull() = moduleOrNull(Modules.REFERRALS)
+
fun RuntimeMetadata.assetConversion() = module(Modules.ASSET_CONVERSION)
fun RuntimeMetadata.proxyOrNull() = moduleOrNull(Modules.PROXY)
fun RuntimeMetadata.proxy() = module(Modules.PROXY)
+fun RuntimeMetadata.utility() = module(Modules.UTILITY)
+
fun RuntimeMetadata.firstExistingModuleName(vararg options: String): String {
return options.first(::hasModule)
}
@@ -396,4 +426,18 @@ object Modules {
const val MULTISIG = "Multisig"
const val REGISTRAR = "Registrar"
const val FAST_UNSTAKE = "FastUnstake"
+
+ const val OMNIPOOL = "Omnipool"
+
+ const val DYNAMIC_FEES = "DynamicFees"
+
+ const val MULTI_TRANSACTION_PAYMENT = "MultiTransactionPayment"
+
+ const val REFERRALS = "Referrals"
+
+ const val ROUTER = "Router"
+
+ const val STABLE_SWAP = "Stableswap"
+
+ const val ASSET_REGISTRY = "AssetRegistry"
}
diff --git a/common/src/main/java/io/novafoundation/nova/common/utils/KotlinExt.kt b/common/src/main/java/io/novafoundation/nova/common/utils/KotlinExt.kt
index bbb69e4d84..0202c1244b 100644
--- a/common/src/main/java/io/novafoundation/nova/common/utils/KotlinExt.kt
+++ b/common/src/main/java/io/novafoundation/nova/common/utils/KotlinExt.kt
@@ -5,7 +5,12 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.cancel
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.runningFold
import org.web3j.utils.Numeric
import java.io.ByteArrayOutputStream
import java.io.InputStream
@@ -40,6 +45,14 @@ inline fun Result.flatMap(transform: (T) -> Result): Result {
)
}
+fun List>>.toMultiSubscription(expectedSize: Int): Flow