manifest.json is the single machine-readable record of every
Wraith deployment on EVM, Stellar, Solana and CKB. Read contract addresses, start
heights, artifact hashes and ABIs from it rather than from other files or docs.
deployments/
schema/v1.json JSON Schema (draft 2020-12) for the manifest and records
manifest.json generated: every record, sorted by chain then network
evm/horizen-testnet.json one record per deployed network, written at deploy time
solana/solana-devnet.json
ckb/ckb-testnet.json
stellar/ written by stellar/deploy.sh on first deployment
Do not edit manifest.json by hand. It is built from the records, and CI fails
if the two disagree.
A change that breaks existing readers bumps schemaVersion and adds schema/v2.json.
Adding an optional field does not.
| Chain | networkId |
id |
deployment.height |
artifactHash |
|---|---|---|---|---|
| EVM | decimal chain ID | contract address | block of the creation tx | keccak256 of runtime bytecode (EXTCODEHASH) |
| Stellar | network passphrase | contract ID (C…) |
ledger that created the instance | sha256 of the wasm (the on-chain wasm hash) |
| Solana | genesis hash | program ID | slot of the last (re)deploy | sha256 of the program ELF without zero padding (solana-verify get-program-hash) |
| CKB | genesis block hash | script code hash | block of the cell dep tx | CKB blake2b-256 of the cell data |
Every value can be checked against the chain: npm run verify (below) does exactly
that. Chain-specific extras: EVM contracts carry abi (hash over the canonical ABI,
so formatting does not matter). Solana programs carry solana.programData and
solana.upgradeAuthority. CKB scripts carry ckb.hashType and ckb.cellDep, and
external: true marks third-party scripts Wraith depends on (ckb-auth).
For EVM, a network's record must account for every contract the repository supports
(ERC5564Announcer, ERC6538Registry, WraithSender, WraithNames,
WraithWithdrawer; the list is EXPECTED_CONTRACTS in
catalogue.ts). Each one is either:
- in
contracts, with its deployment transaction (deployment.txHash), block (deployment.height),artifactHashandabi(path and canonical hash), or - in
undeployed, with a reason:
"undeployed": [
{ "name": "WraithSender", "reason": "Not deployed on horizen-testnet yet." }
]A name in both lists, a name that is not a supported contract, or a supported contract
in neither list fails npm run check, npm run build and npm run verify. Recording a
contract removes it from undeployed.
recordedBy: "chain-query" means the record was reconstructed from chain state after
the fact.
Every first-party contract carries provenance:
source-verified:sourceCommitis a full 40-hex commit and building it reproducesartifactHash.npm run verify-sourcerebuilds and checks this (EVM via Hardhat, Stellar via thestellar/deploy.shbuild; contracts with Solidity immutables are compared against the deployed code everywhere except the immutable slots).unverifiable: a historical record.sourceCommitisnullandreasonsays why. The current EVM, Solana and CKB records are of this kind, because they predate the manifest and no build has been shown to reproduce them. Solana and CKB have no reproducible build yet, so they cannot be source-verified.
Records written by a deploy script must be source-verified: record rejects a missing
or malformed --source-commit (only HEAD or a full hash), a working tree with
uncommitted changes, and a commit whose build does not reproduce the artifact hash.
Re-recording unchanged bytecode keeps its existing provenance; changed bytecode resets
it to unverifiable unless a commit is supplied.
To upgrade a historical record, prove the commit offline:
npm run attest -- evm --network horizen-testnet --contract ERC5564Announcer --search
npm run attest -- evm --network horizen-testnet --contract ERC5564Announcer --commit <full sha>A record is only changed when a build reproduces the recorded hash.
npm run check fails if any of these disagree with the manifest:
| File | Checked | Generated by build |
|---|---|---|
evm/subgraph/subgraph.template.yaml |
each source.abi is an EVM contract; no network, address or startBlock |
no |
evm/subgraph/<network>/instant-config.json |
address, chain, startBlock |
yes |
evm/subgraph/abis/*.json, evm/subgraph/<network>/abis/*.json |
canonical ABI hash | no |
evm/hardhat.config.ts |
chainId of the matching network |
no |
solana/Anchor.toml, solana/programs/*/src/lib.rs |
[programs.<cluster>] and declare_id! |
no |
ckb/<network>.toml |
code hashes and cell deps | no |
stellar/contract-ids.json |
IDs from stellar-testnet (used by the TS bindings) |
yes |
README.md (between deployments:begin/end) |
deployed addresses tables | yes |
The tool lives in scripts/deployment-manifest:
cd scripts/deployment-manifest
npm ci- EVM:
evm/scripts/deploy.tsrecords automatically on any non-local network, using each contract's deployment receipt. Then regenerate the subgraph manifest from the record (see Subgraph manifest). - Stellar:
stellar/deploy.shrecords automatically, beforeinit. - Solana, CKB, or anything deployed by hand: record from the chain.
npm run record -- solana --network solana-devnet --rpc https://api.devnet.solana.com \
--public-rpc https://api.devnet.solana.com \
--program wraith_announcer=<program id> \
--recorded-by deploy-script --source-commit HEAD --version 0.1.0
npm run record -- ckb --network ckb-testnet --rpc https://testnet.ckbapp.dev \
--public-rpc https://testnet.ckbapp.dev \
--script wraith-stealth-lock=<cell dep tx hash>:0:data2
npm run record -- evm --network horizen-testnet --rpc <rpc> \
--contract WraithSender=<address>@<deploy tx hash>
# a supported contract that is not deployed on this network
npm run record -- evm --network horizen-testnet --rpc <rpc> \
--undeployed WraithNames="Not deployed on horizen-testnet yet."record reads heights and artifact hashes from the chain, merges into the existing
record for that network, and runs build. --rpc is only used for queries, so it
may contain an API key; only --public-rpc is written to the record. Commit the
record, manifest.json and every file build regenerated.
evm/subgraph/subgraph.yaml is not checked in. It is generated for one network from
evm/subgraph/subgraph.template.yaml and deployments/evm/<network>.json:
npm run subgraph -- --network horizen-testnetEach data source gets network, source.address and source.startBlock from the
contract its source.abi names. If any contract is not recorded, has no start block
(or start block 0), or has the placeholder address 0x…dead, nothing is written and
every such contract is listed. npm run subgraph -- --check fails unless the
existing subgraph.yaml is exactly what --network would write now for the network
it names; npm run deploy and npm run deploy-local in evm/subgraph run it before
graph deploy.
| Command | What it does |
|---|---|
npm run build |
Validate records, write manifest.json, regenerate the generated consumers. |
npm run check |
Offline validation of records, manifest and consumers. Runs in CI. |
npm run verify-source |
Rebuild each source-verified contract from its commit and compare artifact hashes. |
npm run attest |
Backfill: mark a historical record source-verified once a commit reproduces it. |
npm run verify |
Re-read every deployment from its chain and compare IDs, heights and hashes. Runs weekly in CI. |
npm run subgraph |
Write evm/subgraph/subgraph.yaml for --network <slug>; --check verifies it. |
npm test |
Unit and end-to-end tests for the tool. |
{ "schemaVersion": 1, "deployments": [ { "chain": "evm", // evm | stellar | solana | ckb "network": "horizen-testnet", // slug; also the record's file name "networkId": "2651420", // see "Network identity" below "rpcUrl": "https://…", // public endpoint used by `verify` "recordedBy": "chain-query", // deploy-script | chain-query "contracts": [ { "name": "ERC5564Announcer", // contract, crate or Anchor program name "id": "0x8AE6…", // address / contract ID / program ID / code hash "version": "0.1.0", // package or crate version, null if unknown "sourceCommit": "…", // full 40-hex git commit; null only when unverifiable "provenance": { "status": "source-verified" }, // or { "status": "unverifiable", "reason": "…" } "artifactHash": { "algorithm": "keccak256", "value": "0x…" }, "abi": { "path": "evm/subgraph/abis/ERC5564Announcer.json", "sha256": "0x…" }, "deployment": { "height": 14202900, "txHash": "0x…", "deployer": "0x…", "timestamp": "…Z", }, }, ], }, ], }