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
This commit is contained in:
+1
-1
@@ -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" }
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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<T: Config> {
|
||||
/// 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<T::AccountId>,
|
||||
}
|
||||
|
||||
#[pezpallet::genesis_build]
|
||||
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
|
||||
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::<T>::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::<T>::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::<T>::insert(founder, 0u32);
|
||||
NextItemId::<T>::put(1u32);
|
||||
UserTikis::<T>::mutate(founder, |tikis| {
|
||||
let _ = tikis.try_push(Tiki::Welati);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pezpallet::call]
|
||||
impl<T: Config> Pezpallet<T> {
|
||||
/// Admin tarafından belirli bir kullanıcıya Tiki (rol) verme
|
||||
|
||||
@@ -68,7 +68,7 @@ construct_runtime!(
|
||||
Identity: pezpallet_identity::{Pezpallet, Call, Storage, Event<T>},
|
||||
IdentityKyc: pezpallet_identity_kyc::{Pezpallet, Call, Storage, Event<T>},
|
||||
Nfts: pezpallet_nfts::{Pezpallet, Call, Storage, Event<T>},
|
||||
Tiki: pezpallet_tiki::{Pezpallet, Call, Storage, Event<T>},
|
||||
Tiki: pezpallet_tiki::{Pezpallet, Call, Config<T>, Storage, Event<T>},
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
+16
-1
@@ -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<AccountId>,
|
||||
endowment: Balance,
|
||||
id: ParaId,
|
||||
founding_citizens: Vec<(AccountId, H256)>,
|
||||
founding_citizen: Option<AccountId>,
|
||||
) -> 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<Vec<u8>> {
|
||||
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<Vec<u8>> {
|
||||
(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<Vec<u8>> {
|
||||
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,
|
||||
|
||||
@@ -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<PezkuwiConfig>,
|
||||
account: &AccountId32,
|
||||
) -> Result<u128, Box<dyn std::error::Error>> {
|
||||
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<PezkuwiConfig>,
|
||||
pallet: &str,
|
||||
entry: &str,
|
||||
account: &AccountId32,
|
||||
) -> Result<Option<Value<()>>, Box<dyn std::error::Error>> {
|
||||
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<PezkuwiConfig>,
|
||||
pallet: &str,
|
||||
entry: &str,
|
||||
) -> Result<Option<Value<()>>, Box<dyn std::error::Error>> {
|
||||
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<dyn std::error::Error>> {
|
||||
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::<PezkuwiConfig>::from_insecure_url(RELAY_RPC).await?;
|
||||
println!(" ✓ Relay Chain connected ({})", RELAY_RPC);
|
||||
|
||||
let ah_api = OnlineClient::<PezkuwiConfig>::from_insecure_url(ASSET_HUB_RPC).await?;
|
||||
println!(" ✓ Asset Hub connected ({})", ASSET_HUB_RPC);
|
||||
|
||||
let people_api = OnlineClient::<PezkuwiConfig>::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::<relay::xcm_pallet::events::Attempted>()? {
|
||||
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::<relay::xcm_pallet::events::Attempted>()? {
|
||||
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::<String>()
|
||||
);
|
||||
|
||||
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<u32>
|
||||
Value::unnamed_variant("None", []),
|
||||
// mint_settings: MintSettings
|
||||
Value::unnamed_composite([
|
||||
// mint_type
|
||||
Value::unnamed_variant("Issuer", []),
|
||||
// price: Option<Balance>
|
||||
Value::unnamed_variant("None", []),
|
||||
// start_block: Option<BlockNumber>
|
||||
Value::unnamed_variant("None", []),
|
||||
// end_block: Option<BlockNumber>
|
||||
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(())
|
||||
}
|
||||
@@ -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<PezkuwiConfig>,
|
||||
pallet: &str,
|
||||
entry: &str,
|
||||
key: u32,
|
||||
) -> Result<Option<scale_value::Value<()>>, Box<dyn std::error::Error>> {
|
||||
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<PezkuwiConfig>,
|
||||
pallet: &str,
|
||||
entry: &str,
|
||||
key1: u32,
|
||||
key2: u32,
|
||||
) -> Result<Option<scale_value::Value<()>>, Box<dyn std::error::Error>> {
|
||||
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<PezkuwiConfig>,
|
||||
pallet: &str,
|
||||
entry: &str,
|
||||
account: &AccountId32,
|
||||
) -> Result<Option<scale_value::Value<()>>, Box<dyn std::error::Error>> {
|
||||
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<dyn std::error::Error>> {
|
||||
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::<PezkuwiConfig>::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::<PezkuwiConfig>::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)),
|
||||
]),
|
||||
],
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user