mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-04-22 06:47:57 +00:00
feat: Add Pezkuwi chain support with custom signed extensions
- Add PezkuwiAddressConstructor for custom address type handling - Add custom signed extensions (CheckNonZeroSender, CheckWeight, WeightReclaim, PezkuwiCheckMortality) - Add pezkuwi.json type definitions - Update RuntimeSnapshotExt for multiple address type lookups - Update CHAINS_URL to use GitHub-hosted chains.json with types config - Add feeViaRuntimeCall support for Pezkuwi chains - Add debug diagnostics for runtime type issues (to be cleaned before production) - Add CHANGELOG_PEZKUWI.md documenting all changes
This commit is contained in:
@@ -0,0 +1,282 @@
|
||||
# PezWallet Android - Pezkuwi Uyumluluk Değişiklikleri
|
||||
|
||||
Bu dosya, Pezkuwi chain uyumluluğu için yapılan tüm değişiklikleri takip eder.
|
||||
Context sıfırlanması durumunda referans olarak kullanılmalıdır.
|
||||
|
||||
---
|
||||
|
||||
## DEBUG KODLARI (Production öncesi KALDIRILMALI)
|
||||
|
||||
### 1. FeeLoaderV2Provider.kt - Hata mesajı gösterimi
|
||||
**Dosya:** `feature-wallet-api/src/main/java/io/novafoundation/nova/feature_wallet_api/presentation/mixin/fee/v2/FeeLoaderV2Provider.kt`
|
||||
**Değişiklik:**
|
||||
```kotlin
|
||||
// ÖNCE:
|
||||
message = resourceManager.getString(R.string.choose_amount_error_fee),
|
||||
|
||||
// SONRA (DEBUG):
|
||||
message = "DEBUG: $errorMsg | Runtime: $diagnostics",
|
||||
```
|
||||
**Temizleme:**
|
||||
- `"DEBUG: $errorMsg | Runtime: $diagnostics"` → `resourceManager.getString(R.string.choose_amount_error_fee)` olarak geri al
|
||||
- `val diagnostics = try { ... }` bloğunu kaldır
|
||||
|
||||
---
|
||||
|
||||
### 2. RuntimeFactory.kt - Diagnostic değişken ve log'lar
|
||||
**Dosya:** `runtime/src/main/java/io/novafoundation/nova/runtime/multiNetwork/runtime/RuntimeFactory.kt`
|
||||
**Eklenenler:**
|
||||
```kotlin
|
||||
// Companion object içinde:
|
||||
companion object {
|
||||
@Volatile
|
||||
var lastDiagnostics: String = "not yet initialized"
|
||||
}
|
||||
|
||||
// constructRuntimeInternal içinde:
|
||||
lastDiagnostics = "typesUsage=$typesUsage, ExtrinsicSig=$hasExtrinsicSignature, MultiSig=$hasMultiSignature, typeCount=${types.size}"
|
||||
|
||||
// Log satırları:
|
||||
Log.d("RuntimeFactory", "DEBUG: TypesUsage for chain $chainId = $typesUsage")
|
||||
Log.d("RuntimeFactory", "DEBUG: Loading BASE types for $chainId")
|
||||
Log.d("RuntimeFactory", "DEBUG: BASE types loaded, hash=$baseHash, typeCount=${types.size}")
|
||||
Log.d("RuntimeFactory", "DEBUG: Chain $chainId - ExtrinsicSignature=$hasExtrinsicSignature, MultiSignature=$hasMultiSignature, typesUsage=$typesUsage, typeCount=${types.size}")
|
||||
Log.d("RuntimeFactory", "DEBUG: BaseTypes loaded, length=${baseTypesRaw.length}, contains ExtrinsicSignature=${baseTypesRaw.contains("ExtrinsicSignature")}")
|
||||
Log.e("RuntimeFactory", "DEBUG: BaseTypes NOT in cache!")
|
||||
```
|
||||
**Temizleme:**
|
||||
- `companion object { ... }` bloğunu kaldır
|
||||
- `lastDiagnostics = ...` satırını kaldır
|
||||
- Tüm `Log.d/Log.e("RuntimeFactory", "DEBUG: ...")` satırlarını kaldır
|
||||
|
||||
---
|
||||
|
||||
### 3. CustomTransactionExtensions.kt - Log satırları
|
||||
**Dosya:** `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/CustomTransactionExtensions.kt`
|
||||
**Temizlenecek:** Tüm `Log.d(TAG, ...)` satırları ve `private const val TAG` tanımı
|
||||
|
||||
---
|
||||
|
||||
### 4. ExtrinsicBuilderFactory.kt - Log satırları
|
||||
**Dosya:** `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/ExtrinsicBuilderFactory.kt`
|
||||
**Temizlenecek:** Tüm `Log.d(TAG, ...)` satırları ve `private const val TAG` tanımı
|
||||
|
||||
---
|
||||
|
||||
### 5. PezkuwiAddressConstructor.kt - Log satırları
|
||||
**Dosya:** `common/src/main/java/io/novafoundation/nova/common/utils/PezkuwiAddressConstructor.kt`
|
||||
**Temizlenecek:** Tüm `Log.d(TAG, ...)` satırları ve `private const val TAG` tanımı
|
||||
|
||||
---
|
||||
|
||||
## FEATURE DEĞİŞİKLİKLERİ (Kalıcı)
|
||||
|
||||
### 1. PezkuwiAddressConstructor.kt - YENİ DOSYA
|
||||
**Dosya:** `common/src/main/java/io/novafoundation/nova/common/utils/PezkuwiAddressConstructor.kt`
|
||||
**Açıklama:** Pezkuwi chain'leri için özel address constructor. SDK'nın AddressInstanceConstructor'ı "Address" type'ını ararken, Pezkuwi "pezsp_runtime::multiaddress::MultiAddress" kullanıyor.
|
||||
|
||||
---
|
||||
|
||||
### 2. RuntimeSnapshotExt.kt - Address type lookup
|
||||
**Dosya:** `runtime/src/main/java/io/novafoundation/nova/runtime/util/RuntimeSnapshotExt.kt`
|
||||
**Değişiklik:** Birden fazla address type ismi deneniyor:
|
||||
```kotlin
|
||||
val addressType = typeRegistry["Address"]
|
||||
?: typeRegistry["MultiAddress"]
|
||||
?: typeRegistry["sp_runtime::multiaddress::MultiAddress"]
|
||||
?: typeRegistry["pezsp_runtime::multiaddress::MultiAddress"]
|
||||
?: return false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Signed Extension Dosyaları - YENİ DOSYALAR
|
||||
**Dosyalar:**
|
||||
- `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/AuthorizeCall.kt`
|
||||
- `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/CheckNonZeroSender.kt`
|
||||
- `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/CheckWeight.kt`
|
||||
- `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/WeightReclaim.kt`
|
||||
- `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/PezkuwiCheckMortality.kt`
|
||||
|
||||
**Açıklama:** Pezkuwi chain'leri için özel signed extension'lar
|
||||
|
||||
---
|
||||
|
||||
### 3.1. PezkuwiCheckMortality.kt - YENİ DOSYA
|
||||
**Dosya:** `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/PezkuwiCheckMortality.kt`
|
||||
**Açıklama:** SDK'nın CheckMortality'si metadata type lookup yaparak encode ediyor ve Pezkuwi'de bu başarısız oluyordu ("failed to encode extension CheckMortality" hatası). Bu custom extension, Era ve blockHash'i doğrudan encode ediyor.
|
||||
**Neden gerekli:** SDK CheckMortality, Era type'ını metadata'dan arıyor. Pezkuwi metadata'sında Era type'ı `pezsp_runtime.generic.era.Era` DictEnum olarak tanımlı ve SDK bunu handle edemiyor.
|
||||
|
||||
**Kod:**
|
||||
```kotlin
|
||||
class PezkuwiCheckMortality(
|
||||
era: Era.Mortal,
|
||||
blockHash: ByteArray
|
||||
) : FixedValueTransactionExtension(
|
||||
name = "CheckMortality",
|
||||
implicit = blockHash, // blockHash goes into signer payload
|
||||
explicit = createEraEntry(era) // Era as DictEnum.Entry
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. CustomTransactionExtensions.kt - Pezkuwi extension logic
|
||||
**Dosya:** `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/CustomTransactionExtensions.kt`
|
||||
**Değişiklik:** `isPezkuwiChain()` fonksiyonu eklendi, Pezkuwi için farklı extension'lar kullanılıyor
|
||||
|
||||
---
|
||||
|
||||
### 5. Address encoding yaklaşımı değişikliği
|
||||
**Eski yaklaşım:** `AddressInstanceConstructor` veya `PezkuwiAddressConstructor` ile type ismine göre tahmin
|
||||
**Yeni yaklaşım:** `argumentType("dest").constructAccountLookupInstance(accountId)` ile metadata'dan gerçek type alınıyor
|
||||
|
||||
**Güncellenen dosyalar:**
|
||||
1. `feature-wallet-api/.../ExtrinsicBuilderExt.kt` - **YENİ YAKLAŞIM**: metadata'dan type alıyor
|
||||
2. `feature-governance-impl/.../ExtrinsicBuilderExt.kt` - Zaten doğru yaklaşımı kullanıyordu
|
||||
3. Diğer dosyalar hala PezkuwiAddressConstructor kullanıyor (gerekirse güncellenecek):
|
||||
- `feature-staking-impl/.../ExtrinsicBuilderExt.kt`
|
||||
- `feature-staking-impl/.../NominationPoolsCalls.kt`
|
||||
- `feature-proxy-api/.../ExtrinsicBuilderExt.kt`
|
||||
- `feature-wallet-impl/.../StatemineAssetTransfers.kt`
|
||||
- `feature-wallet-impl/.../OrmlAssetTransfers.kt`
|
||||
- `feature-wallet-impl/.../NativeAssetIssuer.kt`
|
||||
- `feature-wallet-impl/.../OrmlAssetIssuer.kt`
|
||||
- `feature-wallet-impl/.../StatemineAssetIssuer.kt`
|
||||
- `feature-account-impl/.../ProxiedSigner.kt`
|
||||
|
||||
---
|
||||
|
||||
### 6. chains.json - feeViaRuntimeCall ve types
|
||||
**Dosya:** `pezkuwi-config/chains.json`
|
||||
**Değişiklik:**
|
||||
- Pezkuwi chain'lerine `"feeViaRuntimeCall": true` eklendi
|
||||
- `"types": { "overridesCommon": false }` eklendi (base types yüklenmesi için - ExtrinsicSignature type hatası düzeltmek için)
|
||||
**Etkilenen chain'ler:**
|
||||
- Pezkuwi Mainnet (bb4a61ab0c4b8c12f5eab71d0c86c482e03a275ecdafee678dea712474d33d75)
|
||||
- Pezkuwi Asset Hub (00d0e1d0581c3cd5c5768652d52f4520184018b44f56a2ae1e0dc9d65c00c948)
|
||||
- Pezkuwi People Chain (58269e9c184f721e0309332d90cafc410df1519a5dc27a5fd9b3bf5fd2d129f8)
|
||||
- Zagros Testnet (96eb58af1bb7288115b5e4ff1590422533e749293f231974536dc6672417d06f)
|
||||
|
||||
---
|
||||
|
||||
### 7. pezkuwi.json - Chain-specific types (ASSETS)
|
||||
**Dosya:** `runtime/src/main/assets/types/pezkuwi.json`
|
||||
**Açıklama:** Pezkuwi chain'leri için özel type tanımları
|
||||
```json
|
||||
{
|
||||
"types": {
|
||||
"ExtrinsicSignature": "MultiSignature",
|
||||
"Address": "pezsp_runtime::multiaddress::MultiAddress",
|
||||
"LookupSource": "pezsp_runtime::multiaddress::MultiAddress"
|
||||
},
|
||||
"typesAlias": {
|
||||
"pezsp_runtime::multiaddress::MultiAddress": "MultiAddress",
|
||||
"pezsp_runtime::MultiSignature": "MultiSignature",
|
||||
"pezsp_runtime.generic.era.Era": "Era"
|
||||
}
|
||||
}
|
||||
```
|
||||
**NOT:** Bu dosya şu anda kullanılmıyor çünkü TypesUsage.BASE kullanılıyor. TypesUsage.BOTH veya OWN için chains.json'da URL eklenebilir.
|
||||
|
||||
---
|
||||
|
||||
## SORUN GEÇMİŞİ
|
||||
|
||||
1. **"Network not responding"** - Fee hesaplama hatası
|
||||
- Çözüm: feeViaRuntimeCall eklendi, custom signed extension'lar eklendi
|
||||
|
||||
2. **"IllegalStateException: Type Address was not found"** - Address type lookup hatası
|
||||
- Çözüm: RuntimeSnapshotExt.kt'de birden fazla type ismi deneniyor
|
||||
|
||||
3. **"EncodeDecodeException: is not a valid instance"** - Address encoding hatası
|
||||
- Çözüm: `argumentType("dest").constructAccountLookupInstance(accountId)` ile metadata'dan gerçek type alınıyor (ExtrinsicBuilderExt.kt)
|
||||
|
||||
4. **"failed to encode extension CheckMortality"** - CheckMortality encoding hatası
|
||||
- SDK'nın CheckMortality'si metadata type lookup yaparak Era'yı encode etmeye çalışıyor
|
||||
- Pezkuwi Era type'ı `pezsp_runtime.generic.era.Era` DictEnum olarak tanımlı
|
||||
- Çözüm: `PezkuwiCheckMortality` custom extension'ı oluşturuldu, Era'yı `DictEnum.Entry("MortalX", secondByte)` olarak veriyor
|
||||
|
||||
5. **"IllegalStateException: Type ExtrinsicSignature was not found"** - ExtrinsicSignature type hatası (DEVAM EDİYOR)
|
||||
- SDK "ExtrinsicSignature" type'ını arıyor ama Pezkuwi chain'leri `"types": null` kullanıyordu
|
||||
- `TypesUsage.NONE` olduğu için base types (default.json) yüklenmiyordu
|
||||
- Denenen çözüm: chains.json'da `"types": { "overridesCommon": false }` eklendi → `TypesUsage.BASE` kullanılıyor
|
||||
- Base types URL: `https://raw.githubusercontent.com/pezkuwichain/pezkuwi-wallet-utils/master/chains/types/default.json`
|
||||
- default.json'da `"ExtrinsicSignature": "MultiSignature"` tanımlı
|
||||
- Cache temizlenip uninstall/reinstall yapıldı ama hata devam ediyor
|
||||
- Debug diagnostics eklendi - test sonucunu bekliyoruz
|
||||
|
||||
---
|
||||
|
||||
## ÇALIŞAN İMPLEMENTASYONLAR (Referans)
|
||||
|
||||
### 1. pezkuwi-extension (Browser Extension)
|
||||
**Konum:** `/home/mamostehp/pezkuwi-extension/`
|
||||
**Nasıl çalışıyor:**
|
||||
- `@pezkuwi/types` (polkadot.js fork) kullanıyor
|
||||
- `TypeRegistry` ile dynamic type handling
|
||||
- Custom user extensions:
|
||||
```javascript
|
||||
const PEZKUWI_USER_EXTENSIONS = {
|
||||
AuthorizeCall: {
|
||||
extrinsic: {},
|
||||
payload: {}
|
||||
}
|
||||
};
|
||||
```
|
||||
- `registry.setSignedExtensions(payload.signedExtensions, PEZKUWI_USER_EXTENSIONS)` ile extension'lar ekleniyor
|
||||
- Metadata'dan registry oluşturuluyor: `metadataExpand(metadata, false)`
|
||||
|
||||
### 2. pezkuwi-subxt (Rust)
|
||||
**Konum:** `/home/mamostehp/pezkuwi-sdk/vendor/pezkuwi-subxt/`
|
||||
**Nasıl çalışıyor:**
|
||||
- Rust'ta compile-time type generation
|
||||
- Metadata'dan otomatik type oluşturma
|
||||
|
||||
### 3. Telegram Miniapp
|
||||
- Web tabanlı, polkadot.js kullanıyor
|
||||
- `"types": null` ile çalışıyor çünkü metadata v14+ self-contained
|
||||
|
||||
---
|
||||
|
||||
## TEMİZLEME KONTROL LİSTESİ
|
||||
|
||||
Production release öncesi yapılacaklar:
|
||||
|
||||
- [ ] FeeLoaderV2Provider.kt - DEBUG mesajını ve diagnostics'i kaldır
|
||||
- [ ] RuntimeFactory.kt - companion object ve debug log'ları kaldır
|
||||
- [ ] CustomTransactionExtensions.kt - Log satırlarını kaldır
|
||||
- [ ] ExtrinsicBuilderFactory.kt - Log satırlarını kaldır
|
||||
- [ ] PezkuwiAddressConstructor.kt - Log satırlarını kaldır (varsa)
|
||||
- [ ] Test et: Transfer işlemi çalışıyor mu?
|
||||
- [ ] Test et: Fee hesaplama çalışıyor mu?
|
||||
|
||||
---
|
||||
|
||||
## TYPE LOADING AKIŞI (Referans)
|
||||
|
||||
```
|
||||
chains.json
|
||||
↓
|
||||
"types": { "overridesCommon": false } → TypesUsage.BASE
|
||||
"types": { "url": "...", "overridesCommon": false } → TypesUsage.BOTH
|
||||
"types": { "url": "...", "overridesCommon": true } → TypesUsage.OWN
|
||||
"types": null → TypesUsage.NONE
|
||||
↓
|
||||
RuntimeFactory.constructRuntime()
|
||||
↓
|
||||
TypesUsage.BASE → constructBaseTypes() → fetch from DEFAULT_TYPES_URL
|
||||
TypesUsage.BOTH → constructBaseTypes() + constructOwnTypes()
|
||||
TypesUsage.OWN → constructOwnTypes() only
|
||||
TypesUsage.NONE → use v14Preset() only
|
||||
↓
|
||||
TypeRegistry
|
||||
↓
|
||||
RuntimeSnapshot
|
||||
```
|
||||
|
||||
**DEFAULT_TYPES_URL:** `https://raw.githubusercontent.com/pezkuwichain/pezkuwi-wallet-utils/master/chains/types/default.json`
|
||||
|
||||
---
|
||||
|
||||
*Son güncelleme: 2026-02-03 (Debug diagnostics eklendi, extension analizi yapıldı)*
|
||||
@@ -0,0 +1,72 @@
|
||||
package io.novafoundation.nova.common.utils
|
||||
|
||||
import android.util.Log
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.registry.TypeRegistry
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.composite.DictEnum
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.generics.MULTI_ADDRESS_ID
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.primitives.FixedByteArray
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.skipAliases
|
||||
|
||||
private const val TAG = "PezkuwiAddressConstructor"
|
||||
|
||||
/**
|
||||
* Custom address constructor that handles Pezkuwi chains which use different type names.
|
||||
* Pezkuwi uses "pezsp_runtime::multiaddress::MultiAddress" instead of standard "Address".
|
||||
*/
|
||||
object PezkuwiAddressConstructor {
|
||||
|
||||
private val ADDRESS_TYPE_NAMES = listOf(
|
||||
"Address",
|
||||
"MultiAddress",
|
||||
"sp_runtime::multiaddress::MultiAddress",
|
||||
"pezsp_runtime::multiaddress::MultiAddress"
|
||||
)
|
||||
|
||||
/**
|
||||
* Constructs an address instance compatible with both standard Substrate and Pezkuwi chains.
|
||||
* Checks the actual type structure to determine the correct encoding format.
|
||||
*/
|
||||
fun constructInstance(typeRegistry: TypeRegistry, accountId: AccountId): Any {
|
||||
// Try to find the address type
|
||||
var foundTypeName: String? = null
|
||||
val addressType = ADDRESS_TYPE_NAMES.firstNotNullOfOrNull { name ->
|
||||
typeRegistry[name]?.also { foundTypeName = name }
|
||||
}
|
||||
|
||||
Log.d(TAG, "Found address type: $foundTypeName, type class: ${addressType?.javaClass?.simpleName}")
|
||||
|
||||
// If no address type found, return the raw accountId (for chains with simple AccountId)
|
||||
if (addressType == null) {
|
||||
Log.d(TAG, "No address type found, returning raw accountId")
|
||||
return accountId
|
||||
}
|
||||
|
||||
val resolvedType = addressType.skipAliases()
|
||||
Log.d(TAG, "Resolved type after skipAliases: ${resolvedType?.javaClass?.simpleName}, name: ${resolvedType?.name}")
|
||||
|
||||
// Check the actual type structure
|
||||
return when (resolvedType) {
|
||||
is DictEnum -> {
|
||||
Log.d(TAG, "Type is DictEnum with variants: ${resolvedType.elements.keys}")
|
||||
// MultiAddress type - wrap in Id variant
|
||||
DictEnum.Entry(MULTI_ADDRESS_ID, accountId)
|
||||
}
|
||||
is FixedByteArray -> {
|
||||
Log.d(TAG, "Type is FixedByteArray with length: ${resolvedType.length}, returning raw accountId")
|
||||
// GenericAccountId or similar - return raw
|
||||
accountId
|
||||
}
|
||||
null -> {
|
||||
Log.d(TAG, "Resolved type is null, returning raw accountId for Pezkuwi")
|
||||
// For Pezkuwi, if alias doesn't resolve, try raw accountId
|
||||
accountId
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, "Unknown type: ${resolvedType.javaClass.simpleName}, returning raw accountId")
|
||||
// Unknown type, try raw accountId instead of DictEnum
|
||||
accountId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 333 KiB |
+3
-1
@@ -20,7 +20,9 @@ internal class RealFeePaymentProviderRegistry(
|
||||
val chain = chainRegistry.getChain(chainId)
|
||||
|
||||
return when (chainId) {
|
||||
Chain.Geneses.POLKADOT_ASSET_HUB -> assetHubFactory.create(chain)
|
||||
Chain.Geneses.PEZKUWI_ASSET_HUB,
|
||||
Chain.Geneses.POLKADOT_ASSET_HUB,
|
||||
Chain.Geneses.KUSAMA_ASSET_HUB -> assetHubFactory.create(chain)
|
||||
Chain.Geneses.HYDRA_DX -> hydrationFactory.create(chain)
|
||||
else -> DefaultFeePaymentProvider(chain)
|
||||
}
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@ import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.composite.DictEnum
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.generics.GenericCall
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.instances.AddressInstanceConstructor
|
||||
import io.novafoundation.nova.common.utils.PezkuwiAddressConstructor
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.builder.ExtrinsicBuilder
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.SignedRaw
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.SignerPayloadRaw
|
||||
@@ -205,7 +205,7 @@ class ProxiedSigner(
|
||||
moduleName = Modules.PROXY,
|
||||
callName = "proxy",
|
||||
arguments = mapOf(
|
||||
"real" to AddressInstanceConstructor.constructInstance(runtime.typeRegistry, proxiedAccountId),
|
||||
"real" to PezkuwiAddressConstructor.constructInstance(runtime.typeRegistry, proxiedAccountId),
|
||||
"force_proxy_type" to DictEnum.Entry(proxyType.name, null),
|
||||
"call" to proxiedCall
|
||||
)
|
||||
|
||||
+1
-1
@@ -231,7 +231,7 @@ class PolkadotExternalSignInteractor(
|
||||
setTransactionExtension(CheckTxVersion(transactionVersion))
|
||||
|
||||
call(parsedExtrinsic.call)
|
||||
CustomTransactionExtensions.applyDefaultValues(builder = this)
|
||||
CustomTransactionExtensions.applyDefaultValues(builder = this, runtime = runtime)
|
||||
applyCustomSignedExtensions(parsedExtrinsic)
|
||||
|
||||
signer.setSignerData(signingContext, signingMode)
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@ import io.novafoundation.nova.runtime.multiNetwork.runtime.types.custom.vote.Con
|
||||
import io.novafoundation.nova.runtime.util.constructAccountLookupInstance
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.composite.DictEnum
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.instances.AddressInstanceConstructor
|
||||
import io.novafoundation.nova.common.utils.PezkuwiAddressConstructor
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.builder.ExtrinsicBuilder
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.call
|
||||
import io.novasama.substrate_sdk_android.runtime.metadata.call
|
||||
@@ -55,7 +55,7 @@ fun ExtrinsicBuilder.convictionVotingUnlock(
|
||||
callName = "unlock",
|
||||
arguments = mapOf(
|
||||
"class" to trackId.value,
|
||||
"target" to AddressInstanceConstructor.constructInstance(runtime.typeRegistry, accountId)
|
||||
"target" to PezkuwiAddressConstructor.constructInstance(runtime.typeRegistry, accountId)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -137,7 +137,7 @@ fun CallBuilder.convictionVotingDelegate(
|
||||
callName = "delegate",
|
||||
arguments = mapOf(
|
||||
"class" to trackId.value,
|
||||
"to" to AddressInstanceConstructor.constructInstance(runtime.typeRegistry, delegate),
|
||||
"to" to PezkuwiAddressConstructor.constructInstance(runtime.typeRegistry, delegate),
|
||||
"conviction" to conviction.prepareForEncoding(),
|
||||
"balance" to amount
|
||||
)
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import java.math.BigInteger
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import io.novasama.substrate_sdk_android.runtime.RuntimeSnapshot
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.composite.DictEnum
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.instances.AddressInstanceConstructor
|
||||
import io.novafoundation.nova.common.utils.PezkuwiAddressConstructor
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.builder.ExtrinsicBuilder
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.call
|
||||
|
||||
@@ -28,7 +28,7 @@ fun ExtrinsicBuilder.removeProxyCall(proxyAccountId: AccountId, proxyType: Proxy
|
||||
|
||||
private fun argumentsForProxy(runtime: RuntimeSnapshot, proxyAccountId: AccountId, proxyType: ProxyType): Map<String, Any> {
|
||||
return mapOf(
|
||||
"delegate" to AddressInstanceConstructor.constructInstance(runtime.typeRegistry, proxyAccountId),
|
||||
"delegate" to PezkuwiAddressConstructor.constructInstance(runtime.typeRegistry, proxyAccountId),
|
||||
"proxy_type" to DictEnum.Entry(proxyType.name, null),
|
||||
"delay" to BigInteger.ZERO
|
||||
)
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import io.novafoundation.nova.common.utils.voterListName
|
||||
import io.novafoundation.nova.feature_staking_api.domain.model.RewardDestination
|
||||
import io.novafoundation.nova.feature_staking_impl.data.network.blockhain.bindings.bindRewardDestination
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.instances.AddressInstanceConstructor
|
||||
import io.novafoundation.nova.common.utils.PezkuwiAddressConstructor
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.builder.ExtrinsicBuilder
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.call
|
||||
import java.math.BigInteger
|
||||
@@ -110,7 +110,7 @@ fun ExtrinsicBuilder.rebag(dislocated: AccountId): ExtrinsicBuilder {
|
||||
moduleName = runtime.metadata.voterListName(),
|
||||
callName = "rebag",
|
||||
arguments = mapOf(
|
||||
"dislocated" to AddressInstanceConstructor.constructInstance(runtime.typeRegistry, dislocated)
|
||||
"dislocated" to PezkuwiAddressConstructor.constructInstance(runtime.typeRegistry, dislocated)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
+4
-4
@@ -6,7 +6,7 @@ import io.novafoundation.nova.feature_staking_impl.data.nominationPools.network.
|
||||
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.types.Balance
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.composite.DictEnum
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.instances.AddressInstanceConstructor
|
||||
import io.novafoundation.nova.common.utils.PezkuwiAddressConstructor
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.builder.ExtrinsicBuilder
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.call
|
||||
import java.math.BigInteger
|
||||
@@ -47,7 +47,7 @@ fun NominationPoolsCalls.unbond(unbondAccount: AccountId, unbondPoints: PoolPoin
|
||||
moduleName = Modules.NOMINATION_POOLS,
|
||||
callName = "unbond",
|
||||
arguments = mapOf(
|
||||
"member_account" to AddressInstanceConstructor.constructInstance(extrinsicBuilder.runtime.typeRegistry, unbondAccount),
|
||||
"member_account" to PezkuwiAddressConstructor.constructInstance(extrinsicBuilder.runtime.typeRegistry, unbondAccount),
|
||||
"unbonding_points" to unbondPoints.value
|
||||
)
|
||||
)
|
||||
@@ -58,7 +58,7 @@ fun NominationPoolsCalls.withdrawUnbonded(memberAccount: AccountId, numberOfSlas
|
||||
moduleName = Modules.NOMINATION_POOLS,
|
||||
callName = "withdraw_unbonded",
|
||||
arguments = mapOf(
|
||||
"member_account" to AddressInstanceConstructor.constructInstance(extrinsicBuilder.runtime.typeRegistry, memberAccount),
|
||||
"member_account" to PezkuwiAddressConstructor.constructInstance(extrinsicBuilder.runtime.typeRegistry, memberAccount),
|
||||
"num_slashing_spans" to numberOfSlashingSpans
|
||||
)
|
||||
)
|
||||
@@ -77,7 +77,7 @@ fun NominationPoolsCalls.migrateDelegation(memberAccount: AccountId) {
|
||||
moduleName = Modules.NOMINATION_POOLS,
|
||||
callName = "migrate_delegation",
|
||||
arguments = mapOf(
|
||||
"member_account" to AddressInstanceConstructor.constructInstance(extrinsicBuilder.runtime.typeRegistry, memberAccount),
|
||||
"member_account" to PezkuwiAddressConstructor.constructInstance(extrinsicBuilder.runtime.typeRegistry, memberAccount),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
+11
-4
@@ -1,13 +1,15 @@
|
||||
package io.novafoundation.nova.feature_wallet_api.data.network.blockhain
|
||||
|
||||
import io.novafoundation.nova.common.utils.Modules
|
||||
import io.novafoundation.nova.common.utils.argumentType
|
||||
import io.novafoundation.nova.common.utils.balances
|
||||
import io.novafoundation.nova.common.utils.firstExistingCallName
|
||||
import io.novafoundation.nova.common.utils.hasCall
|
||||
import io.novafoundation.nova.runtime.util.constructAccountLookupInstance
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.instances.AddressInstanceConstructor
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.builder.ExtrinsicBuilder
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.call
|
||||
import io.novasama.substrate_sdk_android.runtime.metadata.call
|
||||
import java.math.BigInteger
|
||||
|
||||
enum class TransferMode {
|
||||
@@ -25,11 +27,13 @@ fun ExtrinsicBuilder.nativeTransfer(accountId: AccountId, amount: BigInteger, mo
|
||||
}
|
||||
|
||||
private fun ExtrinsicBuilder.transferKeepAlive(accountId: AccountId, amount: BigInteger) {
|
||||
val destType = runtime.metadata.balances().call("transfer_keep_alive").argumentType("dest")
|
||||
|
||||
call(
|
||||
moduleName = Modules.BALANCES,
|
||||
callName = "transfer_keep_alive",
|
||||
arguments = mapOf(
|
||||
"dest" to AddressInstanceConstructor.constructInstance(runtime.typeRegistry, accountId),
|
||||
"dest" to destType.constructAccountLookupInstance(accountId),
|
||||
"value" to amount
|
||||
)
|
||||
)
|
||||
@@ -37,12 +41,13 @@ private fun ExtrinsicBuilder.transferKeepAlive(accountId: AccountId, amount: Big
|
||||
|
||||
private fun ExtrinsicBuilder.transferAllowDeath(accountId: AccountId, amount: BigInteger) {
|
||||
val callName = runtime.metadata.balances().firstExistingCallName("transfer_allow_death", "transfer")
|
||||
val destType = runtime.metadata.balances().call(callName).argumentType("dest")
|
||||
|
||||
call(
|
||||
moduleName = Modules.BALANCES,
|
||||
callName = callName,
|
||||
arguments = mapOf(
|
||||
"dest" to AddressInstanceConstructor.constructInstance(runtime.typeRegistry, accountId),
|
||||
"dest" to destType.constructAccountLookupInstance(accountId),
|
||||
"value" to amount
|
||||
)
|
||||
)
|
||||
@@ -52,11 +57,13 @@ private fun ExtrinsicBuilder.transferAll(accountId: AccountId, amount: BigIntege
|
||||
val transferAllPresent = runtime.metadata.balances().hasCall("transfer_all")
|
||||
|
||||
if (transferAllPresent) {
|
||||
val destType = runtime.metadata.balances().call("transfer_all").argumentType("dest")
|
||||
|
||||
call(
|
||||
moduleName = Modules.BALANCES,
|
||||
callName = "transfer_all",
|
||||
arguments = mapOf(
|
||||
"dest" to AddressInstanceConstructor.constructInstance(runtime.typeRegistry, accountId),
|
||||
"dest" to destType.constructAccountLookupInstance(accountId),
|
||||
"keep_alive" to false
|
||||
)
|
||||
)
|
||||
|
||||
+27
-4
@@ -144,13 +144,36 @@ internal class FeeLoaderV2Provider<F, D>(
|
||||
|
||||
private suspend fun onFeeError(error: Throwable, feeConstructor: FeeConstructor<F>) {
|
||||
if (error !is CancellationException) {
|
||||
Log.e(LOG_TAG, "Failed to sync fee", error)
|
||||
// Build full error chain for debugging
|
||||
val errorChain = buildString {
|
||||
var current: Throwable? = error
|
||||
var depth = 0
|
||||
while (current != null && depth < 5) {
|
||||
if (depth > 0) append(" -> ")
|
||||
append("${current.javaClass.simpleName}: ${current.message}")
|
||||
current = current.cause
|
||||
depth++
|
||||
}
|
||||
}
|
||||
val errorMsg = errorChain
|
||||
Log.e(LOG_TAG, "Failed to sync fee: $errorMsg", error)
|
||||
|
||||
fee.emit(FeeStatus.Error)
|
||||
|
||||
awaitFeeRetry()
|
||||
|
||||
loadFee(feeConstructor)
|
||||
// Show detailed error in retry dialog with runtime diagnostics
|
||||
val diagnostics = try {
|
||||
io.novafoundation.nova.runtime.multiNetwork.runtime.RuntimeFactory.lastDiagnostics
|
||||
} catch (e: Exception) { "N/A" }
|
||||
retryEvent.postValue(
|
||||
Event(
|
||||
RetryPayload(
|
||||
title = resourceManager.getString(R.string.choose_amount_network_error),
|
||||
message = "DEBUG: $errorMsg | Runtime: $diagnostics",
|
||||
onRetry = { loadFee(feeConstructor) },
|
||||
onCancel = { }
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novafoundation.nova.runtime.multiNetwork.getRuntime
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.generics.GenericCall
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.instances.AddressInstanceConstructor
|
||||
import io.novafoundation.nova.common.utils.PezkuwiAddressConstructor
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.builder.ExtrinsicBuilder
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.call
|
||||
import java.math.BigInteger
|
||||
@@ -88,7 +88,7 @@ open class OrmlAssetTransfers(
|
||||
moduleIndex = moduleIndex,
|
||||
callIndex = callIndex,
|
||||
arguments = mapOf(
|
||||
"dest" to AddressInstanceConstructor.constructInstance(runtime.typeRegistry, target),
|
||||
"dest" to PezkuwiAddressConstructor.constructInstance(runtime.typeRegistry, target),
|
||||
"currency_id" to chainAsset.ormlCurrencyId(runtime),
|
||||
"amount" to amount
|
||||
)
|
||||
|
||||
+2
-2
@@ -27,7 +27,7 @@ import io.novafoundation.nova.runtime.multiNetwork.getRuntime
|
||||
import io.novafoundation.nova.runtime.storage.source.StorageDataSource
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.generics.GenericCall
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.instances.AddressInstanceConstructor
|
||||
import io.novafoundation.nova.common.utils.PezkuwiAddressConstructor
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.builder.ExtrinsicBuilder
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.call
|
||||
import io.novasama.substrate_sdk_android.runtime.metadata.storage
|
||||
@@ -109,7 +109,7 @@ class StatemineAssetTransfers(
|
||||
callName = "transfer",
|
||||
arguments = mapOf(
|
||||
"id" to assetType.prepareIdForEncoding(runtime),
|
||||
"target" to AddressInstanceConstructor.constructInstance(runtime.typeRegistry, target),
|
||||
"target" to PezkuwiAddressConstructor.constructInstance(runtime.typeRegistry, target),
|
||||
"amount" to amount
|
||||
)
|
||||
)
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import io.novafoundation.nova.common.utils.composeCall
|
||||
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.types.Balance
|
||||
import io.novasama.substrate_sdk_android.runtime.RuntimeSnapshot
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.generics.GenericCall
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.instances.AddressInstanceConstructor
|
||||
import io.novafoundation.nova.common.utils.PezkuwiAddressConstructor
|
||||
|
||||
class NativeAssetIssuer(
|
||||
private val runtimeSnapshot: RuntimeSnapshot
|
||||
@@ -17,7 +17,7 @@ class NativeAssetIssuer(
|
||||
moduleName = Modules.BALANCES,
|
||||
callName = "force_set_balance",
|
||||
arguments = mapOf(
|
||||
"who" to AddressInstanceConstructor.constructInstance(runtimeSnapshot.typeRegistry, destination.value),
|
||||
"who" to PezkuwiAddressConstructor.constructInstance(runtimeSnapshot.typeRegistry, destination.value),
|
||||
"new_free" to amount
|
||||
)
|
||||
)
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ import io.novafoundation.nova.runtime.ext.currencyId
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novasama.substrate_sdk_android.runtime.RuntimeSnapshot
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.generics.GenericCall
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.instances.AddressInstanceConstructor
|
||||
import io.novafoundation.nova.common.utils.PezkuwiAddressConstructor
|
||||
|
||||
class OrmlAssetIssuer(
|
||||
private val ormlType: Chain.Asset.Type.Orml,
|
||||
@@ -20,7 +20,7 @@ class OrmlAssetIssuer(
|
||||
moduleName = Modules.TOKENS,
|
||||
callName = "set_balance",
|
||||
arguments = mapOf(
|
||||
"who" to AddressInstanceConstructor.constructInstance(runtimeSnapshot.typeRegistry, destination.value),
|
||||
"who" to PezkuwiAddressConstructor.constructInstance(runtimeSnapshot.typeRegistry, destination.value),
|
||||
"currency_id" to ormlType.currencyId(runtimeSnapshot),
|
||||
"new_free" to amount,
|
||||
"new_reserved" to Balance.ZERO
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.prepareIdForEncoding
|
||||
import io.novasama.substrate_sdk_android.runtime.RuntimeSnapshot
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.generics.GenericCall
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.instances.AddressInstanceConstructor
|
||||
import io.novafoundation.nova.common.utils.PezkuwiAddressConstructor
|
||||
|
||||
class StatemineAssetIssuer(
|
||||
private val chainId: ChainId,
|
||||
@@ -38,7 +38,7 @@ class StatemineAssetIssuer(
|
||||
callName = "mint",
|
||||
arguments = mapOf(
|
||||
"id" to assetType.prepareIdForEncoding(runtimeSnapshot),
|
||||
"beneficiary" to AddressInstanceConstructor.constructInstance(runtimeSnapshot.typeRegistry, destination.value),
|
||||
"beneficiary" to PezkuwiAddressConstructor.constructInstance(runtimeSnapshot.typeRegistry, destination.value),
|
||||
"amount" to amount
|
||||
)
|
||||
)
|
||||
|
||||
@@ -53,10 +53,13 @@
|
||||
"typeExtras": null
|
||||
}
|
||||
],
|
||||
"types": null,
|
||||
"types": {
|
||||
"overridesCommon": false
|
||||
},
|
||||
"additional": {
|
||||
"themeColor": "#009639",
|
||||
"defaultBlockTimeMillis": 6000
|
||||
"defaultBlockTimeMillis": 6000,
|
||||
"feeViaRuntimeCall": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -88,8 +91,12 @@
|
||||
"icon": "https://pezkuwichain.io/tokens/HEZ.png"
|
||||
}
|
||||
],
|
||||
"types": {
|
||||
"overridesCommon": false
|
||||
},
|
||||
"additional": {
|
||||
"themeColor": "#009639"
|
||||
"themeColor": "#009639",
|
||||
"feeViaRuntimeCall": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -166,10 +173,13 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"types": null,
|
||||
"types": {
|
||||
"overridesCommon": false
|
||||
},
|
||||
"additional": {
|
||||
"themeColor": "#009639",
|
||||
"defaultBlockTimeMillis": 6000
|
||||
"defaultBlockTimeMillis": 6000,
|
||||
"feeViaRuntimeCall": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -214,10 +224,13 @@
|
||||
"typeExtras": null
|
||||
}
|
||||
],
|
||||
"types": null,
|
||||
"types": {
|
||||
"overridesCommon": false
|
||||
},
|
||||
"additional": {
|
||||
"themeColor": "#009639",
|
||||
"defaultBlockTimeMillis": 6000
|
||||
"defaultBlockTimeMillis": 6000,
|
||||
"feeViaRuntimeCall": true
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ android {
|
||||
|
||||
|
||||
|
||||
buildConfigField "String", "CHAINS_URL", "\"https://wallet.pezkuwichain.io/chains.json\""
|
||||
buildConfigField "String", "CHAINS_URL", "\"https://raw.githubusercontent.com/pezkuwichain/pezkuwi-wallet-utils/master/chains/v22/android/chains.json\""
|
||||
buildConfigField "String", "EVM_ASSETS_URL", "\"https://wallet.pezkuwichain.io/evm_assets.json\""
|
||||
buildConfigField "String", "PRE_CONFIGURED_CHAINS_URL", "\"https://wallet.pezkuwichain.io/pre_configured_chains.json\""
|
||||
buildConfigField "String", "PRE_CONFIGURED_CHAIN_DETAILS_URL", "\"https://wallet.pezkuwichain.io/chain_details\""
|
||||
@@ -27,7 +27,7 @@ android {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
|
||||
buildConfigField "String", "CHAINS_URL", "\"https://wallet.pezkuwichain.io/chains.json\""
|
||||
buildConfigField "String", "CHAINS_URL", "\"https://raw.githubusercontent.com/pezkuwichain/pezkuwi-wallet-utils/master/chains/v22/android/chains.json\""
|
||||
buildConfigField "String", "EVM_ASSETS_URL", "\"https://wallet.pezkuwichain.io/evm_assets.json\""
|
||||
buildConfigField "String", "PRE_CONFIGURED_CHAINS_URL", "\"https://wallet.pezkuwichain.io/pre_configured_chains.json\""
|
||||
buildConfigField "String", "PRE_CONFIGURED_CHAIN_DETAILS_URL", "\"https://wallet.pezkuwichain.io/chain_details\""
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"types": {
|
||||
"ExtrinsicSignature": "MultiSignature",
|
||||
"Address": "pezsp_runtime::multiaddress::MultiAddress",
|
||||
"LookupSource": "pezsp_runtime::multiaddress::MultiAddress"
|
||||
},
|
||||
"typesAlias": {
|
||||
"pezsp_runtime::multiaddress::MultiAddress": "MultiAddress",
|
||||
"pezsp_runtime::MultiSignature": "MultiSignature",
|
||||
"pezsp_runtime.generic.era.Era": "Era"
|
||||
}
|
||||
}
|
||||
+26
-8
@@ -1,12 +1,18 @@
|
||||
package io.novafoundation.nova.runtime.extrinsic
|
||||
|
||||
import android.util.Log
|
||||
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.novafoundation.nova.runtime.extrinsic.extensions.CheckNonZeroSender
|
||||
import io.novafoundation.nova.runtime.extrinsic.extensions.CheckWeight
|
||||
import io.novafoundation.nova.runtime.extrinsic.extensions.WeightReclaim
|
||||
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
|
||||
|
||||
private const val TAG = "CustomTxExtensions"
|
||||
|
||||
object CustomTransactionExtensions {
|
||||
|
||||
// PezkuwiChain genesis hashes (mainnet and teyrchains)
|
||||
@@ -34,21 +40,33 @@ object CustomTransactionExtensions {
|
||||
|
||||
fun defaultValues(runtime: RuntimeSnapshot): List<TransactionExtension> {
|
||||
val extensions = mutableListOf<TransactionExtension>()
|
||||
val isPezkuwi = isPezkuwiChain(runtime)
|
||||
|
||||
// Add AuthorizeCall only for PezkuwiChain networks
|
||||
if (isPezkuwiChain(runtime)) {
|
||||
Log.d(TAG, "isPezkuwiChain: $isPezkuwi")
|
||||
|
||||
if (isPezkuwi) {
|
||||
// Pezkuwi needs: AuthorizeCall, CheckNonZeroSender, CheckWeight, WeightReclaim
|
||||
// Other extensions (CheckMortality, CheckGenesis, etc.) are set in ExtrinsicBuilderFactory
|
||||
Log.d(TAG, "Adding Pezkuwi extensions: AuthorizeCall, CheckNonZeroSender, CheckWeight, WeightReclaim")
|
||||
extensions.add(AuthorizeCall())
|
||||
extensions.add(CheckNonZeroSender())
|
||||
extensions.add(CheckWeight())
|
||||
extensions.add(WeightReclaim())
|
||||
} else {
|
||||
// Other chains (Asset Hub, etc.) use ChargeAssetTxPayment and CheckAppId
|
||||
Log.d(TAG, "Adding default extensions: ChargeAssetTxPayment, CheckAppId")
|
||||
extensions.add(ChargeAssetTxPayment())
|
||||
extensions.add(CheckAppId())
|
||||
}
|
||||
|
||||
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
|
||||
val signedExtIds = runtime.metadata.extrinsic.signedExtensions.map { it.id }
|
||||
Log.d(TAG, "Metadata signed extensions: $signedExtIds")
|
||||
val hasAuthorizeCall = signedExtIds.any { it == "AuthorizeCall" }
|
||||
Log.d(TAG, "Has AuthorizeCall: $hasAuthorizeCall")
|
||||
return hasAuthorizeCall
|
||||
}
|
||||
}
|
||||
|
||||
+30
-1
@@ -1,12 +1,15 @@
|
||||
package io.novafoundation.nova.runtime.extrinsic
|
||||
|
||||
import android.util.Log
|
||||
import io.novafoundation.nova.common.utils.orZero
|
||||
import io.novafoundation.nova.runtime.ext.requireGenesisHash
|
||||
import io.novafoundation.nova.runtime.extrinsic.extensions.PezkuwiCheckMortality
|
||||
import io.novafoundation.nova.runtime.extrinsic.metadata.MetadataShortenerService
|
||||
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novafoundation.nova.runtime.multiNetwork.getRuntime
|
||||
import io.novasama.substrate_sdk_android.extensions.fromHex
|
||||
import io.novasama.substrate_sdk_android.runtime.RuntimeSnapshot
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.BatchMode
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.ExtrinsicVersion
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.builder.ExtrinsicBuilder
|
||||
@@ -17,6 +20,8 @@ import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtensi
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.CheckTxVersion
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.checkMetadataHash.CheckMetadataHash
|
||||
|
||||
private const val TAG = "ExtrinsicBuilderFactory"
|
||||
|
||||
class ExtrinsicBuilderFactory(
|
||||
private val chainRegistry: ChainRegistry,
|
||||
private val mortalityConstructor: MortalityConstructor,
|
||||
@@ -40,16 +45,33 @@ class ExtrinsicBuilderFactory(
|
||||
): Sequence<ExtrinsicBuilder> {
|
||||
val runtime = chainRegistry.getRuntime(chain.id)
|
||||
|
||||
// Log metadata extensions
|
||||
val metadataExtensions = runtime.metadata.extrinsic.signedExtensions.map { it.id }
|
||||
Log.d(TAG, "Chain: ${chain.name}, Metadata extensions: $metadataExtensions")
|
||||
|
||||
val mortality = mortalityConstructor.constructMortality(chain.id)
|
||||
val metadataProof = metadataShortenerService.generateMetadataProof(chain.id)
|
||||
|
||||
// Log custom extensions
|
||||
val customExtensions = CustomTransactionExtensions.defaultValues(runtime).map { it.name }
|
||||
Log.d(TAG, "Custom extensions to add: $customExtensions")
|
||||
|
||||
val isPezkuwi = isPezkuwiChain(runtime)
|
||||
Log.d(TAG, "isPezkuwiChain: $isPezkuwi")
|
||||
|
||||
return generateSequence {
|
||||
ExtrinsicBuilder(
|
||||
runtime = runtime,
|
||||
extrinsicVersion = ExtrinsicVersion.V4,
|
||||
batchMode = options.batchMode,
|
||||
).apply {
|
||||
setTransactionExtension(CheckMortality(mortality.era, mortality.blockHash.fromHex()))
|
||||
// Use custom CheckMortality for Pezkuwi chains to avoid type lookup issues
|
||||
if (isPezkuwi) {
|
||||
Log.d(TAG, "Using PezkuwiCheckMortality for ${chain.name}")
|
||||
setTransactionExtension(PezkuwiCheckMortality(mortality.era, mortality.blockHash.fromHex()))
|
||||
} else {
|
||||
setTransactionExtension(CheckMortality(mortality.era, mortality.blockHash.fromHex()))
|
||||
}
|
||||
setTransactionExtension(CheckGenesis(chain.requireGenesisHash().fromHex()))
|
||||
setTransactionExtension(ChargeTransactionPayment(chain.additional?.defaultTip.orZero()))
|
||||
setTransactionExtension(CheckMetadataHash(metadataProof.checkMetadataHash))
|
||||
@@ -57,7 +79,14 @@ class ExtrinsicBuilderFactory(
|
||||
setTransactionExtension(CheckTxVersion(metadataProof.usedVersion.transactionVersion))
|
||||
|
||||
CustomTransactionExtensions.defaultValues(runtime).forEach(::setTransactionExtension)
|
||||
|
||||
Log.d(TAG, "All extensions set for ${chain.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isPezkuwiChain(runtime: RuntimeSnapshot): Boolean {
|
||||
val signedExtIds = runtime.metadata.extrinsic.signedExtensions.map { it.id }
|
||||
return signedExtIds.any { it == "AuthorizeCall" }
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -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 checks for non-zero sender.
|
||||
* This extension ensures the sender is not the zero address.
|
||||
*
|
||||
* In the runtime, CheckNonZeroSender is defined as:
|
||||
* pub struct CheckNonZeroSender<T>(core::marker::PhantomData<T>);
|
||||
*
|
||||
* It uses PhantomData internally, so it has no payload (empty encoding).
|
||||
*/
|
||||
class CheckNonZeroSender : FixedValueTransactionExtension(
|
||||
name = "CheckNonZeroSender",
|
||||
implicit = null,
|
||||
explicit = null // PhantomData encodes to nothing
|
||||
)
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package io.novafoundation.nova.runtime.extrinsic.extensions
|
||||
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.FixedValueTransactionExtension
|
||||
|
||||
/**
|
||||
* Signed extension that checks weight limits.
|
||||
* This extension uses PhantomData internally, so it has no payload (empty encoding).
|
||||
*
|
||||
* In the runtime, CheckWeight is defined as:
|
||||
* pub struct CheckWeight<T>(core::marker::PhantomData<T>);
|
||||
*/
|
||||
class CheckWeight : FixedValueTransactionExtension(
|
||||
name = "CheckWeight",
|
||||
implicit = null,
|
||||
explicit = null // PhantomData encodes to nothing
|
||||
)
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package io.novafoundation.nova.runtime.extrinsic.extensions
|
||||
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.composite.DictEnum
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.generics.Era
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.FixedValueTransactionExtension
|
||||
import java.math.BigInteger
|
||||
|
||||
/**
|
||||
* Custom CheckMortality extension for Pezkuwi chains.
|
||||
*
|
||||
* Pezkuwi uses pezsp_runtime.generic.era.Era which is a DictEnum with variants:
|
||||
* - Immortal
|
||||
* - Mortal1(u8), Mortal2(u8), ..., Mortal255(u8)
|
||||
*
|
||||
* The variant name is "MortalX" where X is the first byte of the encoded era,
|
||||
* and the variant's value is the second byte (u8).
|
||||
*
|
||||
* @param era The mortal era from MortalityConstructor
|
||||
* @param blockHash The block hash (32 bytes) for the signer payload
|
||||
*/
|
||||
class PezkuwiCheckMortality(
|
||||
era: Era.Mortal,
|
||||
blockHash: ByteArray
|
||||
) : FixedValueTransactionExtension(
|
||||
name = "CheckMortality",
|
||||
implicit = blockHash, // blockHash goes into signer payload
|
||||
explicit = createEraEntry(era) // Era as DictEnum.Entry
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
* Creates a DictEnum.Entry for the Era.
|
||||
*
|
||||
* Standard Era encoding produces 2 bytes:
|
||||
* - First byte determines the variant name (Mortal1, Mortal2, ..., Mortal255)
|
||||
* - Second byte is the variant's value (u8)
|
||||
*/
|
||||
private fun createEraEntry(era: Era.Mortal): DictEnum.Entry<BigInteger> {
|
||||
val period = era.period.toLong()
|
||||
val phase = era.phase.toLong()
|
||||
val quantizeFactor = maxOf(period shr 12, 1)
|
||||
|
||||
// Calculate the two-byte encoding
|
||||
val encoded = ((countTrailingZeroBits(period) - 1).coerceIn(1, 15)) or
|
||||
((phase / quantizeFactor).toInt() shl 4)
|
||||
|
||||
val firstByte = encoded and 0xFF
|
||||
val secondByte = (encoded shr 8) and 0xFF
|
||||
|
||||
// DictEnum variant: "MortalX" where X is the first byte
|
||||
// Variant value: second byte as u8 (BigInteger)
|
||||
return DictEnum.Entry(
|
||||
name = "Mortal$firstByte",
|
||||
value = BigInteger.valueOf(secondByte.toLong())
|
||||
)
|
||||
}
|
||||
|
||||
private fun countTrailingZeroBits(value: Long): Int {
|
||||
if (value == 0L) return 64
|
||||
var n = 0
|
||||
var x = value
|
||||
while ((x and 1L) == 0L) {
|
||||
n++
|
||||
x = x shr 1
|
||||
}
|
||||
return n
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -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 handles weight reclamation.
|
||||
* This extension reclaims unused weight after transaction execution.
|
||||
*
|
||||
* In the runtime, WeightReclaim is defined as:
|
||||
* pub struct WeightReclaim<T>(core::marker::PhantomData<T>);
|
||||
*
|
||||
* It uses PhantomData internally, so it has no payload (empty encoding).
|
||||
*/
|
||||
class WeightReclaim : FixedValueTransactionExtension(
|
||||
name = "WeightReclaim",
|
||||
implicit = null,
|
||||
explicit = null // PhantomData encodes to nothing
|
||||
)
|
||||
+23
-1
@@ -47,6 +47,10 @@ class RuntimeFactory(
|
||||
private val gson: Gson,
|
||||
private val concurrencyLimit: Int = 1
|
||||
) {
|
||||
companion object {
|
||||
@Volatile
|
||||
var lastDiagnostics: String = "not yet initialized"
|
||||
}
|
||||
|
||||
private val dispatcher = newLimitedThreadPoolExecutor(concurrencyLimit).asCoroutineDispatcher()
|
||||
private val semaphore = Semaphore(concurrencyLimit)
|
||||
@@ -88,9 +92,13 @@ class RuntimeFactory(
|
||||
)
|
||||
}
|
||||
|
||||
Log.d("RuntimeFactory", "DEBUG: TypesUsage for chain $chainId = $typesUsage")
|
||||
|
||||
val (types, baseHash, ownHash) = when (typesUsage) {
|
||||
TypesUsage.BASE -> {
|
||||
Log.d("RuntimeFactory", "DEBUG: Loading BASE types for $chainId")
|
||||
val (types, baseHash) = constructBaseTypes(typePreset)
|
||||
Log.d("RuntimeFactory", "DEBUG: BASE types loaded, hash=$baseHash, typeCount=${types.size}")
|
||||
|
||||
Triple(types, baseHash, null)
|
||||
}
|
||||
@@ -104,6 +112,15 @@ class RuntimeFactory(
|
||||
}
|
||||
|
||||
val typeRegistry = TypeRegistry(types, DynamicTypeResolver(DynamicTypeResolver.DEFAULT_COMPOUND_EXTENSIONS + GenericsExtension))
|
||||
|
||||
// DEBUG: Check for ExtrinsicSignature
|
||||
val hasExtrinsicSignature = typeRegistry["ExtrinsicSignature"] != null
|
||||
val hasMultiSignature = typeRegistry["MultiSignature"] != null
|
||||
Log.d("RuntimeFactory", "DEBUG: Chain $chainId - ExtrinsicSignature=$hasExtrinsicSignature, MultiSignature=$hasMultiSignature, typesUsage=$typesUsage, typeCount=${types.size}")
|
||||
|
||||
// Store diagnostic info for error messages
|
||||
lastDiagnostics = "typesUsage=$typesUsage, ExtrinsicSig=$hasExtrinsicSignature, MultiSig=$hasMultiSignature, typeCount=${types.size}"
|
||||
|
||||
val runtimeMetadata = VersionedRuntimeBuilder.buildMetadata(metadataReader, typeRegistry)
|
||||
|
||||
ConstructedRuntime(
|
||||
@@ -154,7 +171,12 @@ class RuntimeFactory(
|
||||
|
||||
private suspend fun constructBaseTypes(initialPreset: TypePreset): Pair<TypePreset, String> {
|
||||
val baseTypesRaw = runCatching { runtimeFilesCache.getBaseTypes() }
|
||||
.getOrElse { throw BaseTypesNotInCacheException }
|
||||
.getOrElse {
|
||||
Log.e("RuntimeFactory", "DEBUG: BaseTypes NOT in cache!")
|
||||
throw BaseTypesNotInCacheException
|
||||
}
|
||||
|
||||
Log.d("RuntimeFactory", "DEBUG: BaseTypes loaded, length=${baseTypesRaw.length}, contains ExtrinsicSignature=${baseTypesRaw.contains("ExtrinsicSignature")}")
|
||||
|
||||
val typePreset = parseBaseDefinitions(fromJson(baseTypesRaw), initialPreset)
|
||||
|
||||
|
||||
@@ -5,7 +5,14 @@ import io.novasama.substrate_sdk_android.runtime.definitions.types.primitives.Fi
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.skipAliases
|
||||
|
||||
fun RuntimeSnapshot.isEthereumAddress(): Boolean {
|
||||
val addressType = typeRegistry["Address"]!!.skipAliases()!!
|
||||
// Try different address type names used by different chains
|
||||
val addressType = typeRegistry["Address"]
|
||||
?: typeRegistry["MultiAddress"]
|
||||
?: typeRegistry["sp_runtime::multiaddress::MultiAddress"]
|
||||
?: typeRegistry["pezsp_runtime::multiaddress::MultiAddress"]
|
||||
?: return false // If no address type found, assume not Ethereum
|
||||
|
||||
return addressType is FixedByteArray && addressType.length == 20
|
||||
val resolvedType = addressType.skipAliases() ?: return false
|
||||
|
||||
return resolvedType is FixedByteArray && resolvedType.length == 20
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
VERSION_CODE=6
|
||||
VERSION_CODE=31
|
||||
Reference in New Issue
Block a user