Skip to content

Commit 06ee585

Browse files
critesjoshclaude
andcommitted
Merge origin/next into onboarding branch
Resolve CLAUDE.md conflict by taking the more comprehensive version from next and appending the ONBOARDING.md maintenance section from this branch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2 parents 6f38e8c + d9383ca commit 06ee585

16 files changed

Lines changed: 444 additions & 90 deletions

.github/workflows/devnet.yaml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Devnet Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- next
7+
- dev
8+
pull_request:
9+
branches:
10+
- next
11+
- dev
12+
workflow_dispatch:
13+
14+
jobs:
15+
devnet-deploy-account:
16+
name: Deploy Account to Devnet
17+
runs-on: ubuntu-latest
18+
env:
19+
AZTEC_ENV: devnet
20+
AZTEC_VERSION: 4.0.0-devnet.2-patch.1
21+
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@v5
25+
26+
- name: Set up Node.js
27+
uses: actions/setup-node@v4
28+
with:
29+
node-version: "22"
30+
cache: "yarn"
31+
32+
- name: Install project dependencies
33+
run: yarn
34+
35+
- name: Deploy account to devnet
36+
run: yarn deploy-account::devnet

AGENTS.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,29 @@ There are two independent test systems:
2424
- **TypeScript E2E tests** (`yarn test:js`): Require a running local network.
2525
- `yarn test` runs both. Ensure tests pass before committing.
2626

27+
## Simulate Before Send
28+
29+
**Always call `.simulate()` before `.send()` for every state-changing transaction.** Simulation runs the transaction locally and surfaces revert reasons immediately instead of waiting for the send timeout with an opaque error.
30+
31+
```typescript
32+
// Simulate first
33+
await contract.methods.create_game(gameId).simulate({ from: address });
34+
// Then send
35+
await contract.methods.create_game(gameId).send({
36+
from: address,
37+
fee: { paymentMethod },
38+
wait: { timeout }
39+
});
40+
```
41+
42+
For deployments, store the deploy request to reuse it:
43+
44+
```typescript
45+
const deployRequest = MyContract.deploy(wallet, ...args);
46+
await deployRequest.simulate({ from: address });
47+
const contract = await deployRequest.send({ ... });
48+
```
49+
2750
## Pull Requests
2851
- Use clear commit messages and provide a concise description in the PR body about the change.
2952
- Mention which tests were executed.

CLAUDE.md

Lines changed: 96 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2,129 +2,149 @@
22

33
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44

5-
## What This Is
5+
## Project Overview
66

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.
88

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.
1010

11-
## Commands
11+
## Build & Development Commands
1212

13-
### Build
13+
Always use `yarn compile` or `aztec compile` to compile contracts, never `nargo compile`.
1414

1515
```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)
1921
```
2022

21-
### Test
23+
## Testing
24+
25+
Two independent test systems:
2226

2327
```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)
2731
```
2832

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:**
3234

3335
```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
3638
```
3739

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:
3943

4044
```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"
4846
```
4947

50-
### Devnet
48+
## Deployment & Scripts
5149

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`):
5351

5452
```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
5760
```
5861

59-
### Clean
62+
## Environment Configuration
6063

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
6568

66-
## Architecture
69+
## Project Structure
6770

68-
### Two Languages, One Contract
71+
**Contract (Noir):**
6972

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
7276

73-
### Contract Structure (`src/`)
77+
**Tests (Noir TXE):**
7478

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)
7883

79-
### Key Aztec Pattern: Private-to-Public Flow
84+
**Tests (TypeScript E2E):**
8085

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
8289

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:**
8591

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
8796

88-
### TypeScript Side
97+
**Generated (do not edit):**
8998

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
96101

97-
### Test Structure
102+
## Key Architectural Patterns
98103

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.
100109

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)
104111

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.
106113

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+
```
109133

110-
### Environment Variables (`.env`)
134+
**Checklist:**
111135

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
113139

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
117141

118-
## Important Conventions
142+
When updating the Aztec version, update all of these locations:
119143

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
128148

129149
## ONBOARDING.md Maintenance
130150

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ You can find a handful of scripts in the `./scripts` folder.
178178
- `./scripts/profile_deploy.ts` shows how to profile a transaction and print the results.
179179
- `./scripts/interaction_existing_contract.ts` demonstrates how to interact with an already deployed pod racing contract, including creating games.
180180
- `./scripts/get_block.ts` is an example of how to retrieve and display block information from the Aztec node.
181+
- `./scripts/read_debug_logs.ts` demonstrates the `#[external("utility")]` pattern for client-side state inspection — runs a full game lifecycle, then calls the `debug_game_state` utility function to read public state and log it via `debug_log_format` without an on-chain transaction. Run with `yarn read-logs` (`LOG_LEVEL` is pre-configured by the yarn script).
181182

182183
### Utility Functions
183184

@@ -189,6 +190,30 @@ The `./src/utils/` folder contains utility functions:
189190
- `./src/utils/sponsored_fpc.ts` provides functions to deploy and manage the SponsoredFPC (Fee Payment Contract) for handling sponsored transaction fees.
190191
- `./config/config.ts` provides environment-aware configuration loading, automatically selecting the correct JSON config file based on the `ENV` variable.
191192

193+
## Simulate Before Send
194+
195+
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 with an opaque error.
196+
197+
```typescript
198+
// Simulate first — surfaces revert reasons instantly
199+
await contract.methods.create_game(gameId).simulate({ from: address });
200+
201+
// Then send — only after simulation succeeds
202+
await contract.methods.create_game(gameId).send({
203+
from: address,
204+
fee: { paymentMethod },
205+
wait: { timeout }
206+
});
207+
```
208+
209+
For deployments, store the deploy request to avoid constructing it twice:
210+
211+
```typescript
212+
const deployRequest = MyContract.deploy(wallet, ...args);
213+
await deployRequest.simulate({ from: address });
214+
const contract = await deployRequest.send({ ... });
215+
```
216+
192217
## **Error Resolution**
193218

194219
:warning: Tests and scripts set up and run the Private Execution Environment (PXE) and store PXE data in the `./store` directory. If you restart the local network, you will need to delete the `./store` directory to avoid errors.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
"get-block::devnet": "NODE_NO_WARNINGS=1 AZTEC_ENV=devnet node --loader ts-node/esm scripts/get_block.ts",
2727
"profile": "NODE_NO_WARNINGS=1 node --loader ts-node/esm scripts/profile_deploy.ts",
2828
"profile::devnet": "NODE_NO_WARNINGS=1 AZTEC_ENV=devnet node --loader ts-node/esm scripts/profile_deploy.ts",
29+
"read-logs": "NODE_NO_WARNINGS=1 LOG_LEVEL='info; debug:contract_log' node --loader ts-node/esm scripts/read_debug_logs.ts",
30+
"read-logs::devnet": "NODE_NO_WARNINGS=1 LOG_LEVEL='info; debug:contract_log' AZTEC_ENV=devnet node --loader ts-node/esm scripts/read_debug_logs.ts",
2931
"test": "yarn test:js && yarn test:nr",
3032
"test::devnet": "AZTEC_ENV=devnet yarn test:js",
3133
"test:js": "rm -rf store/pxe && NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --no-cache --runInBand --config jest.integration.config.json",

scripts/deploy_contract.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,13 @@ async function main() {
4040
logger.info('🏎️ Starting pod racing contract deployment...');
4141
logger.info(`📋 Admin address for pod racing contract: ${address}`);
4242

43-
logger.info('⏳ Waiting for deployment transaction to be mined...');
44-
const { contract: podRacingContract, instance } = await PodRacingContract.deploy(wallet, address).send({
43+
logger.info('⏳ Simulating deployment transaction...');
44+
const deployRequest = PodRacingContract.deploy(wallet, address);
45+
await deployRequest.simulate({
46+
from: address,
47+
});
48+
logger.info('✅ Simulation successful, sending transaction...');
49+
const { contract: podRacingContract, instance } = await deployRequest.send({
4550
from: address,
4651
fee: { paymentMethod: sponsoredPaymentMethod },
4752
wait: { timeout: timeouts.deployTimeout, returnReceipt: true }

0 commit comments

Comments
 (0)