mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-utils.git
synced 2026-08-12 05:51:06 +00:00
80fb0544ed
* fix(pezkuwi): finish the Asset Hub cleanup and guard it against coming back
Three things left half-done, and a check so they stay done.
## Assets that were never created on chain
chains.json and v22/android/chains_minimal.json still declared Pezkuwi Asset Hub
assetIds 1001, 1002 and 1003 as DOT, ETH and BTC. Queried against
wss://asset-hub-rpc.pezkuwichain.io on 2026-08-11, assets.asset() returns None for all
three; only 1 (PEZ) and 1000 (wUSDT) are Live. They were added from a template — the
icons pointed at Nova's own repo — and shown to users, where any transfer would fail.
This is the second removal. The first, on branch fix/remove-nonexistent-asset-hub-assets
(2026-07-09), cleaned 66 files across v10-v22 but never touched these two, and was never
opened as a PR. A third branch, feature/asset-level-balance-test-fixture, went the other
way and added all three to a regression fixture. Both branches sat for a month.
## Assets served from a working branch
252 references under chains/ fetched icons from pending/post-fix-release. Every file
they named is byte-identical on master, main and that branch, so the dependency bought
nothing — and would have broken silently the day the branch was tidied away, which
nearly happened during this cleanup. Repointed to master, the branch the apps read.
## A fixture nothing keeps in step
sync_from_nova.py publishes chains, xcm, icons and config from the overlay, but never
tests/. So tests/pezkuwi_assets_for_testBalance.json and its overlay source are kept
aligned by hand. They agree today; nothing would have said so if they stopped.
Rather than invent a publish step whose conventions I would be guessing at, the drift
is now asserted.
## The check
scripts/check_pezkuwi_integrity.py, wired into the Code Quality job. Four assertions,
one per regression above, each carrying why it exists. Two of the four have already
recurred once, and the Nova sync will keep proposing the first one back — the current
sync branch (f6c3ebb9) reverts the isSufficient declaration, which is why the second
assertion exists.
Mutation-tested: restoring a ghost asset, removing isSufficient, repointing one icon at
a working branch, and nudging the fixture each fail it; reverting each passes.
* fix(overlay): declare sufficiency at the source, not in the generated output
The isSufficient declaration was added to chains/ — which sync_from_nova.py regenerates
from nova-base plus pezkuwi-overlay on every run. So the fix had a shelf life of exactly
one sync, and the sync branch already waiting (sync/nova-base-f6c3ebb9, 2026-08-08)
reverts it: its output carries {"assetId": "1000"} with no sufficiency, because it was
generated from an overlay that does not declare any.
Editing generated files is how this repo keeps losing the same change. The phantom
Asset Hub assets were removed twice and came back twice for the same reason.
Verified rather than assumed: added the field to
pezkuwi-overlay/chains/pezkuwi-chains.json, ran scripts/sync_from_nova.py, and all three
generated files — chains.json, v22/android/chains.json, v22/android/chains_minimal.json
— came out carrying isSufficient: true. git reported no change to any of them, meaning
the sync now produces exactly what the manual edit produced, so the two agree instead of
fighting.
The phantom assets do not return either: the overlay lists only HEZ, PEZ and USDT, so
regeneration drops 1001/1002/1003 by construction rather than by anybody remembering.
118 lines
4.3 KiB
Python
Executable File
118 lines
4.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Refuse the four regressions this repo has already had.
|
|
|
|
Each check below exists because the thing it forbids happened, was fixed, and in two
|
|
cases came back. They are cheap; the failures they catch are not.
|
|
|
|
Run from the repository root. Exits non-zero on the first category that fails, after
|
|
reporting every problem it found.
|
|
"""
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
APP_CHAINS = ROOT / "chains" / "v22" / "android" / "chains.json"
|
|
AH = "Pezkuwi Asset Hub"
|
|
|
|
errors: list[str] = []
|
|
|
|
|
|
def load(path: Path):
|
|
with path.open(encoding="utf-8") as fh:
|
|
return json.load(fh)
|
|
|
|
|
|
def asset_hub_assets(doc):
|
|
for chain in doc:
|
|
if chain.get("name") == AH:
|
|
yield from chain.get("assets", [])
|
|
|
|
|
|
def extras(asset):
|
|
return asset.get("typeExtras") or {}
|
|
|
|
|
|
# ── 1. Only assets that exist on chain may be declared ───────────────────────
|
|
#
|
|
# 1001/1002/1003 were declared as DOT/ETH/BTC with icons pointing at Nova's own repo —
|
|
# copied from a template, never created on Pezkuwi Asset Hub. Queried 2026-08-11 against
|
|
# wss://asset-hub-rpc.pezkuwichain.io, assets.asset() returns None for all three; only
|
|
# 1 (PEZ) and 1000 (wUSDT) are Live. Removed twice, back twice.
|
|
GHOST = {"1001", "1002", "1003"}
|
|
for name in ("chains/chains.json",
|
|
"chains/v22/android/chains.json",
|
|
"chains/v22/android/chains_minimal.json"):
|
|
path = ROOT / name
|
|
if not path.exists():
|
|
continue
|
|
bad = [a.get("symbol") for a in asset_hub_assets(load(path))
|
|
if str(extras(a).get("assetId", "")) in GHOST]
|
|
if bad:
|
|
errors.append(
|
|
f"{name}: declares Asset Hub assets that do not exist on chain: {bad}"
|
|
)
|
|
|
|
# ── 2. Sufficient assets must say so ─────────────────────────────────────────
|
|
#
|
|
# Absent, LocalToDomainChainMapper resolves isSufficient to false, the wallet's
|
|
# DeadRecipientValidation runs at ERROR level, and every transfer to an account holding
|
|
# no HEZ is refused — which is every fresh deposit address, so nobody can fund an
|
|
# exchange account. Both assets are sufficient=true on chain.
|
|
if APP_CHAINS.exists():
|
|
declared = {
|
|
str(extras(a).get("assetId"))
|
|
for a in asset_hub_assets(load(APP_CHAINS))
|
|
if extras(a).get("isSufficient") is True
|
|
}
|
|
missing = {"1", "1000"} - declared
|
|
if missing:
|
|
errors.append(
|
|
f"{APP_CHAINS.name}: Asset Hub assets {sorted(missing)} must declare "
|
|
f"isSufficient: true — the chain says they are sufficient"
|
|
)
|
|
|
|
# ── 3. Assets are served from a branch that is actually served ───────────────
|
|
#
|
|
# 252 icon references once pointed at pending/post-fix-release. Every file they named
|
|
# also existed on master, so the dependency bought nothing — and would have broken
|
|
# silently the day that branch was tidied away.
|
|
grep = subprocess.run(
|
|
[
|
|
"grep",
|
|
"-rlE",
|
|
r"pezkuwi-wallet-utils/(pending|feature|fix|test)/",
|
|
str(ROOT / "chains"),
|
|
],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
if grep.stdout.strip():
|
|
files = grep.stdout.strip().splitlines()
|
|
errors.append(
|
|
f"{len(files)} file(s) under chains/ reference a working branch for "
|
|
f"assets; use master, e.g. {Path(files[0]).name}"
|
|
)
|
|
|
|
# ── 4. A published fixture matches its source ────────────────────────────────
|
|
#
|
|
# sync_from_nova.py does not copy tests/, so these two are kept in step by hand and
|
|
# nothing noticed if they drifted.
|
|
for fixture in ("pezkuwi_assets_for_testBalance.json",):
|
|
src = ROOT / "pezkuwi-overlay" / "tests" / fixture
|
|
pub = ROOT / "tests" / fixture
|
|
if src.exists() and pub.exists() and src.read_bytes() != pub.read_bytes():
|
|
errors.append(
|
|
f"tests/{fixture} has drifted from pezkuwi-overlay/tests/{fixture}"
|
|
)
|
|
|
|
if errors:
|
|
for e in errors:
|
|
print(f"::error::{e}")
|
|
print(f"\n{len(errors)} integrity problem(s).", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
print("Pezkuwi integrity: on-chain assets only, sufficiency declared, "
|
|
"assets served from master, fixture in step with its source.")
|