How-to guide

Prepare a protocol release

Follow this guide before tagging a Zoltar and Statoblast release. It lists the checks that must pass before the tag and, for Sepolia and deterministic testnets, the genesis REP allocation review. Mainnet uses Augur V2's existing REP Yes and REP No tokens and deploys no genesis REP tokens.

Know what a release cannot do

The contracts are immutable: no operator can pause, upgrade, roll back, or rescue them after deployment, so a release publishes verifiable provenance instead (commit and tag, deterministic addresses, CI and QA results, production UI artifact hash, known limitations) and never describes a UI deployment as an emergency control. See EG05.

Run the release checklist

  1. From a fresh dependency install (bun install --frozen-lockfile), run bun run check:mainnet-deployment:generated. It fails when any of the four generated deployment manifests is stale. If so, regenerate them with bun ./tooling/contracts/check-mainnet-deployment.mts --write. Review the Yes manifests for mainnet and Sepolia, and the No manifests for mainnet and Sepolia, then rerun the check.
  2. Run bun run ui:build:prod. It repeats the manifest check, so it fails if a manifest is still stale.
  3. Confirm the build used the pinned compiler configuration in solidity/ts/compile.ts, the exact tool versions in solidity/package.json, and the imported OpenOracle profile in UPSTREAM.md.
  4. Review the pinned runtime-code hashes: the EXPECTED_*_RUNTIME_CODE_HASHES maps in ui/zoltarShared/ts/protocol/deployment.ts and ui/statoblastShared/ts/protocol/deployment.ts. Check them in three ways, then publish the reviewed hashes with the release:
    • Run bun ./tooling/testing/bun-test.mts --preload ./bun-test-setup-ui.ts ui/zoltar/ts/tests/protocol/deployment.test.ts ui/statoblast/ts/tests/protocol/deployment.test.ts. It confirms that a clean local build still matches the pinned hashes of the directly compiled steps (the deployment status oracle, Multicall3, ZoltarQuestionData, and five Statoblast artifacts); it does not cover the other pinned contracts.
    • On Sepolia, rerun bun run deploy:testnet. It checks the runtime code of every installed contract against its pinned hash. It is not read-only: it needs PRIVATE_KEY, deploys any missing step, and then runs explorer verification.
    • For any other deployed address, compare the keccak-256 hash of its eth_getCode result with the pinned hash by hand before marking the release canonical.
  5. Push the final tag only after the CI gate succeeds on the same commit. The tag must match v<number> (for example v12); the Version deploy workflow rejects any other form. That workflow reruns CI, builds the InterPlanetary File System (IPFS) publisher image, reads the content identifier (CID) from that image, checks only that it has the bafy… form, and creates or updates the GitHub release with that CID in its notes. Confirm the release exists and records the CID before announcing.
  6. In the production UI release notes, state that ?simulate=1 is a browser-local sandbox. Treat a stale, unavailable, or unsupported live quote as a blocker for the affected client action; never substitute a simulation price for it.

Review genesis REP allocations (Sepolia and deterministic testnets)

Sepolia genesis REP allocations are canonical source data in sepoliaRepAllocations.ts. Each of the Yes and No tokens mints the same allocations. The configured 11 million REP mint cap per token is divided equally among the listed holders; adding a holder automatically reduces every holder's allocation. Integer division may leave the total minted supply below the cap by less than one attoREP per holder. Review both the holder list and the computed total before deployment. The constructor of GenesisReputationToken rejects an empty list, a zero address, a repeated holder, a zero balance, and a total above the cap.

There is no administrator, post-deployment mint, allocation correction, or recovery entrypoint, so fix an incorrect allocation in the source list before deployment and then regenerate the deterministic manifests. Because constructor arguments are part of CREATE2 init code, any allocation change produces a new genesis REP address and new addresses for every deployment step whose constructor wiring depends on it. Never continue a deployment from a manifest generated for a different allocation list.