From fb97e696fd2030212e15f42887aaa95ca9fa4628 Mon Sep 17 00:00:00 2001 From: Kurdistan Tech Ministry Date: Sat, 14 Feb 2026 04:41:21 +0300 Subject: [PATCH] feat: add Tiki GenesisConfig for Collection 0 bootstrap and XCM creation tools - Add GenesisConfig to pezpallet-tiki that creates NFT Collection 0 and mints NFT #0 for the founding citizen at genesis, solving the chicken-egg problem where Collection 0 must exist before any citizenship NFTs can be minted - Wire founding_citizen parameter through all People Chain genesis presets (genesis, local_testnet, dev) - Add create_nft_collection.rs XCM script for creating Collection 0 on live chain via sudo XCM Transact from Relay Chain - Add comprehensive_test.rs for post-upgrade chain testing - Fix clippy warnings (map().flatten() -> and_then()) and apply taplo formatting --- Cargo.toml | 2 +- .../teyrchains/pezpallets/tiki/Cargo.toml | 70 +- .../teyrchains/pezpallets/tiki/src/lib.rs | 72 ++ .../teyrchains/pezpallets/tiki/src/mock.rs | 2 +- .../people/people-pezkuwichain/Cargo.toml | 502 ++++++------ .../src/genesis_config_presets.rs | 17 +- .../subxt/examples/comprehensive_test.rs | 731 ++++++++++++++++++ .../subxt/examples/create_nft_collection.rs | 322 ++++++++ 8 files changed, 1429 insertions(+), 289 deletions(-) create mode 100644 vendor/pezkuwi-subxt/subxt/examples/comprehensive_test.rs create mode 100644 vendor/pezkuwi-subxt/subxt/examples/create_nft_collection.rs diff --git a/Cargo.toml b/Cargo.toml index 4f52aff7..8b0a1746 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1521,7 +1521,7 @@ pezkuwi-subxt-codegen = { path = "vendor/pezkuwi-subxt/codegen", version = "0.44 pezkuwi-subxt-core = { path = "vendor/pezkuwi-subxt/core", version = "0.44.0", default-features = false } pezkuwi-subxt-lightclient = { path = "vendor/pezkuwi-subxt/lightclient", version = "0.44.0", default-features = false } pezkuwi-subxt-macro = { path = "vendor/pezkuwi-subxt/macro", version = "0.44.0" } -pezkuwi-subxt-metadata = { path = "vendor/pezkuwi-subxt/metadata", version = "0.44.0", default-features = false } +pezkuwi-subxt-metadata = { path = "vendor/pezkuwi-subxt/metadata", version = "0.44.0", default-features = false, features = ["std"] } pezkuwi-subxt-rpcs = { path = "vendor/pezkuwi-subxt/rpcs", version = "0.44.0" } pezkuwi-subxt-signer = { path = "vendor/pezkuwi-subxt/signer", version = "0.44.0" } pezkuwi-subxt-utils-fetchmetadata = { path = "vendor/pezkuwi-subxt/utils/fetch-metadata", version = "0.44.0" } diff --git a/pezcumulus/teyrchains/pezpallets/tiki/Cargo.toml b/pezcumulus/teyrchains/pezpallets/tiki/Cargo.toml index 742cc0e4..4a6d9b95 100644 --- a/pezcumulus/teyrchains/pezpallets/tiki/Cargo.toml +++ b/pezcumulus/teyrchains/pezpallets/tiki/Cargo.toml @@ -26,7 +26,7 @@ pezpallet-nfts = { default-features = false, workspace = true } pezsp-runtime = { default-features = false, workspace = true } pezsp-std = { default-features = false, workspace = true } scale-info = { default-features = false, features = [ - "derive", + "derive", ], workspace = true } serde = { version = "1.0", default-features = false, features = ["derive"] } @@ -37,44 +37,44 @@ pezsp-io = { workspace = true } [features] default = ["std"] std = [ - "codec/std", - "log/std", - "pezframe-benchmarking?/std", - "pezframe-support/std", - "pezframe-system/std", - "pezpallet-balances/std", - "pezpallet-identity-kyc/std", - "pezpallet-identity/std", - "pezpallet-nfts/std", - "pezsp-core/std", - "pezsp-io/std", - "pezsp-runtime/std", - "pezsp-std/std", - "scale-info/std", - "serde/std", + "codec/std", + "log/std", + "pezframe-benchmarking?/std", + "pezframe-support/std", + "pezframe-system/std", + "pezpallet-balances/std", + "pezpallet-identity-kyc/std", + "pezpallet-identity/std", + "pezpallet-nfts/std", + "pezsp-core/std", + "pezsp-io/std", + "pezsp-runtime/std", + "pezsp-std/std", + "scale-info/std", + "serde/std", ] runtime-benchmarks = [ - "dep:pezframe-benchmarking", - "dep:pezpallet-balances", - "pezframe-benchmarking/runtime-benchmarks", - "pezframe-support/runtime-benchmarks", - "pezframe-system/runtime-benchmarks", - "pezpallet-balances/runtime-benchmarks", - "pezpallet-identity-kyc/runtime-benchmarks", - "pezpallet-identity/runtime-benchmarks", - "pezpallet-nfts/runtime-benchmarks", - "pezsp-io/runtime-benchmarks", - "pezsp-runtime/runtime-benchmarks", + "dep:pezframe-benchmarking", + "dep:pezpallet-balances", + "pezframe-benchmarking/runtime-benchmarks", + "pezframe-support/runtime-benchmarks", + "pezframe-system/runtime-benchmarks", + "pezpallet-balances/runtime-benchmarks", + "pezpallet-identity-kyc/runtime-benchmarks", + "pezpallet-identity/runtime-benchmarks", + "pezpallet-nfts/runtime-benchmarks", + "pezsp-io/runtime-benchmarks", + "pezsp-runtime/runtime-benchmarks", ] try-runtime = [ - "pezframe-benchmarking?/try-runtime", - "pezframe-support/try-runtime", - "pezframe-system/try-runtime", - "pezpallet-balances/try-runtime", - "pezpallet-identity-kyc/try-runtime", - "pezpallet-identity/try-runtime", - "pezpallet-nfts/try-runtime", - "pezsp-runtime/try-runtime", + "pezframe-benchmarking?/try-runtime", + "pezframe-support/try-runtime", + "pezframe-system/try-runtime", + "pezpallet-balances/try-runtime", + "pezpallet-identity-kyc/try-runtime", + "pezpallet-identity/try-runtime", + "pezpallet-nfts/try-runtime", + "pezsp-runtime/try-runtime", ] serde = [] experimental = [] diff --git a/pezcumulus/teyrchains/pezpallets/tiki/src/lib.rs b/pezcumulus/teyrchains/pezpallets/tiki/src/lib.rs index 74ba503b..7ea7a78d 100644 --- a/pezcumulus/teyrchains/pezpallets/tiki/src/lib.rs +++ b/pezcumulus/teyrchains/pezpallets/tiki/src/lib.rs @@ -343,6 +343,78 @@ pub mod pezpallet { // Citizenship NFT minting is handled by CitizenNftProvider hooks, // no per-block scanning needed. + // ============= GENESIS CONFIG ============= + + /// Genesis configuration for bootstrapping Collection 0 and founding citizen NFT. + /// + /// When `founding_citizen` is `Some(account)`, genesis will: + /// 1. Create NFT Collection 0 in pezpallet_nfts (with DepositRequired disabled) + /// 2. Mint NFT Item #0 for the founding citizen + /// 3. Populate CitizenNft, NextItemId, and UserTikis storage + #[pezpallet::genesis_config] + #[derive(pezframe_support::DefaultNoBound)] + pub struct GenesisConfig { + /// Optional founding citizen who receives NFT #0 at genesis. + /// If None, Collection 0 is NOT created (must be created via sudo later). + pub founding_citizen: Option, + } + + #[pezpallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + use pezsp_runtime::traits::Zero; + + let collection_id = T::TikiCollectionId::get(); + + if let Some(ref founder) = self.founding_citizen { + // Step 1: Create Collection 0 in pezpallet_nfts + // Disable DepositRequired so genesis minting doesn't need balance + let collection_config = pezpallet_nfts::CollectionConfig { + settings: pezpallet_nfts::CollectionSettings( + pezpallet_nfts::CollectionSetting::DepositRequired.into(), + ), + max_supply: None, + mint_settings: Default::default(), + }; + + pezpallet_nfts::Pezpallet::::do_create_collection( + collection_id, + founder.clone(), + founder.clone(), + collection_config, + Zero::zero(), + pezpallet_nfts::Event::ForceCreated { + collection: collection_id, + owner: founder.clone(), + }, + ) + .expect("Tiki genesis: failed to create Collection 0"); + + // Step 2: Mint NFT #0 for the founding citizen + let item_config = pezpallet_nfts::ItemConfig { + settings: pezpallet_nfts::ItemSettings::all_enabled(), + }; + + pezpallet_nfts::Pezpallet::::do_mint( + collection_id, + 0u32, + None, + founder.clone(), + item_config, + |_, _| Ok(()), + ) + .expect("Tiki genesis: failed to mint NFT #0"); + + // Step 3: Update Tiki storage + CitizenNft::::insert(founder, 0u32); + NextItemId::::put(1u32); + UserTikis::::mutate(founder, |tikis| { + let _ = tikis.try_push(Tiki::Welati); + }); + } + } + } + #[pezpallet::call] impl Pezpallet { /// Admin tarafından belirli bir kullanıcıya Tiki (rol) verme diff --git a/pezcumulus/teyrchains/pezpallets/tiki/src/mock.rs b/pezcumulus/teyrchains/pezpallets/tiki/src/mock.rs index 21e9a799..fc37c13d 100644 --- a/pezcumulus/teyrchains/pezpallets/tiki/src/mock.rs +++ b/pezcumulus/teyrchains/pezpallets/tiki/src/mock.rs @@ -68,7 +68,7 @@ construct_runtime!( Identity: pezpallet_identity::{Pezpallet, Call, Storage, Event}, IdentityKyc: pezpallet_identity_kyc::{Pezpallet, Call, Storage, Event}, Nfts: pezpallet_nfts::{Pezpallet, Call, Storage, Event}, - Tiki: pezpallet_tiki::{Pezpallet, Call, Storage, Event}, + Tiki: pezpallet_tiki::{Pezpallet, Call, Config, Storage, Event}, } ); diff --git a/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/Cargo.toml b/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/Cargo.toml index 292481cd..8518bb5b 100644 --- a/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/Cargo.toml +++ b/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/Cargo.toml @@ -19,7 +19,7 @@ enumflags2 = { workspace = true } hex-literal = { workspace = true } scale-info = { features = ["derive"], workspace = true } serde = { optional = true, features = [ - "derive", + "derive", ], workspace = true, default-features = true } serde_json = { features = ["alloc"], workspace = true } tracing = { workspace = true } @@ -114,240 +114,240 @@ bizinikiwi-wasm-builder = { optional = true, workspace = true, default-features [features] default = ["std"] std = [ - "bizinikiwi-wasm-builder", - "bizinikiwi-wasm-builder?/std", - "codec/std", - "enumflags2/std", - "pezcumulus-pezpallet-aura-ext/std", - "pezcumulus-pezpallet-session-benchmarking/std", - "pezcumulus-pezpallet-teyrchain-system/std", - "pezcumulus-pezpallet-weight-reclaim/std", - "pezcumulus-pezpallet-xcm/std", - "pezcumulus-pezpallet-xcmp-queue/std", - "pezcumulus-primitives-aura/std", - "pezcumulus-primitives-core/std", - "pezcumulus-primitives-utility/std", - "pezframe-benchmarking?/std", - "pezframe-executive/std", - "pezframe-support/std", - "pezframe-system-benchmarking?/std", - "pezframe-system-rpc-runtime-api/std", - "pezframe-system/std", - "pezframe-try-runtime?/std", - "pezkuwi-primitives/std", - "pezkuwi-runtime-common/std", - "pezkuwi-teyrchain-primitives/std", - "pezkuwichain-runtime-constants/std", - "pezpallet-assets/std", - "pezpallet-aura/std", - "pezpallet-authorship/std", - "pezpallet-balances/std", - "pezpallet-collator-selection/std", - "pezpallet-collective/std", - "pezpallet-democracy/std", - "pezpallet-elections-phragmen/std", - "pezpallet-identity-kyc/std", - "pezpallet-identity/std", - "pezpallet-message-queue/std", - "pezpallet-migrations/std", - "pezpallet-multisig/std", - "pezpallet-nfts/std", - "pezpallet-perwerde/std", - "pezpallet-pez-rewards/std", - "pezpallet-proxy/std", - "pezpallet-recovery/std", - "pezpallet-referral/std", - "pezpallet-scheduler/std", - "pezpallet-session/std", - "pezpallet-skip-feeless-payment/std", - "pezpallet-society/std", - "pezpallet-staking-score/std", - "pezpallet-tiki/std", - "pezpallet-timestamp/std", - "pezpallet-transaction-payment-rpc-runtime-api/std", - "pezpallet-transaction-payment/std", - "pezpallet-trust/std", - "pezpallet-utility/std", - "pezpallet-vesting/std", - "pezpallet-welati/std", - "pezpallet-xcm-benchmarks?/std", - "pezpallet-xcm/std", - "pezsp-api/std", - "pezsp-block-builder/std", - "pezsp-consensus-aura/std", - "pezsp-core/std", - "pezsp-genesis-builder/std", - "pezsp-inherents/std", - "pezsp-keyring/std", - "pezsp-offchain/std", - "pezsp-runtime/std", - "pezsp-session/std", - "pezsp-staking/std", - "pezsp-storage/std", - "pezsp-transaction-pool/std", - "pezsp-version/std", - "scale-info/std", - "serde", - "serde_json/std", - "testnet-teyrchains-constants/std", - "teyrchain-info/std", - "teyrchains-common/std", - "tracing/std", - "xcm-builder/std", - "xcm-executor/std", - "xcm-runtime-pezapis/std", - "xcm/std", + "bizinikiwi-wasm-builder", + "bizinikiwi-wasm-builder?/std", + "codec/std", + "enumflags2/std", + "pezcumulus-pezpallet-aura-ext/std", + "pezcumulus-pezpallet-session-benchmarking/std", + "pezcumulus-pezpallet-teyrchain-system/std", + "pezcumulus-pezpallet-weight-reclaim/std", + "pezcumulus-pezpallet-xcm/std", + "pezcumulus-pezpallet-xcmp-queue/std", + "pezcumulus-primitives-aura/std", + "pezcumulus-primitives-core/std", + "pezcumulus-primitives-utility/std", + "pezframe-benchmarking?/std", + "pezframe-executive/std", + "pezframe-support/std", + "pezframe-system-benchmarking?/std", + "pezframe-system-rpc-runtime-api/std", + "pezframe-system/std", + "pezframe-try-runtime?/std", + "pezkuwi-primitives/std", + "pezkuwi-runtime-common/std", + "pezkuwi-teyrchain-primitives/std", + "pezkuwichain-runtime-constants/std", + "pezpallet-assets/std", + "pezpallet-aura/std", + "pezpallet-authorship/std", + "pezpallet-balances/std", + "pezpallet-collator-selection/std", + "pezpallet-collective/std", + "pezpallet-democracy/std", + "pezpallet-elections-phragmen/std", + "pezpallet-identity-kyc/std", + "pezpallet-identity/std", + "pezpallet-message-queue/std", + "pezpallet-migrations/std", + "pezpallet-multisig/std", + "pezpallet-nfts/std", + "pezpallet-perwerde/std", + "pezpallet-pez-rewards/std", + "pezpallet-proxy/std", + "pezpallet-recovery/std", + "pezpallet-referral/std", + "pezpallet-scheduler/std", + "pezpallet-session/std", + "pezpallet-skip-feeless-payment/std", + "pezpallet-society/std", + "pezpallet-staking-score/std", + "pezpallet-tiki/std", + "pezpallet-timestamp/std", + "pezpallet-transaction-payment-rpc-runtime-api/std", + "pezpallet-transaction-payment/std", + "pezpallet-trust/std", + "pezpallet-utility/std", + "pezpallet-vesting/std", + "pezpallet-welati/std", + "pezpallet-xcm-benchmarks?/std", + "pezpallet-xcm/std", + "pezsp-api/std", + "pezsp-block-builder/std", + "pezsp-consensus-aura/std", + "pezsp-core/std", + "pezsp-genesis-builder/std", + "pezsp-inherents/std", + "pezsp-keyring/std", + "pezsp-offchain/std", + "pezsp-runtime/std", + "pezsp-session/std", + "pezsp-staking/std", + "pezsp-storage/std", + "pezsp-transaction-pool/std", + "pezsp-version/std", + "scale-info/std", + "serde", + "serde_json/std", + "testnet-teyrchains-constants/std", + "teyrchain-info/std", + "teyrchains-common/std", + "tracing/std", + "xcm-builder/std", + "xcm-executor/std", + "xcm-runtime-pezapis/std", + "xcm/std", ] runtime-benchmarks = [ - "bizinikiwi-wasm-builder?/runtime-benchmarks", - "pezcumulus-pezpallet-aura-ext/runtime-benchmarks", - "pezcumulus-pezpallet-session-benchmarking/runtime-benchmarks", - "pezcumulus-pezpallet-teyrchain-system/runtime-benchmarks", - "pezcumulus-pezpallet-weight-reclaim/runtime-benchmarks", - "pezcumulus-pezpallet-xcm/runtime-benchmarks", - "pezcumulus-pezpallet-xcmp-queue/runtime-benchmarks", - "pezcumulus-primitives-aura/runtime-benchmarks", - "pezcumulus-primitives-core/runtime-benchmarks", - "pezcumulus-primitives-utility/runtime-benchmarks", - "pezframe-benchmarking/runtime-benchmarks", - "pezframe-executive/runtime-benchmarks", - "pezframe-support/runtime-benchmarks", - "pezframe-system-benchmarking/runtime-benchmarks", - "pezframe-system-rpc-runtime-api/runtime-benchmarks", - "pezframe-system/runtime-benchmarks", - "pezframe-try-runtime?/runtime-benchmarks", - "pezkuwi-primitives/runtime-benchmarks", - "pezkuwi-runtime-common/runtime-benchmarks", - "pezkuwi-teyrchain-primitives/runtime-benchmarks", - "pezkuwichain-runtime-constants/runtime-benchmarks", - "pezpallet-assets/runtime-benchmarks", - "pezpallet-aura/runtime-benchmarks", - "pezpallet-authorship/runtime-benchmarks", - "pezpallet-balances/runtime-benchmarks", - "pezpallet-collator-selection/runtime-benchmarks", - "pezpallet-collective/runtime-benchmarks", - "pezpallet-democracy/runtime-benchmarks", - "pezpallet-elections-phragmen/runtime-benchmarks", - "pezpallet-identity-kyc/runtime-benchmarks", - "pezpallet-identity/runtime-benchmarks", - "pezpallet-message-queue/runtime-benchmarks", - "pezpallet-migrations/runtime-benchmarks", - "pezpallet-multisig/runtime-benchmarks", - "pezpallet-nfts/runtime-benchmarks", - "pezpallet-perwerde/runtime-benchmarks", - "pezpallet-pez-rewards/runtime-benchmarks", - "pezpallet-proxy/runtime-benchmarks", - "pezpallet-recovery/runtime-benchmarks", - "pezpallet-referral/runtime-benchmarks", - "pezpallet-scheduler/runtime-benchmarks", - "pezpallet-session/runtime-benchmarks", - "pezpallet-skip-feeless-payment/runtime-benchmarks", - "pezpallet-society/runtime-benchmarks", - "pezpallet-staking-score/runtime-benchmarks", - "pezpallet-tiki/runtime-benchmarks", - "pezpallet-timestamp/runtime-benchmarks", - "pezpallet-transaction-payment-rpc-runtime-api/runtime-benchmarks", - "pezpallet-transaction-payment/runtime-benchmarks", - "pezpallet-trust/runtime-benchmarks", - "pezpallet-utility/runtime-benchmarks", - "pezpallet-vesting/runtime-benchmarks", - "pezpallet-welati/runtime-benchmarks", - "pezpallet-xcm-benchmarks/runtime-benchmarks", - "pezpallet-xcm/runtime-benchmarks", - "pezsp-api/runtime-benchmarks", - "pezsp-block-builder/runtime-benchmarks", - "pezsp-consensus-aura/runtime-benchmarks", - "pezsp-genesis-builder/runtime-benchmarks", - "pezsp-inherents/runtime-benchmarks", - "pezsp-keyring/runtime-benchmarks", - "pezsp-offchain/runtime-benchmarks", - "pezsp-runtime/runtime-benchmarks", - "pezsp-session/runtime-benchmarks", - "pezsp-staking/runtime-benchmarks", - "pezsp-transaction-pool/runtime-benchmarks", - "pezsp-version/runtime-benchmarks", - "testnet-teyrchains-constants/runtime-benchmarks", - "teyrchain-info/runtime-benchmarks", - "teyrchains-common/runtime-benchmarks", - "teyrchains-runtimes-test-utils/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", - "xcm-executor/runtime-benchmarks", - "xcm-runtime-pezapis/runtime-benchmarks", - "xcm/runtime-benchmarks", + "bizinikiwi-wasm-builder?/runtime-benchmarks", + "pezcumulus-pezpallet-aura-ext/runtime-benchmarks", + "pezcumulus-pezpallet-session-benchmarking/runtime-benchmarks", + "pezcumulus-pezpallet-teyrchain-system/runtime-benchmarks", + "pezcumulus-pezpallet-weight-reclaim/runtime-benchmarks", + "pezcumulus-pezpallet-xcm/runtime-benchmarks", + "pezcumulus-pezpallet-xcmp-queue/runtime-benchmarks", + "pezcumulus-primitives-aura/runtime-benchmarks", + "pezcumulus-primitives-core/runtime-benchmarks", + "pezcumulus-primitives-utility/runtime-benchmarks", + "pezframe-benchmarking/runtime-benchmarks", + "pezframe-executive/runtime-benchmarks", + "pezframe-support/runtime-benchmarks", + "pezframe-system-benchmarking/runtime-benchmarks", + "pezframe-system-rpc-runtime-api/runtime-benchmarks", + "pezframe-system/runtime-benchmarks", + "pezframe-try-runtime?/runtime-benchmarks", + "pezkuwi-primitives/runtime-benchmarks", + "pezkuwi-runtime-common/runtime-benchmarks", + "pezkuwi-teyrchain-primitives/runtime-benchmarks", + "pezkuwichain-runtime-constants/runtime-benchmarks", + "pezpallet-assets/runtime-benchmarks", + "pezpallet-aura/runtime-benchmarks", + "pezpallet-authorship/runtime-benchmarks", + "pezpallet-balances/runtime-benchmarks", + "pezpallet-collator-selection/runtime-benchmarks", + "pezpallet-collective/runtime-benchmarks", + "pezpallet-democracy/runtime-benchmarks", + "pezpallet-elections-phragmen/runtime-benchmarks", + "pezpallet-identity-kyc/runtime-benchmarks", + "pezpallet-identity/runtime-benchmarks", + "pezpallet-message-queue/runtime-benchmarks", + "pezpallet-migrations/runtime-benchmarks", + "pezpallet-multisig/runtime-benchmarks", + "pezpallet-nfts/runtime-benchmarks", + "pezpallet-perwerde/runtime-benchmarks", + "pezpallet-pez-rewards/runtime-benchmarks", + "pezpallet-proxy/runtime-benchmarks", + "pezpallet-recovery/runtime-benchmarks", + "pezpallet-referral/runtime-benchmarks", + "pezpallet-scheduler/runtime-benchmarks", + "pezpallet-session/runtime-benchmarks", + "pezpallet-skip-feeless-payment/runtime-benchmarks", + "pezpallet-society/runtime-benchmarks", + "pezpallet-staking-score/runtime-benchmarks", + "pezpallet-tiki/runtime-benchmarks", + "pezpallet-timestamp/runtime-benchmarks", + "pezpallet-transaction-payment-rpc-runtime-api/runtime-benchmarks", + "pezpallet-transaction-payment/runtime-benchmarks", + "pezpallet-trust/runtime-benchmarks", + "pezpallet-utility/runtime-benchmarks", + "pezpallet-vesting/runtime-benchmarks", + "pezpallet-welati/runtime-benchmarks", + "pezpallet-xcm-benchmarks/runtime-benchmarks", + "pezpallet-xcm/runtime-benchmarks", + "pezsp-api/runtime-benchmarks", + "pezsp-block-builder/runtime-benchmarks", + "pezsp-consensus-aura/runtime-benchmarks", + "pezsp-genesis-builder/runtime-benchmarks", + "pezsp-inherents/runtime-benchmarks", + "pezsp-keyring/runtime-benchmarks", + "pezsp-offchain/runtime-benchmarks", + "pezsp-runtime/runtime-benchmarks", + "pezsp-session/runtime-benchmarks", + "pezsp-staking/runtime-benchmarks", + "pezsp-transaction-pool/runtime-benchmarks", + "pezsp-version/runtime-benchmarks", + "testnet-teyrchains-constants/runtime-benchmarks", + "teyrchain-info/runtime-benchmarks", + "teyrchains-common/runtime-benchmarks", + "teyrchains-runtimes-test-utils/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", + "xcm-runtime-pezapis/runtime-benchmarks", + "xcm/runtime-benchmarks", ] try-runtime = [ - "pezcumulus-pezpallet-aura-ext/try-runtime", - "pezcumulus-pezpallet-session-benchmarking/try-runtime", - "pezcumulus-pezpallet-teyrchain-system/try-runtime", - "pezcumulus-pezpallet-weight-reclaim/try-runtime", - "pezcumulus-pezpallet-xcm/try-runtime", - "pezcumulus-pezpallet-xcmp-queue/try-runtime", - "pezcumulus-primitives-core/try-runtime", - "pezcumulus-primitives-utility/try-runtime", - "pezframe-benchmarking?/try-runtime", - "pezframe-executive/try-runtime", - "pezframe-support/try-runtime", - "pezframe-system-benchmarking?/try-runtime", - "pezframe-system/try-runtime", - "pezframe-try-runtime/try-runtime", - "pezkuwi-primitives/try-runtime", - "pezkuwi-runtime-common/try-runtime", - "pezkuwi-teyrchain-primitives/try-runtime", - "pezkuwichain-runtime-constants/try-runtime", - "pezpallet-assets/try-runtime", - "pezpallet-aura/try-runtime", - "pezpallet-authorship/try-runtime", - "pezpallet-balances/try-runtime", - "pezpallet-collator-selection/try-runtime", - "pezpallet-collective/try-runtime", - "pezpallet-democracy/try-runtime", - "pezpallet-elections-phragmen/try-runtime", - "pezpallet-identity-kyc/try-runtime", - "pezpallet-identity/try-runtime", - "pezpallet-message-queue/try-runtime", - "pezpallet-migrations/try-runtime", - "pezpallet-multisig/try-runtime", - "pezpallet-nfts/try-runtime", - "pezpallet-perwerde/try-runtime", - "pezpallet-pez-rewards/try-runtime", - "pezpallet-proxy/try-runtime", - "pezpallet-recovery/try-runtime", - "pezpallet-referral/try-runtime", - "pezpallet-scheduler/try-runtime", - "pezpallet-session/try-runtime", - "pezpallet-skip-feeless-payment/try-runtime", - "pezpallet-society/try-runtime", - "pezpallet-staking-score/try-runtime", - "pezpallet-tiki/try-runtime", - "pezpallet-timestamp/try-runtime", - "pezpallet-transaction-payment-rpc-runtime-api/try-runtime", - "pezpallet-transaction-payment/try-runtime", - "pezpallet-trust/try-runtime", - "pezpallet-utility/try-runtime", - "pezpallet-vesting/try-runtime", - "pezpallet-welati/try-runtime", - "pezpallet-xcm-benchmarks?/try-runtime", - "pezpallet-xcm/try-runtime", - "pezsp-api/try-runtime", - "pezsp-block-builder/try-runtime", - "pezsp-consensus-aura/try-runtime", - "pezsp-genesis-builder/try-runtime", - "pezsp-inherents/try-runtime", - "pezsp-keyring/try-runtime", - "pezsp-offchain/try-runtime", - "pezsp-runtime/try-runtime", - "pezsp-session/try-runtime", - "pezsp-staking/try-runtime", - "pezsp-transaction-pool/try-runtime", - "pezsp-version/try-runtime", - "testnet-teyrchains-constants/try-runtime", - "teyrchain-info/try-runtime", - "teyrchains-common/try-runtime", - "teyrchains-runtimes-test-utils/try-runtime", - "xcm-builder/try-runtime", - "xcm-executor/try-runtime", - "xcm-runtime-pezapis/try-runtime", - "xcm/try-runtime", + "pezcumulus-pezpallet-aura-ext/try-runtime", + "pezcumulus-pezpallet-session-benchmarking/try-runtime", + "pezcumulus-pezpallet-teyrchain-system/try-runtime", + "pezcumulus-pezpallet-weight-reclaim/try-runtime", + "pezcumulus-pezpallet-xcm/try-runtime", + "pezcumulus-pezpallet-xcmp-queue/try-runtime", + "pezcumulus-primitives-core/try-runtime", + "pezcumulus-primitives-utility/try-runtime", + "pezframe-benchmarking?/try-runtime", + "pezframe-executive/try-runtime", + "pezframe-support/try-runtime", + "pezframe-system-benchmarking?/try-runtime", + "pezframe-system/try-runtime", + "pezframe-try-runtime/try-runtime", + "pezkuwi-primitives/try-runtime", + "pezkuwi-runtime-common/try-runtime", + "pezkuwi-teyrchain-primitives/try-runtime", + "pezkuwichain-runtime-constants/try-runtime", + "pezpallet-assets/try-runtime", + "pezpallet-aura/try-runtime", + "pezpallet-authorship/try-runtime", + "pezpallet-balances/try-runtime", + "pezpallet-collator-selection/try-runtime", + "pezpallet-collective/try-runtime", + "pezpallet-democracy/try-runtime", + "pezpallet-elections-phragmen/try-runtime", + "pezpallet-identity-kyc/try-runtime", + "pezpallet-identity/try-runtime", + "pezpallet-message-queue/try-runtime", + "pezpallet-migrations/try-runtime", + "pezpallet-multisig/try-runtime", + "pezpallet-nfts/try-runtime", + "pezpallet-perwerde/try-runtime", + "pezpallet-pez-rewards/try-runtime", + "pezpallet-proxy/try-runtime", + "pezpallet-recovery/try-runtime", + "pezpallet-referral/try-runtime", + "pezpallet-scheduler/try-runtime", + "pezpallet-session/try-runtime", + "pezpallet-skip-feeless-payment/try-runtime", + "pezpallet-society/try-runtime", + "pezpallet-staking-score/try-runtime", + "pezpallet-tiki/try-runtime", + "pezpallet-timestamp/try-runtime", + "pezpallet-transaction-payment-rpc-runtime-api/try-runtime", + "pezpallet-transaction-payment/try-runtime", + "pezpallet-trust/try-runtime", + "pezpallet-utility/try-runtime", + "pezpallet-vesting/try-runtime", + "pezpallet-welati/try-runtime", + "pezpallet-xcm-benchmarks?/try-runtime", + "pezpallet-xcm/try-runtime", + "pezsp-api/try-runtime", + "pezsp-block-builder/try-runtime", + "pezsp-consensus-aura/try-runtime", + "pezsp-genesis-builder/try-runtime", + "pezsp-inherents/try-runtime", + "pezsp-keyring/try-runtime", + "pezsp-offchain/try-runtime", + "pezsp-runtime/try-runtime", + "pezsp-session/try-runtime", + "pezsp-staking/try-runtime", + "pezsp-transaction-pool/try-runtime", + "pezsp-version/try-runtime", + "testnet-teyrchains-constants/try-runtime", + "teyrchain-info/try-runtime", + "teyrchains-common/try-runtime", + "teyrchains-runtimes-test-utils/try-runtime", + "xcm-builder/try-runtime", + "xcm-executor/try-runtime", + "xcm-runtime-pezapis/try-runtime", + "xcm/try-runtime", ] # A feature that should be enabled when the runtime should be built for on-chain @@ -355,26 +355,26 @@ try-runtime = [ # to make it smaller, like logging for example. on-chain-release-build = [] serde = [ - "codec/serde", - "dep:serde", - "enumflags2/serde", - "pezframe-benchmarking?/serde", - "pezpallet-democracy/serde", - "pezpallet-message-queue/serde", - "pezpallet-perwerde/serde", - "pezpallet-pez-rewards/serde", - "pezpallet-staking-score/serde", - "pezpallet-transaction-payment/serde", - "pezpallet-trust/serde", - "pezpallet-welati/serde", - "pezpallet-xcm/serde", - "pezsp-consensus-aura/serde", - "pezsp-core/serde", - "pezsp-runtime/serde", - "pezsp-staking/serde", - "pezsp-storage/serde", - "pezsp-version/serde", - "scale-info/serde", + "codec/serde", + "dep:serde", + "enumflags2/serde", + "pezframe-benchmarking?/serde", + "pezpallet-democracy/serde", + "pezpallet-message-queue/serde", + "pezpallet-perwerde/serde", + "pezpallet-pez-rewards/serde", + "pezpallet-staking-score/serde", + "pezpallet-transaction-payment/serde", + "pezpallet-trust/serde", + "pezpallet-welati/serde", + "pezpallet-xcm/serde", + "pezsp-consensus-aura/serde", + "pezsp-core/serde", + "pezsp-runtime/serde", + "pezsp-staking/serde", + "pezsp-storage/serde", + "pezsp-version/serde", + "scale-info/serde", ] experimental = [] with-tracing = [] diff --git a/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/src/genesis_config_presets.rs b/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/src/genesis_config_presets.rs index d3d6f5c0..5bc8378a 100644 --- a/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/src/genesis_config_presets.rs +++ b/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/src/genesis_config_presets.rs @@ -56,12 +56,14 @@ fn default_founding_citizen_identity_hash() -> H256 { /// - `endowment`: HEZ amount for each endowed account /// - `id`: Parachain ID /// - `founding_citizens`: Accounts that start as Approved citizens (can accept referrals) +/// - `founding_citizen`: The account that receives NFT #0 and Collection 0 ownership fn people_pezkuwichain_genesis( invulnerables: Vec<(AccountId, AuraId)>, endowed_accounts: Vec, endowment: Balance, id: ParaId, founding_citizens: Vec<(AccountId, H256)>, + founding_citizen: Option, ) -> serde_json::Value { build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { @@ -92,6 +94,13 @@ fn people_pezkuwichain_genesis( // These accounts start with Approved status and can accept referrals immediately // This solves the chicken-egg problem: first citizens need to exist for others to join identity_kyc: IdentityKycConfig { founding_citizens, _phantom: Default::default() }, + + // ==================================================================== + // Tiki - NFT Collection 0 + Founding Citizen NFT #0 + // ==================================================================== + // Creates Collection 0 in pezpallet_nfts and mints NFT #0 for the founder + // This is required before any citizenship NFTs can be minted + tiki: TikiConfig { founding_citizen }, }) } @@ -137,7 +146,9 @@ pub fn get_preset(id: &PresetId) -> Option> { PEOPLE_PEZKUWICHAIN_ED * 524_288, PEOPLE_PARA_ID, // Founding citizens: Founder starts as Approved citizen - vec![(founder_account, default_founding_citizen_identity_hash())], + vec![(founder_account.clone(), default_founding_citizen_identity_hash())], + // Founding citizen gets NFT #0 and Collection 0 ownership + Some(founder_account), ) }, @@ -158,6 +169,8 @@ pub fn get_preset(id: &PresetId) -> Option> { (Sr25519Keyring::Alice.to_account_id(), default_founding_citizen_identity_hash()), (Sr25519Keyring::Bob.to_account_id(), default_founding_citizen_identity_hash()), ], + // Alice gets NFT #0 for testing + Some(Sr25519Keyring::Alice.to_account_id()), ), // ==================================================================== @@ -176,6 +189,8 @@ pub fn get_preset(id: &PresetId) -> Option> { PEOPLE_PARA_ID, // Founding citizen: Alice is the founding citizen for dev vec![(Sr25519Keyring::Alice.to_account_id(), default_founding_citizen_identity_hash())], + // Alice gets NFT #0 for dev + Some(Sr25519Keyring::Alice.to_account_id()), ), _ => return None, diff --git a/vendor/pezkuwi-subxt/subxt/examples/comprehensive_test.rs b/vendor/pezkuwi-subxt/subxt/examples/comprehensive_test.rs new file mode 100644 index 00000000..e76bcd62 --- /dev/null +++ b/vendor/pezkuwi-subxt/subxt/examples/comprehensive_test.rs @@ -0,0 +1,731 @@ +//! Comprehensive Post-Upgrade Test Suite (spec_version 1_020_003) +//! +//! Tests all functionality after runtime upgrade: +//! - Balance queries on all 3 chains +//! - XCM teleport from Relay to Asset Hub (1000 HEZ) +//! - XCM teleport from Relay to People Chain (1000 HEZ) +//! - Welati (citizenship) application on People Chain +//! - Staking queries on Relay Chain +//! - Trust score verification on People Chain +//! +//! Run with: +//! FOUNDER_MNEMONIC='foam hope ...' cargo run --example comprehensive_test + +#![allow(missing_docs)] +use pezkuwi_subxt::dynamic::{At, Value}; +use pezkuwi_subxt::utils::AccountId32; +use pezkuwi_subxt::{OnlineClient, PezkuwiConfig}; +use pezkuwi_subxt_signer::bip39::Mnemonic; +use pezkuwi_subxt_signer::sr25519::Keypair; +use scale_value::Composite; +use std::time::Duration; + +// Generate interface from relay chain metadata (spec_version 1_020_003) +#[pezkuwi_subxt::subxt(runtime_metadata_path = "../artifacts/relay_mainnet_v3.scale")] +pub mod relay {} + +// XCM type aliases for convenience +use relay::runtime_types::pezstaging_xcm::v4::{ + asset::{Asset, AssetId, Assets, Fungibility}, + junction::Junction, + junctions::Junctions, + location::Location, +}; +use relay::runtime_types::xcm::{ + v3::WeightLimit, VersionedAssetId, VersionedAssets, VersionedLocation, +}; + +// RPC endpoints (direct to VPS3) +const RELAY_RPC: &str = "ws://217.77.6.126:9944"; +const ASSET_HUB_RPC: &str = "ws://217.77.6.126:40944"; +const PEOPLE_RPC: &str = "ws://217.77.6.126:41944"; + +// 1 HEZ = 10^12 TYR +const TYR_PER_HEZ: u128 = 1_000_000_000_000; + +// Para IDs +const ASSET_HUB_ID: u32 = 1000; +const PEOPLE_CHAIN_ID: u32 = 1004; + +// Test wallet mnemonic +const TEST_MNEMONIC: &str = + "REDACTED_MNEMONIC"; + +fn format_hez(tyr: u128) -> String { + let whole = tyr / TYR_PER_HEZ; + let frac = (tyr % TYR_PER_HEZ) / (TYR_PER_HEZ / 10000); + format!("{}.{:04} HEZ", whole, frac) +} + +/// Query balance using dynamic storage query (works on any chain) +async fn query_balance( + api: &OnlineClient, + account: &AccountId32, +) -> Result> { + let storage_query = + pezkuwi_subxt::dynamic::storage::<(AccountId32,), Value>("System", "Account"); + let client_at = api.storage().at_latest().await?; + match client_at.entry(storage_query)?.try_fetch((account.clone(),)).await? { + Some(val) => { + let decoded = val.decode()?; + let free = decoded + .at("data") + .at("free") + .ok_or("Could not find free balance")? + .as_u128() + .ok_or("Could not parse balance")?; + Ok(free) + }, + None => Ok(0), + } +} + +/// Dynamic storage query helper for People Chain - map types +async fn people_storage_map( + api: &OnlineClient, + pallet: &str, + entry: &str, + account: &AccountId32, +) -> Result>, Box> { + let query = pezkuwi_subxt::dynamic::storage::<(AccountId32,), Value>(pallet, entry); + match api + .storage() + .at_latest() + .await? + .entry(query)? + .try_fetch((account.clone(),)) + .await? + { + Some(val) => Ok(Some(val.decode()?)), + None => Ok(None), + } +} + +/// Dynamic storage query helper for People Chain - value types (no key) +async fn people_storage_value( + api: &OnlineClient, + pallet: &str, + entry: &str, +) -> Result>, Box> { + let query = pezkuwi_subxt::dynamic::storage::<(), Value>(pallet, entry); + match api.storage().at_latest().await?.entry(query)?.try_fetch(()).await? { + Some(val) => Ok(Some(val.decode()?)), + None => Ok(None), + } +} + +/// Build XCM destination for a teyrchain +fn xcm_dest(para_id: u32) -> VersionedLocation { + VersionedLocation::V4(Location { + parents: 0, + interior: Junctions::X1([Junction::Teyrchain(para_id)]), + }) +} + +/// Build XCM beneficiary for an account +fn xcm_beneficiary(pubkey: [u8; 32]) -> VersionedLocation { + VersionedLocation::V4(Location { + parents: 0, + interior: Junctions::X1([Junction::AccountId32 { network: None, id: pubkey }]), + }) +} + +/// Build XCM assets (native HEZ token) +fn xcm_native_assets(amount: u128) -> VersionedAssets { + VersionedAssets::V4(Assets(vec![Asset { + id: AssetId(Location { parents: 0, interior: Junctions::Here }), + fun: Fungibility::Fungible(amount), + }])) +} + +/// Native fee asset ID +fn xcm_fee_asset() -> VersionedAssetId { + VersionedAssetId::V4(AssetId(Location { parents: 0, interior: Junctions::Here })) +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + println!("╔══════════════════════════════════════════════════════════════╗"); + println!("║ PEZKUWICHAIN COMPREHENSIVE POST-UPGRADE TEST SUITE ║"); + println!("║ spec_version: 1_020_003 (Trust Score System) ║"); + println!("╚══════════════════════════════════════════════════════════════╝\n"); + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 1: Setup wallets + // ═══════════════════════════════════════════════════════════════════ + println!("═══ SECTION 1: Setup Wallets ═══\n"); + + let test_mnemonic = Mnemonic::parse(TEST_MNEMONIC)?; + let test_wallet = Keypair::from_phrase(&test_mnemonic, None)?; + let test_account = AccountId32(test_wallet.public_key().0); + println!(" Test Wallet: {}", test_account); + + let founder_mnemonic_str = std::env::var("FOUNDER_MNEMONIC") + .expect("FOUNDER_MNEMONIC env var required (founder/sudo seed)"); + let founder_mnemonic = Mnemonic::parse(&founder_mnemonic_str)?; + let founder_wallet = Keypair::from_phrase(&founder_mnemonic, None)?; + let founder_account = AccountId32(founder_wallet.public_key().0); + println!(" Founder: {}", founder_account); + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 2: Connect to all chains + // ═══════════════════════════════════════════════════════════════════ + println!("\n═══ SECTION 2: Connect to Chains ═══\n"); + + let relay_api = OnlineClient::::from_insecure_url(RELAY_RPC).await?; + println!(" ✓ Relay Chain connected ({})", RELAY_RPC); + + let ah_api = OnlineClient::::from_insecure_url(ASSET_HUB_RPC).await?; + println!(" ✓ Asset Hub connected ({})", ASSET_HUB_RPC); + + let people_api = OnlineClient::::from_insecure_url(PEOPLE_RPC).await?; + println!(" ✓ People Chain connected ({})", PEOPLE_RPC); + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 3: Verify spec_version on all chains + // ═══════════════════════════════════════════════════════════════════ + println!("\n═══ SECTION 3: Verify Runtime Versions ═══\n"); + + let relay_ver = relay_api.runtime_version().spec_version; + let ah_ver = ah_api.runtime_version().spec_version; + let people_ver = people_api.runtime_version().spec_version; + + println!(" Relay Chain: spec_version = {}", relay_ver); + println!(" Asset Hub: spec_version = {}", ah_ver); + println!(" People Chain: spec_version = {}", people_ver); + + assert_eq!(relay_ver, 1_020_003, "Relay spec_version mismatch!"); + assert_eq!(ah_ver, 1_020_003, "Asset Hub spec_version mismatch!"); + assert_eq!(people_ver, 1_020_003, "People Chain spec_version mismatch!"); + println!(" ✓ All chains at spec_version 1_020_003"); + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 4: Check initial balances + // ═══════════════════════════════════════════════════════════════════ + println!("\n═══ SECTION 4: Initial Balances ═══\n"); + + let relay_balance = query_balance(&relay_api, &test_account).await?; + let ah_balance = query_balance(&ah_api, &test_account).await?; + let people_balance = query_balance(&people_api, &test_account).await?; + + println!(" Test Wallet Balances:"); + println!(" Relay Chain: {} ({} TYR)", format_hez(relay_balance), relay_balance); + println!(" Asset Hub: {} ({} TYR)", format_hez(ah_balance), ah_balance); + println!(" People Chain: {} ({} TYR)", format_hez(people_balance), people_balance); + + let need_teleport = + relay_balance >= 2000 * TYR_PER_HEZ && ah_balance == 0 && people_balance == 0; + + let (ah_received, people_received); + + if need_teleport { + println!(" ✓ Sufficient balance for teleports"); + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 5: XCM Teleport — Relay → Asset Hub (1000 HEZ) + // ═══════════════════════════════════════════════════════════════════ + println!("\n═══ SECTION 5: XCM Teleport → Asset Hub (1000 HEZ) ═══\n"); + + let teleport_amount: u128 = 1000 * TYR_PER_HEZ; + + let xcm_ah_tx = relay::tx().xcm_pallet().limited_teleport_assets( + xcm_dest(ASSET_HUB_ID), + xcm_beneficiary(test_wallet.public_key().0), + xcm_native_assets(teleport_amount), + xcm_fee_asset(), + WeightLimit::Unlimited, + ); + + println!(" Submitting XCM teleport to Asset Hub (1000 HEZ)..."); + let events_ah = relay_api + .tx() + .sign_and_submit_then_watch_default(&xcm_ah_tx, &test_wallet) + .await? + .wait_for_finalized_success() + .await?; + + println!(" ✓ Teleport to Asset Hub finalized!"); + match events_ah.find_first::()? { + Some(event) => println!(" XCM Attempted: {:?}", event.outcome), + None => println!(" ⚠ No Attempted event"), + } + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 6: XCM Teleport — Relay → People Chain (1000 HEZ) + // ═══════════════════════════════════════════════════════════════════ + println!("\n═══ SECTION 6: XCM Teleport → People Chain (1000 HEZ) ═══\n"); + + let xcm_people_tx = relay::tx().xcm_pallet().limited_teleport_assets( + xcm_dest(PEOPLE_CHAIN_ID), + xcm_beneficiary(test_wallet.public_key().0), + xcm_native_assets(teleport_amount), + xcm_fee_asset(), + WeightLimit::Unlimited, + ); + + println!(" Submitting XCM teleport to People Chain (1000 HEZ)..."); + let events_people = relay_api + .tx() + .sign_and_submit_then_watch_default(&xcm_people_tx, &test_wallet) + .await? + .wait_for_finalized_success() + .await?; + + println!(" ✓ Teleport to People Chain finalized!"); + match events_people.find_first::()? { + Some(event) => println!(" XCM Attempted: {:?}", event.outcome), + None => println!(" ⚠ No Attempted event"), + } + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 7: Wait for XCM delivery & verify teleport balances + // ═══════════════════════════════════════════════════════════════════ + println!("\n═══ SECTION 7: Verify Teleport Results ═══\n"); + println!(" Waiting 30 seconds for XCM messages to be delivered..."); + tokio::time::sleep(Duration::from_secs(30)).await; + + let relay_after = query_balance(&relay_api, &test_account).await?; + let ah_after = query_balance(&ah_api, &test_account).await?; + let people_after = query_balance(&people_api, &test_account).await?; + + println!(" Post-Teleport Balances:"); + println!( + " Relay Chain: {} (was {})", + format_hez(relay_after), + format_hez(relay_balance) + ); + println!(" Asset Hub: {} (was {})", format_hez(ah_after), format_hez(ah_balance)); + println!( + " People Chain: {} (was {})", + format_hez(people_after), + format_hez(people_balance) + ); + + ah_received = ah_after.saturating_sub(ah_balance); + people_received = people_after.saturating_sub(people_balance); + + println!("\n Transfer Summary:"); + println!(" Relay spent: {}", format_hez(relay_balance.saturating_sub(relay_after))); + println!(" AH received: {}", format_hez(ah_received)); + println!(" People received: {}", format_hez(people_received)); + println!(" → Asset Hub: {}", if ah_received > 0 { "✓ SUCCESS" } else { "✗ FAILED" }); + println!( + " → People Chain: {}", + if people_received > 0 { "✓ SUCCESS" } else { "✗ FAILED" } + ); + } else { + println!("\n═══ SECTIONS 5-7: XCM Teleport (SKIPPED - already done) ═══\n"); + println!(" Relay: {}", format_hez(relay_balance)); + println!(" AH: {}", format_hez(ah_balance)); + println!(" People: {}", format_hez(people_balance)); + println!(" ✓ Balances already distributed across chains"); + ah_received = ah_balance; + people_received = people_balance; + } + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 8: Staking Queries (Relay Chain) + // ═══════════════════════════════════════════════════════════════════ + println!("\n═══ SECTION 8: Staking Status (Relay Chain) ═══\n"); + + let block = relay_api.blocks().at_latest().await?; + println!(" Current Block: #{}", block.number()); + + let storage = relay_api.storage().at_latest().await?; + + let active_era = storage.try_fetch(relay::storage().staking().active_era(), ()).await?; + println!(" Active Era: {:?}", active_era.and_then(|v| v.decode().ok())); + + let current_era = storage.try_fetch(relay::storage().staking().current_era(), ()).await?; + println!(" Current Era: {:?}", current_era.and_then(|v| v.decode().ok())); + + let session_index = storage.try_fetch(relay::storage().session().current_index(), ()).await?; + println!(" Session Index: {:?}", session_index.and_then(|v| v.decode().ok())); + + let val_count = storage.try_fetch(relay::storage().staking().validator_count(), ()).await?; + println!(" Validator Count: {:?}", val_count.and_then(|v| v.decode().ok())); + + let force_era = storage.try_fetch(relay::storage().staking().force_era(), ()).await?; + println!(" Force Era: {:?}", force_era.and_then(|v| v.decode().ok())); + + println!(" ✓ Staking system operational"); + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 9: Welati (Citizenship) Application on People Chain + // ═══════════════════════════════════════════════════════════════════ + println!("\n═══ SECTION 9: Welati (Citizenship) Application ═══\n"); + + // Check if test wallet already has KYC status + let kyc_status = + people_storage_map(&people_api, "IdentityKyc", "KycStatuses", &test_account).await?; + + let already_citizen = if let Some(status) = &kyc_status { + let status_str = format!("{:?}", status); + println!(" Current KYC status: {}", status_str); + status_str.contains("Approved") + } else { + println!(" KYC status: NotStarted (no entry)"); + false + }; + + if already_citizen { + println!(" ✓ Already a citizen (KYC Approved), skipping application steps"); + } else { + // Step 1: Apply for citizenship + let identity_data = b"Azad Qasimlo|DR.QAsimlo"; + let identity_hash = pezsp_crypto_hashing::blake2_256(identity_data); + println!( + " Identity hash: 0x{}", + identity_hash.iter().map(|b| format!("{:02x}", b)).collect::() + ); + + let apply_tx = pezkuwi_subxt::dynamic::tx( + "IdentityKyc", + "apply_for_citizenship", + Composite::unnamed([ + Value::from_bytes(identity_hash), + Value::unnamed_variant("None", []), + ]), + ); + + println!(" Submitting citizenship application..."); + let apply_events = people_api + .tx() + .sign_and_submit_then_watch_default(&apply_tx, &test_wallet) + .await? + .wait_for_finalized_success() + .await?; + println!(" ✓ Citizenship application submitted and finalized!"); + + for event in apply_events.iter() { + let event = event?; + if event.pallet_name() == "IdentityKyc" { + println!(" Event: {}::{}", event.pallet_name(), event.variant_name()); + } + } + + // Step 2: Founder approves referral + println!("\n Founder approving referral..."); + let approve_tx = pezkuwi_subxt::dynamic::tx( + "IdentityKyc", + "approve_referral", + Composite::unnamed([Value::from_bytes(&test_account.0)]), + ); + + let approve_events = people_api + .tx() + .sign_and_submit_then_watch_default(&approve_tx, &founder_wallet) + .await? + .wait_for_finalized_success() + .await?; + println!(" ✓ Referral approved by founder!"); + + for event in approve_events.iter() { + let event = event?; + if event.pallet_name() == "IdentityKyc" { + println!(" Event: {}::{}", event.pallet_name(), event.variant_name()); + } + } + + // Step 3: Test wallet confirms citizenship + println!("\n Confirming citizenship (self-confirmation)..."); + let confirm_tx = pezkuwi_subxt::dynamic::tx( + "IdentityKyc", + "confirm_citizenship", + Composite::unnamed([]), + ); + + let confirm_events = people_api + .tx() + .sign_and_submit_then_watch_default(&confirm_tx, &test_wallet) + .await? + .wait_for_finalized_success() + .await?; + println!(" ✓ Citizenship confirmed!"); + + println!("\n Confirmation events:"); + for event in confirm_events.iter() { + let event = event?; + let pallet = event.pallet_name(); + match pallet { + "IdentityKyc" | "Tiki" | "Referral" | "Trust" | "Nfts" | "Balances" => { + println!(" {}::{}", pallet, event.variant_name()); + }, + _ => {}, + } + } + } + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 9b: Ensure NFT Collection exists & mint CitizenNft + // ═══════════════════════════════════════════════════════════════════ + println!("\n═══ SECTION 9b: NFT Collection & Citizen NFT Fix ═══\n"); + + // Check if Tiki collection (ID=0) exists + let _collection_exists = people_storage_map( + &people_api, + "Nfts", + "Collection", + // Collection storage key is a u32 (collection_id = 0), not an AccountId + // Use a raw dynamic query instead + &AccountId32([0u8; 32]), // placeholder + ) + .await; + + // Use a different approach: check if NextItemId storage exists in Tiki + let next_item_id = people_storage_value(&people_api, "Tiki", "NextItemId").await?; + println!(" Tiki NextItemId: {:?}", next_item_id); + + // Check if test wallet already has CitizenNft + let has_citizen_nft = + people_storage_map(&people_api, "Tiki", "CitizenNft", &test_account).await?; + + if has_citizen_nft.is_some() { + println!(" ✓ Test Wallet already has Citizen NFT"); + } else { + println!(" ✗ Test Wallet does NOT have Citizen NFT - attempting sudo fix..."); + + // Step 1: Create NFT collection 0 via sudo (Nfts::force_create) + // force_create(owner, config) where owner = founder, config = default + println!(" Creating NFT Collection 0 via sudo..."); + let create_collection_inner = pezkuwi_subxt::dynamic::tx( + "Nfts", + "force_create", + Composite::unnamed([ + // owner + Value::unnamed_variant("Id", [Value::from_bytes(&founder_account.0)]), + // config (CollectionConfig) + Value::unnamed_composite([ + // settings: u64 (all features enabled = 0) + Value::u128(0), + // max_supply: Option + Value::unnamed_variant("None", []), + // mint_settings: MintSettings + Value::unnamed_composite([ + // mint_type + Value::unnamed_variant("Issuer", []), + // price: Option + Value::unnamed_variant("None", []), + // start_block: Option + Value::unnamed_variant("None", []), + // end_block: Option + Value::unnamed_variant("None", []), + // default_item_settings: u64 + Value::u128(0), + ]), + ]), + ]), + ); + + let sudo_create = + pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![create_collection_inner.into_value()]); + + match people_api + .tx() + .sign_and_submit_then_watch_default(&sudo_create, &founder_wallet) + .await + { + Ok(progress) => match progress.wait_for_finalized_success().await { + Ok(events) => { + println!(" ✓ NFT Collection 0 created!"); + for event in events.iter() { + let event = event?; + if event.pallet_name() == "Nfts" || event.pallet_name() == "Sudo" { + println!(" {}::{}", event.pallet_name(), event.variant_name()); + } + } + }, + Err(e) => println!(" ⚠ Collection creation finalized with error: {}", e), + }, + Err(e) => println!(" ⚠ Collection creation failed: {}", e), + } + + // Step 2: Force mint citizen NFT for test wallet via sudo (Tiki::force_mint_citizen_nft) + tokio::time::sleep(Duration::from_secs(6)).await; + + println!(" Force minting Citizen NFT for test wallet..."); + let mint_inner = pezkuwi_subxt::dynamic::tx( + "Tiki", + "force_mint_citizen_nft", + Composite::unnamed([Value::from_bytes(&test_account.0)]), + ); + + let sudo_mint = pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![mint_inner.into_value()]); + + match people_api + .tx() + .sign_and_submit_then_watch_default(&sudo_mint, &founder_wallet) + .await + { + Ok(progress) => match progress.wait_for_finalized_success().await { + Ok(events) => { + println!(" ✓ Citizen NFT minted!"); + for event in events.iter() { + let event = event?; + let pallet = event.pallet_name(); + match pallet { + "Tiki" | "Nfts" | "Sudo" => { + println!(" {}::{}", pallet, event.variant_name()); + }, + _ => {}, + } + } + }, + Err(e) => println!(" ⚠ NFT mint finalized with error: {}", e), + }, + Err(e) => println!(" ⚠ NFT mint failed: {}", e), + } + + tokio::time::sleep(Duration::from_secs(6)).await; + } + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 10: Trust Score Verification (People Chain) + // ═══════════════════════════════════════════════════════════════════ + println!("\n═══ SECTION 10: Trust Score Verification ═══\n"); + + match people_storage_map(&people_api, "Trust", "TrustScores", &test_account).await? { + Some(val) => println!(" Test Wallet Trust Score: {:?}", val), + None => println!(" Test Wallet Trust Score: 0 (not set)"), + } + + match people_storage_value(&people_api, "Trust", "TotalActiveTrustScore").await? { + Some(val) => println!(" Total Active Trust Score: {:?}", val), + None => println!(" Total Active Trust Score: 0"), + } + + match people_storage_map(&people_api, "Trust", "TrustScores", &founder_account).await? { + Some(val) => println!(" Founder Trust Score: {:?}", val), + None => println!(" Founder Trust Score: 0 (not set)"), + } + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 11: Tiki (Role NFT) Verification + // ═══════════════════════════════════════════════════════════════════ + println!("\n═══ SECTION 11: Tiki (Role NFT) Verification ═══\n"); + + match people_storage_map(&people_api, "Tiki", "CitizenNft", &test_account).await? { + Some(val) => println!(" ✓ Test Wallet has Citizen NFT: {:?}", val), + None => println!(" ✗ Test Wallet does NOT have Citizen NFT"), + } + + match people_storage_map(&people_api, "Tiki", "UserTikis", &test_account).await? { + Some(val) => println!(" Test Wallet Roles: {:?}", val), + None => println!(" Test Wallet has no roles assigned"), + } + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 12: Referral System Check + // ═══════════════════════════════════════════════════════════════════ + println!("\n═══ SECTION 12: Referral System ═══\n"); + + match people_storage_map(&people_api, "IdentityKyc", "CitizenReferrers", &test_account).await? { + Some(val) => println!(" Test Wallet referrer: {:?}", val), + None => println!(" No referrer recorded"), + } + + match people_storage_map(&people_api, "Referral", "ReferralCount", &founder_account).await { + Ok(Some(val)) => println!(" Founder referral count: {:?}", val), + Ok(None) => println!(" Founder referral count: 0"), + Err(e) => println!(" ⚠ ReferralCount query error: {}", e), + } + + match people_storage_map(&people_api, "Referral", "Referrals", &test_account).await { + Ok(Some(val)) => println!(" Test Wallet referral info: {:?}", val), + Ok(None) => println!(" No referral info for test wallet"), + Err(e) => println!(" ⚠ Referrals query error: {}", e), + } + + match people_storage_map(&people_api, "Referral", "ReferrerStatsStorage", &founder_account) + .await + { + Ok(Some(val)) => println!(" Founder referrer stats: {:?}", val), + Ok(None) => println!(" No referrer stats for founder"), + Err(e) => println!(" ⚠ ReferrerStats query error: {}", e), + } + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 13: Staking Score (People Chain - Cross-chain) + // ═══════════════════════════════════════════════════════════════════ + println!("\n═══ SECTION 13: Staking Score System ═══\n"); + + match people_storage_map(&people_api, "StakingScore", "StakingScores", &test_account).await { + Ok(Some(val)) => println!(" Test Wallet Staking Score: {:?}", val), + Ok(None) => println!(" Test Wallet Staking Score: 0 (not staking)"), + Err(e) => println!(" ⚠ StakingScore query error: {}", e), + } + + // Check if staking score bridge from relay is configured + match people_storage_value(&people_api, "StakingScore", "LastRelayUpdate").await { + Ok(Some(val)) => println!(" Last Relay Update: {:?}", val), + Ok(None) => println!(" Last Relay Update: None (no cross-chain data yet)"), + Err(_) => println!(" StakingScore: LastRelayUpdate storage not found"), + } + + // ═══════════════════════════════════════════════════════════════════ + // SECTION 14: Final Balances & Summary + // ═══════════════════════════════════════════════════════════════════ + println!("\n═══ SECTION 14: Final Summary ═══\n"); + + let final_relay = query_balance(&relay_api, &test_account).await?; + let final_ah = query_balance(&ah_api, &test_account).await?; + let final_people = query_balance(&people_api, &test_account).await?; + + // Re-check critical states for summary + let final_kyc = people_storage_map(&people_api, "IdentityKyc", "KycStatuses", &test_account) + .await + .ok() + .flatten(); + let final_nft = people_storage_map(&people_api, "Tiki", "CitizenNft", &test_account) + .await + .ok() + .flatten(); + let final_trust = people_storage_map(&people_api, "Trust", "TrustScores", &test_account) + .await + .ok() + .flatten(); + + let kyc_ok = final_kyc + .as_ref() + .map(|v| format!("{:?}", v).contains("Approved")) + .unwrap_or(false); + let nft_ok = final_nft.is_some(); + + println!("╔══════════════════════════════════════════════════════════════╗"); + println!("║ PEZKUWICHAIN TEST RESULTS SUMMARY ║"); + println!("╠══════════════════════════════════════════════════════════════╣"); + println!("║ ║"); + println!("║ spec_version: {} / {} / {} ║", relay_ver, ah_ver, people_ver); + println!("║ ║"); + println!("║ Balances (Test Wallet): ║"); + println!("║ Relay: {:>42} ║", format_hez(final_relay)); + println!("║ AH: {:>42} ║", format_hez(final_ah)); + println!("║ People: {:>42} ║", format_hez(final_people)); + println!("║ ║"); + println!( + "║ XCM Teleport: {} AH {} People ║", + if ah_received > 0 { "✓" } else { "✗" }, + if people_received > 0 { "✓" } else { "✗" } + ); + println!( + "║ Welati (KYC): {} ║", + if kyc_ok { "✓ Approved" } else { "✗ NOT Approved" } + ); + println!( + "║ Citizen NFT: {} ║", + if nft_ok { "✓ Minted " } else { "✗ NOT Minted " } + ); + println!( + "║ Trust Score: {:?} ║", + final_trust + .as_ref() + .map(|v| format!("{:?}", v)) + .unwrap_or_else(|| "0".to_string()) + ); + println!("║ ║"); + println!("╚══════════════════════════════════════════════════════════════╝"); + + println!("\n✓ COMPREHENSIVE TEST SUITE COMPLETED"); + + Ok(()) +} diff --git a/vendor/pezkuwi-subxt/subxt/examples/create_nft_collection.rs b/vendor/pezkuwi-subxt/subxt/examples/create_nft_collection.rs new file mode 100644 index 00000000..dce1c162 --- /dev/null +++ b/vendor/pezkuwi-subxt/subxt/examples/create_nft_collection.rs @@ -0,0 +1,322 @@ +//! Create NFT Collection 0 on People Chain via XCM from Relay Chain +//! +//! This script sends XCM Transact messages from the Relay Chain (via sudo) +//! to the People Chain to: +//! 1. Create NFT Collection 0 using Nfts::force_create +//! 2. Mint NFT Item 0 in that collection for the founder +//! +//! Both calls are executed as XCM Transact with Superuser origin, wrapped +//! in sudo_unchecked_weight on the Relay Chain. +//! +//! Run with: +//! FOUNDER_MNEMONIC='foam hope ...' cargo run --release --example create_nft_collection + +#![allow(missing_docs)] +use pezkuwi_subxt::dynamic::Value; +use pezkuwi_subxt::utils::AccountId32; +use pezkuwi_subxt::{OnlineClient, PezkuwiConfig}; +use pezkuwi_subxt_signer::bip39::Mnemonic; +use pezkuwi_subxt_signer::sr25519::Keypair; +use scale_value::Composite; +use std::time::Duration; + +const RELAY_RPC: &str = "ws://217.77.6.126:9944"; +const PEOPLE_RPC: &str = "ws://217.77.6.126:41944"; +const PEOPLE_CHAIN_PARA_ID: u128 = 1004; + +/// Query a single-key storage map on the People Chain. +async fn query_storage_map( + api: &OnlineClient, + pallet: &str, + entry: &str, + key: u32, +) -> Result>, Box> { + let query = pezkuwi_subxt::dynamic::storage::<(u32,), Value>(pallet, entry); + let storage = api.storage().at_latest().await?; + match storage.entry(query)?.try_fetch((key,)).await? { + Some(val) => Ok(Some(val.decode()?)), + None => Ok(None), + } +} + +/// Query a double-key storage map on the People Chain. +async fn query_storage_double_map( + api: &OnlineClient, + pallet: &str, + entry: &str, + key1: u32, + key2: u32, +) -> Result>, Box> { + let query = pezkuwi_subxt::dynamic::storage::<(u32, u32), Value>(pallet, entry); + let storage = api.storage().at_latest().await?; + match storage.entry(query)?.try_fetch((key1, key2)).await? { + Some(val) => Ok(Some(val.decode()?)), + None => Ok(None), + } +} + +/// Query an AccountId-keyed storage map on the People Chain. +async fn query_storage_by_account( + api: &OnlineClient, + pallet: &str, + entry: &str, + account: &AccountId32, +) -> Result>, Box> { + let query = pezkuwi_subxt::dynamic::storage::<(AccountId32,), Value>(pallet, entry); + let storage = api.storage().at_latest().await?; + match storage.entry(query)?.try_fetch((account.clone(),)).await? { + Some(val) => Ok(Some(val.decode()?)), + None => Ok(None), + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + println!("=== CREATE NFT COLLECTION ON PEOPLE CHAIN VIA XCM ===\n"); + + // Step 1: Load founder keypair from environment + let mnemonic_str = + std::env::var("FOUNDER_MNEMONIC").expect("FOUNDER_MNEMONIC environment variable required"); + let mnemonic = Mnemonic::parse(&mnemonic_str)?; + let founder_keypair = Keypair::from_phrase(&mnemonic, None)?; + let founder_account = AccountId32(founder_keypair.public_key().0); + println!("Founder account: {}", founder_account); + + // Step 2: Connect to Relay Chain and People Chain + println!("\nConnecting to Relay Chain at {} ...", RELAY_RPC); + let relay_api = OnlineClient::::from_insecure_url(RELAY_RPC).await?; + println!( + "Connected to Relay Chain (spec_version: {})", + relay_api.runtime_version().spec_version + ); + + println!("Connecting to People Chain at {} ...", PEOPLE_RPC); + let people_api = OnlineClient::::from_insecure_url(PEOPLE_RPC).await?; + println!( + "Connected to People Chain (spec_version: {})", + people_api.runtime_version().spec_version + ); + + // Step 3: Check if Collection 0 already exists + println!("\n--- Checking if Collection 0 already exists ---"); + match query_storage_map(&people_api, "Nfts", "Collection", 0).await? { + Some(_val) => { + println!("Collection 0 ALREADY EXISTS on People Chain!"); + println!("Skipping collection creation."); + }, + None => { + println!("Collection 0 does NOT exist. Creating...\n"); + + // Encode Nfts::force_create call bytes via People Chain metadata + let force_create = pezkuwi_subxt::dynamic::tx( + "Nfts", + "force_create", + Composite::unnamed([ + // owner: MultiAddress::Id(founder) + Value::unnamed_variant("Id", [Value::from_bytes(&founder_account.0)]), + // config: CollectionConfig { settings, max_supply, mint_settings } + Value::unnamed_composite([ + Value::u128(0), // settings: u64 + Value::unnamed_variant("None", []), // max_supply + Value::unnamed_composite([ + // mint_settings + Value::unnamed_variant("Issuer", []), // mint_type + Value::unnamed_variant("None", []), // price + Value::unnamed_variant("None", []), // start_block + Value::unnamed_variant("None", []), // end_block + Value::u128(0), // default_item_settings + ]), + ]), + ]), + ); + + let force_create_bytes = people_api.tx().call_data(&force_create)?; + println!( + "force_create encoded: {} bytes (0x{}...)", + force_create_bytes.len(), + hex::encode(&force_create_bytes[..force_create_bytes.len().min(16)]) + ); + + // Send force_create via XCM Transact from Relay Chain + let sudo_create = build_xcm_sudo_transact(&force_create_bytes); + + let call_bytes = relay_api.tx().call_data(&sudo_create)?; + println!( + "sudo XCM encoded: {} bytes (0x{}...)", + call_bytes.len(), + hex::encode(&call_bytes[..call_bytes.len().min(32)]) + ); + + println!("Submitting sudo XCM transact for force_create..."); + let create_progress = relay_api + .tx() + .sign_and_submit_then_watch_default(&sudo_create, &founder_keypair) + .await?; + + println!("Transaction submitted. Waiting for finalization..."); + let create_events = create_progress.wait_for_finalized_success().await?; + println!("Transaction finalized on Relay Chain!"); + + for event in create_events.iter() { + let event = event?; + let pallet = event.pallet_name(); + if pallet == "Sudo" || pallet == "XcmPallet" { + println!(" Event: {}::{}", pallet, event.variant_name()); + } + } + + println!("\nWaiting 18 seconds for XCM delivery..."); + tokio::time::sleep(Duration::from_secs(18)).await; + + match query_storage_map(&people_api, "Nfts", "Collection", 0).await? { + Some(_) => println!("Collection 0 created successfully!"), + None => { + println!("WARNING: Collection 0 NOT found yet."); + println!("Check People Chain logs. Continuing anyway..."); + }, + } + }, + } + + // Step 4: Check if NFT #0 already exists + println!("\n--- Checking if NFT #0 already exists ---"); + match query_storage_double_map(&people_api, "Nfts", "Item", 0, 0).await? { + Some(_val) => { + println!("NFT #0 ALREADY EXISTS in Collection 0!"); + println!("No minting needed."); + }, + None => { + println!("NFT #0 does NOT exist. Minting for founder...\n"); + + let force_mint = pezkuwi_subxt::dynamic::tx( + "Nfts", + "force_mint", + Composite::unnamed([ + Value::u128(0), // collection + Value::u128(0), // item + Value::unnamed_variant("Id", [Value::from_bytes(&founder_account.0)]), + Value::unnamed_composite([Value::u128(0)]), // item_config + ]), + ); + + let force_mint_bytes = people_api.tx().call_data(&force_mint)?; + println!( + "force_mint encoded: {} bytes (0x{}...)", + force_mint_bytes.len(), + hex::encode(&force_mint_bytes[..force_mint_bytes.len().min(16)]) + ); + + let sudo_mint = build_xcm_sudo_transact(&force_mint_bytes); + + println!("Submitting sudo XCM transact for force_mint..."); + let mint_progress = relay_api + .tx() + .sign_and_submit_then_watch_default(&sudo_mint, &founder_keypair) + .await?; + + println!("Transaction submitted. Waiting for finalization..."); + let mint_events = mint_progress.wait_for_finalized_success().await?; + println!("Transaction finalized on Relay Chain!"); + + for event in mint_events.iter() { + let event = event?; + let pallet = event.pallet_name(); + if pallet == "Sudo" || pallet == "XcmPallet" { + println!(" Event: {}::{}", pallet, event.variant_name()); + } + } + + println!("\nWaiting 18 seconds for XCM delivery..."); + tokio::time::sleep(Duration::from_secs(18)).await; + }, + } + + // Step 5: Final verification + println!("\n--- Final Verification ---"); + + match query_storage_map(&people_api, "Nfts", "Collection", 0).await? { + Some(val) => println!("Collection 0: EXISTS - {:?}", val), + None => println!("Collection 0: NOT FOUND"), + } + + match query_storage_double_map(&people_api, "Nfts", "Item", 0, 0).await? { + Some(val) => println!("NFT #0: EXISTS - {:?}", val), + None => println!("NFT #0: NOT FOUND"), + } + + match query_storage_by_account(&people_api, "Tiki", "CitizenNft", &founder_account).await? { + Some(val) => println!("Tiki CitizenNft for founder: {:?}", val), + None => { + println!("Tiki CitizenNft for founder: None"); + println!( + " (Expected - Tiki storage is populated by confirm_citizenship, not force_mint)" + ); + }, + } + + println!("\n=== NFT COLLECTION CREATION COMPLETE ==="); + Ok(()) +} + +/// Build an XCM V3 Transact message wrapped in sudo_unchecked_weight. +/// +/// Uses the same proven pattern as the runtime-upgrade tool. +fn build_xcm_sudo_transact(encoded_call: &[u8]) -> pezkuwi_subxt_core::tx::payload::DynamicPayload { + let dest = Value::unnamed_variant( + "V3", + vec![Value::named_composite([ + ("parents", Value::u128(0)), + ( + "interior", + Value::unnamed_variant( + "X1", + vec![Value::unnamed_variant( + "Teyrchain", + vec![Value::u128(PEOPLE_CHAIN_PARA_ID)], + )], + ), + ), + ])], + ); + + let message = Value::unnamed_variant( + "V3", + vec![Value::unnamed_composite(vec![ + Value::named_variant( + "UnpaidExecution", + [ + ("weight_limit", Value::unnamed_variant("Unlimited", vec![])), + ("check_origin", Value::unnamed_variant("None", vec![])), + ], + ), + Value::named_variant( + "Transact", + [ + ("origin_kind", Value::unnamed_variant("Superuser", vec![])), + ( + "require_weight_at_most", + Value::named_composite([ + ("ref_time", Value::u128(5_000_000_000u128)), + ("proof_size", Value::u128(500_000u128)), + ]), + ), + ("call", Value::from_bytes(encoded_call)), + ], + ), + ])], + ); + + let xcm_send = pezkuwi_subxt::dynamic::tx("XcmPallet", "send", vec![dest, message]); + + pezkuwi_subxt::dynamic::tx( + "Sudo", + "sudo_unchecked_weight", + vec![ + xcm_send.into_value(), + Value::named_composite([ + ("ref_time", Value::u128(1u128)), + ("proof_size", Value::u128(1u128)), + ]), + ], + ) +}