# Guide: Local Build Environment (Solidity + Hardhat)

Most ChainRender apps need no Solidity — you compose a tree, grade it and register it. When you do write a contract (a
custom resolver, an app contract, a token), match the toolchain ChainRender itself is built with, so your contract
compiles against the same interfaces and deploys the same bytes on every chain. These settings come from the
project's own `hardhat.config.ts`, `package.json` and contracts.

## The settings

| Setting | Value | Why |
|---|---|---|
| Framework | **Hardhat 3** (`hardhat ^3.13`), `@nomicfoundation/hardhat-toolbox-viem ^5`, `viem ^2.56` | The config uses Hardhat 3's `defineConfig`, build profiles and `configVariable`. Not Foundry. |
| Compiler | **solc 0.8.36** from the npm `solc` package, pinned exactly (`npm i -D solc@0.8.36`) | What every ChainRender deploy is built with. A `^0.8.x` range lets a fresh install drift. |
| `pragma` | `pragma solidity ^0.8.28;` (or exact `0.8.36`) | What the repo's contracts use, so yours compile alongside ChainRender's interfaces. |
| `evmVersion` | **`"cancun"`** | Byte-identical builds across chains depend on one target; the repo needs Cancun's MCOPY and transient storage. Don't move to `prague`/`osaka` for one chain. |
| Optimizer | `enabled: true`, `runs: 200`, `details: { yul: false }` | The legacy optimizer keeps large renderers under the code-size limit; `yul: false` avoids stack-too-deep in the big interpreters. |
| viaIR | **Off — never enable it.** | `yul: false` is the deliberately less aggressive alternative. |
| Build profiles | `default` and `production` with identical settings | Tests compile the bytecode that deploys. |

## Rules your contract should follow

- **Ownerless and immutable** — no admin keys, no upgrade proxies. A new version is a new deployment.
- **No hardcoded EVM limits** — read code-size and gas bounds from the chain profile, never bake `24576` or a gas cap
  into a contract.
- **Few storage writes.** Glamsterdam's EIP-8038 makes slot writes the expensive part (a cold first write goes from
  ~5,000 to ~12,100 gas); reads barely change.
- **Keys and RPC URLs only through Hardhat config variables** (`configVariable("SEPOLIA_PRIVATE_KEY")`). On a testnet,
  supply them with `read -rs VAR && export VAR` so they never reach shell history. On mainnet use a keystore or a
  hardware wallet.

## Two Hardhat limits to know

- **Hardhat's simulated network prices storage pre-Glamsterdam.** Sepolia now charges ~7× more per stored byte (see
  **[Size Limits & Glamsterdam](../concepts/size-limits-and-glamsterdam.md)**). Measure on the target chain.
- **Hardhat's local signer refuses any transaction above 30,000,000 gas** ("fields had validation errors"). After
  Glamsterdam a single Sepolia transaction storing more than ~19 KB exceeds it. Keep such transactions under it, or
  sign them with viem directly.

## The Zod advisory from `npm audit`

`npm audit` reports a **high-severity Zod advisory** (GHSA-mrhx-c7xc-m3w5, Zod below 4.6.6: memory exhaustion while
validating oversized input). It reaches the project through Hardhat 3, which depends on Zod 3, and no patched Zod 3
exists. It is a known, accepted, **dev-only** finding:

- **Don't run `npm audit fix --force`.** It "fixes" it by downgrading to Hardhat 2, which breaks a Hardhat 3 project.
- **Don't override Zod to 4.** It silences the warning, but Hardhat's config validation is written for Zod 3 and
  crashes on a bad config instead of naming the setting.
- **Why it's low risk:** Zod only validates your own `hardhat.config.ts` on your own machine; it never sees network
  input and is not shipped. Confirm with `npm audit --omit=dev` (production dependencies only — Zod should not appear).
- **When it goes away:** when Hardhat moves to Zod ≥ 4.6.6. `npm view hardhat dependencies.zod` shows what the latest
  release uses.

## A block to paste into an AI's instructions

```text
Build environment for ChainRender contracts:
- Hardhat 3 (^3.13) with @nomicfoundation/hardhat-toolbox-viem ^5 and viem ^2.56. Never Foundry.
- solc 0.8.36 from the npm `solc` package (pinned exactly).
- Contracts: `pragma solidity ^0.8.28;` (or exact 0.8.36), evmVersion "cancun".
- optimizer { enabled: true, runs: 200, details: { yul: false } }. viaIR OFF — never enable it.
- Same settings in both build profiles (default, production).
- Ownerless and immutable; no hardcoded EVM limits (read them from the chain profile); minimize storage writes.
- Keys and RPC URLs only via Hardhat configVariable (testnet: `read -rs VAR && export VAR`; mainnet: keystore/hardware wallet).
- Measure gas on the target chain, not Hardhat's simulated network. Hardhat's signer refuses txs above 30M gas.
- The npm audit Zod advisory (GHSA-mrhx-c7xc-m3w5) is a known dev-only finding via Hardhat 3: do NOT run
  `npm audit fix --force` (downgrades to Hardhat 2) and do NOT override zod to 4 (breaks Hardhat config validation).
```

## Related
- **[Writing a Resolver](./writing-a-resolver.md)** — the resolver interface your contract implements.
- **[Building with AI](./building-with-ai.md)** · **[Instructing an AI](../reference/instructing-ai.md)**
