|
2 | 2 |
|
3 | 3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
4 | 4 |
|
5 | | -## What This Is |
| 5 | +## Project Overview |
6 | 6 |
|
7 | | -Aztec Starter — a template repo for learning Aztec smart contract development. Contains a **Pod Racing** game contract (Noir) with TypeScript scripts and tests. The contract is a two-player competitive game using private state for commit-reveal mechanics. |
| 7 | +Aztec Starter — a Pod Racing game contract built with Noir on the Aztec network. Two players allocate points across 5 tracks over 3 rounds with private state; scores are revealed at the end (commit-reveal pattern). The player who wins more tracks (best of 5) wins. |
8 | 8 |
|
9 | | -**Aztec version pinned:** `4.0.0-devnet.2-patch.1` (check `Nargo.toml` and `package.json` for source of truth). |
| 9 | +**Aztec version: `4.0.0-devnet.2-patch.1`** — pinned across `Nargo.toml`, `package.json`, `config/*.json`, and README. All must stay in sync when updating. |
10 | 10 |
|
11 | | -## Commands |
| 11 | +## Build & Development Commands |
12 | 12 |
|
13 | | -### Build |
| 13 | +Always use `yarn compile` or `aztec compile` to compile contracts, never `nargo compile`. |
14 | 14 |
|
15 | 15 | ```bash |
16 | | -yarn compile # Compile Noir contract (runs `aztec compile`) -> ./target/ |
17 | | -yarn codegen # Generate TypeScript bindings -> ./src/artifacts/PodRacing.ts |
18 | | -yarn compile && yarn codegen # Full rebuild (always run both after contract changes) |
| 16 | +yarn compile # Compile Noir contract (aztec compile) |
| 17 | +yarn codegen # Generate TS artifacts from compiled contract |
| 18 | +yarn compile && yarn codegen # Full rebuild (always run both together) |
| 19 | +yarn clean # Remove compiled artifacts (src/artifacts, target) |
| 20 | +yarn clear-store # Remove PXE data store (rm -rf ./store) |
19 | 21 | ``` |
20 | 22 |
|
21 | | -### Test |
| 23 | +## Testing |
| 24 | + |
| 25 | +Two independent test systems: |
22 | 26 |
|
23 | 27 | ```bash |
24 | | -yarn test # Run ALL tests (Noir unit + TypeScript E2E) |
25 | | -yarn test:nr # Noir unit tests only (no network needed, uses TXE) |
26 | | -yarn test:js # TypeScript E2E tests only (requires local network running) |
| 28 | +yarn test:nr # Noir TXE tests — no network needed, runs in simulator |
| 29 | +yarn test:js # TypeScript E2E tests — requires running local network |
| 30 | +yarn test # Runs both (test:js then test:nr) |
27 | 31 | ``` |
28 | 32 |
|
29 | | -The `test:js` command clears `store/pxe` before running, uses Jest with ESM (`--experimental-vm-modules`), and has a 600-second test timeout. Tests run sequentially (`--runInBand`). |
30 | | - |
31 | | -### Local Network (required for `test:js`, scripts, and deploy) |
| 33 | +**E2E tests require a running local network:** |
32 | 34 |
|
33 | 35 | ```bash |
34 | | -aztec start --local-network # Start Aztec node + PXE + Anvil L1 |
35 | | -rm -rf ./store # MUST delete after restarting local network |
| 36 | +aztec start --local-network # Start local network first |
| 37 | +rm -rf ./store # Always clear store after network restart |
36 | 38 | ``` |
37 | 39 |
|
38 | | -### Scripts (all use `node --loader ts-node/esm`) |
| 40 | +Jest config: `jest.integration.config.json` — 600s test timeout, ESM mode via `ts-jest`, matches `./src/**/*.test.ts`. |
| 41 | + |
| 42 | +Run a single E2E test: |
39 | 43 |
|
40 | 44 | ```bash |
41 | | -yarn deploy # Deploy account + Pod Racing contract |
42 | | -yarn deploy-account # Deploy a Schnorr account only |
43 | | -yarn interaction-existing-contract # Interact with deployed contract (needs .env vars) |
44 | | -yarn multiple-wallet # Multi-PXE demo |
45 | | -yarn fees # Fee payment methods demo |
46 | | -yarn profile # Transaction profiling |
47 | | -yarn get-block # Query block data |
| 45 | +NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --no-cache --runInBand --config jest.integration.config.json -t "test name pattern" |
48 | 46 | ``` |
49 | 47 |
|
50 | | -### Devnet |
| 48 | +## Deployment & Scripts |
51 | 49 |
|
52 | | -Append `::devnet` to any script to target devnet (sets `AZTEC_ENV=devnet`): |
| 50 | +All scripts support `::devnet` suffix for devnet targeting (sets `AZTEC_ENV=devnet`): |
53 | 51 |
|
54 | 52 | ```bash |
55 | | -yarn deploy::devnet |
56 | | -yarn test::devnet |
| 53 | +yarn deploy # Deploy contract to local network |
| 54 | +yarn deploy::devnet # Deploy contract to devnet |
| 55 | +yarn deploy-account # Deploy a Schnorr account |
| 56 | +yarn multiple-wallet # Deploy from one wallet, interact from another |
| 57 | +yarn profile # Profile a transaction deployment |
| 58 | +yarn read-logs # Demo utility function for client-side debug logging |
| 59 | +yarn read-logs::devnet # Same on devnet |
57 | 60 | ``` |
58 | 61 |
|
59 | | -### Clean |
| 62 | +## Environment Configuration |
60 | 63 |
|
61 | | -```bash |
62 | | -yarn clean # Delete ./src/artifacts and ./target |
63 | | -yarn clear-store # Delete ./store (PXE data) |
64 | | -``` |
| 64 | +- `AZTEC_ENV` variable selects config: `local-network` (default) or `devnet` |
| 65 | +- Config files: `config/local-network.json`, `config/devnet.json` |
| 66 | +- `config/config.ts` — singleton `ConfigManager` loads the appropriate JSON based on `AZTEC_ENV` |
| 67 | +- `.env` stores secrets (SECRET, SIGNING_KEY, SALT, contract keys) — never commit |
65 | 68 |
|
66 | | -## Architecture |
| 69 | +## Project Structure |
67 | 70 |
|
68 | | -### Two Languages, One Contract |
| 71 | +**Contract (Noir):** |
69 | 72 |
|
70 | | -- **Noir** (`.nr` files in `src/`) — the smart contract, compiled to ZK circuits |
71 | | -- **TypeScript** (`.ts` files in `scripts/`, `src/test/e2e/`, `src/utils/`) — deployment scripts, E2E tests, and utilities |
| 73 | +- `src/main.nr` — PodRacing contract: public functions (`create_game`, `join_game`, `finalize_game`), private functions (`play_round`, `finish_game`), and internal `#[only_self]` validators |
| 74 | +- `src/race.nr` — `Race` struct: public game state (players, round counters, track scores, end_block) |
| 75 | +- `src/game_round_note.nr` — `GameRoundNote`: private note storing one round's point allocation per player |
72 | 76 |
|
73 | | -### Contract Structure (`src/`) |
| 77 | +**Tests (Noir TXE):** |
74 | 78 |
|
75 | | -- `main.nr` — Pod Racing contract entry point. Contains all public/private functions. Imports `mod test`, `mod game_round_note`, `mod race`. |
76 | | -- `race.nr` — `Race` struct with public game state (players, rounds, track scores, expiration). Key methods: `new()`, `join()`, `increment_player_round()`, `set_player_scores()`, `calculate_winner()`. |
77 | | -- `game_round_note.nr` — `GameRoundNote` private note storing one round's point allocation. The `#[note]` macro makes it a private state primitive. |
| 79 | +- `src/test/mod.nr` — test module root |
| 80 | +- `src/test/pod_racing.nr` — unit tests using TXE simulator (`env.call_public`, `env.call_private`, `env.public_context_at`) |
| 81 | +- `src/test/utils.nr` — test setup helper |
| 82 | +- `src/test/helpers.nr` — test helpers (game setup, allocation strategies) |
78 | 83 |
|
79 | | -### Key Aztec Pattern: Private-to-Public Flow |
| 84 | +**Tests (TypeScript E2E):** |
80 | 85 |
|
81 | | -The contract demonstrates the core Aztec pattern where private functions enqueue public calls: |
| 86 | +- `src/test/e2e/index.test.ts` — full game lifecycle tests against real network |
| 87 | +- `src/test/e2e/accounts.test.ts` — account tests |
| 88 | +- `src/test/e2e/public_logging.test.ts` — logging tests |
82 | 89 |
|
83 | | -1. `play_round` (private) — creates encrypted `GameRoundNote`, then enqueues `validate_and_play_round` (public) to update round counter |
84 | | -2. `finish_game` (private) — reads player's private notes, sums totals, then enqueues `validate_finish_game_and_reveal` (public) to publish scores |
| 90 | +**TypeScript utilities:** |
85 | 91 |
|
86 | | -Functions marked `#[only_self]` can only be called by the contract itself via `self.enqueue(...)`. |
| 92 | +- `src/utils/setup_wallet.ts` — creates `EmbeddedWallet` with environment-aware config (prover enabled on devnet) |
| 93 | +- `src/utils/create_account_from_env.ts` — Schnorr account from env vars |
| 94 | +- `src/utils/deploy_account.ts` — account deployment with sponsored fees |
| 95 | +- `src/utils/sponsored_fpc.ts` — SponsoredFPC (Fee Payment Contract) setup |
87 | 96 |
|
88 | | -### TypeScript Side |
| 97 | +**Generated (do not edit):** |
89 | 98 |
|
90 | | -- `config/config.ts` — Singleton `ConfigManager` that loads `config/local-network.json` or `config/devnet.json` based on `AZTEC_ENV`. Exports: `getAztecNodeUrl()`, `getTimeouts()`, `getEnv()`. |
91 | | -- `src/utils/setup_wallet.ts` — Creates `TestWallet` connected to the Aztec node. Enables prover on non-local environments. |
92 | | -- `src/utils/sponsored_fpc.ts` — Gets the canonical `SponsoredFPC` instance (salt=0) for sponsored fee payment. |
93 | | -- `src/utils/deploy_account.ts` — Deploys a Schnorr account with random keys using sponsored fees. |
94 | | -- `src/utils/create_account_from_env.ts` — Recreates an account from `SECRET`, `SIGNING_KEY`, `SALT` env vars. |
95 | | -- `src/artifacts/PodRacing.ts` — **Generated file, do not edit.** TypeScript bindings for the contract. |
| 99 | +- `src/artifacts/PodRacing.ts` — generated by `yarn codegen` |
| 100 | +- `target/` — compiled contract output |
96 | 101 |
|
97 | | -### Test Structure |
| 102 | +## Key Architectural Patterns |
98 | 103 |
|
99 | | -**Noir tests** (`src/test/`): Use TXE (Testing eXecution Environment), no network needed. |
| 104 | +- **ESM project**: `"type": "module"` in package.json. All TS scripts run via `node --loader ts-node/esm`. |
| 105 | +- **Private-public interaction**: `play_round` is private (creates `GameRoundNote` notes), then enqueues a public call (`validate_and_play_round`) to update round counters. `finish_game` reads private notes, sums them, and enqueues a public call to reveal totals. |
| 106 | +- **Fee payment**: All transactions use `SponsoredFeePaymentMethod` via the SponsoredFPC contract. |
| 107 | +- **Wallet setup**: `EmbeddedWallet.create()` with `ephemeral: true` for tests; prover is enabled only on devnet. |
| 108 | +- **PXE store**: Data persists in `./store`. Must delete after local network restart to avoid stale state errors. |
100 | 109 |
|
101 | | -- `utils.nr` — `setup()` deploys contract, returns `(TestEnvironment, contract_address, admin)` |
102 | | -- `helpers.nr` — Reusable allocation strategies and game setup helpers |
103 | | -- `pod_racing.nr` — Test cases. Uses `#[test]` and `#[test(should_fail)]` attributes. |
| 110 | +## Simulate Before Send (IMPORTANT) |
104 | 111 |
|
105 | | -**TypeScript E2E tests** (`src/test/e2e/`): Use Jest, require local network. |
| 112 | +**Always call `.simulate()` before `.send()` for every state-changing transaction.** Simulation runs the transaction locally and surfaces revert reasons immediately. Without it, a failing transaction will hang until the send timeout (up to 600s) with an opaque error. |
106 | 113 |
|
107 | | -- `index.test.ts` — Pod Racing game lifecycle tests |
108 | | -- `accounts.test.ts` — Account deployment and fee payment tests |
| 114 | +```typescript |
| 115 | +// Simulate first — surfaces revert reasons instantly |
| 116 | +await contract.methods.create_game(gameId).simulate({ from: address }); |
| 117 | + |
| 118 | +// Then send — only after simulation succeeds |
| 119 | +await contract.methods.create_game(gameId).send({ |
| 120 | + from: address, |
| 121 | + fee: { paymentMethod }, |
| 122 | + wait: { timeout: timeouts.txTimeout } |
| 123 | +}); |
| 124 | +``` |
| 125 | + |
| 126 | +For deployments, store the deploy request to avoid constructing it twice: |
| 127 | + |
| 128 | +```typescript |
| 129 | +const deployRequest = MyContract.deploy(wallet, ...args); |
| 130 | +await deployRequest.simulate({ from: address }); |
| 131 | +const contract = await deployRequest.send({ ... }); |
| 132 | +``` |
109 | 133 |
|
110 | | -### Environment Variables (`.env`) |
| 134 | +**Checklist:** |
111 | 135 |
|
112 | | -See `.env.example` for format. Key vars: |
| 136 | +- Every `.send()` call must be preceded by a `.simulate()` call |
| 137 | +- `.simulate()` does not need fee parameters — only `from` is required |
| 138 | +- View/read-only calls (e.g. `balance_of_private`) already use `.simulate()` to return values — no `.send()` needed for those |
113 | 139 |
|
114 | | -- `SECRET`, `SIGNING_KEY`, `SALT` — Account credentials |
115 | | -- `AZTEC_ENV` — `local-network` (default) or `devnet` |
116 | | -- `POD_RACING_CONTRACT_ADDRESS`, `CONTRACT_SALT`, `CONTRACT_DEPLOYER`, `CONTRACT_CONSTRUCTOR_ARGS` — For interacting with existing contracts |
| 140 | +## Version Update Procedure |
117 | 141 |
|
118 | | -## Important Conventions |
| 142 | +When updating the Aztec version, update all of these locations: |
119 | 143 |
|
120 | | -- **Node.js v22.15.0** required |
121 | | -- **Yarn 1.x** as package manager |
122 | | -- **ESM modules** — package.json has `"type": "module"`, tsconfig uses `NodeNext` module resolution |
123 | | -- **4-space indentation** in TypeScript |
124 | | -- **Do not commit** `src/artifacts/`, `target/`, `store/`, or `.env` |
125 | | -- After restarting the local network, always delete `./store` to avoid stale PXE data |
126 | | -- All transactions use `SponsoredFeePaymentMethod` for simplicity — register the FPC with `wallet.registerContract(sponsoredFPC, SponsoredFPCContract.artifact)` before use |
127 | | -- When modifying the contract, always run `yarn compile && yarn codegen` before testing TypeScript |
| 144 | +1. `Nargo.toml` — `aztec` dependency tag |
| 145 | +2. `package.json` — all `@aztec/*` dependency versions |
| 146 | +3. `config/local-network.json` and `config/devnet.json` — `settings.version` |
| 147 | +4. `README.md` — install command version |
128 | 148 |
|
129 | 149 | ## ONBOARDING.md Maintenance |
130 | 150 |
|
|
0 commit comments