fix: complete Pezkuwi overlay + proper Nova merge

Pezkuwi overlay (B source):
- pezkuwi-chains.json: 4 chains (Relay, Zagros, Asset Hub, People)
- pezkuwi-xcm.json: full XCM config with networkDeliveryFee, networkBaseWeight
- pezkuwi-xcm-dynamic.json: dynamic XCM config

Merge script:
- Takes Nova as base (A source)
- Adds Pezkuwi entries to ALL XCM sections
- Pezkuwi chains appear first
- Nothing gets deleted

All chains have disabledCheckMetadataHash: true
This commit is contained in:
2026-02-09 06:56:35 +03:00
parent 5f6b6471f9
commit 39ca67dab8
85 changed files with 4252 additions and 161 deletions
+86 -97
View File
@@ -1,24 +1,26 @@
#!/usr/bin/env python3
"""
Sync and merge Nova-base with Pezkuwi overlay.
Merge Nova (Polkadot ecosystem) + Pezkuwi overlay.
Architecture:
- nova-base/ : Git submodule with Nova's Polkadot ecosystem (98+ chains)
- pezkuwi-overlay/ : Pezkuwi ecosystem configs (3 chains + XCM)
- Output dirs : Merged configs ready for wallet use
Sources:
A: nova-base/ -> Polkadot ecosystem (98+ chains, XCM) - git submodule
B: pezkuwi-overlay/ -> Pezkuwi ecosystem (4 chains, XCM) - we maintain
Merge rules:
- Chains: Pezkuwi chains first, then Nova chains
- XCM: Pezkuwi XCM entries merged with Nova XCM (Pezkuwi takes priority)
- Icons: Pezkuwi icons override Nova icons
- NOTHING GETS DELETED - only merge operations
Output:
chains/ -> Merged chains (Pezkuwi first, then Nova)
xcm/ -> Merged XCM (Nova base + Pezkuwi entries added to each section)
icons/ -> Merged icons (Pezkuwi overrides Nova)
Rules:
- NOTHING gets deleted
- Pezkuwi entries are ADDED to Nova's base
- Pezkuwi chains appear first in the list
"""
import json
import shutil
from pathlib import Path
# Paths
ROOT = Path(__file__).parent.parent
NOVA_BASE = ROOT / "nova-base"
PEZKUWI_OVERLAY = ROOT / "pezkuwi-overlay"
@@ -26,14 +28,12 @@ OUTPUT_CHAINS = ROOT / "chains"
OUTPUT_XCM = ROOT / "xcm"
def load_json(path: Path) -> list | dict:
"""Load JSON file."""
def load_json(path: Path) -> dict | list:
with open(path, 'r', encoding='utf-8') as f:
return json.load(f)
def save_json(path: Path, data: list | dict):
"""Save JSON file with pretty formatting."""
def save_json(path: Path, data: dict | list):
path.parent.mkdir(parents=True, exist_ok=True)
with open(path, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
@@ -41,10 +41,7 @@ def save_json(path: Path, data: list | dict):
def merge_chains(nova_chains: list, pezkuwi_chains: list) -> list:
"""
Merge chains: Pezkuwi chains first, then Nova chains.
Pezkuwi chains take priority (appear first in list).
"""
"""Pezkuwi chains first, then Nova chains (no duplicates)."""
pezkuwi_ids = {c['chainId'] for c in pezkuwi_chains}
nova_filtered = [c for c in nova_chains if c['chainId'] not in pezkuwi_ids]
return pezkuwi_chains + nova_filtered
@@ -52,29 +49,35 @@ def merge_chains(nova_chains: list, pezkuwi_chains: list) -> list:
def merge_xcm(nova_xcm: dict, pezkuwi_xcm: dict) -> dict:
"""
Merge XCM configs: Pezkuwi entries take priority.
Merge XCM: Nova base + Pezkuwi entries added to EACH section.
Structure:
- assetsLocation: dict of asset locations (merge, Pezkuwi overrides)
- chains: list of chain configs (Pezkuwi first, then Nova)
- instructions: dict (keep from Nova, Pezkuwi can override)
Sections: assetsLocation, instructions, networkDeliveryFee, networkBaseWeight, chains
"""
merged = {}
# Merge assetsLocation (Pezkuwi overrides Nova)
# 1. assetsLocation - dict, Pezkuwi overrides/adds
merged['assetsLocation'] = {
**nova_xcm.get('assetsLocation', {}),
**pezkuwi_xcm.get('assetsLocation', {})
}
# Merge instructions if present (Pezkuwi overrides Nova)
if 'instructions' in nova_xcm or 'instructions' in pezkuwi_xcm:
merged['instructions'] = {
**nova_xcm.get('instructions', {}),
**pezkuwi_xcm.get('instructions', {})
}
# 2. instructions - dict, keep Nova's (Pezkuwi uses same instruction types)
if 'instructions' in nova_xcm:
merged['instructions'] = nova_xcm['instructions']
# Merge chains: Pezkuwi first, then Nova (no duplicates)
# 3. networkDeliveryFee - dict keyed by chainId, Pezkuwi adds entries
merged['networkDeliveryFee'] = {
**nova_xcm.get('networkDeliveryFee', {}),
**pezkuwi_xcm.get('networkDeliveryFee', {})
}
# 4. networkBaseWeight - dict keyed by chainId, Pezkuwi adds entries
merged['networkBaseWeight'] = {
**nova_xcm.get('networkBaseWeight', {}),
**pezkuwi_xcm.get('networkBaseWeight', {})
}
# 5. chains - list, Pezkuwi first then Nova (no duplicates)
pezkuwi_chain_ids = {c['chainId'] for c in pezkuwi_xcm.get('chains', [])}
nova_chains_filtered = [
c for c in nova_xcm.get('chains', [])
@@ -86,138 +89,124 @@ def merge_xcm(nova_xcm: dict, pezkuwi_xcm: dict) -> dict:
def sync_chains():
"""Sync chain configurations."""
print("Syncing chains...")
# Load Pezkuwi overlay chains
pezkuwi_chains_file = PEZKUWI_OVERLAY / "chains" / "pezkuwi-chains.json"
pezkuwi_chains = load_json(pezkuwi_chains_file) if pezkuwi_chains_file.exists() else []
print(f" Loaded {len(pezkuwi_chains)} Pezkuwi chains")
print(f" Pezkuwi: {len(pezkuwi_chains)} chains")
# Sync each version
for version_dir in sorted(NOVA_BASE.glob("chains/v*")):
version = version_dir.name
output_version_dir = OUTPUT_CHAINS / version
output_dir = OUTPUT_CHAINS / version
# Sync chains.json
nova_chains_file = version_dir / "chains.json"
if nova_chains_file.exists():
nova_chains = load_json(nova_chains_file)
# chains.json
nova_file = version_dir / "chains.json"
if nova_file.exists():
nova_chains = load_json(nova_file)
merged = merge_chains(nova_chains, pezkuwi_chains)
save_json(output_version_dir / "chains.json", merged)
print(f" {version}/chains.json: {len(merged)} chains ({len(pezkuwi_chains)} Pezkuwi + {len(nova_chains)} Nova)")
save_json(output_dir / "chains.json", merged)
print(f" {version}/chains.json: {len(pezkuwi_chains)} + {len(nova_chains)} = {len(merged)}")
# Sync chains_dev.json
nova_dev_file = version_dir / "chains_dev.json"
if nova_dev_file.exists():
nova_dev = load_json(nova_dev_file)
merged_dev = merge_chains(nova_dev, pezkuwi_chains)
save_json(output_version_dir / "chains_dev.json", merged_dev)
print(f" {version}/chains_dev.json: {len(merged_dev)} chains")
# chains_dev.json
nova_dev = version_dir / "chains_dev.json"
if nova_dev.exists():
nova_chains = load_json(nova_dev)
merged = merge_chains(nova_chains, pezkuwi_chains)
save_json(output_dir / "chains_dev.json", merged)
# Copy preConfigured directory if exists
# android/chains.json
android_dir = output_dir / "android"
if nova_file.exists():
save_json(android_dir / "chains.json", merge_chains(load_json(nova_file), pezkuwi_chains))
# preConfigured (copy from Nova)
nova_preconfig = version_dir / "preConfigured"
if nova_preconfig.exists():
output_preconfig = output_version_dir / "preConfigured"
output_preconfig = output_dir / "preConfigured"
if output_preconfig.exists():
shutil.rmtree(output_preconfig)
shutil.copytree(nova_preconfig, output_preconfig)
print(f" {version}/preConfigured: copied")
# Create android subdirectory with same merged chains
android_dir = output_version_dir / "android"
android_dir.mkdir(parents=True, exist_ok=True)
if nova_chains_file.exists():
save_json(android_dir / "chains.json", merged)
print(f" {version}/android/chains.json: created")
def sync_xcm():
"""Sync XCM configurations by MERGING Nova and Pezkuwi."""
print("\nSyncing XCM configs...")
print("\nSyncing XCM...")
# Load Pezkuwi XCM configs
# Load Pezkuwi XCM overlays
pezkuwi_xcm_file = PEZKUWI_OVERLAY / "xcm" / "pezkuwi-xcm.json"
pezkuwi_xcm_dynamic_file = PEZKUWI_OVERLAY / "xcm" / "pezkuwi-xcm-dynamic.json"
pezkuwi_xcm = load_json(pezkuwi_xcm_file) if pezkuwi_xcm_file.exists() else {}
pezkuwi_xcm_dynamic = load_json(pezkuwi_xcm_dynamic_file) if pezkuwi_xcm_dynamic_file.exists() else {}
print(f" Pezkuwi XCM: {len(pezkuwi_xcm.get('chains', []))} chains, {len(pezkuwi_xcm.get('assetsLocation', {}))} assets")
print(f" Pezkuwi XCM: {len(pezkuwi_xcm.get('chains', []))} chains")
print(f" Pezkuwi XCM dynamic: {len(pezkuwi_xcm_dynamic.get('chains', []))} chains")
# Sync each XCM version
for version_dir in sorted(NOVA_BASE.glob("xcm/v*")):
version = version_dir.name
output_version_dir = OUTPUT_XCM / version
output_version_dir.mkdir(parents=True, exist_ok=True)
output_dir = OUTPUT_XCM / version
output_dir.mkdir(parents=True, exist_ok=True)
# Process each JSON file in the version directory
for nova_file in version_dir.glob("*.json"):
filename = nova_file.name
nova_data = load_json(nova_file)
# Choose appropriate Pezkuwi overlay based on filename
# Choose overlay based on filename
if 'dynamic' in filename:
pezkuwi_data = pezkuwi_xcm_dynamic
overlay = pezkuwi_xcm_dynamic
else:
pezkuwi_data = pezkuwi_xcm
overlay = pezkuwi_xcm
# Merge Nova + Pezkuwi
merged = merge_xcm(nova_data, pezkuwi_data)
save_json(output_version_dir / filename, merged)
print(f" {version}/{filename}: merged ({len(merged.get('chains', []))} chains)")
merged = merge_xcm(nova_data, overlay)
save_json(output_dir / filename, merged)
print(f" {version}/{filename}: {len(merged.get('chains', []))} chains")
# Copy root XCM files (these don't need Pezkuwi merge)
# Root XCM files
for xcm_file in NOVA_BASE.glob("xcm/*.json"):
shutil.copy(xcm_file, OUTPUT_XCM / xcm_file.name)
print(f" {xcm_file.name}: copied")
def sync_icons():
"""Sync icon files from nova-base, preserving Pezkuwi icons."""
print("\nSyncing icons...")
nova_icons = NOVA_BASE / "icons"
pezkuwi_icons = PEZKUWI_OVERLAY / "icons"
output_icons = ROOT / "icons"
# Copy Nova icons
for icon_dir in nova_icons.iterdir():
if icon_dir.is_dir():
output_dir = output_icons / icon_dir.name
if not output_dir.exists():
shutil.copytree(icon_dir, output_dir)
else:
# Merge - copy Nova files that don't exist
# Copy Nova icons (don't overwrite existing)
if nova_icons.exists():
for icon_dir in nova_icons.iterdir():
if icon_dir.is_dir():
output_dir = output_icons / icon_dir.name
output_dir.mkdir(parents=True, exist_ok=True)
for icon_file in icon_dir.rglob("*"):
if icon_file.is_file():
rel_path = icon_file.relative_to(icon_dir)
target = output_dir / rel_path
rel = icon_file.relative_to(icon_dir)
target = output_dir / rel
if not target.exists():
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy(icon_file, target)
# Copy Pezkuwi icons (override Nova if exists)
# Copy Pezkuwi icons (override Nova)
if pezkuwi_icons.exists():
for icon_file in pezkuwi_icons.rglob("*"):
if icon_file.is_file():
rel_path = icon_file.relative_to(pezkuwi_icons)
target = output_icons / rel_path
rel = icon_file.relative_to(pezkuwi_icons)
target = output_icons / rel
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy(icon_file, target)
print(f" Pezkuwi icon: {rel_path}")
print(f" Pezkuwi: {rel}")
def main():
print("=" * 60)
print("Nova-base + Pezkuwi Overlay Sync")
print("Nova + Pezkuwi Merge")
print("=" * 60)
print(f"Nova-base: {NOVA_BASE}")
print(f"Pezkuwi overlay: {PEZKUWI_OVERLAY}")
print(f"Nova (A): {NOVA_BASE}")
print(f"Pezkuwi (B): {PEZKUWI_OVERLAY}")
print()
if not NOVA_BASE.exists():
print("ERROR: nova-base submodule not found!")
print("ERROR: nova-base not found!")
print("Run: git submodule update --init --recursive")
return 1
@@ -226,7 +215,7 @@ def main():
sync_icons()
print("\n" + "=" * 60)
print("Sync complete!")
print("Done!")
print("=" * 60)
return 0