* 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.
* fix(pezkuwi-asset-hub): declare PEZ and USDT as sufficient assets
The wallet refused USDT.p transfers to any account holding no HEZ:
Your transfer will fail since the destination account does not have
enough HEZ to accept other token transfers
It is a false positive, and the chain disagrees with this config.
DeadRecipientValidation skips the whole check when the destination asset is
self-sufficient:
skipIf = { assetSourceRegistry.isAssetSelfSufficient(destinationChainAsset) }
and StatemineAssetBalance reads that flag from HERE, not from the chain:
chainAsset.requireStatemine().isSufficient → runCatching { … }.getOrDefault(false)
`isSufficient` was absent from typeExtras in every version of this config, so it
resolved to false, the validation ran, and every transfer to a fresh account was
warned against.
Measured on chain (asset-hub-rpc.pezkuwichain.io):
asset 1 PEZ sufficient=true minBalance=1 18 accounts
asset 1000 wUSDT sufficient=true minBalance=10000 21 accounts
Both are sufficient, so neither needs the recipient to hold native balance.
Proof it works in practice rather than only in theory: account
5E2yqgUVVNjU… holds 1750.00 USDT with free=0, providers=0, sufficients=1 and
nonce=0. Zero native balance, no provider reference, and it has never signed a
transaction — so it was brought into existence by the incoming USDT transfer
itself, which is exactly what the wallet was saying could not happen.
Applied to v22 (the version the app fetches, runtime/build.gradle CHAINS_URL) and
to the top-level chains.json.
Noted, not changed here: this config also declares assets 1001/1002/1003 on
Pezkuwi Asset Hub as DOT, ETH and BTC. None of the three exists on chain —
assets.asset() returns None for all of them. They are shown to users and any
transfer would fail.
* Revert "fix(pezkuwi-asset-hub): declare PEZ and USDT as sufficient assets"
This reverts commit af1a8a5468.
* fix(pezkuwi-asset-hub): declare PEZ and USDT as sufficient, matching Polkadot AH
Pezkuwi Asset Hub's two statemine assets carried only `assetId` in typeExtras. The
equivalent entries on Polkadot Asset Hub — USDT (1984) and USDC (1337) — carry
`isSufficient: true`. Same kind of asset, same file, two different declarations.
The omission is not inert. `LocalToDomainChainMapper` reads
typeExtras["isSufficient"] as Boolean? ?: STATEMINE_IS_SUFFICIENT_DEFAULT // false
so an absent field resolves to false, `StatemineAssetBalance.isSelfSufficient` returns
false, `DeadRecipientValidation`'s skipIf does not fire, and the validation runs — at
`validOrError`, which is DefaultFailureLevel.ERROR, so the transfer is blocked rather
than warned about.
The chain says otherwise. Measured on asset-hub-rpc.pezkuwichain.io, 2026-08-10:
asset 1 PEZ sufficient=true minBalance=1 18 accounts
asset 1000 wUSDT sufficient=true minBalance=10000 21 accounts
Neither needs the recipient to hold native HEZ, and one account demonstrates it:
5E2yqgUVVNjU… holds 1750.00 USDT with free=0, providers=0, sufficients=1 and nonce=0 —
no native balance, no provider reference, never signed a transaction. It exists because
the incoming USDT transfer created it, which is exactly what the wallet was reporting
could not happen.
43 of the 67 statemine assets in this config declare the field. The 24 that omit it are
genuinely non-sufficient on chain — the Polkadot AH meme tokens. Pezkuwi Asset Hub was
the only case where the declaration disagreed with the chain.
Applied to v22 (the version runtime/build.gradle CHAINS_URL fetches) and the top-level
chains.json, in the same shape as the Polkadot entries.
The earlier PR (#55) only updated the pezkuwi-overlay source file, not
the generated chains/v*/[android/]chains*.json outputs the app actually
fetches at runtime (BuildConfig.CHAINS_URL points at
chains/v22/android/chains.json on this exact branch) - this is why a
real-device install still showed no Solana network/token. Mirrors
Bitcoin's own commit (78b0af9), which updated the overlay AND all 63
generated files in one change. Inserted right after the Bitcoin entry
in every v2-v22 chains.json/chains_dev.json/android/chains.json, using
ensure_ascii=False to avoid re-escaping unrelated non-ASCII chain names
already present in these files (caught and reverted a first attempt
that had done this by accident). Also copies the Solana icons into
pezkuwi-overlay/icons/ to match Bitcoin's dual-location icon pattern.
Master never got the Bitcoin icon files (they landed only on
pending/post-fix-release, same branch CHAINS_URL is currently testing
against) - Tron's icon happened to already exist on master from an
earlier, unrelated commit, so it worked by coincidence while Bitcoin's
404'd. Confirmed live: master/icons/tokens/colored/BTC.svg -> 404,
pending/post-fix-release/... -> 200.
Same caveat as the CHAINS_URL override itself: revert to master once
this lands there in the coordinated release.
Adds a Bitcoin entry (assetId 0, type "bitcoinNative", precision 8) mirroring
Tron's chain-family shape: "bitcoinBased" + "noSubstrateRuntime" options, a
mempool.space REST API URL as the "node" (same role TronGrid's URL plays for
Tron), and mempool.space itself as the block explorer.
Adds gradient chain icon + colored token icon (orange #F7931A, built from
the existing white Bitcoin glyph already vendored from Nova, matching this
repo's established icon-construction pattern for every other coin).
Lands on pending/post-fix-release, not master - per the existing plan to
ship Tron and Bitcoin support together in one coordinated release once
wallet-android's corresponding send features are done.
These 3 assetIds (1001/1002/1003) were added in 173f08a with icons
pointing at novasamatech/nova-utils (Nova's own repo, not Pezkuwi's)
and generic names/priceIds - clearly copied from a template rather than
verified against Pezkuwi's actual chain state.
Verified live via @pezkuwi/api against wss://asset-hub-rpc.pezkuwichain.io:
api.query.assets.asset(1) -> Live, real supply (PEZ, correct)
api.query.assets.asset(1000) -> Live, real supply (USDT, correct)
api.query.assets.asset(1001) -> None (DOT, does not exist)
api.query.assets.asset(1002) -> None (ETH, does not exist)
api.query.assets.asset(1003) -> None (BTC, does not exist)
This is what caused wallet-android's StatemineAssetBalance to silently
never complete a sync for these 3 assets - it was fetching details for
assets that were never created in pallet-assets, not a wallet bug. Also
removes them from the new pezkuwi_assets_for_testBalance.json fixture
so the test doesn't assert on assets that were never real.
The diagnostic (temporarily-remove-tron) didn't fix the live app -
confirmed the real cause was stale local app data on a specific test
device (large 98->68 chain diff against old cached state), resolved by
a clean reinstall on that device, and unrelated to Tron entirely.
Also fixes a real inconsistency found while restoring: Tron's USDT icon
pointed at the "main" branch while every other icon URL in this file
(including Tron's own chain icon and TRX's icon) points at "master" -
now consistent.
* diagnostic: temporarily remove Tron from published chains.json
Hypothesis: the live (already-published) Play Store app - unrelated to
any of today's wallet-android code changes, compiled months before Tron
support was ever added - stopped showing any tokens/networks around the
same time Tron was added to this shared chains.json (2026-07-07).
Tron's chainId ("tron:0x2b6653dc") is a third, previously-unseen format -
neither a 32-byte substrate genesis hash nor an "eip155:N" EVM id. If the
live app's pre-Tron code has any hardcoded assumption about chainId shape,
encountering this one malformed-from-its-perspective entry could throw
while processing the shared chain list - and per a related bug already
fixed on the wallet-android side today, a single chain's processing
failure can permanently kill sync for every chain, matching the observed
"completely empty" symptom.
This removes Tron from the published feed as a live, falsifiable test: if
the already-installed live app recovers after this goes out, the
hypothesis is confirmed and Tron needs a compatibility fix (in wallet-utils
and/or wallet-android) before being reintroduced. If it does NOT recover,
the hypothesis is wrong and the real cause is elsewhere.
Tron entry preserved for restoration once the compatibility issue (if
confirmed) is fixed - see pezkuwi-overlay/chains/pezkuwi-chains.json git
history (this commit's parent) for the full entry.
* chore: retrigger CI now that master triggers are fixed
Real user hit a stuck "Connecting..." state on the Crab chain on a
live build. Root cause: the ORIGINAL blacklist (before today's
cleanup) correctly listed this chain as "Darwinia Crab" with chain_id
86e49c...c3a - I dropped that entry during the 2026-07-08 blacklist ID
correction, having concluded it "no longer exists upstream" based on
a name search for "darwinia"/"crab" that didn't match because the
chain's actual name field is just "Crab", not "Darwinia Crab" - a
search-term miss, not evidence the chain or its chain_id had changed.
It's been in chains.json all along with the same id and the same dead
RPC (crab-rpc.darwinia.network, confirmed unreachable again live
2026-07-08).
Re-verified against live chains.json before re-adding - it's really
there, and the RPC is really dead.
Follow-up to the previous stale-blacklist fix: with that fix live,
Android's balances_test.yml CI job progressed much further but still
hung for the full 25-minute job timeout. Live logcat + a full-log
retry analysis (grouping by host, comparing first/last timestamp
against the ~23 minute test window) showed 27 more chains whose nodes
were retried continuously for the ENTIRE run duration with zero
successful connections - as opposed to the ~90 other chains, which
connect once (or retry once or twice in the first ~2 minutes) and
then go quiet, i.e. genuinely healthy.
Every one of these 27 is a real, currently-dead third-party RPC
(Kusama Asset Hub, KILT, Crust Shadow, Phala, Mangata X, Joystream,
Mythos, Base, and 19 others) - not anything related to Tron or this
session's other changes. Each was matched to its current chain_id by
node URL against live chains.json, not guessed.
sync_from_nova.py's new stale-blacklist warning confirms all 33
entries (6 previous + 27 new) now match something in Nova's current
chain data - zero silently-ineffective entries this time.
This is inherently a moving target (third-party community RPC uptime
changes over time) - the warning added in the previous commit is what
makes that maintainable going forward instead of silently rotting for
another five months.
Root cause of the balances-test hang in pezkuwi-wallet-android (silent
for over an hour, cancelled multiple times before finding this):
every single chain_id in blocked-chains.json was stale - none matched
anything in Nova's current chains.json (last_updated 2026-02-11, ~5
months of upstream chain-id drift since). The blacklist mechanism
silently no-opped for all 6 entries, so none of the chains it was
meant to exclude were actually excluded.
With the blacklist ineffective, several chains with genuinely dead RPC
endpoints (confirmed live 2026-07-08: 3DPass, Curio, Quartz, Subsocial
- 502s, timeouts, SSL failures) stayed in the merged output. The
Android app's ChainConnection/NodeAutobalancer has no backoff/circuit
breaker for dead endpoints, so it retried them in an extremely tight
loop (~1 attempt/second, 1500-1800+ attempts observed in a 25 minute
window per chain) - burning resources indefinitely and starving the
app's actual chain-sync/test work, which is what made the Android CI
job hang for over an hour with zero progress.
Fix: re-verified and corrected all chain_ids (matching by current
name against live chains.json), added 3DPass/Curio/Subsocial which
weren't blacklisted before, dropped entries for chains no longer
present upstream at all (Passet Hub Testnet, Darwinia Crab, DeepBrain,
Exosama - nothing to block, kept them as historical noise otherwise).
Also added a stale-blacklist warning to sync_from_nova.py: it now
tracks which blocked chain_ids actually matched something in Nova's
current chains across all versions, and prints a warning listing any
that didn't - so this exact silent drift is caught at sync time going
forward instead of rotting unnoticed for another five months.
- pezkuwi-overlay/chains/pezkuwi-chains.json: add Tron mainnet
(native TRX + USDT-TRC20) chain entry, plus new TRX/Tron icons
under pezkuwi-overlay/icons/ (the correct source location - synced
into the top-level icons/ output by sync_from_nova.py).
- Regenerate all chains/*, xcm/*, staking/* merged output via
sync_from_nova.py against the current nova-base submodule state.
- Also picks up previously-uncommitted local fixes: canonical
Telegram/Twitter links (t.me/kurdishmedya, x.com/bizinikiwi) in
README/docs/banners, and Pezkuwi mainnet + People chain SubQuery
staking overrides.
- Add staking, staking-rewards, history externalApi endpoints (subquery.pezkuwichain.io)
for both Pezkuwi Relay and Asset Hub
- Add identityChain reference to People chain on Relay and Asset Hub
- Add stakingWiki link to wiki.pezkuwichain.io/staking
- Add pushSupport option to Asset Hub
- Add defaultBlockTime to Asset Hub additional config
- Regenerate all merged chain versions via sync script
- Add blocked-chains.json with DNS-failing endpoints (AlephZero, InvArch, Quartz, Passet Hub Testnet)
- Update merge-chains.py to filter PAUSED chains, testnets (except Pezkuwi), and broken RPCs
- Reduce chain count from 102 to 86 (4 Pezkuwi + 82 working Nova chains)
- This fixes staking page not loading due to DNS resolution failures
Source changes (pezkuwi-overlay/chains/pezkuwi-chains.json):
- Pezkuwi Relay Chain: Add stakingMaxElectingVoters: 22500
- Pezkuwi Asset Hub HEZ: Change staking from ["nomination-pools"] to ["relaychain"]
- Pezkuwi Asset Hub: Add stakingMaxElectingVoters: 22500
This fixes the "Loading staking info" issue where findStakingTypeBackingNominationPools()
was failing because nomination-pools had no backing staking type.
Regenerated all chain versions via sync_from_nova.py
Asset Hub uses parent relay chain for era calculations since
it doesn't have Babe consensus module (uses Aura instead).
This fixes the crash when accessing nomination pools staking.
- Asset Hub HEZ staking set to null (was nomination-pools)
- Relay chain HEZ keeps relaychain staking only
- Wallet code prepared for Polkadot 2.0 but config disabled for now
- Add sync_from_nova.py script to merge Nova chains with Pezkuwi overlay
- Add GitHub Action for daily auto-sync
- Sync all chains from nova-base (includes Polkadot Coretime and other missing chains)
- Pezkuwi chains appear first and take priority
This fixes DOT swap crash caused by missing Polkadot Coretime chain.
- Remove 0x prefix from chainIds to match ChainGeneses constants
- Place Pezkuwi, Pezkuwi Asset Hub, and Pezkuwi People at the top
- Add empty evm_assets.json for EVM asset support
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>