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.
This commit is contained in:
2026-02-02 17:43:59 +03:00
parent a5cc31f82c
commit bb189aeb8a
4 changed files with 54 additions and 2 deletions
@@ -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<TransactionExtension> {
return listOf(
ChargeAssetTxPayment(),
CheckAppId()
)
}
fun defaultValues(runtime: RuntimeSnapshot): List<TransactionExtension> {
val extensions = mutableListOf<TransactionExtension>()
// 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
}
}
@@ -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)
}
}
}
@@ -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<T>(core::marker::PhantomData<T>);
*
* It's placed first in the TxExtension tuple for PezkuwiChain.
*/
class AuthorizeCall : FixedValueTransactionExtension(
name = "AuthorizeCall",
implicit = null,
explicit = null // PhantomData encodes to nothing
)