Portaldot's mainnet docs list Balances.transfer. On the V3 testnet that call does not exist.
V3 is a new chain: the testnet reports portaldot-v3 spec 3002 with 69 pallets and 480 calls, where mainnet spec 1002 has 31 pallets. The written sources disagree with it, too: the V3 guide names the token tPOTv3, while the chain reports tPOT. From the docs alone, nobody can say which documented call runs on the chain.
Plumbline answers with transactions, not opinions: documented call → verdict → proof.
Four commands
-
plumb reads runtime metadata through Portaldot's Python SDK (
substrate-interface) and parses the documented surfaces: the 22 mainnet extrinsic doc pages (196 calls), the V3 guide's 46 sample-codecompose_callsites and its FAQ module table (12 modules). Every entry gets a verdict on V3: PROVEN, UNPROVEN, ABSENT, ARG_DRIFT, DRIFTED or UNDOCUMENTED. That is 549 verdict rows, each stamped with the spec and metadata hash, and byte-identical on re-run. -
canary proves calls. A call is PROVEN only by a real transaction read back from its block. Three were signed on V3 (
System.remark_with_event,Balances.transfer_keep_alive,Utility.batch_all, blocks 106424–106426), andplumbline readbackre-reads them from a fresh connection: 3/3 match. -
diff lists the pallet, call, argument and constant changes from mainnet 1002 to V3: 516 rows, with 40 pallets added and 2 removed (
Proxy.anonymous→create_pureamong the renames). - check flags call sites in your own dApp that will not run: the example dApp has 0 blocking findings on V3 and 5 on mainnet spec 1002, with exit code 1 on blocking so it can gate CI.
Findings on V3
The 79 calls whose docs and runtime disagree on mainnet 1002 become 2 on V3: Balances.transfer is absent, and ElectionProviderMultiPhase.governance_fallback takes different arguments. Five of the 12 modules the V3 guide's FAQ lists are not on the testnet.
Deterministic code decides every verdict, a metadata set-difference plus a transaction receipt. There is no model in the loop.
Things I learned
-
The metadata hash is not a stable identity. On V3 it varied by one byte between blocks, so Plumbline records the runtime
:codehash as the identity instead. - "Exists in metadata" is not "works". Only a receipt read back from a block upgrades a call to PROVEN; everything else stays UNPROVEN.
Honest limits
No Plumbline contract is deployed: the on-chain evidence is the 3 canary transactions, and every other documented call that exists on V3 stays UNPROVEN until a transaction is sent for it. Runtime APIs (ReviveApi) are not checked yet.
Links
- Code: https://github.com/edycutjong/portaldot-hackathon-2026-plumbline-edycu
- Demo video: https://youtu.be/edvFuOdUZNk
- Site: https://plumbline.edycu.dev
- Proof:
docs/PROOF.md
263 tests pass (live tests are opt-in). Built for the Portaldot Hacker House 2026.

Top comments (0)