mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-04-22 02:07:58 +00:00
feat(xcm): Add Pezkuwi Teyrchain junction support for cross-chain transfers
- Add TEYRCHAIN_INFO constant and TeyrchainInfo pallet lookup - Add PezkuwiXcm pallet support in xcmPalletName functions - Update ParachainInfoRepository to query TeyrchainId storage - Add junctionTypeName to ParachainId for Teyrchain encoding - Update MultiLocationEncoding to handle both Parachain and Teyrchain - Detect Pezkuwi chains by genesis hash for correct junction type Fixes cross-chain transfers between Pezkuwi, Asset Hub, and People chains.
This commit is contained in:
@@ -301,7 +301,7 @@ fun RuntimeMetadata.identity() = module(Modules.IDENTITY)
|
||||
|
||||
fun RuntimeMetadata.automationTime() = module(Modules.AUTOMATION_TIME)
|
||||
|
||||
fun RuntimeMetadata.parachainInfoOrNull() = moduleOrNull(Modules.PARACHAIN_INFO)
|
||||
fun RuntimeMetadata.parachainInfoOrNull() = firstExistingModuleOrNull(Modules.PARACHAIN_INFO, Modules.TEYRCHAIN_INFO)
|
||||
fun RuntimeMetadata.parasOrNull() = moduleOrNull(Modules.PARAS)
|
||||
|
||||
fun RuntimeMetadata.referenda() = module(Modules.REFERENDA)
|
||||
@@ -382,7 +382,9 @@ fun Module.firstExistingCallName(vararg options: String): String {
|
||||
return options.first(::hasCall)
|
||||
}
|
||||
|
||||
fun RuntimeMetadata.xcmPalletName() = firstExistingModuleName("XcmPallet", "PolkadotXcm")
|
||||
fun RuntimeMetadata.xcmPalletName() = firstExistingModuleName("XcmPallet", "PolkadotXcm", "PezkuwiXcm")
|
||||
|
||||
fun RuntimeMetadata.xcmPalletNameOrNull(): String? = firstExistingModuleOrNull("XcmPallet", "PolkadotXcm", "PezkuwiXcm")?.name
|
||||
|
||||
fun RuntimeMetadata.xTokensName() = firstExistingModuleName("XTokens", "Xtokens")
|
||||
|
||||
@@ -612,6 +614,7 @@ object Modules {
|
||||
const val IDENTITY = "Identity"
|
||||
|
||||
const val PARACHAIN_INFO = "ParachainInfo"
|
||||
const val TEYRCHAIN_INFO = "TeyrchainInfo"
|
||||
const val PARAS = "Paras"
|
||||
|
||||
const val AUTOMATION_TIME = "AutomationTime"
|
||||
|
||||
+15
-1
@@ -3,16 +3,30 @@ package io.novafoundation.nova.feature_wallet_api.data.repository
|
||||
import io.novafoundation.nova.feature_xcm_api.chain.XcmChain
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.AbsoluteMultiLocation
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.ChainLocation
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.MultiLocation.Junction.ParachainId.Companion.JUNCTION_TYPE_PARACHAIN
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.MultiLocation.Junction.ParachainId.Companion.JUNCTION_TYPE_TEYRCHAIN
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.chainLocation
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId
|
||||
import io.novafoundation.nova.runtime.repository.ParachainInfoRepository
|
||||
|
||||
// Pezkuwi chain IDs - these chains use "Teyrchain" instead of "Parachain" in XCM
|
||||
private val PEZKUWI_CHAIN_IDS = setOf(
|
||||
"bb4a61ab0c4b8c12f5eab71d0c86c482e03a275ecdafee678dea712474d33d75", // PEZKUWI
|
||||
"00d0e1d0581c3cd5c5768652d52f4520184018b44f56a2ae1e0dc9d65c00c948", // PEZKUWI_ASSET_HUB
|
||||
"58269e9c184f721e0309332d90cafc410df1519a5dc27a5fd9b3bf5fd2d129f8" // PEZKUWI_PEOPLE
|
||||
)
|
||||
|
||||
private fun junctionTypeNameForChain(chainId: ChainId): String {
|
||||
return if (chainId in PEZKUWI_CHAIN_IDS) JUNCTION_TYPE_TEYRCHAIN else JUNCTION_TYPE_PARACHAIN
|
||||
}
|
||||
|
||||
suspend fun ParachainInfoRepository.getXcmChain(chain: Chain): XcmChain {
|
||||
return XcmChain(paraId(chain.id), chain)
|
||||
}
|
||||
|
||||
suspend fun ParachainInfoRepository.getChainLocation(chainId: ChainId): ChainLocation {
|
||||
val location = AbsoluteMultiLocation.chainLocation(paraId(chainId))
|
||||
val junctionTypeName = junctionTypeNameForChain(chainId)
|
||||
val location = AbsoluteMultiLocation.chainLocation(paraId(chainId), junctionTypeName)
|
||||
return ChainLocation(chainId, location)
|
||||
}
|
||||
|
||||
+14
@@ -1,5 +1,6 @@
|
||||
package io.novafoundation.nova.feature_wallet_impl.data.network.crosschain.common
|
||||
|
||||
import android.util.Log
|
||||
import io.novafoundation.nova.common.di.scope.FeatureScope
|
||||
import io.novafoundation.nova.common.utils.composeCall
|
||||
import io.novafoundation.nova.common.utils.metadata
|
||||
@@ -54,6 +55,19 @@ class TransferAssetUsingTypeTransactor @Inject constructor(
|
||||
|
||||
val transferTypeParam = configuration.transferTypeParam(multiAssetsVersion)
|
||||
|
||||
// Debug logging for XCM transfer
|
||||
val destLocation = configuration.destinationChainLocationOnOrigin()
|
||||
Log.d("XCM_TRANSFER", "=== XCM TRANSFER DEBUG ===")
|
||||
Log.d("XCM_TRANSFER", "Origin chain: ${configuration.originChain.chain.name} (${configuration.originChain.chain.id})")
|
||||
Log.d("XCM_TRANSFER", "Origin parachainId: ${configuration.originChain.parachainId}")
|
||||
Log.d("XCM_TRANSFER", "Destination chain: ${configuration.destinationChain.chain.name} (${configuration.destinationChain.chain.id})")
|
||||
Log.d("XCM_TRANSFER", "Destination parachainId: ${configuration.destinationChain.parachainId}")
|
||||
Log.d("XCM_TRANSFER", "Destination location (relative): parents=${destLocation.parents}, interior=${destLocation.interior}")
|
||||
Log.d("XCM_TRANSFER", "Destination junctions: ${destLocation.interior}")
|
||||
Log.d("XCM_TRANSFER", "Transfer type: ${configuration.transferType}")
|
||||
Log.d("XCM_TRANSFER", "XCM Version: $multiLocationVersion")
|
||||
Log.d("XCM_TRANSFER", "==========================")
|
||||
|
||||
return chainRegistry.withRuntime(configuration.originChainId) {
|
||||
composeCall(
|
||||
moduleName = metadata.xcmPalletName(),
|
||||
|
||||
+11
@@ -1,5 +1,6 @@
|
||||
package io.novafoundation.nova.feature_wallet_impl.data.network.crosschain.dynamic
|
||||
|
||||
import android.util.Log
|
||||
import io.novafoundation.nova.common.address.AccountIdKey
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.WeightV2
|
||||
import io.novafoundation.nova.common.di.scope.FeatureScope
|
||||
@@ -130,6 +131,16 @@ class DynamicCrossChainTransactor @Inject constructor(
|
||||
val totalTransferAmount = transfer.amountPlanks + crossChainFee
|
||||
val assetAbsoluteMultiLocation = configuration.transferType.assetAbsoluteLocation
|
||||
|
||||
// Debug logging for Dynamic XCM transfer
|
||||
Log.d("XCM_DYNAMIC", "=== DYNAMIC XCM TRANSFER DEBUG ===")
|
||||
Log.d("XCM_DYNAMIC", "Origin chain: ${configuration.originChain.chain.name} (${configuration.originChain.chain.id})")
|
||||
Log.d("XCM_DYNAMIC", "Origin parachainId: ${configuration.originChain.parachainId}")
|
||||
Log.d("XCM_DYNAMIC", "Destination chain: ${configuration.destinationChain.chain.name} (${configuration.destinationChain.chain.id})")
|
||||
Log.d("XCM_DYNAMIC", "Destination parachainId: ${configuration.destinationChain.parachainId}")
|
||||
Log.d("XCM_DYNAMIC", "Destination location: ${configuration.destinationChainLocation}")
|
||||
Log.d("XCM_DYNAMIC", "Transfer type: ${configuration.transferType}")
|
||||
Log.d("XCM_DYNAMIC", "================================")
|
||||
|
||||
when (val transferType = configuration.transferType) {
|
||||
is XcmTransferType.Teleport -> buildTeleportProgram(
|
||||
assetLocation = assetAbsoluteMultiLocation,
|
||||
|
||||
+11
-1
@@ -2,17 +2,27 @@ package io.novafoundation.nova.feature_xcm_api.chain
|
||||
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.AbsoluteMultiLocation
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.ChainLocation
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.MultiLocation.Junction.ParachainId.Companion.JUNCTION_TYPE_PARACHAIN
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.MultiLocation.Junction.ParachainId.Companion.JUNCTION_TYPE_TEYRCHAIN
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.chainLocation
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import java.math.BigInteger
|
||||
|
||||
// Pezkuwi chain IDs - these chains use "Teyrchain" instead of "Parachain" in XCM
|
||||
private val PEZKUWI_CHAIN_IDS = setOf(
|
||||
"bb4a61ab0c4b8c12f5eab71d0c86c482e03a275ecdafee678dea712474d33d75", // PEZKUWI
|
||||
"00d0e1d0581c3cd5c5768652d52f4520184018b44f56a2ae1e0dc9d65c00c948", // PEZKUWI_ASSET_HUB
|
||||
"58269e9c184f721e0309332d90cafc410df1519a5dc27a5fd9b3bf5fd2d129f8" // PEZKUWI_PEOPLE
|
||||
)
|
||||
|
||||
class XcmChain(
|
||||
val parachainId: BigInteger?,
|
||||
val chain: Chain
|
||||
)
|
||||
|
||||
fun XcmChain.absoluteLocation(): AbsoluteMultiLocation {
|
||||
return AbsoluteMultiLocation.chainLocation(parachainId)
|
||||
val junctionTypeName = if (chain.id in PEZKUWI_CHAIN_IDS) JUNCTION_TYPE_TEYRCHAIN else JUNCTION_TYPE_PARACHAIN
|
||||
return AbsoluteMultiLocation.chainLocation(parachainId, junctionTypeName)
|
||||
}
|
||||
|
||||
fun XcmChain.isRelay(): Boolean {
|
||||
|
||||
+5
-2
@@ -41,6 +41,9 @@ class AbsoluteMultiLocation(
|
||||
}
|
||||
}
|
||||
|
||||
fun AbsoluteMultiLocation.Companion.chainLocation(parachainId: ParaId?): AbsoluteMultiLocation {
|
||||
return listOfNotNull(parachainId?.let(MultiLocation.Junction::ParachainId)).asLocation()
|
||||
fun AbsoluteMultiLocation.Companion.chainLocation(
|
||||
parachainId: ParaId?,
|
||||
junctionTypeName: String = MultiLocation.Junction.ParachainId.JUNCTION_TYPE_PARACHAIN
|
||||
): AbsoluteMultiLocation {
|
||||
return listOfNotNull(parachainId?.let { MultiLocation.Junction.ParachainId(it, junctionTypeName) }).asLocation()
|
||||
}
|
||||
|
||||
+10
-2
@@ -37,9 +37,17 @@ abstract class MultiLocation(
|
||||
|
||||
sealed class Junction {
|
||||
|
||||
data class ParachainId(val id: ParaId) : Junction() {
|
||||
data class ParachainId(
|
||||
val id: ParaId,
|
||||
val junctionTypeName: String = JUNCTION_TYPE_PARACHAIN
|
||||
) : Junction() {
|
||||
|
||||
constructor(id: Int) : this(id.toBigInteger())
|
||||
constructor(id: Int, junctionTypeName: String = JUNCTION_TYPE_PARACHAIN) : this(id.toBigInteger(), junctionTypeName)
|
||||
|
||||
companion object {
|
||||
const val JUNCTION_TYPE_PARACHAIN = "Parachain"
|
||||
const val JUNCTION_TYPE_TEYRCHAIN = "Teyrchain"
|
||||
}
|
||||
}
|
||||
|
||||
data class GeneralKey(val key: HexString) : Junction()
|
||||
|
||||
+7
-2
@@ -1,5 +1,6 @@
|
||||
package io.novafoundation.nova.feature_xcm_api.multiLocation
|
||||
|
||||
import android.util.Log
|
||||
import io.novafoundation.nova.common.address.AccountIdKey
|
||||
import io.novafoundation.nova.common.address.intoKey
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindAccountId
|
||||
@@ -69,7 +70,8 @@ private fun bindJunction(instance: Any?): Junction {
|
||||
return when (asDictEnum.name) {
|
||||
"GeneralKey" -> Junction.GeneralKey(bindGeneralKey(asDictEnum.value))
|
||||
"PalletInstance" -> Junction.PalletInstance(bindNumber(asDictEnum.value))
|
||||
"Parachain" -> Junction.ParachainId(bindNumber(asDictEnum.value))
|
||||
// Accept both "Parachain" (Polkadot ecosystem) and "Teyrchain" (Pezkuwi ecosystem)
|
||||
"Parachain", "Teyrchain" -> Junction.ParachainId(bindNumber(asDictEnum.value), asDictEnum.name)
|
||||
"GeneralIndex" -> Junction.GeneralIndex(bindNumber(asDictEnum.value))
|
||||
"GlobalConsensus" -> bindGlobalConsensusJunction(asDictEnum.value)
|
||||
"AccountKey20" -> Junction.AccountKey20(bindAccountIdJunction(asDictEnum.value, accountIdKey = "key"))
|
||||
@@ -146,7 +148,10 @@ private fun MultiLocation.Interior.toEncodableInstance(xcmVersion: XcmVersion) =
|
||||
private fun Junction.toEncodableInstance(xcmVersion: XcmVersion) = when (this) {
|
||||
is Junction.GeneralKey -> DictEnum.Entry("GeneralKey", encodableGeneralKey(xcmVersion, key))
|
||||
is Junction.PalletInstance -> DictEnum.Entry("PalletInstance", index)
|
||||
is Junction.ParachainId -> DictEnum.Entry("Parachain", id)
|
||||
is Junction.ParachainId -> {
|
||||
Log.d("XCM_ENCODE", "Encoding ParachainId: id=$id, junctionTypeName=$junctionTypeName")
|
||||
DictEnum.Entry(junctionTypeName, id)
|
||||
}
|
||||
is Junction.AccountKey20 -> DictEnum.Entry("AccountKey20", accountId.toJunctionAccountIdInstance(accountIdKey = "key", xcmVersion))
|
||||
is Junction.AccountId32 -> DictEnum.Entry("AccountId32", accountId.toJunctionAccountIdInstance(accountIdKey = "id", xcmVersion))
|
||||
is Junction.GeneralIndex -> DictEnum.Entry("GeneralIndex", index)
|
||||
|
||||
+11
-3
@@ -2,10 +2,12 @@ package io.novafoundation.nova.runtime.repository
|
||||
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.ParaId
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindNumber
|
||||
import io.novafoundation.nova.common.utils.parachainInfoOrNull
|
||||
import io.novafoundation.nova.common.utils.Modules
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.ChainId
|
||||
import io.novafoundation.nova.runtime.storage.source.StorageDataSource
|
||||
import io.novasama.substrate_sdk_android.runtime.metadata.storage
|
||||
import io.novasama.substrate_sdk_android.runtime.metadata.module.Module
|
||||
import io.novasama.substrate_sdk_android.runtime.metadata.moduleOrNull
|
||||
import io.novasama.substrate_sdk_android.runtime.metadata.storageOrNull
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
@@ -26,7 +28,13 @@ internal class RealParachainInfoRepository(
|
||||
paraIdCache.getValue(chainId)
|
||||
} else {
|
||||
remoteStorageSource.query(chainId) {
|
||||
runtime.metadata.parachainInfoOrNull()?.storage("ParachainId")?.query(binding = ::bindNumber)
|
||||
// Try Polkadot-style first (ParachainInfo.ParachainId)
|
||||
// Then try Pezkuwi-style (TeyrchainInfo.TeyrchainId)
|
||||
val polkadotModule = runtime.metadata.moduleOrNull(Modules.PARACHAIN_INFO)
|
||||
val pezkuwiModule = runtime.metadata.moduleOrNull(Modules.TEYRCHAIN_INFO)
|
||||
|
||||
polkadotModule?.storageOrNull("ParachainId")?.query(binding = ::bindNumber)
|
||||
?: pezkuwiModule?.storageOrNull("TeyrchainId")?.query(binding = ::bindNumber)
|
||||
}
|
||||
.also { paraIdCache[chainId] = it }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user