From bb189aeb8a151fdb07006b4ec253cca2ef2017ac Mon Sep 17 00:00:00 2001 From: Kurdistan Tech Ministry Date: Mon, 2 Feb 2026 17:43:59 +0300 Subject: [PATCH] fix: add AuthorizeCall signed extension for PezkuwiChain PezkuwiChain requires AuthorizeCall as the first signed extension in the TxExtension tuple. This commit adds: - AuthorizeCall.kt: FixedValueTransactionExtension with empty payload (PhantomData equivalent) - CustomTransactionExtensions: Detects PezkuwiChain via metadata and adds AuthorizeCall extension - ExtrinsicBuilderFactory/ExtrinsicSplitter: Uses CustomTransactionExtensions Without this fix, transactions fail with "bad signature" errors and fee calculation (payment_queryInfo) fails with WASM trap errors. --- .../data/extrinsic/ExtrinsicSplitter.kt | 2 +- .../extrinsic/CustomTransactionExtensions.kt | 34 +++++++++++++++++++ .../extrinsic/ExtrinsicBuilderFactory.kt | 2 +- .../extrinsic/extensions/AuthorizeCall.kt | 18 ++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/AuthorizeCall.kt diff --git a/feature-account-impl/src/main/java/io/novafoundation/nova/feature_account_impl/data/extrinsic/ExtrinsicSplitter.kt b/feature-account-impl/src/main/java/io/novafoundation/nova/feature_account_impl/data/extrinsic/ExtrinsicSplitter.kt index 221b0d4..1fa5f38 100644 --- a/feature-account-impl/src/main/java/io/novafoundation/nova/feature_account_impl/data/extrinsic/ExtrinsicSplitter.kt +++ b/feature-account-impl/src/main/java/io/novafoundation/nova/feature_account_impl/data/extrinsic/ExtrinsicSplitter.kt @@ -153,7 +153,7 @@ internal class RealExtrinsicSplitter @Inject constructor( setTransactionExtension(CheckSpecVersion(0)) setTransactionExtension(CheckTxVersion(0)) - CustomTransactionExtensions.defaultValues().forEach(::setTransactionExtension) + CustomTransactionExtensions.defaultValues(runtime).forEach(::setTransactionExtension) call(call) diff --git a/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/CustomTransactionExtensions.kt b/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/CustomTransactionExtensions.kt index 2d2d5c5..ade6851 100644 --- a/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/CustomTransactionExtensions.kt +++ b/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/CustomTransactionExtensions.kt @@ -1,20 +1,54 @@ package io.novafoundation.nova.runtime.extrinsic +import io.novafoundation.nova.runtime.extrinsic.extensions.AuthorizeCall import io.novafoundation.nova.runtime.extrinsic.extensions.ChargeAssetTxPayment import io.novafoundation.nova.runtime.extrinsic.extensions.CheckAppId +import io.novasama.substrate_sdk_android.runtime.RuntimeSnapshot import io.novasama.substrate_sdk_android.runtime.extrinsic.builder.ExtrinsicBuilder import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.TransactionExtension object CustomTransactionExtensions { + // PezkuwiChain genesis hashes (mainnet and teyrchains) + private val PEZKUWI_GENESIS_HASHES = setOf( + "bb4a61ab0c4b8c12f5eab71d0c86c482e03a275ecdafee678dea712474d33d75", // Pezkuwi Mainnet + "00d0e1d0581c3cd5c5768652d52f4520184018b44f56a2ae1e0dc9d65c00c948", // Asset Hub + "58269e9c184f721e0309332d90cafc410df1519a5dc27a5fd9b3bf5fd2d129f8", // People Chain + "96eb58af1bb7288115b5e4ff1590422533e749293f231974536dc6672417d06f" // Zagros Testnet + ) + fun applyDefaultValues(builder: ExtrinsicBuilder) { defaultValues().forEach(builder::setTransactionExtension) } + fun applyDefaultValues(builder: ExtrinsicBuilder, runtime: RuntimeSnapshot) { + defaultValues(runtime).forEach(builder::setTransactionExtension) + } + fun defaultValues(): List { return listOf( ChargeAssetTxPayment(), CheckAppId() ) } + + fun defaultValues(runtime: RuntimeSnapshot): List { + val extensions = mutableListOf() + + // Add AuthorizeCall only for PezkuwiChain networks + if (isPezkuwiChain(runtime)) { + extensions.add(AuthorizeCall()) + } + + extensions.add(ChargeAssetTxPayment()) + extensions.add(CheckAppId()) + + return extensions + } + + private fun isPezkuwiChain(runtime: RuntimeSnapshot): Boolean { + val genesisHash = runtime.metadata.extrinsic.signedExtensions + .any { it.id == "AuthorizeCall" } + return genesisHash + } } diff --git a/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/ExtrinsicBuilderFactory.kt b/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/ExtrinsicBuilderFactory.kt index 2005cc2..a86b3ed 100644 --- a/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/ExtrinsicBuilderFactory.kt +++ b/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/ExtrinsicBuilderFactory.kt @@ -56,7 +56,7 @@ class ExtrinsicBuilderFactory( setTransactionExtension(CheckSpecVersion(metadataProof.usedVersion.specVersion)) setTransactionExtension(CheckTxVersion(metadataProof.usedVersion.transactionVersion)) - CustomTransactionExtensions.defaultValues().forEach(::setTransactionExtension) + CustomTransactionExtensions.defaultValues(runtime).forEach(::setTransactionExtension) } } } diff --git a/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/AuthorizeCall.kt b/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/AuthorizeCall.kt new file mode 100644 index 0000000..8e5af6a --- /dev/null +++ b/runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/AuthorizeCall.kt @@ -0,0 +1,18 @@ +package io.novafoundation.nova.runtime.extrinsic.extensions + +import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.FixedValueTransactionExtension + +/** + * Signed extension for PezkuwiChain that authorizes calls. + * This extension uses PhantomData internally, so it has no payload (empty encoding). + * + * In the runtime, AuthorizeCall is defined as: + * pub struct AuthorizeCall(core::marker::PhantomData); + * + * It's placed first in the TxExtension tuple for PezkuwiChain. + */ +class AuthorizeCall : FixedValueTransactionExtension( + name = "AuthorizeCall", + implicit = null, + explicit = null // PhantomData encodes to nothing +)