# Reproducible contract build GAUNTLET.md G10: "An auditor must be able to rebuild your bytecode." This records the exact toolchain, the exact procedure, and the code hash every contract compiled to as of the commit this file was last regenerated at — so an auditor rebuilds from source and diffs against a fixed target, rather than trusting that a build "looks right." ## Toolchain, pinned - **Package manager**: `pnpm@11.21.0`, pinned via `package.json`'s `packageManager` field and enforced by Corepack — `pnpm install` refuses to run under a different pnpm version unless overridden. - **Dependency graph, including the compiler**: `pnpm-lock.yaml`, committed. The Tolk compiler is not a separately-fetched binary — it is the `@ton/tolk-js` npm package (a WASM build of the compiler), so `pnpm install --frozen-lockfile` resolves the exact same compiler build every time, not just the exact same TypeScript dependency versions. - **Tolk compiler version actually used**: `1.4.2` (confirmed by the build's own log line, `🔧 Using tolk version 1.4.2...`; the exact resolved version is `@ton/tolk-js@1.4.2` in `pnpm-lock.yaml`). - **Node.js**: `>=20` (`package.json` `engines`), CI pins `20`. WASM execution is deterministic independent of the host Node minor/patch version — the compiler runs inside the WASM sandbox, not as native V8-JIT'd code whose output could vary — so this bound is generous on purpose rather than an exact pin. Not independently verified against a second Node major version in this pass (see "What this does not prove" below). ### The version-mismatch warning, and why the pin above is what actually matters Every `.tolk` source file declares `tolk 1.4` (a *language* version), while the installed compiler is `1.4.2` (an *implementation* version) — a real, deliberate patch-version gap this repo carries. The compiler itself warns about this on every build: ``` warning: the contract is written in Tolk v1.4, but you use Tolk compiler v1.4.2; probably, it will lead to compilation errors or hash changes ``` This is not a bug to silence — it is Tolk's own way of saying "a different patch version of me might compile this source to different bytecode." That is exactly why the toolchain pin above is `@ton/tolk-js@1.4.2` (an exact resolved version in the lockfile), not `tolk 1.4` (the source's own declaration, which several different compiler patch releases could satisfy). **Rebuilding without honouring the lockfile's exact `@ton/tolk-js` resolution is not a valid reproduction attempt**, even though nothing would stop `pnpm install` (without `--frozen-lockfile`) from picking up a newer compatible patch release and silently producing different bytecode. ## Procedure ``` pnpm install --frozen-lockfile cd contracts pnpm exec blueprint build --all ``` Each contract's compiled BOC is written to `contracts/build/.compiled.json` (gitignored — this is a build artifact, not source). The `hashBase64` field in each file is the value to compare against the table below. ## Verified reproducible, this pass Two separate `blueprint build --all` invocations (separate Node processes, same lockfile, Node v24.16.0, pnpm 11.21.0) at commit `ac3eab67` produced **byte-identical output on all 21 contracts** — every `.compiled.json` file diffed with zero differences between the two runs, not just matching hashes. This is what "reproducible" is actually being claimed to mean here: not merely two runs agreeing, but the full serialized BOC (hex + hash + hashBase64) matching exactly. **Regenerated 2026-09-16 (the `/verify` page).** The previous table had gone badly stale and nobody had noticed, because nothing read it: it listed 16 contracts against the 21 in `contracts/contracts/`, still carried `PrimesMarketShard` (a contract `DECISIONS.md` D-90 deleted along with the escrow), omitted `PrimesLpPosition`, `PrimesLpSink`, `PrimesToolItem`, `PrimesToolsCollection`, `PrimesTreasury` and `PrimesVesting` entirely, and 12 of the 16 hashes it did carry no longer reproduced. The webapp route `/verify` publishes this table now, parsed straight out of this file, which is the reason the staleness surfaced — and the reason it has to be regenerated with any change to a `.tolk` source, not merely "should be". The two `Mock*` artifacts in `contracts/build/` are test fixtures for the DeDust vault and are deliberately not listed: they are never deployed. | Contract | Code hash (base64) | |---|---| | `PrimesBountyVault` | `p5NSBQWUf0TmUtUWIm1SPGJWGUPVcHB1eHuq4ENT3aY=` | | `PrimesCollection` | `JVrB5iWiShXNovZ1vxTp++mKtoePnkk+lI0s77OJypI=` | | `PrimesConstellationItem` | `gMiVWfDbi6CZ41SjhM1csGsVz+Jz2EV3zeUD3vKI/yk=` | | `PrimesConstellationsCollection` | `SNssAJjJvlQAn7ZYwXRoj1IyOm1KxPZUSOGrSbMHqVE=` | | `PrimesItem` | `JmTM6d45M5jJvHma7Wo6Bz3O19UehrFtlSlTTAXsGGE=` | | `PrimesJettonMaster` | `52do9h55QgsUaVshb3d3YDnStF1vSmXTMYdxltBsQCY=` | | `PrimesJettonWallet` | `2fTqwNDRWXD4hJl3MPPflpVIjhRLhgqRKhZ3hSWHMHo=` | | `PrimesLedger` | `VsBMxphu9eRWKjooHZE8roH9ngnQJWI2MLC/tSLcqZI=` | | `PrimesLpPosition` | `Rd90/HEfFffce5cYUwgXFpntssWOkKNBcVZFVL4pRoU=` | | `PrimesLpSink` | `Wyd8aihB2HdtunCtdNrAXX0kqO+kgujP+PW0Rc+eWHU=` | | `PrimesMarket` | `nt3S6ymO+tKsKaSWUAM6C5kK7NUIWEfe3ueYkgqIJno=` | | `PrimesRatchet` | `yoIVeLv9tMSaLTsmk/CF0ruKVBl+TgLSuZF3EHG0BLc=` | | `PrimesRegistrar` | `isBD5ngpOwx7IkwWsodvhH61fEA55PL0qrCJtqnT0Pc=` | | `PrimesSink` | `Z2NhbqpTZErVmo5gkbmSrdDHG5Bn7BrJNfgtS8sIwZA=` | | `PrimesStake` | `7eMyJDsOphLjQCoO74aJVGPK4BQMq93vzbfpd8/v9Nk=` | | `PrimesToolItem` | `IMH1YKtcApRVxT1vbNLHbkvudDmukYwBqdUTxrB1OTU=` | | `PrimesToolsCollection` | `uVg1vMpLxuW/jNKxs6anVm6AWDgwVNXe3n5IS2u9nnA=` | | `PrimesTreasury` | `54jDe3wc93MFmq9qybc98oVufAm+RSnKS4nJhjNI9GQ=` | | `PrimesVenueTimelock` | `9zNxDApNHQj5/mGCSyXhEXyKJ7ygmJEwraVF2LrAt8M=` | | `PrimesVesting` | `b6GvNk7ST9eeffFSQKw5rhjz1zaI3053RmoQtBjte2M=` | | `PrimesVoucher` | `j4Xd4R3XUQPHI8VGngSDDpkY6EUixkEt6Oqgg8yJ0r0=` | **This table goes stale the moment a `.tolk` source file changes** (correctly — a real source change should change the hash) or the moment `@ton/tolk-js`'s pinned version moves in `pnpm-lock.yaml`. Regenerate it (rerun the procedure above, paste the new `hashBase64` values) as part of any change that touches `contracts/contracts/*.tolk` or the lockfile's `@ton/tolk-js` entry — the same discipline `docs/audit/contract-surface.md` and `wire-format.md` already follow for their own regeneration. ## What this does not prove - **Not verified across a different OS or Node major version.** Both runs were on the same machine, same Node v24.16.0. WASM execution is architecture-independent by design, so a different OS/CPU is not expected to matter, but that expectation has not been tested here against a second real environment (a second CI runner, a different developer's machine). - **Not verified against the actual bytecode deployed on testnet.** This table records what the CURRENT source compiles to; `docs/testnet-deployment.md` records which contracts are live and when they were deployed, but the two have not been cross-checked hash-for-hash in this pass — a genesis redeploy or a `.tolk` change since the last deploy would make today's rebuild NOT match what is actually live, which is expected and correct (the whole point of a redeploy), not a reproducibility failure.